home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / proxy / proxy_ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  38.1 KB  |  1,254 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1996-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /* FTP routines for Apache proxy */
  59.  
  60. #include "mod_proxy.h"
  61. #include "http_main.h"
  62. #include "http_log.h"
  63.  
  64. #define AUTODETECT_PWD
  65.  
  66. DEF_Explain
  67.  
  68. /*
  69.  * Decodes a '%' escaped string, and returns the number of characters
  70.  */
  71. static int decodeenc(char *x)
  72. {
  73.     int i, j, ch;
  74.  
  75.     if (x[0] == '\0')
  76.     return 0;        /* special case for no characters */
  77.     for (i = 0, j = 0; x[i] != '\0'; i++, j++) {
  78. /* decode it if not already done */
  79.     ch = x[i];
  80.     if (ch == '%' && isxdigit(x[i + 1]) && isxdigit(x[i + 2])) {
  81.         ch = ap_proxy_hex2c(&x[i + 1]);
  82.         i += 2;
  83.     }
  84.     x[j] = ch;
  85.     }
  86.     x[j] = '\0';
  87.     return j;
  88. }
  89.  
  90. /*
  91.  * checks an encoded ftp string for bad characters, namely, CR, LF or
  92.  * non-ascii character
  93.  */
  94. static int ftp_check_string(const char *x)
  95. {
  96.     int i, ch;
  97.  
  98.     for (i = 0; x[i] != '\0'; i++) {
  99.     ch = x[i];
  100.     if (ch == '%' && isxdigit(x[i + 1]) && isxdigit(x[i + 2])) {
  101.         ch = ap_proxy_hex2c(&x[i + 1]);
  102.         i += 2;
  103.     }
  104. #ifndef CHARSET_EBCDIC
  105.     if (ch == '\015' || ch == '\012' || (ch & 0x80))
  106. #else /*CHARSET_EBCDIC*/
  107.     if (ch == '\r' || ch == '\n' || (os_toascii[ch] & 0x80))
  108. #endif /*CHARSET_EBCDIC*/
  109.         return 0;
  110.     }
  111.     return 1;
  112. }
  113.  
  114. /*
  115.  * Canonicalise ftp URLs.
  116.  */
  117. int ap_proxy_ftp_canon(request_rec *r, char *url)
  118. {
  119.     char *user, *password, *host, *path, *parms, *strp, sport[7];
  120.     pool *p = r->pool;
  121.     const char *err;
  122.     int port;
  123.  
  124.     port = DEFAULT_FTP_PORT;
  125.     err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port);
  126.     if (err)
  127.     return HTTP_BAD_REQUEST;
  128.     if (user != NULL && !ftp_check_string(user))
  129.     return HTTP_BAD_REQUEST;
  130.     if (password != NULL && !ftp_check_string(password))
  131.     return HTTP_BAD_REQUEST;
  132.  
  133. /* now parse path/parameters args, according to rfc1738 */
  134. /* N.B. if this isn't a true proxy request, then the URL path
  135.  * (but not query args) has already been decoded.
  136.  * This gives rise to the problem of a ; being decoded into the
  137.  * path.
  138.  */
  139.     strp = strchr(url, ';');
  140.     if (strp != NULL) {
  141.     *(strp++) = '\0';
  142.     parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, r->proxyreq);
  143.     if (parms == NULL)
  144.         return HTTP_BAD_REQUEST;
  145.     }
  146.     else
  147.     parms = "";
  148.  
  149.     path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq);
  150.     if (path == NULL)
  151.     return HTTP_BAD_REQUEST;
  152.     if (!ftp_check_string(path))
  153.     return HTTP_BAD_REQUEST;
  154.  
  155.     if (!r->proxyreq && r->args != NULL) {
  156.     if (strp != NULL) {
  157.         strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1);
  158.         if (strp == NULL)
  159.         return HTTP_BAD_REQUEST;
  160.         parms = ap_pstrcat(p, parms, "?", strp, NULL);
  161.     }
  162.     else {
  163.         strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1);
  164.         if (strp == NULL)
  165.         return HTTP_BAD_REQUEST;
  166.         path = ap_pstrcat(p, path, "?", strp, NULL);
  167.     }
  168.     r->args = NULL;
  169.     }
  170.  
  171. /* now, rebuild URL */
  172.  
  173.     if (port != DEFAULT_FTP_PORT)
  174.     ap_snprintf(sport, sizeof(sport), ":%d", port);
  175.     else
  176.     sport[0] = '\0';
  177.  
  178.     r->filename = ap_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "",
  179.                    (password != NULL) ? ":" : "",
  180.                    (password != NULL) ? password : "",
  181.                   (user != NULL) ? "@" : "", host, sport, "/", path,
  182.                    (parms[0] != '\0') ? ";" : "", parms, NULL);
  183.  
  184.     return OK;
  185. }
  186.  
  187. /*
  188.  * Returns the ftp status code;
  189.  *  or -1 on I/O error, 0 on data error
  190.  */
  191. static int ftp_getrc(BUFF *f)
  192. {
  193.     int len, status;
  194.     char linebuff[100], buff[5];
  195.  
  196.     len = ap_bgets(linebuff, sizeof linebuff, f);
  197.     if (len == -1)
  198.     return -1;
  199. /* check format */
  200.     if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
  201.     !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
  202.     status = 0;
  203.     else
  204.     status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
  205.  
  206.     if (linebuff[len - 1] != '\n') {
  207.     (void)ap_bskiplf(f);
  208.     }
  209.  
  210. /* skip continuation lines */
  211.     if (linebuff[3] == '-') {
  212.     memcpy(buff, linebuff, 3);
  213.     buff[3] = ' ';
  214.     do {
  215.         len = ap_bgets(linebuff, sizeof linebuff, f);
  216.         if (len == -1)
  217.         return -1;
  218.         if (linebuff[len - 1] != '\n') {
  219.         (void)ap_bskiplf(f);
  220.         }
  221.     } while (memcmp(linebuff, buff, 4) != 0);
  222.     }
  223.  
  224.     return status;
  225. }
  226.  
  227. /*
  228.  * Like ftp_getrc but returns both the ftp status code and 
  229.  * remembers the response message in the supplied buffer
  230.  */
  231. static int ftp_getrc_msg(BUFF *f, char *msgbuf, int msglen)
  232. {
  233.     int len, status;
  234.     char linebuff[100], buff[5];
  235.     char *mb = msgbuf,
  236.      *me = &msgbuf[msglen];
  237.  
  238.     len = ap_bgets(linebuff, sizeof linebuff, f);
  239.     if (len == -1)
  240.     return -1;
  241.     if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
  242.     !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
  243.     status = 0;
  244.     else
  245.     status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
  246.  
  247.     mb = ap_cpystrn(mb, linebuff+4, me - mb);
  248.  
  249.     if (linebuff[len - 1] != '\n')
  250.     (void)ap_bskiplf(f);
  251.  
  252.     if (linebuff[3] == '-') {
  253.     memcpy(buff, linebuff, 3);
  254.     buff[3] = ' ';
  255.     do {
  256.         len = ap_bgets(linebuff, sizeof linebuff, f);
  257.         if (len == -1)
  258.         return -1;
  259.         if (linebuff[len - 1] != '\n') {
  260.         (void)ap_bskiplf(f);
  261.         }
  262.         mb = ap_cpystrn(mb, linebuff+4, me - mb);
  263.     } while (memcmp(linebuff, buff, 4) != 0);
  264.     }
  265.     return status;
  266. }
  267.  
  268. static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *cwd)
  269. {
  270.     char buf[IOBUFSIZE];
  271.     char buf2[IOBUFSIZE];
  272.     char *filename;
  273.     int searchidx = 0;
  274.     char *searchptr = NULL;
  275.     int firstfile = 1;
  276.     unsigned long total_bytes_sent = 0;
  277.     register int n, o, w;
  278.     conn_rec *con = r->connection;
  279.     char *dir, *path, *reldir, *site;
  280.  
  281.     /* Save "scheme://site" prefix without password */
  282.     site = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO);
  283.     /* ... and path without query args */
  284.     path = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY);
  285.     (void)decodeenc(path);
  286.  
  287.     /* Copy path, strip (all except the last) trailing slashes */
  288.     path = dir = ap_pstrcat(r->pool, path, "/", NULL);
  289.     while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/')
  290.     path[n-1] = '\0';
  291.  
  292.     /* print "ftp://host/" */
  293.     n = ap_snprintf(buf, sizeof(buf), "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
  294.         "<HTML><HEAD><TITLE>%s%s</TITLE>\n"
  295.         "<BASE HREF=\"%s%s\"></HEAD>\n"
  296.         "<BODY><H2>Directory of "
  297.         "<A HREF=\"/\">%s</A>/",
  298.         site, path, site, path, site);
  299.     total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  300.  
  301.     while ((dir = strchr(dir+1, '/')) != NULL)
  302.     {
  303.     *dir = '\0';
  304.     if ((reldir = strrchr(path+1, '/'))==NULL)
  305.         reldir = path+1;
  306.     else
  307.         ++reldir;
  308.     /* print "path/" component */
  309.     ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir);
  310.     total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  311.     *dir = '/';
  312.     }
  313.     /* If the caller has determined the current directory, and it differs */
  314.     /* from what the client requested, then show the real name */
  315.     if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) {
  316.     ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>");
  317.     } else {
  318.     ap_snprintf(buf, sizeof(buf), "</H2>\n(%s)\n<HR><PRE>", cwd);
  319.     }
  320.     total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  321.  
  322.     while (!con->aborted) {
  323.     n = ap_bgets(buf, sizeof buf, f);
  324.     if (n == -1) {        /* input error */
  325.         if (c != NULL)
  326.         c = ap_proxy_cache_error(c);
  327.         break;
  328.     }
  329.     if (n == 0)
  330.         break;        /* EOF */
  331.     if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
  332.         char *link_ptr = filename;
  333.  
  334.         do {
  335.         filename--;
  336.         } while (filename[0] != ' ');
  337.         *(filename++) = '\0';
  338.         *(link_ptr++) = '\0';
  339.         if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
  340.           link_ptr[n - 1] = '\0';
  341.         ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s %s</A>\n", buf, filename, filename, link_ptr);
  342.         ap_cpystrn(buf, buf2, sizeof(buf));
  343.         n = strlen(buf);
  344.     }
  345.     else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
  346.         if (ap_isdigit(buf[0])) {    /* handle DOS dir */
  347.         searchptr = strchr(buf, '<');
  348.         if (searchptr != NULL)
  349.             *searchptr = '[';
  350.         searchptr = strchr(buf, '>');
  351.         if (searchptr != NULL)
  352.             *searchptr = ']';
  353.         }
  354.  
  355.         filename = strrchr(buf, ' ');
  356.         *(filename++) = 0;
  357.         filename[strlen(filename) - 1] = 0;
  358.  
  359.         /* handle filenames with spaces in 'em */
  360.         if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
  361.         firstfile = 0;
  362.         searchidx = filename - buf;
  363.         }
  364.         else if (searchidx != 0 && buf[searchidx] != 0) {
  365.         *(--filename) = ' ';
  366.         buf[searchidx - 1] = 0;
  367.         filename = &buf[searchidx];
  368.         }
  369.  
  370.         /* Special handling for '.' and '..' */
  371.         if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
  372.         ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s/\">%s</A>\n",
  373.             buf, filename, filename);
  374.         }
  375.         else {
  376.         ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\n", buf, filename, filename);
  377.         }
  378.         ap_cpystrn(buf, buf2, sizeof(buf));
  379.         n = strlen(buf);
  380.     }
  381.  
  382.     o = 0;
  383.     total_bytes_sent += n;
  384.  
  385.     if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n)
  386.         c = ap_proxy_cache_error(c);
  387.  
  388.     while (n && !r->connection->aborted) {
  389.         w = ap_bwrite(con->client, &buf[o], n);
  390.         if (w <= 0)
  391.         break;
  392.         ap_reset_timeout(r);    /* reset timeout after successfule write */
  393.         n -= w;
  394.         o += w;
  395.     }
  396.     }
  397.  
  398.     total_bytes_sent += ap_proxy_bputs2("</PRE><HR>\n", con->client, c);
  399.     total_bytes_sent += ap_proxy_bputs2(ap_psignature("", r), con->client, c);
  400.     total_bytes_sent += ap_proxy_bputs2("</BODY></HTML>\n", con->client, c);
  401.  
  402.     ap_bflush(con->client);
  403.  
  404.     return total_bytes_sent;
  405. }
  406.  
  407. /* Common routine for failed authorization (i.e., missing or wrong password)
  408.  * to an ftp service. This causes most browsers to retry the request
  409.  * with username and password (which was presumably queried from the user)
  410.  * supplied in the Authorization: header.
  411.  * Note that we "invent" a realm name which consists of the
  412.  * ftp://user@host part of the reqest (sans password -if supplied but invalid-)
  413.  */
  414. static int ftp_unauthorized (request_rec *r, int log_it)
  415. {
  416.     r->proxyreq = 0;
  417.     /* Log failed requests if they supplied a password
  418.      * (log username/password guessing attempts)
  419.      */
  420.     if (log_it)
  421.     ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
  422.               "proxy: missing or failed auth to %s",
  423.               ap_unparse_uri_components(r->pool,
  424.               &r->parsed_uri, UNP_OMITPATHINFO));
  425.  
  426.     ap_table_setn(r->err_headers_out, "WWW-Authenticate",
  427.                   ap_pstrcat(r->pool, "Basic realm=\"",
  428.           ap_unparse_uri_components(r->pool, &r->parsed_uri,
  429.                         UNP_OMITPASSWORD|UNP_OMITPATHINFO),
  430.           "\"", NULL));
  431.  
  432.     return HTTP_UNAUTHORIZED;
  433. }
  434.  
  435. /*
  436.  * Handles direct access of ftp:// URLs
  437.  * Original (Non-PASV) version from
  438.  * Troy Morrison <spiffnet@zoom.com>
  439.  * PASV added by Chuck
  440.  */
  441. int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
  442. {
  443.     char *host, *path, *strp, *parms;
  444.     char *cwd = NULL;
  445.     char *user = NULL;
  446. /*    char *account = NULL; how to supply an account in a URL? */
  447.     const char *password = NULL;
  448.     const char *err;
  449.     int port, i, j, len, sock, dsock, rc, nocache = 0;
  450.     int csd = 0;
  451.     struct sockaddr_in server;
  452.     struct hostent server_hp;
  453.     struct in_addr destaddr;
  454.     table *resp_hdrs;
  455.     BUFF *f;
  456.     BUFF *data = NULL;
  457.     pool *p = r->pool;
  458.     int one = 1;
  459.     const long int zero = 0L;
  460.     NET_SIZE_T clen;
  461.     struct tbl_do_args tdo;
  462.  
  463.     void *sconf = r->server->module_config;
  464.     proxy_server_conf *conf =
  465.     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  466.     struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
  467.     struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
  468.  
  469. /* stuff for PASV mode */
  470.     unsigned int presult, h0, h1, h2, h3, p0, p1;
  471.     unsigned int paddr;
  472.     unsigned short pport;
  473.     struct sockaddr_in data_addr;
  474.     int pasvmode = 0;
  475.     char pasv[64];
  476.     char *pstr;
  477.  
  478. /* stuff for responses */
  479.     char resp[MAX_STRING_LEN];
  480.     char *size = NULL;
  481.  
  482. /* we only support GET and HEAD */
  483.  
  484.     if (r->method_number != M_GET)
  485.     return HTTP_NOT_IMPLEMENTED;
  486.  
  487. /* We break the URL into host, port, path-search */
  488.  
  489.     host = r->parsed_uri.hostname;
  490.     port = (r->parsed_uri.port != 0)
  491.         ? r->parsed_uri.port
  492.         : ap_default_port_for_request(r);
  493.     path = ap_pstrdup(p, r->parsed_uri.path);
  494.     path = (path != NULL && path[0] != '\0') ? &path[1] : "";
  495.  
  496.     /* The "Authorization:" header must be checked first.
  497.      * We allow the user to "override" the URL-coded user [ & password ]
  498.      * in the Browsers' User&Password Dialog.
  499.      * NOTE that this is only marginally more secure than having the
  500.      * password travel in plain as part of the URL, because Basic Auth
  501.      * simply uuencodes the plain text password. 
  502.      * But chances are still smaller that the URL is logged regularly.
  503.      */
  504.     if ((password = ap_table_get(r->headers_in, "Authorization")) != NULL
  505.     && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0
  506.     && (password = ap_uudecode(r->pool, password))[0] != ':') {
  507.     /* Note that this allocation has to be made from r->connection->pool
  508.      * because it has the lifetime of the connection.  The other allocations
  509.      * are temporary and can be tossed away any time.
  510.      */
  511.     user = ap_getword_nulls (r->connection->pool, &password, ':');
  512.     r->connection->ap_auth_type = "Basic";
  513.     r->connection->user = r->parsed_uri.user = user;
  514.     nocache = 1;    /* This resource only accessible with username/password */
  515.     }
  516.     else if ((user = r->parsed_uri.user) != NULL) {
  517.     user = ap_pstrdup(p, user);
  518.     decodeenc(user);
  519.     if ((password = r->parsed_uri.password) != NULL) {
  520.         char *tmp = ap_pstrdup(p, password);
  521.         decodeenc(tmp);
  522.         password = tmp;
  523.     }
  524.     nocache = 1;    /* This resource only accessible with username/password */
  525.     }
  526.     else {
  527.     user = "anonymous";
  528.     password = "apache_proxy@";
  529.     }
  530.  
  531. /* check if ProxyBlock directive on this host */
  532.     destaddr.s_addr = ap_inet_addr(host);
  533.     for (i = 0; i < conf->noproxies->nelts; i++) {
  534.     if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
  535.         || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
  536.         return ap_proxyerror(r, /*HTTP_FORBIDDEN*/ "Connect to remote machine blocked");
  537.     }
  538.  
  539.     Explain2("FTP: connect to %s:%d", host, port);
  540.  
  541.     parms = strchr(path, ';');
  542.     if (parms != NULL)
  543.     *(parms++) = '\0';
  544.  
  545.     memset(&server, 0, sizeof(struct sockaddr_in));
  546.     server.sin_family = AF_INET;
  547.     server.sin_port = htons(port);
  548.     err = ap_proxy_host2addr(host, &server_hp);
  549.     if (err != NULL)
  550.     return ap_proxyerror(r, err);    /* give up */
  551.  
  552.     sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
  553.     if (sock == -1) {
  554.     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  555.              "proxy: error creating socket");
  556.     return HTTP_INTERNAL_SERVER_ERROR;
  557.     }
  558.  
  559.     if (conf->recv_buffer_size > 0
  560.     && setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
  561.                (const char *) &conf->recv_buffer_size, sizeof(int))
  562.         == -1) {
  563.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  564.              "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
  565.     }
  566.  
  567.     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
  568.            sizeof(one)) == -1) {
  569. #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
  570.     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  571.              "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)");
  572.     ap_pclosesocket(p, sock);
  573.     return HTTP_INTERNAL_SERVER_ERROR;
  574. #endif /*_OSD_POSIX*/
  575.     }
  576.  
  577. #ifdef SINIX_D_RESOLVER_BUG
  578.     {
  579.     struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
  580.  
  581.     for (; ip_addr->s_addr != 0; ++ip_addr) {
  582.         memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
  583.         i = ap_proxy_doconnect(sock, &server, r);
  584.         if (i == 0)
  585.         break;
  586.     }
  587.     }
  588. #else
  589.     j = 0;
  590.     while (server_hp.h_addr_list[j] != NULL) {
  591.     memcpy(&server.sin_addr, server_hp.h_addr_list[j],
  592.            sizeof(struct in_addr));
  593.     i = ap_proxy_doconnect(sock, &server, r);
  594.     if (i == 0)
  595.         break;
  596.     j++;
  597.     }
  598. #endif
  599.     if (i == -1) {
  600.     ap_pclosesocket(p, sock);
  601.     return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ ap_pstrcat(r->pool,
  602.                 "Could not connect to remote machine: ",
  603.                 strerror(errno), NULL));
  604.     }
  605.  
  606.     f = ap_bcreate(p, B_RDWR | B_SOCKET);
  607.     ap_bpushfd(f, sock, sock);
  608. /* shouldn't we implement telnet control options here? */
  609.  
  610. #ifdef CHARSET_EBCDIC
  611.     ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
  612. #endif /*CHARSET_EBCDIC*/
  613.  
  614. /* possible results: */
  615.     /* 120 Service ready in nnn minutes. */
  616.     /* 220 Service ready for new user. */
  617.     /* 421 Service not available, closing control connection. */
  618.     ap_hard_timeout("proxy ftp", r);
  619.     i = ftp_getrc_msg(f, resp, sizeof resp);
  620.     Explain1("FTP: returned status %d", i);
  621.     if (i == -1) {
  622.     ap_kill_timeout(r);
  623.     return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  624.     }
  625. #if 0
  626.     if (i == 120) {
  627.     ap_kill_timeout(r);
  628.     /* RFC2068 states:
  629.      * 14.38 Retry-After
  630.      * 
  631.      *  The Retry-After response-header field can be used with a 503 (Service
  632.      *  Unavailable) response to indicate how long the service is expected to
  633.      *  be unavailable to the requesting client. The value of this field can
  634.      *  be either an HTTP-date or an integer number of seconds (in decimal)
  635.      *  after the time of the response.
  636.      *     Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
  637.      */
  638.     ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins);
  639.     return ap_proxyerror(r, /*HTTP_SERVICE_UNAVAILABLE*/ resp);
  640.     }
  641. #endif
  642.     if (i != 220) {
  643.     ap_kill_timeout(r);
  644.     return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ resp);
  645.     }
  646.  
  647.     Explain0("FTP: connected.");
  648.  
  649.     ap_bvputs(f, "USER ", user, CRLF, NULL);
  650.     ap_bflush(f);            /* capture any errors */
  651.     Explain1("FTP: USER %s", user);
  652.  
  653. /* possible results; 230, 331, 332, 421, 500, 501, 530 */
  654. /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
  655.     /* 230 User logged in, proceed. */
  656.     /* 331 User name okay, need password. */
  657.     /* 332 Need account for login. */
  658.     /* 421 Service not available, closing control connection. */
  659.     /* 500 Syntax error, command unrecognized. */
  660.     /*     (This may include errors such as command line too long.) */
  661.     /* 501 Syntax error in parameters or arguments. */
  662.     /* 530 Not logged in. */
  663.     i = ftp_getrc(f);
  664.     Explain1("FTP: returned status %d", i);
  665.     if (i == -1) {
  666.     ap_kill_timeout(r);
  667.     return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  668.     }
  669.     if (i == 530) {
  670.     ap_kill_timeout(r);
  671.     return ftp_unauthorized (r, 1);    /* log it: user name guessing attempt? */
  672.     }
  673.     if (i != 230 && i != 331) {
  674.     ap_kill_timeout(r);
  675.     return HTTP_BAD_GATEWAY;
  676.     }
  677.  
  678.     if (i == 331) {        /* send password */
  679.     if (password == NULL) {
  680.         return ftp_unauthorized (r, 0);
  681.     }
  682.     ap_bvputs(f, "PASS ", password, CRLF, NULL);
  683.     ap_bflush(f);
  684.     Explain1("FTP: PASS %s", password);
  685. /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
  686.     /* 230 User logged in, proceed. */
  687.     /* 332 Need account for login. */
  688.     /* 421 Service not available, closing control connection. */
  689.     /* 500 Syntax error, command unrecognized. */
  690.     /* 501 Syntax error in parameters or arguments. */
  691.     /* 503 Bad sequence of commands. */
  692.     /* 530 Not logged in. */
  693.     i = ftp_getrc(f);
  694.     Explain1("FTP: returned status %d", i);
  695.     if (i == -1) {
  696.         ap_kill_timeout(r);
  697.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  698.     }
  699.     if (i == 332) {
  700.         ap_kill_timeout(r);
  701.         return ap_proxyerror(r, /*HTTP_UNAUTHORIZED*/ "Need account for login");
  702.     }
  703.     /* @@@ questionable -- we might as well return a 403 Forbidden here */
  704.     if (i == 530) {
  705.         ap_kill_timeout(r);
  706.         return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */
  707.     }
  708.     if (i != 230 && i != 202) {
  709.         ap_kill_timeout(r);
  710.         return HTTP_BAD_GATEWAY;
  711.     }
  712.     }
  713.  
  714. /* set the directory (walk directory component by component):
  715.  * this is what we must do if we don't know the OS type of the remote
  716.  * machine
  717.  */
  718.     for (;;) {
  719.     strp = strchr(path, '/');
  720.     if (strp == NULL)
  721.         break;
  722.     *strp = '\0';
  723.  
  724.     len = decodeenc(path);
  725.     ap_bvputs(f, "CWD ", path, CRLF, NULL);
  726.     ap_bflush(f);
  727.     Explain1("FTP: CWD %s", path);
  728.     *strp = '/';
  729. /* responses: 250, 421, 500, 501, 502, 530, 550 */
  730.     /* 250 Requested file action okay, completed. */
  731.     /* 421 Service not available, closing control connection. */
  732.     /* 500 Syntax error, command unrecognized. */
  733.     /* 501 Syntax error in parameters or arguments. */
  734.     /* 502 Command not implemented. */
  735.     /* 530 Not logged in. */
  736.     /* 550 Requested action not taken. */
  737.     i = ftp_getrc(f);
  738.     Explain1("FTP: returned status %d", i);
  739.     if (i == -1) {
  740.         ap_kill_timeout(r);
  741.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  742.     }
  743.     if (i == 550) {
  744.         ap_kill_timeout(r);
  745.         return HTTP_NOT_FOUND;
  746.     }
  747.     if (i != 250) {
  748.         ap_kill_timeout(r);
  749.         return HTTP_BAD_GATEWAY;
  750.     }
  751.  
  752.     path = strp + 1;
  753.     }
  754.  
  755.     if (parms != NULL && strncmp(parms, "type=", 5) == 0) {
  756.     parms += 5;
  757.     if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
  758.         parms[1] != '\0')
  759.         parms = "";
  760.     }
  761.     else
  762.     parms = "";
  763.  
  764.     /* changed to make binary transfers the default */
  765.  
  766.     if (parms[0] != 'a') {
  767.     /* set type to image */
  768.     /* TM - Added \015\012 to the end of TYPE I, otherwise it hangs the
  769.        connection */
  770.     ap_bputs("TYPE I" CRLF, f);
  771.     ap_bflush(f);
  772.     Explain0("FTP: TYPE I");
  773. /* responses: 200, 421, 500, 501, 504, 530 */
  774.     /* 200 Command okay. */
  775.     /* 421 Service not available, closing control connection. */
  776.     /* 500 Syntax error, command unrecognized. */
  777.     /* 501 Syntax error in parameters or arguments. */
  778.     /* 504 Command not implemented for that parameter. */
  779.     /* 530 Not logged in. */
  780.     i = ftp_getrc(f);
  781.     Explain1("FTP: returned status %d", i);
  782.     if (i == -1) {
  783.         ap_kill_timeout(r);
  784.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  785.     }
  786.     if (i != 200 && i != 504) {
  787.         ap_kill_timeout(r);
  788.         return HTTP_BAD_GATEWAY;
  789.     }
  790. /* Allow not implemented */
  791.     if (i == 504)
  792.         parms[0] = '\0';
  793.     }
  794.  
  795. /* try to set up PASV data connection first */
  796.     dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
  797.     if (dsock == -1) {
  798.     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  799.              "proxy: error creating PASV socket");
  800.     ap_bclose(f);
  801.     ap_kill_timeout(r);
  802.     return HTTP_INTERNAL_SERVER_ERROR;
  803.     }
  804.  
  805.     if (conf->recv_buffer_size) {
  806.     if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF,
  807.            (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) {
  808.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  809.              "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
  810.     }
  811.     }
  812.  
  813.     ap_bputs("PASV" CRLF, f);
  814.     ap_bflush(f);
  815.     Explain0("FTP: PASV command issued");
  816. /* possible results: 227, 421, 500, 501, 502, 530 */
  817.     /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
  818.     /* 421 Service not available, closing control connection. */
  819.     /* 500 Syntax error, command unrecognized. */
  820.     /* 501 Syntax error in parameters or arguments. */
  821.     /* 502 Command not implemented. */
  822.     /* 530 Not logged in. */
  823.     i = ap_bgets(pasv, sizeof(pasv), f);
  824.     if (i == -1) {
  825.     ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
  826.              "PASV: control connection is toast");
  827.     ap_pclosesocket(p, dsock);
  828.     ap_bclose(f);
  829.     ap_kill_timeout(r);
  830.     return HTTP_INTERNAL_SERVER_ERROR;
  831.     }
  832.     else {
  833.     pasv[i - 1] = '\0';
  834.     pstr = strtok(pasv, " ");    /* separate result code */
  835.     if (pstr != NULL) {
  836.         presult = atoi(pstr);
  837.         if (*(pstr + strlen(pstr) + 1) == '=')
  838.             pstr += strlen(pstr) + 2;
  839.         else
  840.         {
  841.             pstr = strtok(NULL, "(");  /* separate address & port params */
  842.         if (pstr != NULL)
  843.             pstr = strtok(NULL, ")");
  844.         }
  845.     }
  846.     else
  847.         presult = atoi(pasv);
  848.  
  849.     Explain1("FTP: returned status %d", presult);
  850.  
  851.     if (presult == 227 && pstr != NULL && (sscanf(pstr,
  852.          "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
  853.         /* pardon the parens, but it makes gcc happy */
  854.         paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0;
  855.         pport = (p1 << 8) + p0;
  856.         Explain5("FTP: contacting host %d.%d.%d.%d:%d",
  857.              h3, h2, h1, h0, pport);
  858.         data_addr.sin_family = AF_INET;
  859.         data_addr.sin_addr.s_addr = htonl(paddr);
  860.         data_addr.sin_port = htons(pport);
  861.         i = ap_proxy_doconnect(dsock, &data_addr, r);
  862.  
  863.         if (i == -1) {
  864.         ap_kill_timeout(r);
  865.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ ap_pstrcat(r->pool,
  866.                 "Could not connect to remote machine: ",
  867.                 strerror(errno), NULL));
  868.         }
  869.         else {
  870.         pasvmode = 1;
  871.         }
  872.     }
  873.     else
  874.         ap_pclosesocket(p, dsock);    /* and try the regular way */
  875.     }
  876.  
  877.     if (!pasvmode) {        /* set up data connection */
  878.     clen = sizeof(struct sockaddr_in);
  879.     if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) {
  880.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  881.              "proxy: error getting socket address");
  882.         ap_bclose(f);
  883.         ap_kill_timeout(r);
  884.         return HTTP_INTERNAL_SERVER_ERROR;
  885.     }
  886.  
  887.     dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
  888.     if (dsock == -1) {
  889.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  890.              "proxy: error creating socket");
  891.         ap_bclose(f);
  892.         ap_kill_timeout(r);
  893.         return HTTP_INTERNAL_SERVER_ERROR;
  894.     }
  895.  
  896.     if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
  897.                sizeof(one)) == -1) {
  898. #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
  899.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  900.              "proxy: error setting reuseaddr option");
  901.         ap_pclosesocket(p, dsock);
  902.         ap_bclose(f);
  903.         ap_kill_timeout(r);
  904.         return HTTP_INTERNAL_SERVER_ERROR;
  905. #endif /*_OSD_POSIX*/
  906.     }
  907.  
  908.     if (bind(dsock, (struct sockaddr *) &server,
  909.          sizeof(struct sockaddr_in)) == -1) {
  910.         char buff[22];
  911.  
  912.         ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
  913.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  914.              "proxy: error binding to ftp data socket %s", buff);
  915.         ap_bclose(f);
  916.         ap_pclosesocket(p, dsock);
  917.         return HTTP_INTERNAL_SERVER_ERROR;
  918.     }
  919.     listen(dsock, 2);    /* only need a short queue */
  920.     }
  921.  
  922. /* set request; "path" holds last path component */
  923.     len = decodeenc(path);
  924.  
  925.     /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */
  926.  
  927.     if (len == 0) {
  928.     parms = "d";
  929.     }
  930.     else {
  931.     ap_bvputs(f, "SIZE ", path, CRLF, NULL);
  932.     ap_bflush(f);
  933.     Explain1("FTP: SIZE %s", path);
  934.     i = ftp_getrc_msg(f, resp, sizeof resp);
  935.     Explain2("FTP: returned status %d with response %s", i, resp);
  936.     if (i != 500) {        /* Size command not recognized */
  937.         if (i == 550) {    /* Not a regular file */
  938.         Explain0("FTP: SIZE shows this is a directory");
  939.         parms = "d";
  940.         ap_bvputs(f, "CWD ", path, CRLF, NULL);
  941.         ap_bflush(f);
  942.         Explain1("FTP: CWD %s", path);
  943.         i = ftp_getrc(f);
  944.         /* possible results: 250, 421, 500, 501, 502, 530, 550 */
  945.         /* 250 Requested file action okay, completed. */
  946.         /* 421 Service not available, closing control connection. */
  947.         /* 500 Syntax error, command unrecognized. */
  948.         /* 501 Syntax error in parameters or arguments. */
  949.         /* 502 Command not implemented. */
  950.         /* 530 Not logged in. */
  951.         /* 550 Requested action not taken. */
  952.         Explain1("FTP: returned status %d", i);
  953.         if (i == -1) {
  954.             ap_kill_timeout(r);
  955.             return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  956.         }
  957.         if (i == 550) {
  958.             ap_kill_timeout(r);
  959.             return HTTP_NOT_FOUND;
  960.         }
  961.         if (i != 250) {
  962.             ap_kill_timeout(r);
  963.             return HTTP_BAD_GATEWAY;
  964.         }
  965.         path = "";
  966.         len = 0;
  967.         }
  968.         else if (i == 213) { /* Size command ok */
  969.         for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++)
  970.             ;
  971.         resp[j] = '\0';
  972.         if (resp[0] != '\0')
  973.             size = ap_pstrdup(p, resp);
  974.         }
  975.     }
  976.     }
  977.  
  978. #ifdef AUTODETECT_PWD
  979.     ap_bvputs(f, "PWD", CRLF, NULL);
  980.     ap_bflush(f);
  981.     Explain0("FTP: PWD");
  982. /* responses: 257, 500, 501, 502, 421, 550 */
  983.     /* 257 "<directory-name>" <commentary> */
  984.     /* 421 Service not available, closing control connection. */
  985.     /* 500 Syntax error, command unrecognized. */
  986.     /* 501 Syntax error in parameters or arguments. */
  987.     /* 502 Command not implemented. */
  988.     /* 550 Requested action not taken. */
  989.     i = ftp_getrc_msg(f, resp, sizeof resp);
  990.     Explain1("FTP: PWD returned status %d", i);
  991.     if (i == -1 || i == 421) {
  992.     ap_kill_timeout(r);
  993.     return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  994.     }
  995.     if (i == 550) {
  996.     ap_kill_timeout(r);
  997.     return HTTP_NOT_FOUND;
  998.     }
  999.     if (i == 257) {
  1000.     const char *dirp = resp;
  1001.     cwd = ap_getword_conf(r->pool, &dirp);
  1002.     }
  1003. #endif /*AUTODETECT_PWD*/
  1004.  
  1005.     if (parms[0] == 'd') {
  1006.     if (len != 0)
  1007.         ap_bvputs(f, "LIST ", path, CRLF, NULL);
  1008.     else
  1009.         ap_bputs("LIST -lag" CRLF, f);
  1010.     Explain1("FTP: LIST %s", (len == 0 ? "" : path));
  1011.     }
  1012.     else {
  1013.     ap_bvputs(f, "RETR ", path, CRLF, NULL);
  1014.     Explain1("FTP: RETR %s", path);
  1015.     }
  1016.     ap_bflush(f);
  1017. /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
  1018.    NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */
  1019.     /* 110 Restart marker reply. */
  1020.     /* 125 Data connection already open; transfer starting. */
  1021.     /* 150 File status okay; about to open data connection. */
  1022.     /* 226 Closing data connection. */
  1023.     /* 250 Requested file action okay, completed. */
  1024.     /* 421 Service not available, closing control connection. */
  1025.     /* 425 Can't open data connection. */
  1026.     /* 426 Connection closed; transfer aborted. */
  1027.     /* 450 Requested file action not taken. */
  1028.     /* 451 Requested action aborted. Local error in processing. */
  1029.     /* 500 Syntax error, command unrecognized. */
  1030.     /* 501 Syntax error in parameters or arguments. */
  1031.     /* 530 Not logged in. */
  1032.     /* 550 Requested action not taken. */
  1033.     rc = ftp_getrc(f);
  1034.     Explain1("FTP: returned status %d", rc);
  1035.     if (rc == -1) {
  1036.     ap_kill_timeout(r);
  1037.     return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  1038.     }
  1039.     if (rc == 550) {
  1040.     Explain0("FTP: RETR failed, trying LIST instead");
  1041.     parms = "d";
  1042.     ap_bvputs(f, "CWD ", path, CRLF, NULL);
  1043.     ap_bflush(f);
  1044.     Explain1("FTP: CWD %s", path);
  1045.     /* possible results: 250, 421, 500, 501, 502, 530, 550 */
  1046.     /* 250 Requested file action okay, completed. */
  1047.     /* 421 Service not available, closing control connection. */
  1048.     /* 500 Syntax error, command unrecognized. */
  1049.     /* 501 Syntax error in parameters or arguments. */
  1050.     /* 502 Command not implemented. */
  1051.     /* 530 Not logged in. */
  1052.     /* 550 Requested action not taken. */
  1053.     rc = ftp_getrc(f);
  1054.     Explain1("FTP: returned status %d", rc);
  1055.     if (rc == -1) {
  1056.         ap_kill_timeout(r);
  1057.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  1058.     }
  1059.     if (rc == 550) {
  1060.         ap_kill_timeout(r);
  1061.         return HTTP_NOT_FOUND;
  1062.     }
  1063.     if (rc != 250) {
  1064.         ap_kill_timeout(r);
  1065.         return HTTP_BAD_GATEWAY;
  1066.     }
  1067.  
  1068. #ifdef AUTODETECT_PWD
  1069.     ap_bvputs(f, "PWD", CRLF, NULL);
  1070.     ap_bflush(f);
  1071.     Explain0("FTP: PWD");
  1072. /* responses: 257, 500, 501, 502, 421, 550 */
  1073.     /* 257 "<directory-name>" <commentary> */
  1074.     /* 421 Service not available, closing control connection. */
  1075.     /* 500 Syntax error, command unrecognized. */
  1076.     /* 501 Syntax error in parameters or arguments. */
  1077.     /* 502 Command not implemented. */
  1078.     /* 550 Requested action not taken. */
  1079.     i = ftp_getrc_msg(f, resp, sizeof resp);
  1080.     Explain1("FTP: PWD returned status %d", i);
  1081.     if (i == -1 || i == 421) {
  1082.         ap_kill_timeout(r);
  1083.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  1084.     }
  1085.     if (i == 550) {
  1086.         ap_kill_timeout(r);
  1087.         return HTTP_NOT_FOUND;
  1088.     }
  1089.     if (i == 257) {
  1090.         const char *dirp = resp;
  1091.         cwd = ap_getword_conf(r->pool, &dirp);
  1092.     }
  1093. #endif /*AUTODETECT_PWD*/
  1094.  
  1095.     ap_bputs("LIST -lag" CRLF, f);
  1096.     ap_bflush(f);
  1097.     Explain0("FTP: LIST -lag");
  1098.     rc = ftp_getrc(f);
  1099.     Explain1("FTP: returned status %d", rc);
  1100.     if (rc == -1)
  1101.         return ap_proxyerror(r, /*HTTP_BAD_GATEWAY*/ "Error reading from remote server");
  1102.     }
  1103.     ap_kill_timeout(r);
  1104.     if (rc != 125 && rc != 150 && rc != 226 && rc != 250)
  1105.     return HTTP_BAD_GATEWAY;
  1106.  
  1107.     r->status = HTTP_OK;
  1108.     r->status_line = "200 OK";
  1109.  
  1110.     resp_hdrs = ap_make_table(p, 2);
  1111.     c->hdrs = resp_hdrs;
  1112.  
  1113.     if (parms[0] == 'd')
  1114.     ap_table_set(resp_hdrs, "Content-Type", "text/html");
  1115.     else {
  1116.     if (r->content_type != NULL) {
  1117.         ap_table_set(resp_hdrs, "Content-Type", r->content_type);
  1118.         Explain1("FTP: Content-Type set to %s", r->content_type);
  1119.     }
  1120.     else {
  1121.         ap_table_set(resp_hdrs, "Content-Type", "text/plain");
  1122.     }
  1123.     if (parms[0] != 'a' && size != NULL) {
  1124.         /* We "trust" the ftp server to really serve (size) bytes... */
  1125.         ap_table_set(resp_hdrs, "Content-Length", size);
  1126.         Explain1("FTP: Content-Length set to %s", size);
  1127.     }
  1128.     }
  1129.  
  1130. /* check if NoCache directive on this host */
  1131.     for (i = 0; i < conf->nocaches->nelts; i++) {
  1132.     if ((ncent[i].name != NULL && strstr(host, ncent[i].name) != NULL)
  1133.         || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*')
  1134.         nocache = 1;
  1135.     }
  1136.  
  1137.     i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache);
  1138.  
  1139.     if (i != DECLINED) {
  1140.     ap_pclosesocket(p, dsock);
  1141.     ap_bclose(f);
  1142.     return i;
  1143.     }
  1144.  
  1145.     if (!pasvmode) {        /* wait for connection */
  1146.     ap_hard_timeout("proxy ftp data connect", r);
  1147.     clen = sizeof(struct sockaddr_in);
  1148.     do
  1149.         csd = accept(dsock, (struct sockaddr *) &server, &clen);
  1150.     while (csd == -1 && errno == EINTR);
  1151.     if (csd == -1) {
  1152.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1153.              "proxy: failed to accept data connection");
  1154.         ap_pclosesocket(p, dsock);
  1155.         ap_bclose(f);
  1156.         ap_kill_timeout(r);
  1157.         if (c != NULL)
  1158.         c = ap_proxy_cache_error(c);
  1159.         return HTTP_BAD_GATEWAY;
  1160.     }
  1161.     ap_note_cleanups_for_socket(p, csd);
  1162.     data = ap_bcreate(p, B_RDWR | B_SOCKET);
  1163.     ap_bpushfd(data, csd, -1);
  1164.     ap_kill_timeout(r);
  1165.     }
  1166.     else {
  1167.     data = ap_bcreate(p, B_RDWR | B_SOCKET);
  1168.     ap_bpushfd(data, dsock, dsock);
  1169.     }
  1170.  
  1171. #ifdef CHARSET_EBCDIC
  1172. /*    bsetflag(data, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);*/
  1173. #endif /*CHARSET_EBCDIC*/
  1174.  
  1175.     ap_hard_timeout("proxy receive", r);
  1176. /* send response */
  1177. /* write status line */
  1178.     if (!r->assbackwards)
  1179.     ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL);
  1180.     if (c != NULL && c->fp != NULL
  1181.     && ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1)
  1182.     c = ap_proxy_cache_error(c);
  1183.  
  1184. /* send headers */
  1185.     tdo.req = r;
  1186.     tdo.cache = c;
  1187.     ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
  1188.  
  1189.     if (!r->assbackwards)
  1190.     ap_rputs(CRLF, r);
  1191.     if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1)
  1192.     c = ap_proxy_cache_error(c);
  1193.  
  1194.     ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
  1195.     r->sent_bodyct = 1;
  1196. /* send body */
  1197.     if (!r->header_only) {
  1198.     if (parms[0] != 'd') {
  1199. /* we need to set this for ap_proxy_send_fb()... */
  1200.         if (c != NULL)
  1201.         c->cache_completion = 0;
  1202.         ap_proxy_send_fb(data, r, c);
  1203.     } else
  1204.         send_dir(data, r, c, cwd);
  1205.  
  1206.     if (rc == 125 || rc == 150)
  1207.         rc = ftp_getrc(f);
  1208.  
  1209.     /* XXX: we checked for 125||150||226||250 above. This is redundant. */
  1210.     if (rc != 226 && rc != 250)
  1211.         c = ap_proxy_cache_error(c);
  1212.     }
  1213.     else {
  1214. /* abort the transfer */
  1215.     ap_bputs("ABOR" CRLF, f);
  1216.     ap_bflush(f);
  1217.     if (!pasvmode)
  1218.         ap_bclose(data);
  1219.     Explain0("FTP: ABOR");
  1220. /* responses: 225, 226, 421, 500, 501, 502 */
  1221.     /* 225 Data connection open; no transfer in progress. */
  1222.     /* 226 Closing data connection. */
  1223.     /* 421 Service not available, closing control connection. */
  1224.     /* 500 Syntax error, command unrecognized. */
  1225.     /* 501 Syntax error in parameters or arguments. */
  1226.     /* 502 Command not implemented. */
  1227.     i = ftp_getrc(f);
  1228.     Explain1("FTP: returned status %d", i);
  1229.     }
  1230.  
  1231.     ap_kill_timeout(r);
  1232.     ap_proxy_cache_tidy(c);
  1233.  
  1234. /* finish */
  1235.     ap_bputs("QUIT" CRLF, f);
  1236.     ap_bflush(f);
  1237.     Explain0("FTP: QUIT");
  1238. /* responses: 221, 500 */
  1239.     /* 221 Service closing control connection. */
  1240.     /* 500 Syntax error, command unrecognized. */
  1241.     i = ftp_getrc(f);
  1242.     Explain1("FTP: QUIT: status %d", i);
  1243.  
  1244.     if (pasvmode)
  1245.     ap_bclose(data);
  1246.     ap_bclose(f);
  1247.  
  1248.     ap_rflush(r);    /* flush before garbage collection */
  1249.  
  1250.     ap_proxy_garbage_coll(r);
  1251.  
  1252.     return OK;
  1253. }
  1254.