📄 sftpc.cpp
字号:
} CMD("prot") { NO_ARG1 { cout << "current data protection level: " << getDSLString(dataSecLevel) << endl; } ARG1("p") { setDataEncryption(DSL_PRIVATE); } ARG1("c") { setDataEncryption(DSL_CLEAR); } ARG1("t") { // deliberately avoiding 's' because I botched its meaning earlier setDataEncryption(DSL_INTEGRITY); } ELSE_BAD_ARG1 } // -------- "ftp"-like protocol actions ------------ CMD("pwd") { checkedRequest(CMD_PWD); } CMD("cd") { if (tok >= 1) { checkedRequest(CMD_CWD, command + tok.offset(1)); // issues with spaces.. } else { checkedRequest(CMD_CWD); // cd to home directory } } CMD("cdup") { checkedRequest(CMD_CDUP); } CMD("dir") { dataTransfer(CMD_LIST, arg1, NULL); } CMD("nlist") { dataTransfer(CMD_NLST, arg1, NULL); } CMD("get") { REQUIRE_ARGS(1); dataTransfer(CMD_RETR, arg1, arg2? arg2 : arg1); } CMD("put") { REQUIRE_ARGS(1); if (arg2 != NULL) { dataTransfer(CMD_STOR, arg2 /*remote*/, arg1 /*local*/); } else { dataTransfer(CMD_STOR, arg1, arg1); } } CMD("mget") { REQUIRE_ARGS(1); mgetCommand(arg1); } CMD("mput") { REQUIRE_ARGS(1); mputCommand(arg1); } CMD("mls") { REQUIRE_ARGS(1); mlsCommand(arg1); } CMD("mdelete") { REQUIRE_ARGS(1); mdeleteCommand(arg1); } CMD("mkdir") { REQUIRE_ARGS(1); checkedRequest(CMD_MKD, arg1); } CMD("rmdir") { REQUIRE_ARGS(1); checkedRequest(CMD_RMD, arg1); } CMD("mv") { REQUIRE_ARGS(2); checkedRequest(CMD_RNFR, arg1); checkedRequest(CMD_RNTO, arg2); } CMD("abort") { checkedRequest(CMD_ABOR); } CMD("rm") { REQUIRE_ARGS(1); checkedRequest(CMD_DELE, arg1); } // -------------------- help ------------------------- CMD("help") { // detailed command documentation strings are now in sftpcdoc.cpp NO_ARG1 { cout << "sftpc is a command-line SafeTP client. SafeTP is File Transfer\n" "Protocol (FTP), with cryptographic authentication and privacy.\n" "More information: http://safetp.cs.berkeley.edu\n" "\n" "Here are some of the most common commands:\n"; printCommandList(true /*common*/); } ARG1("commands") { cout << "Common commands:\n"; printCommandList(true /*common*/); cout << "Less-common commands:\n"; printCommandList(false /*common*/); } ARG1("aliases") { cout << "Command Alias(es)\n" "---------------- ---------------------"; char const *prevExp = ""; for (int i=0; i<numAliases; i++) { char const *e = aliases[i].expansion; char const *a = aliases[i].alias; if (0==strcmp(prevExp, e)) { // add this alias to the current line cout << ", " << a; } else { // start a new line prevExp = e; cout << endl << e; for (int s = strlen(e); s < 20; s++) { cout << " "; // pad to aliases column } cout << a; } } cout << endl; } ARG1("crlf") { cout << "Unfortunately, one of the most annoying incompatibilities\n" "between unix and windows is the way text file lines are\n" "separated. unix separates lines with a single Line Feed (LF)\n" "character, whereas windows separates lines with an LF followed\n" "by a Carriage Return (CR) character.\n" "\n" "Thus, when transferring files between systems, it is important\n" "to (1) distinguish text files from binary files (the latter do\n" "not need translation, generally), and (2) tell the software which\n" "ones to translate. As far as this note goes, you're on your own\n" "for (1). (When in doubt, use binary, which is sftpc's default.)\n" "\n" "The FTP protocol handles this issue by specifying two transfer\n" "modes, \"ascii\" and \"image\" (binary). In image mode, files\n" "are transferred without translation. In ascii mode, files are\n" "treated as text, and are transferred across the network with\n" "CRLF separators, with each machine translating locally as necessary.\n" "\n" "To tell sftpc to transfer in ascii mode, use the \"ascii\"\n" "command. To transfer in image (binary) mode, use the \"binary\"\n" "command. Both commands affect all subsequent transfer operations,\n" "until another \"ascii\" or \"binary\" command is issued.\n" "\n" "sftpc, when working with ascii data, will report things\n" "that it finds suspicious. In particular, data that is supposed\n" "to be CRLF-separated, but contains LFs or CRs by themselves, is\n" "suspicious. Likewise, data that is supposed to be LF-separated,\n" "but has CRs (any at all) is suspicious. In both circumstances,\n" "the number of suspicious bytes are reported, and they are\n" "*removed* from the data stream.\n" ; } else { // check tables int alias = findAlias(tok[1]); int cmd = findCommand(tok[1]); // shouldn't be documented as both xassert(!( alias!=-1 && cmd!=-1 )); if (alias != -1) { // alias documentation Alias const &a = aliases[alias]; cout << a.alias << ": alias for \"" << a.expansion << "\"\n"; // show docs for expanded command userCommand(stringb("help " << a.expansion)); } else if (cmd != -1) { // command documentation Command const &c = commands[cmd]; cout << c.name << " " << c.args << endl << " " << c.blurb << endl; // per-argument descs { StrtokParse tok(c.arglines, "\n"); for (int t=0; t<tok; t++) { cout << " " << tok[t] << endl; } } // description of command as a whole { StrtokParse tok(c.desc, "\n"); for (int t=0; t<tok; t++) { cout << " " << tok[t] << endl; } } } else { // not found cout << "Unknown help topic: " << tok[1] << "\nTry \"help commands\"\n"; } } } // --------------- last case ---------------- else { // check aliases int i = findAlias(tok[0]); if (i != -1) { // expand the alias and recursively call this fn string expanded = aliases[i].expansion; if (tok > 1) { // append arguments; we only do this if there are arguments // because otherwise we have a trailing space, which some // ftpd's choke on expanded = stringb(aliases[i].expansion << " " << afterCmd); } userCommand(expanded); } else { // nothing matches cout << "Unknown command: " << tok[0] << "\nTry \"help\" or \"help commands\"\n"; } } // manual scoping (ug) #undef WORDnIS #undef CMDIS #undef CMD #undef NO_ARG1 #undef ARG1IS #undef ARG1 #undef SET_VAR #undef TOGGLE_VAR #undef TOGGLE_ON_OFF_VAR #undef REQUIRE_ARGS #undef ELSE_BAD_ARG1}void SFTPC::dumpVariables(){ PVAL(security); PVAL(securityName); PVAL(serverPort); PVAL(serverName); PVAL(control); //PVAL(dataBuffer); PVAL(PBSZ); //PVAL(digt); PVAL(sentFirstAuth); PVAL(authenticated); cout << "data channel protection: " << getDSLString(dataSecLevel) << endl; PVAL(requestedPBSZ); PVAL(transferPassively); PVAL(asciiTransfers); PVAL(localConventionIsCRLF); PVAL(printOutgoing); PVAL(interactivePrompting); PVAL(quitProgram); PVAL(printHashMarks); PVAL(useRfc959); PVAL(acceptNewKeysSilently); PVAL(auto959Dropdown); PVAL(showDiagnostics); PVAL(showAdats); PVAL(useDebuggingLogin); PVAL(quitAfterNegotiation); PVAL(binaryTransferAnyway); PVAL(localGlobbing);}int SFTPC::emptyResponseQueue(){ int ret=0; // since we hang if there isn't a reply, we'd better check while (control->hasUnprocessedData() || pollReadable(control->socket)) { // improve user-friendliness on server closure checkClosed(control->socket); // may throw xSocket // read reply Reply reply = readReply(); // DIGT and ADAT processing handleAdatAndDigt(reply); // since we don't know what this reply was for, // there isn't anything intelligent to do with // its reply code, so we'll just ignore it ret++; } return ret;}// interactive loop to login// returns false to tell caller to exit programbool SFTPC::login(){ if (useDebuggingLogin) { // just me testing at home // I should mention that this 'debug' login is *not* something // sftpd treats differently. Rather, it is the name of the // account I have defined on my debugging ftp daemons for this // purpose. sftpd is oblivious to this fact. checkedRequest(CMD_USER, "pepe"); checkedRequest(CMD_PASS, "pepe"); // clever password, eh? // if no exception thrown, we're in return true; } string defaultUsername = getCurrentUsername(); // loop until successful login for(;;) { // get username cout << "\nUser name (Enter = " << defaultUsername << ")? "; cout.flush(); string username; cin >> username; if (username == string("quit") || username == string("exit")) { // I sometimes do this, trying to exit 'ftp', and it thinks it's // a user name, and traps me in passwd prompt where ^C doesn't // work.. so let's just take the obvious hint and bail. return false; } if (username == string("")) { username = defaultUsername; } try { checkedRequest(CMD_USER, username); } catch (xReply &) { // this is unlikely, because servers don't typically confirm // whether a username is valid //cout << x.why() << endl; // we can always assume that a reply was already // printed, so there's no need to print it here continue; } // if the user name was accepted, let's change the // default (for this session) to what the user just // typed defaultUsername = username; // get password string password; if (username != string("ftp") && username != string("anonymous")) { // read password without echoing to terminal char const *prompt = isControlChannelEncrypted()? "Password: " : "Password (will be sent as cleartext!): "; password = readNonechoString(prompt); } else { // for anonymous login, echo password, since most // places want an email addr as a password cout << "Anonymous login; email addr? "; cout.flush(); cin >> password; } try { checkedRequest(CMD_PASS, password); } catch (xReply &) { //cout << x.why() << endl; continue; } // done return true; // don't exit program }} // returns false to mean the server (or the current mode) cannot// support the requested security level; other errors yield exceptionsbool SFTPC::setDataEncryption(DataSecurityLevel newLevel){ if (useRfc959) { cout << "sftpc: I am current in 959 (no encryption) mode, so\n" " data channel protection is unavailable.\n"; return false; } // if we're turning on data encryption for the first time, // negotiate a PBSZ if (!negotiatedPBSZ()) { // fix: was checking that newLevel != DSL_CLEAR, but 2228 // specifies that PBSZ must come first even for "PROT C" requestPBSZ(); } // figure out how to communicate the desire for this protection // level via the control channel char protCode; if (newLevel != DSL_CLEAR) { xassert(newLevel & security->data().getSupportedProtLevels()); protCode = security->data().getCodeForLevel(newLevel); } else { // cleartext is implemented in sftpc directly protCode = 'C'; } // do so try { checkedRequest(CMD_PROT, stringb(protCode)); } catch (xReply &) { cout << "The server does not support the requested data security level.\n"; return false; } // if it succeeded, we're in the new level dataSecLevel = newLevel; return true;}// for extracting things like "PBSZ=number"bool embeddedValue(int &value, char const *text, char const *prefix){ char const *p = strstr(text, prefix); if (p != NULL) { value = atoi(p+strlen(prefix)); return true; } else { return false; }}// general complaint about server's protocol behaviorvoid protocolError(char const *message){ // it would be nice to have an XProtocol or something
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -