⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sftpd.cpp

📁 伯克利做的SFTP安全文件传输协议
💻 CPP
📖 第 1 页 / 共 5 页
字号:
    SOCKET *active = NULL;    try {      // bugfix for my 226 nemesis: we might have a *complete*      // response buffered in a StreamLineReader, and if so, select()      // won't tell us      if (clientControlStream->hasUnprocessedData()) {        socket_diagnostic("Unprocessed data on client control channel");        // NOTE: this code is repeated below        active = &client_control;        handleClientRequest();        continue;      }      if (serverControlStream->hasUnprocessedData()) {        socket_diagnostic("Unprocessed data on server control channel");        // NOTE: this code is repeated below        active = &server_control;        handleServerReply();        continue;      }      // add all sockets we have; if any are INVALID_SOCKET, sockset      // will simply ignore them      SocketSet sockset;      sockset.add(client_control);      sockset.add(server_control);      sockset.add(client_data);      sockset.add(server_data);      if (sockset.numAdded() == 0) {        // all the sockets are INVALID_SOCKET -- there's no connection,        // so let's exit the process        break;      }                                         // add listeners after checking numAdded, because if all we have      // are listeners, then the connection is dead      sockset.add(client_listen);      sockset.add(server_listen);      socket_diagnostic(        "blocking on select"        ", cc=" << (int)client_control <<        ", sc=" << (int)server_control <<        ", cd=" << (int)client_data <<        ", sd=" << (int)server_data <<        ", cl=" << (int)client_listen <<        ", sl=" << (int)server_listen);      // debugging code that was here, which checked sockets      // for exception info, has been moved to end of file      // wait until there is activity somewhere      sockset.blockUntilReadable();      addEntropy();      // see which socket had activity (ignore case where more than one      // did, since the other socket activity will be handled      // on a future iteration); once one peer starts to talk, we      // ignore the other until the talking peer finishes (there      // isn't anything good to do if one starts but never finishes)      // --- client sends a request ---      if (sockset.contains(client_control)) {        socket_diagnostic("select(): activity on client_control");        // NOTE: this code is repeated above        active = &client_control;        handleClientRequest();      }      // --- server sends a reply ---      else if (sockset.contains(server_control)) {        socket_diagnostic("select(): activity on server_control");        // NOTE: this code is repeated above        active = &server_control;        handleServerReply();      }      // --- ftpd connects to me ---      // start of an encrypted non-PASV data transmission      else if (sockset.contains(server_listen)) {        socket_diagnostic("select(): activity on server_listen");        active = &server_listen;        // should not be in PASV mode        xassert(!pasvMode());        // connect to client        diagnostic("received connection from ftpd, trying to connect to client");        relayDataConnection(          server_listen, server_data,          relayClientAddress, relayClientPort,           client_data, &activeRange);      }      // --- client connects to me ---      // start of an encrypted PASV data transmission      else if (sockset.contains(client_listen)) {        socket_diagnostic("select(): activity on client_listen");        active = &client_listen;        // we should be in PASV mode for this        xassert(pasvMode());        // connect to server        diagnostic("received connection from client, trying to connect to ftpd");        relayDataConnection(          client_listen, client_data,          getRemoteAddress(server_control), serverPassivePort,          server_data, NULL /*port range is unrestricted*/);      }      // --- ftpd sends me data ---      // activity on the ftpd-side data channel: handling      // an encrypted data transmission, server -> client      // (PASV and non-PASV)      else if (sockset.contains(server_data)) {        socket_diagnostic("select(): activity on server_data");        active = &server_data;        //diagnostic("received data block from ftpd on local port " <<        //           getLocalPort(server_data));        if (dataChannelProtected()) {          encryptDataBlock();        }        else {          relaySocketData(client_data /*dest*/, server_data /*src*/);        }      }      // --- client sends me data ---      // activity on the client-side data channel: handling      // an encrypted data transmission, client -> server      // (PASV and non-PASV)      else if (sockset.contains(client_data)) {        socket_diagnostic("select(): activity on client_data");        active = &client_data;        //diagnostic("received data block from client on local port " <<        //           getLocalPort(client_data));        if (dataChannelProtected()) {          decryptDataBlock();        }        else {          relaySocketData(server_data /*dest*/, client_data /*src*/);        }      }      // --- select() screws up ---      else {        // I had been throwing an exception here, but recovery really        // is trivial in this case, and all I really need is a report.        log(LE_ERROR,            "SFTPD INTERNAL BUG: select() call returned but nothing "            "was set... unexpected but nonfatal");      }    }    // --- handle a socket closure (unexpected here) ---    catch (xSocket &x) {      // this is due to a socket closure      log(LE_ERROR, "socket " << sockInfo(x.socket) <<                    " closed: " << x.why());      if (*active == client_control ||          *active == server_control) {        // this should not be possible because handleClientRequest        // and handleServerReply should get these...        breaker();     // breakpoint if debugger attached        closeIf(client_control);        closeIf(server_control);        break;      }      if (*active == client_data ||          *active == server_data) {        // close both data channels        closeIf(client_data);        closeIf(server_data);        // we don't expect to detect data channel closures here,        // because the data-channel relay code is supposed to        // detect and handle them; so this is probably an unexpected        // network failure, and the client may want to know (and I        // hope this 'unprompted' reply doesn't screw up the        // client's internal state machine)        unexpected(x);      }    }    catch (xBase &x) {      // unknown internal error; tell client and log it      unexpected(x);    }  }  // success  return;}void SFTPD::handleClientRequest(){  try {    // here, and below, I'm giving myself the opportunity    // to do more graceful shutdown than via an exception    // (without the check, we learn of it from StreamLineReader    // throwing an exception of a general nature)    checkClosed(client_control);    // get request    diagnostic("got request from client, parsing...");    Request request(*clientControlStream);    diagnostic("request from client: " <<               request.getTextNoPassword());    try {      // handle it      handleOriginalRequest(request);    }    catch (xBase &x) {      unexpected(x);    }  }  catch (xBase &x) {    // client closed connection, do same to server    log(LE_CONTROL_CHANNEL, "client control connection closed: " <<                            x.why());            closeSocket(server_control);    closeSocket(client_control);  }}void SFTPD::handleServerReply(){  try {    // more-graceful shutdown thing    checkClosed(server_control);    // get reply    diagnostic("ftpd sent a reply, parsing it...");    Reply reply(*serverControlStream);    diagnosticNoLF("reply from ftpd: " << reply.getAllText());    try {      // handle it      handleReply(reply);    }    catch (xBase &x) {      unexpected(x);    }  }  catch (xBase &x) {    // server closed connection, do same to client    log(LE_CONTROL_CHANNEL, "server control connection closed: " <<                            x.why());    closeSocket(client_control);    closeSocket(server_control);  }}// called when an unexpected exception is caughtvoid SFTPD::unexpected(xBase &x){  // log it  log(LE_ERROR, "UNEXPECTED: " << x.why());  try {    // Q: is this a possible security hole?  i.e., could it be that    //    a client could cause an error such that the resulting    //    exception text would reveal private data (like a key)?    // tell client    clientReply(RC_INTERNAL_ERROR, x.why());  }  catch (xBase &x) {    log(LE_ERROR, "while relaying UNEXPECTED: " << x.why());  }}// We've just been contacted by a peer "A", and must establish contact with// the other peer, "B", then accept A's connection.  Either A is ftpd and// B is the client, or vice-versa.//   peerA_listen   - listen()ing socket on which we were just contacted//                    by peer "A"//   peerA_data     - socket for accepting A's connection////   addrB, portB   - where to connect 'peerB_data'//   peerB_data     - socket to connect to other peer, "B"//   range          - if non-NULL, constrain local ports we bindbool SFTPD::relayDataConnection(  SOCKET &peerA_listen, SOCKET &peerA_data,  IPAddress addrB, int portB, SOCKET &peerB_data,  PortRange const *range){  // for errors and diagnostics, we need to determine  // who is client and who is server  bool AisClient = (peerA_listen == client_listen);  char const *A = AisClient? "client" : "server";  char const *B = AisClient? "server" : "client";  // this has been a source of bugs: prevent this fn from  // using any of these names (must use peer{A,B}_{listen,data})  #define client_listen No!  #define server_listen No!  #define client_data No!  #define server_data No!  try {    // try to connect to B first, so if that fails,    // we can cause A's conn attempt to fail (this prevents    // 0-length files from being created in the case of a    // data command that fails this way)    // establish the data connection to B    diagnostic("trying to connect to " << B <<               " at " << formatAddress(addrB) <<               ", port " << portB);    peerB_data = connect_socket(addrB, portB);    addEntropy();  }  catch (xSocket &x) {    diagnostic("failed to connect to " << B << "'s data port");    // cause a conn-refused for A    closeSocket(peerA_listen);    // tell client about the problem    if (AisClient) {      // ftpd refused the conn.. that's strange      unexpected(x);    }    else {      // failed to connect to the client      clientReply(RC_FAILED_DATA_CONN_OPEN,        stringb("Failed to connect to " <<                formatAddress(addrB) << ", port " <<                portB << ": " << x.why()));    }    return false;  }  // accept the connection  peerA_data = accept_socket(peerA_listen);  diagnostic("accepted data connection from " << A);  //diagnosticSocketInfo(peerA_data);  // close the listener socket; we won't need it anymore  closeSocket(peerA_listen);  // re-enable use of these names  #undef client_listen  #undef server_listen  #undef client_data  #undef server_data  return true;}bool SFTPD::controlChannelEncrypted() const{  return    STATE_AUTHENTICATED <= state &&                           state <= STATE_GOT_PBSZ;}bool SFTPD::dataChannelProtected() const{  return dataSecLevel != DSL_CLEAR;}bool SFTPD::pasvMode() const{  return serverPassivePort != NONPASV_PORT;}bool SFTPD::amAuthenticating() const{  return state == STATE_UNAUTHENTICATED ||         state == STATE_ADAT;}bool SFTPD::doingDataRelay() const{  return dataChannelProtected() ||         forceDataRelay;}void SFTPD::writeToLog(LogLevel level, LoggableEvent event, char const *msg){  // check masks  if (!( (level & logLevelMask) && (event & logEventMask) )) {    return;  }  // decide whether to append a CR or not (LL_NO_LF should be specified  // when 'msg' already has a CR; it should *not* be used to try to string

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -