home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Networking / ncftp-2.4.2-MIHS / src / FTP.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-18  |  24.1 KB  |  970 lines

  1. /* FTP.c */
  2.  
  3. #include "Sys.h"
  4.  
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <arpa/inet.h>
  8. #include <arpa/telnet.h>
  9. #include <netdb.h>
  10. #include <errno.h>
  11. #ifdef HAVE_NET_ERRNO_H
  12. #    include <net/errno.h>
  13. #endif
  14. #include <setjmp.h>
  15. #include <ctype.h>
  16.  
  17. #include "Util.h"
  18. #include "FTP.h"
  19. #include "RCmd.h"
  20.  
  21. #ifdef HAVE_LIBTERMNET
  22. #    ifdef HAVE_TERMNET_H
  23. #        include <termnet.h>
  24. #    else
  25. #        ifdef HAVE_TERM_TERMNET_H
  26. #            include <term/termnet.h>
  27. #        endif
  28. #    endif
  29. #endif
  30.  
  31. #ifdef HAVE_LIBSOCKS5
  32. #    define SOCKS 5
  33. #    include <socks.h>
  34. #else
  35. #    ifdef HAVE_LIBSOCKS
  36. #        define accept        Raccept
  37. #        define connect        Rconnect
  38. #        define getsockname    Rgetsockname
  39. #        define listen        Rlisten
  40. #    endif
  41. #endif
  42.  
  43.  
  44. /* The control stream we read responses to commands from. */
  45. FILE *gControlIn = NULL;
  46.  
  47. /* The control stream we write our request to. */
  48. FILE *gControlOut = NULL;
  49.  
  50. /* The actual socket for the data streams. */
  51. int gDataSocket = kClosedFileDescriptor;
  52.  
  53. int gDataSocketAccepted = kClosedFileDescriptor;
  54.  
  55. /* Port number we're FTP'ing to. */
  56. unsigned int gFTPPort = kPortUnset;
  57.  
  58. /* Flag indicating whether we are connected to a remote host. */
  59. int gConnected = 0;
  60.  
  61. /* Method we would rather to specify where to send data to our local
  62.  * host, either Passive (PASV) or SendPort (PORT).  If you choose
  63.  * Passive FTP, you can attempt to use SendPort if the PASV command
  64.  * fails, since all FTP implementations must support PORT.
  65.  */
  66. #ifndef FTP_DATA_PORT_MODE
  67. int gPreferredDataPortMode = kSendPortMode;
  68. #else
  69. int gPreferredDataPortMode = FTP_DATA_PORT_MODE;
  70. #endif
  71.  
  72. /* Method we actually ended up using on the current data transfer,
  73.  * PORT or PASV.
  74.  */
  75. int gDataPortMode;
  76.  
  77. /* We keep track of whether we can even attempt to use Passive FTP.
  78.  * After we find out the remote server doesn't support it, we won't
  79.  * keep asking every time we want to do a transfer.
  80.  */
  81. int gHasPASV;
  82.  
  83. /* Need to special case if trying to read the startup message from the
  84.  * server, so our command handler won't jump to the wrong spot if
  85.  * the server hangs up at that point.
  86.  */
  87. int gReadingStartup = 0;
  88.  
  89. int gRemoteServerType;
  90.  
  91. /* Transfer mode.  We support only stream mode and block mode. */
  92. int gMode;
  93.  
  94. HangupProc gHangupProc;
  95.  
  96. /* Network addresses of the sockets we use. */
  97. struct sockaddr_in gServerCtrlAddr;
  98. struct sockaddr_in gServerDataAddr;
  99. struct sockaddr_in gOurClientCtrlAddr;
  100. struct sockaddr_in gOurClientDataAddr;
  101.  
  102. /* Real name (not alias) registered to the host we're connected to. */
  103. string gActualHostName;
  104.  
  105. /* Internet Protocol address of host we're connected to, as a string. */
  106. char gIPStr[32];
  107.  
  108. extern int gDoneApplication;
  109.  
  110. /*
  111.  * Some declare these with int's, others (hpux...) with size_t.
  112.  *
  113. extern int gethostname(char *, int), getdomainname(char *, int);
  114.  *
  115.  */
  116.  
  117. struct hostent *GetHostEntry(char *host, struct in_addr *ip_address);
  118.  
  119. void InitDefaultFTPPort(void)
  120. {
  121. #ifdef FTP_PORT
  122.     /* If FTP_PORT is defined, we use a different port number by default
  123.      * than the one supplied in the servent structure.
  124.      */
  125.     gFTPPort = FTP_PORT;
  126. #else
  127.     struct servent *ftp;    
  128.     
  129.     if ((ftp = getservbyname("ftp", "tcp")) == NULL)
  130.         gFTPPort = (unsigned int) kDefaultFTPPort;
  131.     else
  132.         gFTPPort = (unsigned int) ntohs(ftp->s_port);
  133. #endif    /* FTP_PORT */
  134. #ifdef HAVE_LIBSOCKS
  135.     SOCKSinit("ncftp");
  136. #endif
  137. }    /* InitDefaultFTPPort */
  138.  
  139.  
  140.  
  141.  
  142. void MyInetAddr(char *dst, size_t siz, char **src, int i)
  143. {
  144.     struct in_addr *ia;
  145.     char *cp;
  146.     
  147.     Strncpy(dst, "???", siz);
  148.     if (src != (char **) 0) {
  149.         ia = (struct in_addr *) src[i];
  150.         cp = inet_ntoa(*ia);
  151.         if ((cp != (char *) 0) && (cp != (char *) -1))
  152.             Strncpy(dst, cp, siz);
  153.     }
  154. }    /* MyInetAddr */
  155.  
  156.  
  157.  
  158.  
  159. /* On entry, you should have 'host' be set to a symbolic name (like
  160.  * cse.unl.edu), or set to a numeric address (like 129.93.3.1).
  161.  * If the function fails, it will return NULL, but if the host was
  162.  * a numeric style address, you'll have the ip_address to fall back on.
  163.  */
  164.  
  165. struct hostent *GetHostEntry(char *host, struct in_addr *ip_address)
  166. {
  167.     struct in_addr ip;
  168.     struct hostent *hp;
  169.     
  170.     /* See if the host was given in the dotted IP format, like "36.44.0.2."
  171.      * If it was, inet_addr will convert that to a 32-bit binary value;
  172.      * it not, inet_addr will return (-1L).
  173.      */
  174.     ip.s_addr = inet_addr(host);
  175.     if (ip.s_addr != INADDR_NONE) {
  176.         hp = gethostbyaddr((char *) &ip, (int) sizeof(ip), AF_INET);
  177.     } else {
  178.         /* No IP address, so it must be a hostname, like ftp.wustl.edu. */
  179.         hp = gethostbyname(host);
  180.         if (hp != NULL)
  181.             ip = * (struct in_addr *) hp->h_addr_list;
  182.     }
  183.     if (ip_address != NULL)
  184.         *ip_address = ip;
  185.     return (hp);
  186. }    /* GetHostEntry */
  187.  
  188.  
  189.  
  190.  
  191. /* Makes every effort to return a fully qualified domain name. */
  192. int GetOurHostName(char *host, size_t siz)
  193. {
  194. #ifdef HOSTNAME
  195.     /* You can hardcode in the name if this routine doesn't work
  196.      * the way you want it to.
  197.      */
  198.     Strncpy(host, HOSTNAME, siz);
  199. #else
  200.     struct hostent *hp;
  201.     int result;
  202.     char **curAlias;
  203.     char nodeName[64], domain[64], tmpdomain[64];
  204.     char *cp;
  205.     size_t nodeNameLen;
  206.  
  207.     host[0] = '\0';
  208.     result = gethostname(host, (int) siz);
  209.     if ((result < 0) || (host[0] == '\0')) {
  210. #if 0
  211.         (void) fprintf(stderr,
  212. "Could not determine the hostname. Re-compile with HOSTNAME defined\n\
  213. to be the full name of your hostname, i.e. -DHOSTNAME=\\\"cse.unl.edu\\\".\n");
  214.             Exit(kExitBadHostName);
  215. #endif
  216.         Strncpy(host, "hostname.unknown.com", siz);
  217.         return(6);
  218.     }
  219.  
  220.     if (strchr(host, '.') != NULL) {
  221.         /* gethostname returned full name (like "cse.unl.edu"), instead
  222.          * of just the node name (like "cse").
  223.          */
  224.         return (1);
  225.     }
  226.     
  227.     hp = GetHostEntry(host, NULL);
  228.     if (hp != NULL) {
  229.         /* Maybe the host entry has the full name. */
  230.         Strncpy(host, (char *) hp->h_name, siz);
  231.         if (strchr((char *) hp->h_name, '.') != NULL) {
  232.             /* The 'name' field for the host entry had full name. */
  233.             return (2);
  234.         }
  235.  
  236.         /* Now try the list of aliases, to see if any of those look real. */
  237.         STRNCPY(nodeName, host);
  238.         nodeNameLen = strlen(nodeName);
  239.         for (curAlias = hp->h_aliases; *curAlias != NULL; curAlias++) {
  240.             /* See if this name is longer than the node name;  we assume
  241.              * longer == more detailed.
  242.              */
  243.             if (strlen(*curAlias) > nodeNameLen) {
  244.                 /* We will use this one if it looks like this name is
  245.                  * a superset of the nodename;  so if it contains the
  246.                  * nodename, assume that this will work.
  247.                  */
  248.                 if (strstr(*curAlias, nodeName) != NULL)
  249.                     Strncpy(host, *curAlias, siz);
  250.             }
  251.         }
  252.         
  253.         /* See if the final thing we decided on in the host entry
  254.          * looks complete.
  255.          */
  256.         if (strchr(host, '.') != NULL)
  257.             return (3);
  258.     }
  259.  
  260.     /* Otherwise, we just have the node name.  See if we can get the
  261.      * domain name ourselves.
  262.      * 
  263.      * It'd be so much simpler if you would just define DOMAINNAME
  264.      * and get it over with!
  265.      */
  266. #ifdef DOMAINNAME
  267.     (void) STRNCPY(domain, DOMAINNAME);
  268. #else
  269.     domain[0] = '\0';
  270. #    ifdef HAVE_GETDOMAINNAME
  271.     /* getdomainname() returns just the domain name, without a
  272.      * preceding period.  For example, on "cse.unl.edu", it would
  273.      * return "unl.edu".
  274.      *
  275.      * SunOS note: getdomainname will return an empty string if
  276.      * this machine isn't on NIS.
  277.      */
  278.     if (getdomainname(domain, (int) sizeof(domain)) < 0)
  279.         DebugMsg("getdomainname failed.\n");
  280.     if (domain[0] == '\0')
  281.         DebugMsg("getdomainname did not return anything.\n");
  282.     else {
  283. /*
  284.  * local domain names
  285.  *
  286.  * These can now be determined from the domainname system call.
  287.  * The first component of the NIS domain name is stripped off unless
  288.  * it begins with a dot or a plus sign.
  289.  */
  290.         if (domain[0] == '+')
  291.             domain[0] = '.';
  292. #        ifdef NIS_GETDOMAINNAME
  293.         if (domain[0] != '.') {
  294.             /* Strip off first component. */
  295.             cp = strchr(domain, '.');
  296.             if (cp != NULL) {
  297.                 STRNCPY(tmpdomain, cp + 1);
  298.                 STRNCPY(domain, tmpdomain);
  299.             }
  300.         }
  301. #        else
  302. #        ifdef DNS_GETDOMAINNAME
  303.         /* Don't do anything, since it should have reutrned the
  304.          * whole domain we want.
  305.          */
  306. #        else
  307.         {
  308.             int dots;
  309.  
  310.             for (dots = 0, cp = domain; *cp; cp++)
  311.                 if (*cp == '.')
  312.                     ++dots;
  313.  
  314.             /* You didn't say if you were running NIS, so only assume a
  315.              * NIS domain if there are three components or more (i.e.
  316.              * getdomainname returned something like aaa.bbb.ccc), which
  317.              * would have two dots or more.  Otherwise, it would be an
  318.              * error to strip off "unl." from "unl.edu" if there were
  319.              * only two components returned.
  320.              */
  321.             if ((dots > 1) && (domain[0] != '.')) {
  322.                  /* Strip off first component. */
  323.                 cp = strchr(domain, '.');
  324.                 if (cp != NULL) {
  325.                     STRNCPY(tmpdomain, cp + 1);
  326.                     STRNCPY(domain, tmpdomain);
  327.                 }
  328.             }
  329.         }
  330. #        endif    /* DNS_GETDOMAINNAME */
  331. #        endif    /* NIS_GETDOMAINNAME */
  332.     }
  333. #    endif    /* HAVE_GETDOMAINNAME */
  334. #endif    /* DOMAINNAME */
  335.  
  336.     if (domain[0] != '\0') {
  337.         /* Supposedly, it's legal for getdomainname() to return one with
  338.          * a period at the end.
  339.          */
  340.         cp = domain + strlen(domain) - 1;
  341.         if (*cp == '.')
  342.             *cp = '\0';
  343.         if (domain[0] != '.')
  344.             (void) Strncat(host, ".", siz);
  345.         (void) Strncat(host, domain, siz);
  346.         return(4);
  347.     } else {
  348.         Strncpy(host, "hostname.unknown.com", siz);
  349. #if 0
  350.         fprintf(stderr,
  351. "WARNING: could not determine full host name (have: '%s').\n\
  352. The program should be re-compiled with DOMAINNAME defined to be the\n\
  353. domain name, i.e. -DDOMAINNAME=\\\"unl.edu\\\"\n\n",
  354.             host);
  355. #endif
  356.     }
  357.     return(5);
  358. #endif    /* !HOSTNAME */
  359. }    /* GetOurHostName */
  360.  
  361.  
  362.  
  363.  
  364. void CloseControlConnection(void)
  365. {
  366.     /* This will close each file, if it was open. */
  367.     if (CloseFile(&gControlIn))
  368.         DebugMsg("Closed control connection.\n");
  369.     CloseFile(&gControlOut);
  370.     gConnected = 0;
  371. }    /* CloseControlConnection */
  372.  
  373.  
  374.  
  375. static
  376. int GetSocketAddress(int sockfd, struct sockaddr_in *saddr)
  377. {
  378.     int len = (int) sizeof (struct sockaddr_in);
  379.     int result = 0;
  380.  
  381.     if (getsockname(sockfd, (struct sockaddr *)saddr, &len) < 0) {
  382.         Error(kDoPerror, "Could not get socket name.\n");
  383.         result = -1;
  384.     }
  385.     return (result);
  386. }    /* GetSocketAddress */
  387.  
  388.  
  389.  
  390.  
  391. void SetLinger(int sockfd)
  392. {
  393. #ifdef SO_LINGER
  394.     struct linger li;
  395.  
  396.     li.l_onoff = 1;
  397.     li.l_linger = 120;    /* 2 minutes, but system ignores field. */
  398.  
  399.     /* Have the system make an effort to deliver any unsent data,
  400.      * even after we close the connection.
  401.      */
  402.     if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &li, (int) sizeof(li)) < 0)
  403.         DebugMsg("Note: Linger mode could not be enabled.\n");
  404. #endif    /* SO_LINGER */
  405. }    /* SetLinger */
  406.  
  407.  
  408.  
  409.  
  410. void SetTypeOfService(int sockfd, int tosType)
  411. {
  412. #ifdef IP_TOS
  413.     /* Specify to the router what type of connection this is, so it
  414.      * can prioritize packets.
  415.      */
  416.     if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, (char *) &tosType, (int) sizeof(tosType)) < 0)
  417.         DebugMsg("Note: Type-of-service could not be set.\n");
  418. #endif    /* IP_TOS */
  419. }    /* SetTypeOfService */
  420.  
  421.  
  422.  
  423.  
  424. void SetInlineOutOfBandData(int sockfd)
  425. {
  426. #ifdef SO_OOBINLINE
  427.     int on = 1;
  428.  
  429.     if (setsockopt(sockfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, (int) sizeof(on)) < 0)
  430.         DebugMsg("Note: May not be able to handle out-of-band data.");
  431. #endif /* SO_OOBINLINE */
  432. }    /* SetInlineOutOfBandData */
  433.  
  434.  
  435.  
  436.  
  437. int OpenControlConnection(char *host, unsigned int port)
  438. {
  439.     struct in_addr ip_address;
  440.     int err = 0;
  441.     int result;
  442.     int sockfd = -1, sock2fd = -1;
  443.     ResponsePtr rp;
  444.     char **curaddr;
  445.     struct hostent *hp;
  446.  
  447.     /* Since we're the client, we just have to get a socket() and
  448.      * connect() it.
  449.      */
  450.     ZERO(gServerCtrlAddr);
  451.  
  452.     /* Default transfer mode is stream mode. */
  453.     gMode = 'S';
  454.  
  455.     /* Don't know what type of server it is yet. */
  456.     gRemoteServerType = kUnknownFTPd;
  457.  
  458.     /* Assume it's a fatal error, unless we say otherwise. */
  459.     result = kConnectErrFatal;
  460.  
  461.     /* Make sure we use network byte-order. */
  462.     port = (unsigned int) htons((unsigned short) port);
  463.  
  464.     gServerCtrlAddr.sin_port = port;
  465.  
  466.     hp = GetHostEntry(host, &ip_address);
  467.  
  468.     if (hp == NULL) {
  469.         /* Okay, no host entry, but maybe we have a numeric address
  470.          * in ip_address we can try.
  471.          */
  472.         if (ip_address.s_addr == INADDR_NONE) {
  473.             Error(kDontPerror, "%s: Unknown host.\n", host);
  474.             return (result);
  475.         }
  476.         gServerCtrlAddr.sin_family = AF_INET;
  477.         gServerCtrlAddr.sin_addr.s_addr = ip_address.s_addr;
  478.     } else {
  479.         gServerCtrlAddr.sin_family = hp->h_addrtype;
  480.         /* We'll fill in the rest of the structure below. */
  481.     }
  482.     
  483.     if ((sockfd = socket(gServerCtrlAddr.sin_family, SOCK_STREAM, 0)) < 0) {
  484.         Error(kDoPerror, "Could not get a socket.\n");
  485.         return (result);
  486.     }
  487.  
  488.     /* Okay, we have a socket, now try to connect it to a remote
  489.      * address.  If we didn't get a host entry, we will only have
  490.      * one thing to try (ip_address);  if we do have one, we can try
  491.      * every address in the list from the host entry.
  492.      */
  493.  
  494.     if (hp == NULL) {
  495.         /* Since we're given a single raw address, and not a host entry,
  496.          * we can only try this one address and not any other addresses
  497.          * that could be present for a site with a host entry.
  498.          */
  499.         err = connect(sockfd, (struct sockaddr *) &gServerCtrlAddr,
  500.                   (int) sizeof (gServerCtrlAddr));
  501.     } else {
  502.         /* We can try each address in the list.  We'll quit when we
  503.          * run out of addresses to try or get a successful connection.
  504.          */
  505.         for (curaddr = hp->h_addr_list; *curaddr != NULL; curaddr++) {
  506.             /* This could overwrite the address field in the structure,
  507.              * but this is okay because the structure has a junk field
  508.              * just for this purpose.
  509.              */
  510.             memcpy(&gServerCtrlAddr.sin_addr, *curaddr, (size_t) hp->h_length);
  511.             err = connect(sockfd, (struct sockaddr *) &gServerCtrlAddr,
  512.                       (int) sizeof (gServerCtrlAddr));
  513.             if (err == 0)
  514.                 break;
  515.         }
  516.     }
  517.     
  518.     if (err < 0) {
  519.         /* Could not connect.  Close up shop and go home. */
  520.  
  521.         /* If possible, tell the caller if they should bother
  522.          * calling back later.
  523.          */
  524.         switch (errno) {
  525.             case ENETDOWN:
  526.             case ENETUNREACH:
  527.             case ECONNABORTED:
  528.             case ETIMEDOUT:
  529.             case ECONNREFUSED:
  530.             case EHOSTDOWN:
  531.                 result = kConnectErrReTryable;
  532.                 /*FALLTHROUGH*/
  533.             default:
  534.                 Error(kDoPerror, "Could not connect to %s.\n", host);
  535.         }
  536.         goto fatal;
  537.     }
  538.  
  539.     /* Get our end of the socket address for later use. */
  540.     if (GetSocketAddress(sockfd, &gOurClientCtrlAddr) < 0)
  541.         goto fatal;
  542.  
  543.     /* We want Out-of-band data to appear in the regular stream,
  544.      * since we can handle TELNET.
  545.      */
  546.     SetInlineOutOfBandData(sockfd);
  547.     
  548. #if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
  549.     /* Control connection is somewhat interactive, so quick response
  550.      * is desired.
  551.      */
  552.     SetTypeOfService(sockfd, IPTOS_LOWDELAY);
  553. #endif
  554.  
  555.     if ((sock2fd = dup(sockfd)) < 0) {
  556.         Error(kDoPerror, "Could not duplicate a file descriptor.\n");
  557.         goto fatal;
  558.     }
  559.  
  560.     /* Now setup the FILE pointers for use with the Std I/O library
  561.      * routines.
  562.      */
  563.     if ((gControlIn = fdopen(sockfd, "r")) == NULL) {
  564.         Error(kDoPerror, "Could not fdopen.\n");
  565.         goto fatal;
  566.     }
  567.  
  568.     if ((gControlOut = fdopen(sock2fd, "w")) == NULL) {
  569.         Error(kDoPerror, "Could not fdopen.\n");
  570.         CloseFile(&gControlIn);
  571.         sockfd = kClosedFileDescriptor;
  572.         goto fatal;
  573.     }
  574.  
  575.     /* We'll be reading and writing lines, so use line buffering.  This
  576.      * is necessary since the stdio library will use full buffering
  577.      * for all streams not associated with the tty.
  578.      */
  579. #ifdef HAVE_SETLINEBUF
  580.     setlinebuf(gControlIn);
  581.     setlinebuf(gControlOut);
  582. #else
  583.     (void) SETVBUF(gControlIn, NULL, _IOLBF, (size_t) BUFSIZ);
  584.     (void) SETVBUF(gControlOut, NULL, _IOLBF, (size_t) BUFSIZ);
  585. #endif
  586.  
  587.     (void) STRNCPY(gIPStr, inet_ntoa(gServerCtrlAddr.sin_addr));
  588.     if ((hp == NULL) || (hp->h_name == NULL))
  589.         (void) STRNCPY(gActualHostName, host);
  590.     else
  591.         (void) STRNCPY(gActualHostName, (char *) hp->h_name);
  592.  
  593.     /* Read the startup message from the server. */    
  594.     rp = InitResponse();
  595.     gReadingStartup = 1;
  596.     if (GetResponse(rp) == 5) {
  597.         /* They probably hung up on us right away.  That's too bad,
  598.          * but we can tell the caller that they can call back later
  599.          * and try again.
  600.          */
  601.         gReadingStartup = 0;
  602.         result = kConnectErrReTryable;
  603.         if (CloseFile(&gControlIn))
  604.             DebugMsg("Closed control connection.\n");
  605.         CloseFile(&gControlOut);
  606.         sockfd = kClosedFileDescriptor;
  607.         sock2fd = kClosedFileDescriptor;
  608.         DoneWithResponse(rp);
  609.         goto fatal;
  610.     }
  611.     if ((rp != NULL) && (rp->msg.first != NULL)) {
  612.         if (strstr(rp->msg.first->line, "Version wu-") != NULL) {
  613.             gRemoteServerType = kWuFTPd;
  614.             DebugMsg("Remote server is running wu-ftpd.\n");
  615.         } else if (STRNEQ("NcFTPd", rp->msg.first->line, 6)) {
  616.             gRemoteServerType = kNcFTPd;
  617.             DebugMsg("Remote server is running NcFTPd!\n");
  618.         }
  619.     }
  620.     gReadingStartup = 0;
  621.     DoneWithResponse(rp);
  622.  
  623.     gHasPASV = 1;        /* Assume we have it, until proven otherwise. */
  624.     gConnected = 1;
  625.  
  626.     return (kConnectNoErr);
  627.     
  628. fatal:
  629.     if (sockfd > 0)
  630.         close(sockfd);
  631.     if (sock2fd > 0)
  632.         close(sock2fd);        
  633.     return (result);
  634. }    /* OpenControlConnection */
  635.  
  636.  
  637.  
  638.  
  639. void CloseDataConnection(int mustClose)
  640. {
  641.     /* Don't need to close if in block mode. */
  642.     if ((mustClose == 0) && (gMode == 'B')) {
  643.         DebugMsg("Leaving data connection open.\n");
  644.         return;
  645.     }
  646.     gDataSocketAccepted = kClosedFileDescriptor;
  647.     if (gDataSocket != kClosedFileDescriptor) {
  648.         close(gDataSocket);
  649.         gDataSocket = kClosedFileDescriptor;
  650.         DebugMsg("Closed data connection.\n");
  651.     } else {
  652.         DebugMsg("Data connection was already closed.\n");
  653.     }
  654. }    /* CloseDataConnection */
  655.  
  656.  
  657.  
  658.  
  659. int SetStartOffset(long restartPt)
  660. {
  661.     if (restartPt != SZ(0)) {
  662.         if (RCmd(kDefaultResponse, "REST %lu", (unsigned long) restartPt) == 3) {
  663.             DebugMsg("Starting at %lu bytes.\n", (unsigned long) restartPt);
  664.         } else {
  665.             DebugMsg("Could not start at %lu bytes.\n", (unsigned long) restartPt);
  666.             return (-1);
  667.         }
  668.     }
  669.     return (0);
  670. }    /* SetStartOffset */
  671.  
  672.  
  673.  
  674. static
  675. int SendPort(struct sockaddr_in *saddr)
  676. {
  677.     char *a, *p;
  678.     int result;
  679.     ResponsePtr rp;
  680.  
  681.     rp = InitResponse();
  682.  
  683.     /* These will point to data in network byte order. */
  684.     a = (char *) &saddr->sin_addr;
  685.     p = (char *) &saddr->sin_port;
  686. #define UC(x) (int) (((int) x) & 0xff)
  687.  
  688.     /* Need to tell the other side which host (the address) and
  689.      * which process (port) on that host to send data to.
  690.      */
  691.     RCmd(rp, "PORT %d,%d,%d,%d,%d,%d",
  692.         UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
  693.     
  694.     /* A 500'ish response code means the PORT command failed. */
  695.     result = ((rp->codeType == 5) ? (-1) : 0);
  696.     DoneWithResponse(rp);
  697.     return (result);
  698. }    /* SendPort */
  699.  
  700.  
  701.  
  702.  
  703. static
  704. int Passive(struct sockaddr_in *saddr, int *weird)
  705. {
  706.     ResponsePtr rp;
  707.     int i[6], j;
  708.     unsigned char n[6];
  709.     char *cp;
  710.     int result;
  711.  
  712.     result = -1;
  713.     rp = InitResponse();
  714.     RCmd(rp, "PASV");
  715.     if (rp->codeType != 2) {
  716.         /* Didn't understand or didn't want passive port selection. */
  717.         goto done;
  718.     }
  719.  
  720.     /* The other side returns a specification in the form of
  721.      * an internet address as the first four integers (each
  722.      * integer stands for 8-bits of the real 32-bit address),
  723.      * and two more integers for the port (16-bit port).
  724.      *
  725.      * It should give us something like:
  726.      * "Entering Passive Mode (129,93,33,1,10,187)", so look for
  727.      * digits with sscanf() starting 24 characters down the string.
  728.      */
  729.     for (cp = rp->msg.first->line; ; cp++) {
  730.         if (*cp == '\0') {
  731.             DebugMsg("Cannot parse PASV response: %s\n", rp->msg.first->line);
  732.             goto done;
  733.         }
  734.         if (isdigit(*cp))
  735.             break;
  736.     }
  737.  
  738.     if (sscanf(cp, "%d,%d,%d,%d,%d,%d",
  739.             &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6) {
  740.         DebugMsg("Cannot parse PASV response: %s\n", rp->msg.first->line);
  741.         goto done;
  742.     }
  743.  
  744.     for (j=0, *weird = 0; j<6; j++) {
  745.         /* Some ftp servers return bogus port octets, such as
  746.          * boombox.micro.umn.edu.  Let the caller know if we got a
  747.          * weird looking octet.
  748.          */
  749.         if ((i[j] < 0) || (i[j] > 255))
  750.             *weird = *weird + 1;
  751.         n[j] = (unsigned char) (i[j] & 0xff);
  752.     }
  753.  
  754.     memcpy(&saddr->sin_addr, &n[0], (size_t) 4);
  755.     memcpy(&saddr->sin_port, &n[4], (size_t) 2);
  756.  
  757.     result = 0;
  758. done:
  759.     DoneWithResponse(rp);
  760.     return (result);
  761. }    /* Passive */
  762.  
  763.  
  764.  
  765.  
  766. int OpenDataConnection(int mode)
  767. {
  768.     int dataSocket;
  769.     int weirdPort;
  770.  
  771.     /* Before we can transfer any data, and before we even ask the
  772.      * remote server to start transferring via RETR/NLST/etc, we have
  773.      * to setup the connection.
  774.      */
  775.     if ((gMode == 'B') && (gDataSocket != kClosedFileDescriptor)) {
  776.         /* Re-use existing connection. */
  777.         return (0);
  778.     }
  779. tryPort2:
  780.     dataSocket = socket(AF_INET, SOCK_STREAM, 0);
  781.     if (dataSocket < 0) {
  782.         Error(kDoPerror, "Could not get a data socket.\n");
  783.         return (-1);
  784.     }
  785.  
  786.     if ((gHasPASV == 0) || (mode == kSendPortMode)) {
  787. tryPort:
  788.         gOurClientDataAddr.sin_family = AF_INET;
  789.         gOurClientDataAddr = gOurClientCtrlAddr;
  790.         /* bind will assign us an unused port, typically between 1024-5000. */ 
  791.         gOurClientDataAddr.sin_port = 0;
  792.         
  793. #ifdef HAVE_LIBSOCKS
  794.         if (Rbind(dataSocket, (struct sockaddr *) &gOurClientDataAddr,
  795.             (int) sizeof (gOurClientDataAddr),
  796.             gServerCtrlAddr.sin_addr.s_addr) < 0) 
  797. #else
  798.         if (bind(dataSocket, (struct sockaddr *) &gOurClientDataAddr,
  799.             (int) sizeof (gOurClientDataAddr)) < 0) 
  800. #endif
  801.         {
  802.             Error(kDoPerror, "Could not bind the data socket.\n");
  803.             goto bad;
  804.         }
  805.     
  806.         /* Need to do this so we can figure out which port the system
  807.          * gave to us.
  808.          */
  809.         if (GetSocketAddress(dataSocket, &gOurClientDataAddr) < 0)
  810.             goto bad;
  811.     
  812.         if (listen(dataSocket, 1) < 0) {
  813.             Error(kDoPerror, "listen failed.\n");
  814.             goto bad;
  815.         }
  816.     
  817.         if (SendPort(&gOurClientDataAddr) < 0)
  818.             goto bad;
  819.     
  820.         gDataPortMode = kSendPortMode;
  821.     } else {
  822.         /* Passive mode.  Let the other side decide where to send. */
  823.         
  824.         gOurClientDataAddr.sin_family = AF_INET;
  825.         gOurClientDataAddr = gOurClientCtrlAddr;
  826.  
  827.         if ((!gHasPASV) || (Passive(&gOurClientDataAddr, &weirdPort) < 0)) {
  828.             Error(kDontPerror, "Passive mode refused.\n");
  829.             gHasPASV = 0;
  830.             
  831.             /* We can try using regular PORT commands, which are required
  832.              * by all FTP protocol compliant programs, if you said so.
  833.              *
  834.              * We don't do this automatically, because if your host
  835.              * is running a firewall you (probably) do not want SendPort
  836.              * FTP for security reasons.
  837.              */
  838.             if (mode == kFallBackToSendPortMode)
  839.                 goto tryPort;
  840.             goto bad;
  841.         }
  842.         if (connect(dataSocket, (struct sockaddr *) &gOurClientDataAddr,
  843.             (int) sizeof(gOurClientDataAddr)) < 0 ) {
  844.             if ((weirdPort > 0) && (errno == ECONNREFUSED)) {
  845.                 EPrintF("Server sent back a bogus port number.\nI will fall back to PORT instead of PASV mode.\n");
  846.                 if (mode == kFallBackToSendPortMode) {
  847.                     close(dataSocket);
  848.                     gHasPASV = 0;
  849.                     goto tryPort2;
  850.                 }
  851.                 goto bad;
  852.             }
  853.             Error(kDoPerror, "connect failed.\n");
  854.             goto bad;
  855.         }
  856.  
  857.         gDataPortMode = kPassiveMode;
  858.     }
  859.  
  860.     SetLinger(dataSocket);
  861.     
  862. #if defined(IP_TOS) && defined(IPTOS_THROUGHPUT)
  863.     /* Data connection is a non-interactive data stream, so
  864.      * high throughput is desired, at the expense of low
  865.      * response time.
  866.      */
  867.     SetTypeOfService(dataSocket, IPTOS_THROUGHPUT);
  868. #endif
  869.  
  870.     gDataSocket = dataSocket;
  871.     return (0);
  872. bad:
  873.     (void) close(dataSocket);
  874.     return (-1);
  875. }    /* OpenDataConnection */
  876.  
  877.  
  878.  
  879.  
  880. int AcceptDataConnection(void)
  881. {
  882.     int newSocket;
  883.     int len;
  884.  
  885.     if ((gMode == 'B') && (gDataSocketAccepted != kClosedFileDescriptor)) {
  886.         /* Re-use existing connection. */
  887.         return 0;
  888.     }
  889.  
  890.     /* If we did a PORT, we have some things to finish up.
  891.      * If we did a PASV, we're ready to go.
  892.      */
  893.     if (gDataPortMode == kSendPortMode) {
  894.         /* Accept will give us back the server's data address;  at the
  895.          * moment we don't do anything with it though.
  896.          */
  897.         len = (int) sizeof(gServerDataAddr);
  898.         newSocket = accept(gDataSocket, (struct sockaddr *) &gServerDataAddr, &len);
  899.     
  900.         (void) close(gDataSocket);
  901.         if (newSocket < 0) {
  902.             Error(kDoPerror, "Could not accept a data connection.\n");
  903.             gDataSocket = kClosedFileDescriptor;
  904.             gDataSocketAccepted = kClosedFileDescriptor;
  905.             return (-1);
  906.         }
  907.         
  908.         gDataSocket = newSocket;
  909.         gDataSocketAccepted = newSocket;
  910.     }
  911.  
  912.     return (0);
  913. }    /* AcceptDataConnection */
  914.  
  915.  
  916.  
  917. /* Kind of silly, but I wanted to keep this module as self-contained
  918.  * as possible.
  919.  */
  920. void SetPostHangupOnServerProc(HangupProc proc)
  921. {
  922.     gHangupProc = proc;
  923. }    /* SetPostHangupOnServerProc */
  924.  
  925.  
  926.  
  927.  
  928. void HangupOnServer(void)
  929. {
  930.     int wasConn;
  931.  
  932.     wasConn = gConnected;
  933.  
  934.     /* Since we want to close both sides of the connection for each
  935.      * socket, we can just have them closed with close() instead of
  936.      * using shutdown().
  937.      */
  938.     if (wasConn != 0) {
  939.             CloseControlConnection();
  940.             CloseDataConnection(1);
  941.             if (gHangupProc != (HangupProc)0)
  942.                 (*gHangupProc)();
  943.     }
  944. }    /* HangupOnServer */
  945.  
  946.  
  947.  
  948.  
  949. void SendTelnetInterrupt(void)
  950. {
  951.     unsigned char msg[4];
  952.  
  953.     /* 1. User system inserts the Telnet "Interrupt Process" (IP) signal
  954.      *    in the Telnet stream.
  955.      */
  956.     msg[0] = (unsigned char) IAC;
  957.     msg[1] = (unsigned char) IP;
  958.  
  959.     /* 2. User system sends the Telnet "Sync" signal. */
  960.     msg[2] = (unsigned char) IAC;
  961.     msg[3] = (unsigned char) DM;
  962.  
  963.     if (send(fileno(gControlOut), (char *) msg, 3, 0) != 3)
  964.         Error(kDoPerror, "Could not send a telnet interrupt(a).\n");
  965.     if (send(fileno(gControlOut), (char *) msg + 3, 1, MSG_OOB) != 1)
  966.         Error(kDoPerror, "Could not send a telnet interrupt(b).\n");
  967. }    /* SendTelnetInterrupt */
  968.  
  969. /* eof FTP.c */
  970.