"Fossies" - the Fresh Open Source Software Archive

Member "mod_ftp-0.9.6/modules/ftp/ftp_data_connection.c" (8 Jun 2009, 11871 Bytes) of package /linux/www/apache_httpd_modules/old/mod_ftp-0.9.6-beta.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ftp_data_connection.c" see the Fossies "Dox" file reference documentation.

    1 /* Licensed to the Apache Software Foundation (ASF) under one or more
    2  * contributor license agreements.  See the NOTICE file distributed with
    3  * this work for additional information regarding copyright ownership.
    4  * The ASF licenses this file to You under the Apache License, Version 2.0
    5  * (the "License"); you may not use this file except in compliance with
    6  * the License.  You may obtain a copy of the License at
    7  *
    8  *     http://www.apache.org/licenses/LICENSE-2.0
    9  *
   10  * Unless required by applicable law or agreed to in writing, software
   11  * distributed under the License is distributed on an "AS IS" BASIS,
   12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13  * See the License for the specific language governing permissions and
   14  * limitations under the License.
   15  */
   16 
   17 /*
   18  * Original Copyright (c) 2005 Covalent Technologies
   19  *
   20  * FTP Protocol module for Apache 2.0
   21  */
   22 
   23 #include "mod_ftp.h"
   24 #include "ftp_internal.h"
   25 #include "apr_portable.h"
   26 
   27 #if APR_HAVE_SYS_SOCKET_H
   28 #include <sys/socket.h>
   29 #endif
   30 #ifdef HAVE_NETINET_IP_H
   31 #include <netinet/ip.h>
   32 #endif
   33 
   34 /*
   35  * ftp_reset_dataconn: Close any data channel listen/connect socket and
   36  *                     clear all data channel state from ftp_connection
   37  */
   38 void ftp_reset_dataconn(ftp_connection *fc)
   39 {
   40     if (fc->csock) {
   41         apr_socket_close(fc->csock);
   42         fc->csock = NULL;
   43     }
   44     fc->clientsa = NULL;
   45     fc->passive_created = -1;
   46     apr_pool_clear(fc->data_pool);
   47 }
   48 
   49 /*
   50  * ftp_open_datasock: If we are in passive mode we accept and return a
   51  *                    socket.  If we are in active mode, a socket is
   52  *                    created based on the fc->clientsa sockaddr and
   53  *                    then returned.
   54  *
   55  * Arguments: r - The request.
   56  *
   57  * Returns: apr_status_t
   58  */
   59 static apr_status_t ftp_open_datasock(request_rec *r)
   60 {
   61     ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
   62     ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
   63 
   64     apr_interval_time_t timeout;
   65     apr_pollfd_t pollset[2];
   66     apr_socket_t *s;
   67     apr_status_t rv, res;
   68     int n;
   69 #ifdef HAVE_SOL_IP_H
   70     int sd, sopt;
   71 #endif
   72 
   73     /*
   74      * handle err condition when the creation of the socket had failed, this
   75      * will occur if a PORT command has failed
   76      */
   77     if (!fc->csock) {
   78         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
   79                      "Nonexistant connection");
   80         return APR_EGENERAL;
   81     }
   82 
   83     if (fc->passive_created != -1) {
   84         pollset[0].desc_type = APR_POLL_SOCKET;
   85         pollset[0].desc.s = fc->csock;
   86         pollset[0].reqevents = APR_POLLIN;
   87         pollset[1].desc_type = APR_POLL_SOCKET;
   88         pollset[1].desc.s = fc->cntlsock;
   89         pollset[1].reqevents = (APR_POLLIN | APR_POLLPRI);
   90 
   91         /*
   92          * since it is possible to hang in accept(), poll both the control
   93          * and client sockets waiting for activity.
   94          */
   95         apr_socket_timeout_get(fc->csock, &timeout);    /* likely to be -1 */
   96         res = apr_poll(pollset, 2, &n, timeout);
   97 
   98         if (res == APR_SUCCESS) {
   99             if (pollset[0].rtnevents & APR_POLLIN) {
  100                 /* activity on client socket, fall through to accept() */
  101                 ;
  102             }
  103             else if (pollset[1].rtnevents & (APR_POLLIN | APR_POLLPRI)) {
  104                 /*
  105                  * command channel has activity. since we can only do a
  106                  * single read ahead operation, no use in looping here if the
  107                  * command is not an ABOR.  bail out and let command
  108                  * processing occur as normal.
  109                  */
  110                 ap_log_error(APLOG_MARK, APLOG_ERR, res, r->server,
  111                              "Activity on control channel while waiting "
  112                              "for client connect, processing command");
  113 
  114                 /* manual cleanup, no need to close the socket */
  115                 fc->csock = NULL;
  116                 fc->passive_created = -1;
  117                 return APR_ECONNRESET;
  118             }
  119         }
  120         else {
  121             /*
  122              * not much we can do, one of our sockets was likely disconnected
  123              */
  124             fc->csock = NULL;
  125             fc->passive_created = -1;
  126             return APR_EGENERAL;
  127         }
  128 
  129         /* activity on client socket, attempt an accept() */
  130         rv = apr_socket_accept(&s, fc->csock, fc->data_pool);
  131 
  132         res = apr_socket_close(fc->csock);
  133         fc->csock = NULL;
  134         fc->passive_created = -1;
  135 
  136         /* check csock closure first */
  137         if (res != APR_SUCCESS) {
  138             ap_log_error(APLOG_MARK, APLOG_ERR, res, r->server,
  139                          "Couldn't close passive connection");
  140         }
  141 
  142         /* retest the apr_accept result from above */
  143         if (rv != APR_SUCCESS) {
  144             return rv;
  145         }
  146         fc->datasock = s;
  147     }
  148     else {
  149         if (fc->clientsa) {
  150             int tries;
  151             for (tries = 0;; tries++) {
  152                 rv = apr_socket_connect(fc->csock, fc->clientsa);
  153                 if (rv == APR_SUCCESS) {
  154                     break;
  155                 }
  156                 if (!APR_STATUS_IS_EAGAIN(rv) || tries > FTP_MAX_TRIES) {
  157                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  158                                  "Couldn't connect to client");
  159                     apr_socket_close(fc->csock);
  160                     fc->csock = NULL;
  161                     fc->passive_created = -1;
  162                     return rv;
  163                 }
  164                 apr_sleep(tries * APR_USEC_PER_SEC);
  165             }
  166         }
  167         else {
  168             return APR_EGENERAL;
  169         }
  170         fc->datasock = fc->csock;
  171         fc->csock = NULL;
  172         fc->passive_created = -1;
  173     }
  174 
  175 #ifdef HAVE_SOL_IP_H
  176     sopt = IPTOS_THROUGHPUT;
  177     if (((apr_os_sock_get(&sd, fc->datasock)) == APR_SUCCESS) &&
  178 #ifdef HAVE_SOL_IP
  179          (setsockopt(sd, SOL_IP, IP_TOS, &sopt, sizeof(sopt)) < 0)) {
  180 #else
  181          (setsockopt(sd, IPPROTO_IP, IP_TOS, &sopt, sizeof(sopt)) < 0)) {
  182 #endif
  183         ap_log_error(APLOG_MARK, APLOG_ERR, errno, r->server,
  184                      "Failed to set TOS priority");
  185     }
  186 #endif
  187 
  188     rv = apr_socket_opt_set(fc->datasock, APR_SO_LINGER,
  189                             APR_MAX_SECS_TO_LINGER);
  190     if (rv != APR_SUCCESS) {
  191         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  192                      "Couldn't set APR_SO_LINGER socket option");
  193     }
  194     rv = apr_socket_opt_set(fc->datasock, APR_SO_REUSEADDR, 1);
  195     if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
  196         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  197                      "Couldn't set APR_SO_REUSEADDR socket option");
  198     }
  199 
  200     /* Set the default data connection timeout value */
  201     rv = apr_socket_timeout_set(fc->datasock,
  202                                 fsc->timeout_data * APR_USEC_PER_SEC);
  203     if (rv != APR_SUCCESS) {
  204         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  205                      "Couldn't set socket timeout");
  206     }
  207 
  208     return APR_SUCCESS;
  209 }
  210 
  211 /*
  212  * ftp_open_dataconn: Creates the appropriate data channel
  213  *                    and initializes the
  214  *
  215  * Arguments: r - The request.
  216  *            write - Open for write-to-client only
  217  *
  218  * Returns: apr_status_t
  219  */
  220 conn_rec *ftp_open_dataconn(request_rec *r, int write_not_read)
  221 {
  222     ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
  223     ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
  224     conn_rec *cdata;
  225     ap_filter_t *f;
  226 
  227     if (ftp_open_datasock(r) != APR_SUCCESS) {
  228         return NULL;
  229     }
  230 
  231     cdata = ap_run_create_connection(r->pool, r->server, fc->datasock,
  232                                      r->connection->id, r->connection->sbh,
  233                                      r->connection->bucket_alloc);
  234 
  235     ftp_set_module_config(cdata->conn_config, fc);
  236 
  237     ap_run_pre_connection(cdata, fc->datasock);
  238 
  239     /*
  240      * Open data connection only if the connection is from the client's IP
  241      * address. All other PASV connection attempts are denied, unless
  242      * disabled using:
  243      * 
  244      * FTPOptions AllowProxyPASV
  245      */
  246     if (fc->clientsa == NULL) { /* Only check PASV, never PORT connections */
  247         if (!(fsc->options & FTP_OPT_ALLOWPROXYPASV)) {
  248             if (strcmp(fc->connection->remote_ip, cdata->remote_ip) != 0) {
  249                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
  250                              "PASV data connection attempt from %s "
  251                              "doesn't match the client IP %s",
  252                              cdata->remote_ip, fc->connection->remote_ip);
  253                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
  254                              "PASV data connection attempt denied, "
  255                              "not configured to AllowProxyPASV");
  256                 apr_socket_close(fc->datasock);
  257                 fc->datasock = NULL;
  258                 return NULL;
  259             }
  260         }
  261         fc->passive_created = 0;
  262     }
  263 
  264     if (write_not_read) {
  265         /*
  266          * We need the network-level poll filter to watch both the control
  267          * incoming command and writable data port conditions
  268          */
  269         fc->filter_mask += FTP_NEED_DATA_OUT;
  270     }
  271     else {
  272         /*
  273          * Once Apache has inserted the core i/o filters, we must insert our
  274          * idea of a core socket, based on our own ftp_datasock bucket,
  275          * instead of the socket_bucket. This will capture any abort command
  276          * on the control socket while actually reading from the data socket.
  277          * 
  278          * Insert this bucket type only for read connections
  279          */
  280         for (f = cdata->input_filters; f; f = f->next) {
  281             if (strcasecmp(f->frec->name, "CORE_IN") == 0) {
  282                 core_net_rec *net = f->ctx;
  283                 apr_bucket *e;
  284 
  285                 net->in_ctx = apr_pcalloc(fc->data_pool, sizeof(*net->in_ctx));
  286                 net->in_ctx->b = apr_brigade_create(fc->data_pool,
  287                                                     f->c->bucket_alloc);
  288                 net->in_ctx->tmpbb =
  289                     apr_brigade_create(net->in_ctx->b->p,
  290                                        net->in_ctx->b->bucket_alloc);
  291 
  292                 /* seed the brigade with our client data+control sockets */
  293                 e = ftp_bucket_datasock_create(fc, f->c->bucket_alloc);
  294                 APR_BRIGADE_INSERT_TAIL(net->in_ctx->b, e);
  295                 break;
  296             }
  297         }
  298     }
  299 
  300     /*
  301      * We initalize the data connection here by adding/removing the SSL/TLS
  302      * filters.  We then invoke ftp_ssl_init immediately for writers, since
  303      * negotation would normally occur on the first socket read (and we won't
  304      * be reading from that socket.)
  305      */
  306     if (fc->prot == FTP_PROT_CLEAR) {
  307         for (f = cdata->output_filters; f; f = f->next) {
  308             if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
  309                 ap_remove_output_filter(f);
  310             }
  311         }
  312         for (f = cdata->input_filters; f; f = f->next) {
  313             if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
  314                 ap_remove_input_filter(f);
  315             }
  316         }
  317     }
  318     else if ((fc->prot == FTP_PROT_PRIVATE) && write_not_read) {
  319         if (ftp_ssl_init(cdata) != APR_SUCCESS) {
  320             /*
  321              * In case of failure within ssl_init, return no data connection
  322              */
  323             apr_socket_close(fc->datasock);
  324             fc->datasock = NULL;
  325             return NULL;
  326         }
  327     }
  328 
  329     /*
  330      * We now need to remove the NET_TIME filter to allow
  331      * use to control timeouts ourselves.
  332      */
  333     for (f = cdata->input_filters; f; f = f->next) {
  334         if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
  335             ap_remove_input_filter(f);
  336         }
  337     }
  338 
  339     return cdata;
  340 }