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