"Fossies" - the Fresh Open Source Software Archive

Member "mod_ftp-0.9.6/modules/ftp/mod_ftp.c" (15 Jul 2009, 33170 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 "mod_ftp.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 
   26 #define FTP_SERVER_STRING "mod_ftp/" MODFTP_VERSION
   27 
   28 static ap_filter_rec_t *ftp_crlf_filter_handle;
   29 static ap_filter_rec_t *ftp_data_out_filter_handle;
   30 static ap_filter_rec_t *ftp_byterange_filter_handle;
   31 ap_filter_rec_t *ftp_input_filter_handle;
   32 ap_filter_rec_t *ftp_content_length_filter_handle;
   33 ap_filter_rec_t *ftp_ssl_input_filter_handle;
   34 ap_filter_rec_t *ftp_ssl_output_filter_handle;
   35 
   36 /* Register callbacks for mod_log_config. */
   37 static int ftp_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
   38 {
   39     APR_OPTIONAL_FN_TYPE(ap_register_log_handler) * log_pfn_register;
   40 
   41     /*
   42      * Find the handles of non-ftp filters we will be using
   43      * 
   44      * This must be done after register_hooks since SSL may not be loaded before
   45      * FTP, but before we actually process the config because we test those
   46      * filter handles to determine if SSL-related directives are valid.
   47      */
   48     ftp_byterange_filter_handle
   49         = ap_get_output_filter_handle("BYTERANGE");
   50     ftp_content_length_filter_handle
   51         = ap_get_output_filter_handle("CONTENT_LENGTH");
   52     ftp_ssl_input_filter_handle
   53         = ap_get_input_filter_handle(FTP_SSL_FILTER);
   54     ftp_ssl_output_filter_handle
   55         = ap_get_output_filter_handle(FTP_SSL_FILTER);
   56 
   57     /*
   58      * Register our custom log format flags
   59      */
   60     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
   61 
   62     if (log_pfn_register) {
   63         log_pfn_register(p, "M", ftp_log_transfer_mode, 0);
   64         log_pfn_register(p, "F", ftp_log_action_flags, 0);
   65         log_pfn_register(p, "d", ftp_log_transfer_direction, 0);
   66         log_pfn_register(p, "W", ftp_log_accessed_anonymously, 0);
   67         log_pfn_register(p, "S", ftp_log_service_name, 0);
   68         log_pfn_register(p, "Z", ftp_log_auth_method, 0);
   69         log_pfn_register(p, "Y", ftp_log_auth_user_id, 0);
   70     }
   71 
   72 #ifdef HAVE_FTP_LOWPORTD
   73     return lowportd_pre_config(p, plog, ptemp);
   74 #else
   75     return OK;
   76 #endif
   77 }
   78 
   79 
   80 static int ftp_post_config(apr_pool_t *p, apr_pool_t *plog,
   81                                apr_pool_t *ptemp, server_rec *s)
   82 {
   83     server_rec *base = s;
   84     ftp_server_config *basefsc = ftp_get_module_config(s->module_config);
   85     int lowportd = 0;
   86 
   87     ap_add_version_component(p, FTP_SERVER_STRING);
   88 
   89     /*
   90      * Unless disabled, advertise that UTF8 filenames are preferred/permitted
   91      * RFC2640 never -requires- UTF8 names
   92      */
   93     if (!(basefsc->options & FTP_OPT_NO_UTF8_FEAT))
   94         ftp_feat_advert("UTF8");
   95 
   96     /* Finalize ftp_cmd_help and ftp_cmd_feat messages */
   97     ftp_cmd_finalize(p, ptemp);
   98 
   99     /*
  100      * Fixup global values, then base server and virtual host values
  101      */
  102     if (!basefsc->limitdbfile)
  103         basefsc->limitdbfile = ap_server_root_relative(p, FTP_DEFAULT_DBFILE);
  104 
  105     for (; s; s = s->next) {
  106         ftp_server_config *fsc = ftp_get_module_config(s->module_config);
  107 
  108         if (fsc->timeout_login == FTP_UNSPEC)
  109             fsc->timeout_login = FTP_TIMEOUT_LOGIN;
  110         if (fsc->timeout_idle == FTP_UNSPEC)
  111             fsc->timeout_idle = FTP_TIMEOUT_IDLE;
  112         if (fsc->timeout_data == FTP_UNSPEC)
  113             fsc->timeout_data = FTP_TIMEOUT_DATA;
  114 
  115         if (fsc->max_login_attempts == FTP_UNSPEC)
  116             fsc->max_login_attempts = FTP_MAX_LOGINS;
  117 
  118         if (fsc->active_min == FTP_UNSPEC)
  119             fsc->active_min = fsc->active_max = -1;
  120         else if (fsc->active_min < 1024)
  121             lowportd = 1;
  122 
  123         if (fsc->pasv_min == FTP_UNSPEC)
  124             fsc->pasv_min = fsc->pasv_max = 0;
  125         if (fsc->epsv_ignore_family == FTP_UNSPEC)
  126             fsc->epsv_ignore_family = 0;
  127 
  128         if (fsc->data_block_size == FTP_UNSPEC)
  129             fsc->data_block_size = FTP_DATA_BLOCK_SIZE;
  130 
  131         if (fsc->limit_peruser == FTP_UNSPEC)
  132             fsc->limit_peruser = 0;
  133         if (fsc->limit_perip == FTP_UNSPEC)
  134             fsc->limit_perip = 0;
  135         if (fsc->limit_perserver == FTP_UNSPEC)
  136             fsc->limit_perserver = 0;
  137 
  138         fsc->limitdbfile = basefsc->limitdbfile;
  139     }
  140 
  141     if (ftp_mutexdb_init(base, p) != APR_SUCCESS) {
  142         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, base,
  143                      "Could not initialize FTP mutex");
  144         return HTTP_INTERNAL_SERVER_ERROR;
  145     }
  146 
  147     apr_pool_cleanup_register(p, base, ftp_mutexdb_cleanup,
  148                               apr_pool_cleanup_null);
  149 
  150 #ifdef HAVE_FTP_LOWPORTD
  151     if (lowportd)
  152         /* Initialized only if a server has at least one active_min < 1024 */
  153         return lowportd_post_config(p, plog, ptemp, base);
  154     else
  155 #endif
  156         return OK;
  157 }
  158 
  159 static void ftp_child_init(apr_pool_t *p, server_rec *s)
  160 {
  161     ftp_mutexdb_child_init(s, p);
  162 }
  163 
  164 static void *create_ftp_server_config(apr_pool_t *p, server_rec *s)
  165 {
  166     ftp_server_config *fsc = apr_pcalloc(p, sizeof(*fsc));
  167 
  168     fsc->timeout_login = FTP_UNSPEC;
  169     fsc->timeout_idle = FTP_UNSPEC;
  170     fsc->timeout_data = FTP_UNSPEC;
  171     fsc->max_login_attempts = FTP_UNSPEC;
  172 
  173     fsc->active_min = FTP_UNSPEC;
  174     fsc->active_max = FTP_UNSPEC;
  175     fsc->pasv_min = FTP_UNSPEC;
  176     fsc->pasv_max = FTP_UNSPEC;
  177     fsc->epsv_ignore_family = FTP_UNSPEC;
  178 
  179     fsc->data_block_size = FTP_UNSPEC;
  180 
  181     fsc->limit_peruser = FTP_UNSPEC;
  182     fsc->limit_perip = FTP_UNSPEC;
  183     fsc->limit_perserver = FTP_UNSPEC;
  184 
  185     return fsc;
  186 }
  187 
  188 static void *merge_ftp_server_config(apr_pool_t *p, void *basev, void *addv)
  189 {
  190     ftp_server_config *base = (ftp_server_config *) basev;
  191     ftp_server_config *add = (ftp_server_config *) addv;
  192     ftp_server_config *fsc = apr_palloc(p, sizeof(*fsc));
  193 
  194     /*
  195      * We default to the add config, so any directive not handled here won't
  196      * be inherited from the base server.
  197      */
  198     memcpy(fsc, add, sizeof(*fsc));
  199 
  200     if (fsc->timeout_login == FTP_UNSPEC)
  201         fsc->timeout_login = base->timeout_login;
  202     if (fsc->timeout_idle == FTP_UNSPEC)
  203         fsc->timeout_idle = base->timeout_idle;
  204     if (fsc->timeout_data == FTP_UNSPEC)
  205         fsc->timeout_data = base->timeout_data;
  206 
  207     if (fsc->max_login_attempts == FTP_UNSPEC)
  208         fsc->max_login_attempts = FTP_MAX_LOGINS;
  209 
  210     if (fsc->active_min == FTP_UNSPEC) {
  211         fsc->active_min = base->active_min;
  212         fsc->active_max = base->active_max;
  213     }
  214     if (fsc->pasv_min == FTP_UNSPEC) {
  215         fsc->pasv_min = base->pasv_min;
  216         fsc->pasv_max = base->pasv_max;
  217     }
  218     if (fsc->epsv_ignore_family == FTP_UNSPEC) {
  219         fsc->epsv_ignore_family = base->epsv_ignore_family;
  220     }
  221 
  222     if (fsc->data_block_size == FTP_UNSPEC) {
  223         fsc->data_block_size = base->data_block_size;
  224     }
  225 
  226     if (fsc->limit_peruser == FTP_UNSPEC)
  227         fsc->limit_peruser = base->limit_peruser;
  228     if (fsc->limit_perip == FTP_UNSPEC)
  229         fsc->limit_perip = base->limit_perip;
  230     if (fsc->limit_perserver == FTP_UNSPEC)
  231         fsc->limit_perserver = base->limit_perserver;
  232 
  233     if (!fsc->banner_message) {
  234         fsc->banner_message = base->banner_message;
  235         fsc->banner_message_isfile = base->banner_message_isfile;
  236     }
  237     if (!fsc->exit_message) {
  238         fsc->exit_message = base->exit_message;
  239         fsc->exit_message_isfile = base->exit_message_isfile;
  240     }
  241 
  242     return fsc;
  243 }
  244 
  245 
  246 static void *create_ftp_dir_config(apr_pool_t *p, char *dir)
  247 {
  248     ftp_dir_config *conf = apr_pcalloc(p, sizeof(*conf));
  249 
  250     conf->fileperms = APR_OS_DEFAULT;
  251     conf->dirperms = APR_OS_DEFAULT;
  252 
  253     return conf;
  254 }
  255 
  256 static void *merge_ftp_dir_config(apr_pool_t *p, void *basev, void *addv)
  257 {
  258     ftp_dir_config *base = (ftp_dir_config *) basev;
  259     ftp_dir_config *add = (ftp_dir_config *) addv;
  260     ftp_dir_config *conf = apr_palloc(p, sizeof(*conf));
  261 
  262     /* XXX: NO-INHERIT the FTPReadmeMessage? */
  263     conf->path = add->path;
  264     conf->readme = add->readme;
  265     conf->readme_isfile = add->readme_isfile;
  266 
  267     conf->fileperms = (add->fileperms == APR_OS_DEFAULT)
  268         ? base->fileperms : add->fileperms;
  269     conf->dirperms = (add->dirperms == APR_OS_DEFAULT)
  270         ? base->dirperms : add->dirperms;
  271 
  272     return conf;
  273 }
  274 
  275 int ftp_have_ssl(void)
  276 {
  277     return (ftp_ssl_input_filter_handle
  278             && ftp_ssl_output_filter_handle);
  279 }
  280 
  281 static void ftp_insert_filter(request_rec *r)
  282 {
  283     ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
  284 
  285     if (!fc)
  286         return;
  287 
  288     if (fc->datasock && (fc->filter_mask & FTP_NEED_DATA_OUT)) {
  289         ap_add_output_filter_handle(ftp_data_out_filter_handle, fc, r,
  290                                     r->connection);
  291     }
  292     if (fc->filter_mask & FTP_NEED_BYTERANGE) {
  293         ap_add_output_filter_handle(ftp_byterange_filter_handle, NULL,
  294                                     r, r->connection);
  295     }
  296     if (fc->filter_mask & FTP_NEED_CRLF) {
  297         /* CRLF filter has a single int context, "did we last have a CR?" */
  298         ap_add_output_filter_handle(ftp_crlf_filter_handle,
  299                                     apr_pcalloc(r->pool, sizeof(int *)),
  300                                     r, r->connection);
  301     }
  302 }
  303 
  304 /*
  305  * The create request hook.  Used when Apache does an internal redirect
  306  *
  307  * XXX: because we are connection-oriented, perhaps this can go away?
  308  *      left in place until we finish any request_rec refactoring
  309  */
  310 static int ftp_create_request(request_rec *r)
  311 {
  312     return OK;
  313 }
  314 
  315 #if ((AP_SERVER_MAJORVERSION_NUMBER < 3) && (AP_SERVER_MINORVERSION_NUMBER < 3))
  316 
  317 /* This is something of a hack.  The idea is that we want to be able to look
  318  * at a string with a '*' wildcard, and a string that matches the pattern,
  319  * and determine which part of the second string matches the wildcard.  This
  320  * allows us to extract that information, which is later used to setup
  321  * the require line properly if we are ensuring that users can not switch
  322  * to other user's home directories.
  323  */
  324 static char *find_dir(apr_pool_t *p, const char *wildcard,
  325                            const char *curr_file)
  326 {
  327     char *dirname;
  328     size_t i;
  329 
  330     for (i = 0; i < strlen(wildcard); i++) {
  331         if (wildcard[i] == curr_file[i]) {
  332             /* So far the strings are equal */
  333             continue;
  334         }
  335         if (wildcard[i] == '*' && wildcard[i + 1] == '/') {
  336             /*
  337              * Now we have the portion we want to match against.  Find the
  338              * corresponding string in the current file.
  339              */
  340             size_t j = i;
  341             while (curr_file[j] && curr_file[j] != '/') {
  342                 j++;
  343             }
  344             dirname = apr_pstrmemdup(p, curr_file + i, j - i);
  345             return dirname;
  346         }
  347         return NULL;
  348     }
  349     return NULL;
  350 }
  351 
  352 /*
  353  * The check_user_id hook.  This is a hack that allows us to ensure that only
  354  * the owner of the directory can get access to the current directory.  If the
  355  * user has configured:
  356  *         Require dir-name
  357  * for the current directory, then we require that the logged in user match
  358  * the name of the directory.
  359  */
  360 static int ftp_check_user_id(request_rec *r)
  361 {
  362     apr_array_header_t *newrequire;
  363     const apr_array_header_t *reqs_arr = ap_requires(r);
  364     require_line *reqs;
  365     const char *t, *w;
  366     core_dir_config *conf;
  367     int x;
  368 
  369     conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
  370                                                     &core_module);
  371 
  372     if (!reqs_arr) {
  373         return DECLINED;
  374     }
  375 
  376     newrequire = apr_array_make(r->pool, 2, sizeof(require_line));
  377 
  378     reqs = (require_line *) reqs_arr->elts;
  379     for (x = 0; x < reqs_arr->nelts; x++) {
  380         require_line *rl;
  381 
  382         rl = (require_line *) apr_array_push(newrequire);
  383         rl->method_mask = reqs[x].method_mask;
  384 
  385         t = reqs[x].requirement;
  386         w = ap_getword_white(r->pool, &t);
  387         if (!strcmp(w, "user")) {
  388             rl->requirement = apr_pstrdup(r->pool, w);
  389             while (strcmp(w = ap_getword_white(r->pool, &t), "")) {
  390                 if (!strcmp(w, "dir-name")) {
  391                     char *dirname = find_dir(r->pool, conf->d, r->filename);
  392                     rl->requirement = apr_pstrcat(r->pool, rl->requirement,
  393                                                   " ", dirname, NULL);
  394                 }
  395                 else {
  396                     rl->requirement = apr_pstrcat(r->pool, rl->requirement,
  397                                                   " ", w, NULL);
  398                 }
  399             }
  400         }
  401         else {
  402             rl->requirement = apr_pstrdup(r->pool, reqs[x].requirement);
  403         }
  404     }
  405     conf->ap_requires = newrequire;
  406 
  407     return DECLINED;
  408 }
  409 #endif                          /* AP_VERSION > 2.2 */
  410 
  411 /*
  412  * Handle FTP module directives
  413  */
  414 
  415 /*
  416  * Generic function used for setting values in the server config.
  417  * Adapted from ap_set_int_slot, since it only works with per directory
  418  * configurations.
  419  */
  420 static const char *ftp_set_int_slot(cmd_parms *cmd, void *dummy,
  421                                          const char *arg)
  422 {
  423     char *endptr;
  424     char *error_str = NULL;
  425     int offset = (int) (long) cmd->info;
  426     ftp_server_config *fsc =
  427     ftp_get_module_config(cmd->server->module_config);
  428 
  429     *(int *) ((char *) fsc + offset) = strtol(arg, &endptr, 10);
  430 
  431     if ((*arg == '\0') || (*endptr != '\0')) {
  432         error_str = apr_psprintf(cmd->pool,
  433                          "Invalid value for directive %s, expected integer",
  434                                  cmd->directive->directive);
  435     }
  436 
  437     return error_str;
  438 }
  439 
  440 
  441 static const char *ftp_enable(cmd_parms *cmd, void *dummy, int arg)
  442 {
  443     ftp_server_config *fsc =
  444     ftp_get_module_config(cmd->server->module_config);
  445 
  446     fsc->enabled = arg;
  447 
  448     return NULL;
  449 }
  450 
  451 static const char *ftp_umask(cmd_parms *cmd, void *dconf, const char *arg)
  452 {
  453     int umask, mode;
  454     char *endp;
  455     char *error_str = NULL;
  456 
  457     ftp_dir_config *d = dconf;
  458 
  459     umask = strtol(arg, &endp, 8);
  460     mode = umask & 0666;
  461 
  462     if ((*arg == '\0') || (*endp != '\0')) {
  463         error_str = apr_psprintf(cmd->pool,
  464                                  "%s is not valid for %s", arg,
  465                                  cmd->directive->directive);
  466     }
  467     else {
  468         d->fileperms = ftp_unix_mode2perms(mode);
  469     }
  470     return error_str;
  471 }
  472 
  473 
  474 static const char *ftp_dirumask(cmd_parms *cmd, void *dconf, const char *arg)
  475 {
  476     int umask, xmask, mode;
  477     char *endp;
  478     char *error_str = NULL;
  479 
  480     ftp_dir_config *d = dconf;
  481 
  482     umask = strtol(arg, &endp, 8);
  483 
  484     /*
  485      * Here the r bits  of umask are taken to xmask all other bits set to 1.
  486      */
  487     xmask = umask & 0444;
  488     xmask = xmask >> 2;
  489     xmask |= 0666;
  490 
  491     /*
  492      * Here r bits are & with x bits. Effectively it confirms that x
  493      * permission is given to all who are given r permission. but allow to
  494      * deny r while x is given.
  495      */
  496     mode = umask & xmask;
  497 
  498 
  499     if ((*arg == '\0') || (*endp != '\0')) {
  500         error_str = apr_psprintf(cmd->pool,
  501                                  "%s is not valid for %s", arg,
  502                                  cmd->directive->directive);
  503     }
  504     else {
  505         d->dirperms = ftp_unix_mode2perms(mode);
  506     }
  507     return error_str;
  508 }
  509 
  510 static const char *ftp_implicit_ssl(cmd_parms *cmd, void *dummy, int arg)
  511 {
  512     ftp_server_config *fsc =
  513     ftp_get_module_config(cmd->server->module_config);
  514 
  515     if (!ftp_have_ssl()) {
  516         return "No SSL module found, cannot enable implicit SSL";
  517     }
  518 
  519     fsc->implicit_ssl = arg;
  520     return NULL;
  521 }
  522 
  523 static const char *ftp_set_jail(cmd_parms *cmd, void *dummy, int arg)
  524 {
  525     ftp_server_config *fsc =
  526     ftp_get_module_config(cmd->server->module_config);
  527 
  528     fsc->jailuser = arg;
  529     return NULL;
  530 }
  531 
  532 static const char *ftp_options(cmd_parms *cmd, void *dummy,
  533                                     const char *raw)
  534 {
  535     ftp_server_config *fsc =
  536     ftp_get_module_config(cmd->server->module_config);
  537 
  538     while (raw[0]) {
  539         int opt_mask = 0;
  540         char *op = ap_getword_conf(cmd->pool, &raw);
  541 
  542         if (!strcasecmp(op, "RequireSSL")) {
  543             opt_mask = FTP_OPT_REQUIRESSL;
  544         }
  545         else if (!strcasecmp(op, "CheckMaxClients")) {
  546             opt_mask = FTP_OPT_CHECKMAXCLIENTS;
  547         }
  548         else if (!strcasecmp(op, "RemoveUserGroup")) {
  549             opt_mask = FTP_OPT_REMOVEUSERGROUP;
  550         }
  551         else if (!strcasecmp(op, "NLSTShowDirs")) {
  552             opt_mask = FTP_OPT_NLSTSHOWDIRS;
  553         }
  554         else if (!strcasecmp(op, "NLSTIsLIST")) {
  555             opt_mask = FTP_OPT_NLSTISLIST;
  556         }
  557         else if (!strcasecmp(op, "LISTIsNLST")) {
  558             opt_mask = FTP_OPT_LISTISNLST;
  559         }
  560         else if (!strcasecmp(op, "CreateHomeDirs")) {
  561             opt_mask = FTP_OPT_CREATEHOMEDIRS;
  562         }
  563         else if (!strcasecmp(op, "ShowUnAuthorizedFiles")) {
  564             opt_mask = FTP_OPT_SHOWUNAUTH;
  565         }
  566         else if (!strcasecmp(op, "AllowProxyPORT")) {
  567             opt_mask = FTP_OPT_ALLOWPROXYPORT;
  568         }
  569         else if (!strcasecmp(op, "AllowProxyPASV")) {
  570             opt_mask = FTP_OPT_ALLOWPROXYPASV;
  571         }
  572         else if (!strcasecmp(op, "VirtualHostByUser")) {
  573             opt_mask = FTP_OPT_VHOST_BY_USER;
  574         }
  575         else if (!strcasecmp(op, "StripHostname")) {
  576             opt_mask = FTP_OPT_STRIP_HOSTNAME;
  577         }
  578         else if (!strcasecmp(op, "NoUTF8Feature")) {
  579             opt_mask = FTP_OPT_NO_UTF8_FEAT;
  580         }
  581         else {
  582             return apr_pstrcat(cmd->pool, "Illegal FTPOption ", op, NULL);
  583         }
  584         fsc->options |= opt_mask;
  585     }
  586     if ((fsc->options & FTP_OPT_LISTISNLST) && (fsc->options & FTP_OPT_NLSTISLIST)) {
  587         return "LISTISNLST and NLSTISLIST are mutually exclusive options";
  588     }
  589     return NULL;
  590 }
  591 
  592 static const char *ftp_set_pasv_addr(cmd_parms *cmd, void *dummy,
  593                                           const char *addr)
  594 {
  595     apr_uint32_t ipaddr;
  596     ftp_server_config *fsc =
  597     ftp_get_module_config(cmd->server->module_config);
  598 
  599     if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) != 1) {
  600         return apr_pstrcat(cmd->pool, "Invalid IP address for ",
  601                            cmd->directive->directive, " (", addr, ")", NULL);
  602     }
  603 
  604     fsc->pasv_addr = apr_pstrdup(cmd->pool, addr);
  605 
  606     return NULL;
  607 }
  608 
  609 static const char *ftp_set_pasv_bindaddr(cmd_parms *cmd, void *dummy,
  610                                               const char *addr)
  611 {
  612     ftp_server_config *fsc =
  613     ftp_get_module_config(cmd->server->module_config);
  614 #if APR_HAVE_IPV6
  615     struct in6_addr ipaddr;
  616 #else
  617     struct in_addr ipaddr;
  618 #endif
  619 
  620     if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) == 1) {
  621         fsc->pasv_bindfamily = APR_INET;
  622     }
  623 #if APR_HAVE_IPV6
  624     else if ((ftp_inet_pton(AF_INET6, addr, &ipaddr)) == 1) {
  625         fsc->pasv_bindfamily = APR_INET6;
  626     }
  627 #endif
  628     else {
  629         return apr_pstrcat(cmd->pool, "Invalid IP address for ",
  630                            cmd->directive->directive, " (", addr, ")", NULL);
  631     }
  632 
  633     fsc->pasv_bindaddr = apr_pstrdup(cmd->pool, addr);
  634 
  635     return NULL;
  636 }
  637 
  638 static const char *ftp_set_message_generic(cmd_parms *cmd, const char *arg,
  639                                           const char **dest, int *file_flag)
  640 {
  641     apr_finfo_t finfo;
  642     apr_status_t rv;
  643 
  644     if (*dest != NULL) {
  645         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0,
  646                    cmd->server, "Ignoring duplicate message file: %s", arg);
  647         return NULL;
  648     }
  649 
  650     if (!strncmp(arg, "file:", 5)) {
  651         rv = apr_stat(&finfo,
  652                       ap_server_root_relative(cmd->temp_pool, arg + 5),
  653                       APR_FINFO_TYPE, cmd->temp_pool);
  654         if (rv != APR_SUCCESS || finfo.filetype != APR_REG) {
  655             return apr_pstrcat(cmd->pool, "Invalid message file: ",
  656                                arg + 5, NULL);
  657         }
  658 
  659         *(file_flag) = 1;
  660         *(dest) = ap_server_root_relative(cmd->pool, arg + 5);
  661     }
  662     else {
  663         *(dest) = apr_pstrdup(cmd->pool, arg);
  664     }
  665 
  666     return NULL;
  667 }
  668 
  669 static const char *ftp_set_banner_message(cmd_parms *cmd, void *dummy,
  670                                                const char *arg)
  671 {
  672     ftp_server_config *fsc =
  673     ftp_get_module_config(cmd->server->module_config);
  674 
  675     return ftp_set_message_generic(cmd, arg, &fsc->banner_message,
  676                                    &fsc->banner_message_isfile);
  677 }
  678 
  679 static const char *ftp_set_exit_message(cmd_parms *cmd, void *dummy,
  680                                              const char *arg)
  681 {
  682     ftp_server_config *fsc =
  683     ftp_get_module_config(cmd->server->module_config);
  684 
  685     return ftp_set_message_generic(cmd, arg, &fsc->exit_message,
  686                                    &fsc->exit_message_isfile);
  687 }
  688 
  689 static const char *ftp_set_readme_message(cmd_parms *cmd, void *dconf,
  690                                                const char *arg)
  691 {
  692     ftp_dir_config *d = dconf;
  693 
  694     d->path = apr_pstrdup(cmd->pool, cmd->path);
  695     return ftp_set_message_generic(cmd, arg, &d->readme, &d->readme_isfile);
  696 }
  697 
  698 static const char *ftp_set_pasv_range(cmd_parms *cmd, void *dummy,
  699                                            const char *min, const char *max)
  700 {
  701     char *error_str = NULL;
  702 
  703     ftp_server_config *fsc =
  704     ftp_get_module_config(cmd->server->module_config);
  705 
  706     /* XXX: better error handling */
  707     fsc->pasv_min = (apr_port_t) atoi(min);
  708     fsc->pasv_max = (apr_port_t) atoi(max);
  709 
  710     if (fsc->pasv_min > fsc->pasv_max
  711         || fsc->pasv_min < 0 || fsc->pasv_max > 0xFFFF) {
  712         error_str = apr_psprintf(cmd->pool,
  713                                  "Invalid range for %s (%s > %s)",
  714                                  cmd->directive->directive, min, max);
  715     }
  716 
  717     return error_str;
  718 }
  719 
  720 static const char *ftp_set_active_ports(cmd_parms *cmd, void *dummy,
  721                                            const char *min, const char *max)
  722 {
  723     char *error_str = NULL;
  724 
  725     ftp_server_config *fsc =
  726     ftp_get_module_config(cmd->server->module_config);
  727 
  728     /* XXX: better error handling */
  729     fsc->active_min = atoi(min);
  730     if (!max) {
  731         fsc->active_max = fsc->active_min;
  732     }
  733     else {
  734         /* XXX: better error handling */
  735         fsc->active_max = atoi(max);
  736     }
  737 
  738     if (fsc->active_min > fsc->active_max
  739         || fsc->active_min < 0 || fsc->active_max > 0xFFFF) {
  740         error_str = apr_psprintf(cmd->pool,
  741                                  "Invalid range for %s (%s > %s)",
  742                                  cmd->directive->directive, min, max);
  743     }
  744 
  745     return error_str;
  746 }
  747 
  748 static const char *ftp_set_homedir(cmd_parms *cmd, void *dummy,
  749                                         const char *dir)
  750 {
  751     ftp_server_config *fsc =
  752     ftp_get_module_config(cmd->server->module_config);
  753 
  754     if (*dir != '/') {
  755         return apr_pstrcat(cmd->pool, "Path for ", cmd->directive->directive,
  756                            " must be absolute (", dir, ")", NULL);
  757     }
  758 
  759     fsc->homedir = apr_pstrdup(cmd->pool, dir);
  760 
  761     return NULL;
  762 }
  763 
  764 static const char *ftp_set_docrootenv(cmd_parms *cmd, void *dummy,
  765                                            const char *var)
  766 {
  767     ftp_server_config *fsc =
  768     ftp_get_module_config(cmd->server->module_config);
  769 
  770     fsc->docrootenv = apr_pstrdup(cmd->pool, var);
  771 
  772     return NULL;
  773 }
  774 
  775 static const char *ftp_set_limit_peruser(cmd_parms *cmd, void *dummy,
  776                                               const char *limit)
  777 {
  778     char *error_str = NULL;
  779     ftp_server_config *fsc;
  780 
  781     fsc = ftp_get_module_config(cmd->server->module_config);
  782 
  783     /* XXX: better error handling */
  784     fsc->limit_peruser = (apr_port_t) atoi(limit);
  785 
  786     if (fsc->limit_peruser < 0) {
  787         error_str = apr_psprintf(cmd->pool,
  788                                  "%s value must be 0 or greater (%s)",
  789                                  cmd->directive->directive, limit);
  790     }
  791 
  792     return error_str;
  793 }
  794 
  795 static const char *ftp_set_limit_perip(cmd_parms *cmd, void *dummy,
  796                                             const char *limit)
  797 {
  798     char *error_str = NULL;
  799     ftp_server_config *fsc;
  800 
  801     fsc = ftp_get_module_config(cmd->server->module_config);
  802 
  803     /* XXX: better error handling */
  804     fsc->limit_perip = (apr_port_t) atoi(limit);
  805 
  806     if (fsc->limit_perip < 0) {
  807         error_str = apr_psprintf(cmd->pool,
  808                                  "%s value must be 0 or greater (%s)",
  809                                  cmd->directive->directive, limit);
  810     }
  811 
  812     return error_str;
  813 }
  814 
  815 static const char *ftp_set_limit_perserver(cmd_parms *cmd, void *dummy,
  816                                                 const char *limit)
  817 {
  818     char *error_str = NULL;
  819     ftp_server_config *fsc;
  820 
  821     fsc = ftp_get_module_config(cmd->server->module_config);
  822 
  823     /* XXX: better error handling */
  824     fsc->limit_perserver = (apr_port_t) atoi(limit);
  825 
  826     if (fsc->limit_perserver < 0) {
  827         error_str = apr_psprintf(cmd->pool,
  828                                  "%s value must be 0 or greater (%s)",
  829                                  cmd->directive->directive, limit);
  830     }
  831 
  832     return error_str;
  833 }
  834 
  835 static const char *ftp_set_dbfile(cmd_parms *cmd, void *dummy,
  836                                        const char *dbfile)
  837 {
  838     ftp_server_config *fsc;
  839 
  840     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  841     if (err != NULL) {
  842         return err;
  843     }
  844 
  845     fsc = ftp_get_module_config(cmd->server->module_config);
  846 
  847     fsc->limitdbfile = ap_server_root_relative(cmd->pool, dbfile);
  848     if (!fsc->limitdbfile) {
  849         return apr_pstrcat(cmd->pool, "Invalid FTPLimitDBFile value: ",
  850                            dbfile, NULL);
  851     }
  852 
  853     return NULL;
  854 }
  855 
  856 static const char *ftp_epsv_ignore_family(cmd_parms *cmd, void *dummy, int flag)
  857 {
  858     ftp_server_config *fsc = ftp_get_module_config(cmd->server->module_config);
  859     fsc->epsv_ignore_family = flag;
  860     return NULL;
  861 }
  862 
  863 /*
  864  * Setup command table
  865  */
  866 static const command_rec ftp_cmds[] = {
  867     AP_INIT_TAKE1("FTPLowPortSock", lowportd_set_socket, NULL, RSRC_CONF,
  868                   "name of the socket to use for creating low-numbered-port "
  869                   "connections from ftp (global only)"),
  870     AP_INIT_FLAG("FTP", ftp_enable, NULL, RSRC_CONF,
  871                  "Run an FTP server on this host"),
  872     AP_INIT_TAKE1("FTPTimeoutLogin", ftp_set_int_slot,
  873                   (void *) APR_OFFSETOF(ftp_server_config,
  874                                         timeout_login), RSRC_CONF,
  875                   "Idle time allowed when logging in"),
  876     AP_INIT_TAKE1("FTPTimeoutIdle", ftp_set_int_slot,
  877                   (void *) APR_OFFSETOF(ftp_server_config,
  878                                         timeout_idle), RSRC_CONF,
  879                   "Idle time allowed during a FTP session"),
  880     AP_INIT_TAKE1("FTPTimeoutData", ftp_set_int_slot,
  881                   (void *) APR_OFFSETOF(ftp_server_config,
  882                                         timeout_data), RSRC_CONF,
  883                   "Idle time allowed during a data transfer"),
  884     AP_INIT_TAKE1("FTPMaxLoginAttempts", ftp_set_int_slot,
  885                   (void *) APR_OFFSETOF(ftp_server_config,
  886                                         max_login_attempts), RSRC_CONF,
  887                   "Maximum number of login attempts"),
  888     AP_INIT_FLAG("FTPImplicitSSL", ftp_implicit_ssl, NULL, RSRC_CONF,
  889                  "Use SSL implicitly."),
  890     AP_INIT_RAW_ARGS("FTPOptions", ftp_options, NULL, RSRC_CONF,
  891                      "Set options for this server"),
  892     AP_INIT_TAKE1("FTPPASVaddr", ftp_set_pasv_addr, NULL, RSRC_CONF,
  893                   "Set the allowed PASV server IP address for the data "
  894                   "channel"),
  895     AP_INIT_TAKE1("FTPPASVbindaddr", ftp_set_pasv_bindaddr, NULL, RSRC_CONF,
  896                   "Set and bind the allowed PASV server IP "
  897                   "address for the data channel"),
  898     AP_INIT_TAKE2("FTPPASVrange", ftp_set_pasv_range, NULL, RSRC_CONF,
  899                   "Set the allowed PASV port range"),
  900     AP_INIT_FLAG("FTPEPSVIgnoreFamily", ftp_epsv_ignore_family,
  901                  NULL, RSRC_CONF, \
  902                  "Instructs EPSV handler to ignore the requested IPv4 or IPv6"
  903                  " address family (to accomodate network translation)"),
  904     AP_INIT_TAKE1("FTPBannerMessage", ftp_set_banner_message, NULL, RSRC_CONF,
  905                   "Set initial login message"),
  906     AP_INIT_TAKE1("FTPExitMessage", ftp_set_exit_message, NULL, RSRC_CONF,
  907                   "Set logout message"),
  908     AP_INIT_TAKE1("FTPHomeDir", ftp_set_homedir, NULL, RSRC_CONF,
  909                   "Set the path to directory containing user's "
  910                   "home directories"),
  911     AP_INIT_TAKE1("FTPDocRootEnv", ftp_set_docrootenv, NULL, RSRC_CONF,
  912                   "Set the DocumentRoot based on the given environment "
  913                   "variable, such as a per-user LDAP property"),
  914     AP_INIT_FLAG("FTPJailUser", ftp_set_jail, NULL, RSRC_CONF,
  915                  "Users are not allowed to leave their home directories"),
  916     AP_INIT_TAKE12("FTPActiveRange", ftp_set_active_ports, NULL, RSRC_CONF,
  917                  "Ports the server will use for connecting to the client."),
  918     AP_INIT_TAKE1("FTPLimitLoginUser", ftp_set_limit_peruser, NULL, RSRC_CONF,
  919                   "Set the maximum number of concurrent logins per user"),
  920     AP_INIT_TAKE1("FTPLimitLoginIP", ftp_set_limit_perip, NULL, RSRC_CONF,
  921               "Set the maximum number of concurrent logins per IP address"),
  922     AP_INIT_TAKE1("FTPLimitLoginServer", ftp_set_limit_perserver, NULL, RSRC_CONF,
  923                   "Set the maximum number of concurrent logins per server"),
  924     AP_INIT_TAKE1("FTPLimitDBFile", ftp_set_dbfile, NULL, RSRC_CONF,
  925                   "Set the location for the Login Limit DB file"),
  926     AP_INIT_TAKE1("FTPDataBlockSize", ftp_set_int_slot,
  927                   (void *) APR_OFFSETOF(ftp_server_config,
  928                                         data_block_size), RSRC_CONF,
  929                   "Block size in bytes to use during data transfers"),
  930     AP_INIT_TAKE1("FTPReadmeMessage", ftp_set_readme_message, NULL, OR_ALL,
  931                   "Set per-directory Readme file"),
  932     AP_INIT_TAKE1("FTPUmask", ftp_umask, NULL, OR_FILEINFO,
  933                   "Set the umask for created files"),
  934     AP_INIT_TAKE1("FTPDirUmask", ftp_dirumask, NULL, OR_FILEINFO,
  935                   "Set the umask for created directory"),
  936     {NULL}
  937 };
  938 
  939 static void register_hooks(apr_pool_t *p)
  940 {
  941     ap_hook_pre_config(ftp_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
  942     ap_hook_post_config(ftp_post_config, NULL, NULL, APR_HOOK_MIDDLE);
  943     ap_hook_child_init(ftp_child_init, NULL, NULL, APR_HOOK_MIDDLE);
  944     ap_hook_process_connection(ftp_process_connection, NULL, NULL,
  945                                APR_HOOK_MIDDLE);
  946     ap_hook_create_request(ftp_create_request, NULL, NULL, APR_HOOK_MIDDLE);
  947     ap_hook_insert_filter(ftp_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
  948 
  949 #if ((AP_SERVER_MAJORVERSION_NUMBER < 3) && (AP_SERVER_MINORVERSION_NUMBER < 3))
  950     ap_hook_check_user_id(ftp_check_user_id, NULL, NULL, APR_HOOK_REALLY_FIRST);
  951 #endif
  952 
  953     /* FTP filters */
  954     ftp_input_filter_handle = ap_register_input_filter("FTP_PROTOCOL",
  955                                                        ftp_protocol_filter,
  956                                                        NULL,
  957                                                        AP_FTYPE_PROTOCOL);
  958     ftp_crlf_filter_handle = ap_register_output_filter("FTP_CRLF",
  959                                                        ftp_crlf_filter,
  960                                                        NULL,
  961                                                      AP_FTYPE_RESOURCE - 1);
  962     ftp_data_out_filter_handle = ap_register_output_filter("FTP_DATA_OUT",
  963                                                         ftp_data_out_filter,
  964                                                            NULL,
  965                                                       AP_FTYPE_NETWORK - 2);
  966 
  967     /* Register all core command handlers */
  968     ftp_register_core_cmds(p);
  969 }
  970 
  971 module FTP_DECLARE_DATA ftp_module =
  972 {
  973     STANDARD20_MODULE_STUFF,
  974     create_ftp_dir_config,      /* create per-directory config structure */
  975     merge_ftp_dir_config,       /* merge per-directory config structures */
  976     create_ftp_server_config,   /* create per-server config structure */
  977     merge_ftp_server_config,    /* merge per-server config structures */
  978     ftp_cmds,                   /* command apr_table_t */
  979     register_hooks              /* register hooks */
  980 };