📄 sftpd.cpp
字号:
// sftpd.cpp// secure ftp proxy daemon// copyright SafeTP Development Group, Inc., 2000 Terms of use are as specified in license.txt#include "str.h" // string#include <stdio.h> // printf#include <string.h> // strlen#include <stdlib.h> // atoi#include <ctype.h> // isdigit, isprint#include <signal.h> // signal#include <errno.h> // errno#include "socket.h" // socket funcs#include "sockutil.h" // utilities#include "exc.h" // exceptions#include "breaker.h" // breaker#include "missing.h" // missing_stricmp#include "nonport.h" // portableSleep#include "syserr.h" // xSysError#include "strtokp.h" // StrtokParse#include "reply.h" // Reply#include "request.h" // Request#include "datablok.h" // DataBlock#include "provider.h" // SecurityProvider#include "base64t.h" // Base64Transform#include "globrand.h" // {read,save}RandomSeed#include "keyutils.h" // sm_testKey#include "hcl.h" // inetdStartupHook#ifdef __UNIX__# include <syslog.h> // openlog, syslog#endif#include "sftpver.h" // SFTP_version#include "sftpd.h" // this module// defined in sdsa.cc (this introduces a dependency I don't// like, so I'm hesitant to simply include sdsa.h)void knownDSAPublicKeyVersions(int &minimum, int &maximum);// for now, 959 compatibility is critical for deployment#ifndef DEFAULT_ALLOW_RFC959# define DEFAULT_ALLOW_RFC959 1#endif// it probably doesn't matter whether nonreflexive is// allowed or not; anyway it seems unlikely it could be made// to work on the client side(s), because of the necessary// shared security information#ifndef DEFAULT_ALLOW_NONREFLEXIVE_PORT# define DEFAULT_ALLOW_NONREFLEXIVE_PORT 1#endif// it seems like a good idea to not let people use this// service to, e.g., send mail via SMTP...#ifndef DEFAULT_ALLOW_RESERVED_PORT# define DEFAULT_ALLOW_RESERVED_PORT 0#endif// allow diagnostic messages to be compiled away, so even the cost// of producing the log message in memory is eliminated#ifndef ALLOW_DIAGNOSTIC_LOGS# define ALLOW_DIAGNOSTIC_LOGS 1#endif// convenient forms for calling the log function#define log(event, expr) writeToLog(LL_LOG, event, stringb(expr))#if ALLOW_DIAGNOSTIC_LOGS# define diagnostic(expr) \ writeToLog(LL_DIAGNOSTIC, LE_DEBUG, stringb(expr))# define socket_diagnostic(expr) \ writeToLog(LL_SOCKET_DIAG, LE_DEBUG, stringb(expr))# define diagnosticNoLF(expr) \ writeToLog((LogLevel)(LL_DIAGNOSTIC | LL_NO_LF), LE_DEBUG, stringb(expr))#else# define diagnostic(expr) ((void)0)# define socket_diagnostic(expr) ((void)0)# define diagnosticNoLF(expr) ((void)0)#endif// for debugging (see below)bool attachTrigger;void waitForAttach(){ // wait for debugger to attach and set trigger attachTrigger = false; while (!attachTrigger) { portableSleep(1); breaker(); // so we just set a bkpoint at usual spot to catch this }}// --------------------- general stuff -----------------------------// this is used by the warning handler, and obviously its presence// means it isn't thread-safe to make multiple instances of SFTPD...SFTPD *SFTPD::instance = NULL;int main(int argc, char *argv[]){ // having troubles... //waitForAttach(); // immediately hand off to an SFTPD function so we are // in its scope SFTPD sftpd; sftpd.run(argc, argv); breaker(); // breakpoint return 0;}void SFTPD::run(int argc, char *argv[]){ try {# ifdef __UNIX__ // setup syslog logging openlog("sftpd", LOG_PID, LOG_DAEMON);# endif // capture 'warn' module messages warningHandler = SFTPD::warnHandler; // setup nonport error handling nonportFail = xSysError::xsyserror; // socket api initialization socket_lib_init();# ifdef SIGPIPE // ignore broken-pipe signals signal(SIGPIPE, SIG_IGN);# endif // run the main code innerRun(argc, argv); } catch (xBase &x) { breaker(); // breakpoint log(LE_ERROR, "Exception caught: " << x.why()); } diagnostic("sftpd exiting, " << numSocketsOpen() << " sockets leaked"); // we used to be saving the random seed here, but now we // use the RandomSeedSaver helper class}PortRange::PortRange() : restricted(false), low(0), high(0){}// there should be an entry here for *every* data member, even// if that member doesn't actually need an initializationSFTPD::SFTPD() : // ------ socket state ------ useStdinStream(false), client_control(INVALID_SOCKET), clientControlStream(NULL), server_control(INVALID_SOCKET), serverControlStream(NULL), client_data(INVALID_SOCKET), server_data(INVALID_SOCKET), client_listen(INVALID_SOCKET), server_listen(INVALID_SOCKET), // ------ protocol state ------ state(STATE_UNCONNECTED), relayClientAddress(INADDR_NONE), relayClientPort(0), serverPassivePort(NONPASV_PORT), digt(), // ------ data channel state ------ dataBuffer(), // not allocated maxBlockSize(0), maxCleartextBlockSize(0), forceDataRelay(false), dataSecLevel(DSL_CLEAR), // until we see PROT // ------ configuration options ------ allowRfc959(DEFAULT_ALLOW_RFC959), allowRfc959_anon(false), allowNonReflexivePorts(DEFAULT_ALLOW_NONREFLEXIVE_PORT), allowReservedPorts(DEFAULT_ALLOW_RESERVED_PORT), allowCleartext(false), requireDataEncryption(false), useFakeIPAddress(false), // for -i fakeIPAddress(0), // for -i pasvRange(), activeRange(), kerberosFtpdBinary((char*)NULL), anonExecDropdown(false), anonFtpdBinary((char*)NULL), printAdats(false), printDataChannel(false), artificialDelay(0), logLevelMask(LL_LOG), stdoutLogAnyway(false), logEventMask(LE_DEFAULTS), logFile(stderr), security(NULL){ instance = this;}SFTPD::~SFTPD(){ // attempt to close sockets (don't care about errors here) //try { closeSocket(client_control); } catch (...) {} //try { closeSocket(server_control); } catch (...) {} // update: don't bother because the main driver loop really // should handle it, and that way I don't close something twice // dealloc streams if (clientControlStream) { delete clientControlStream; } if (serverControlStream) { delete serverControlStream; } // dealloc security if (security) { delete security; } // close log file if (logFile != stderr) { fclose(logFile); } // prevent further access via 'instance' if (instance == this) { instance = NULL; }}// this is a convenient way to make sure the randomSeed gets// saved whenever it has been readSFTPD::RandomSeedSaver::~RandomSeedSaver(){ try { saveRandomSeed(); } catch (...) { sftpd->writeToLog(SFTPD::LL_LOG, LE_ERROR, "failed to write random seed"); }}// f-ing egcs-1.1.2 wants to piss on the floor about possibly// uninitalized variables if this code appears below, where it// should, so I'm (very disgruntledly) pulling it up herestatic unsigned long f_ing_strtoul(char const *str, int radix){ char *endptr; return strtoul(str, &endptr, radix);}void SFTPD::innerRun(int argc, char *argv[]){ // ----------- initial setup -------------- // see if getservbyname has a value (only used when not started by inetd) // this port and address are what get bound in to a listener socket int sftpdPort = getServByName("safetp"); IPAddress sftpdInterface = INADDR_ANY; // by default, bind all interfaces // the ftp port is tricky.. int ftpdPort = getServByName("raw-ftp"); IPAddress ftpdAddress = INADDR_ANY; // dummy value // by default forward to same interface the connection is received on bool ftpdForwardNonlocally = false; // use hardcoded values if getservbyname doesn't help if (sftpdPort == 0) { sftpdPort = SFTPD_PORT; } if (ftpdPort == 0) { ftpdPort = FTPD_PORT; } // in case we see an exception before we know where to log them xSysError *delayedError = NULL;# ifdef __WIN32__ SOCKET theStdin = INVALID_SOCKET; // e.h. scope workaround xSysError *firsterr = NULL; xSysError *seconderr = NULL; // the startup hook functions may return INVALID_SOCKET // if they can easily determine their inetd is not the spawning inetd // they may also throw an xSysError if there was an error contacting their inetd try { theStdin = inetdStartupHookWinetd(argc, argv); // try to load Winetd inetd } catch (xSysError &e) { firsterr = new xSysError(e); // throw it again later } if (theStdin == INVALID_SOCKET) { // failed to load winetd, try HCL try { theStdin = inetdStartupHookHCL(argc, argv); // try to load hummingbird inetd } catch (xSysError &e) { seconderr = new xSysError(e); // throw it again later } } if (theStdin != INVALID_SOCKET) { // we're using one of our inetd's so force using this socket useStdinStream = true; } else if (firsterr || seconderr) { // there was some error reported // we need to set a delayed error that includes the failed state from one or both inetd's // (because we're not certain which they're intending to use) string combinedreason = stringb(""); if (firsterr) { // winetd failed combinedreason = stringb(combinedreason << "Winetd: " << firsterr->why() << " "); delete firsterr; } if (seconderr) { // winetd failed combinedreason = stringb(combinedreason << "HCLInetd: " << seconderr->why() << " "); delete seconderr; } delayedError = new xSysError(xSysError::R_UNKNOWN, "sftpd failed to contact inetd software.", combinedreason); }# else SOCKET theStdin = (SOCKET)0; // unix stdin# endif // process command line { bool errors = false; for (int a=1; a<argc; a++) { bool oneError = false; if (argv[a][0] != '-') { oneError = true; } else { // convenience char const *argString = argv[a] + 2; // used letters: acdefhilmoprstvxyDKR389 switch (argv[a][1]) { case 'v': { // version int minv, maxv; knownDSAPublicKeyVersions(minv, maxv); printf("sftpd version %s\n" " Compiled on " __DATE__ " at " __TIME__ "\n" " Known DSA public key versions: min=%d, max=%d\n", SFTP_version, minv, maxv); return; } case '3': // force data relay forceDataRelay = true; break; case 'p': // listen port parseAddrAndPort(sftpdInterface, sftpdPort, argString); break; case 'f': // ftpd port parseAddrAndPort(ftpdAddress, ftpdPort, argString); if (ftpdAddress != INADDR_ANY) { // an address specified here is our cue to send the FTP // traffic to another host ftpdForwardNonlocally = true; // bugfix: was "false"! } break; case 'D': artificialDelay = atoi(argString); break; case 's': // stdin stream useStdinStream = true; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -