diff options
Diffstat (limited to 'modules/proxy/mod_proxy_ftp.c')
-rw-r--r-- | modules/proxy/mod_proxy_ftp.c | 171 |
1 files changed, 81 insertions, 90 deletions
diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c index 4a10987..e0032e5 100644 --- a/modules/proxy/mod_proxy_ftp.c +++ b/modules/proxy/mod_proxy_ftp.c @@ -23,11 +23,6 @@ #endif #include "apr_version.h" -#if (APR_MAJOR_VERSION < 1) -#undef apr_socket_create -#define apr_socket_create apr_socket_create_ex -#endif - #define AUTODETECT_PWD /* Automatic timestamping (Last-Modified header) based on MDTM is used if: * 1) the FTP server supports the MDTM command and @@ -218,7 +213,7 @@ static int ftp_check_string(const char *x) * (EBCDIC) machines either. */ static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, - char *buff, apr_size_t bufflen, int *eos) + char *buff, apr_size_t bufflen, int *eos, apr_size_t *outlen) { apr_bucket *e; apr_status_t rv; @@ -230,6 +225,7 @@ static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, /* start with an empty string */ buff[0] = 0; *eos = 0; + *outlen = 0; /* loop through each brigade */ while (!found) { @@ -273,6 +269,7 @@ static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, if (len > 0) { memcpy(pos, response, len); pos += len; + *outlen += len; } } apr_bucket_delete(e); @@ -292,9 +289,11 @@ static int proxy_ftp_canon(request_rec *r, char *url) apr_pool_t *p = r->pool; const char *err; apr_port_t port, def_port; + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; /* */ - if (strncasecmp(url, "ftp:", 4) == 0) { + if (ap_cstr_casecmpn(url, "ftp:", 4) == 0) { url += 4; } else { @@ -330,7 +329,8 @@ static int proxy_ftp_canon(request_rec *r, char *url) else parms = ""; - path = ap_proxy_canonenc(p, url, strlen(url), enc_path, 0, r->proxyreq); + path = ap_proxy_canonenc_ex(p, url, strlen(url), enc_path, flags, + r->proxyreq); if (path == NULL) return HTTP_BAD_REQUEST; if (!ftp_check_string(path)) @@ -385,28 +385,36 @@ static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbu char buff[5]; char *mb = msgbuf, *me = &msgbuf[msglen]; apr_status_t rv; + apr_size_t nread; + int eos; - if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { + if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { return -1; } /* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(03233) "<%s", response); */ + if (nread < 4) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(10229) "Malformed FTP response '%s'", response); + *mb = '\0'; + return -1; + } + if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || - !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) + !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) status = 0; else status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0'; mb = apr_cpystrn(mb, response + 4, me - mb); - if (response[3] == '-') { + if (response[3] == '-') { /* multi-line reply "123-foo\nbar\n123 baz" */ memcpy(buff, response, 3); buff[3] = ' '; do { - if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { + if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { return -1; } mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); @@ -494,7 +502,7 @@ static apr_status_t proxy_send_dir_filter(ap_filter_t *f, path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY); /* If path began with /%2f, change the basedir */ - if (strncasecmp(path, "/%2f", 4) == 0) { + if (ap_cstr_casecmpn(path, "/%2f", 4) == 0) { basedir = "/%2f"; } @@ -813,17 +821,19 @@ proxy_ftp_command(const char *cmd, request_rec *r, conn_rec *ftp_ctrl, APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(c->bucket_alloc)); ap_pass_brigade(ftp_ctrl->output_filters, bb); - /* strip off the CRLF for logging */ - apr_cpystrn(message, cmd, sizeof(message)); - if ((crlf = strchr(message, '\r')) != NULL || - (crlf = strchr(message, '\n')) != NULL) - *crlf = '\0'; - if (strncmp(message,"PASS ", 5) == 0) - strcpy(&message[5], "****"); - ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, ">%s", message); + if (APLOGrtrace2(r)) { + /* strip off the CRLF for logging */ + apr_cpystrn(message, cmd, sizeof(message)); + if ((crlf = strchr(message, '\r')) != NULL || + (crlf = strchr(message, '\n')) != NULL) + *crlf = '\0'; + if (strncmp(message,"PASS ", 5) == 0) + strcpy(&message[5], "****"); + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, ">%s", message); + } } - rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof message); + rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof(message)); if (rc == -1 || rc == 421) strcpy(message,"<unable to read result>"); if ((crlf = strchr(message, '\r')) != NULL || @@ -909,7 +919,7 @@ static char *ftp_get_PWD(request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade * with username and password (which was presumably queried from the user) * supplied in the Authorization: header. * Note that we "invent" a realm name which consists of the - * ftp://user@host part of the reqest (sans password -if supplied but invalid-) + * ftp://user@host part of the request (sans password -if supplied but invalid-) */ static int ftp_unauthorized(request_rec *r, int log_it) { @@ -965,12 +975,9 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, conn_rec *c = r->connection; proxy_conn_rec *backend; apr_socket_t *sock, *local_sock, *data_sock = NULL; - apr_sockaddr_t *connect_addr = NULL; - apr_status_t rv; conn_rec *origin, *data = NULL; apr_status_t err = APR_SUCCESS; - apr_status_t uerr = APR_SUCCESS; - apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); + apr_bucket_brigade *bb; char *buf, *connectname; apr_port_t connectport; char *ftpmessage = NULL; @@ -993,8 +1000,8 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, /* stuff for PASV mode */ int connect = 0, use_port = 0; char dates[APR_RFC822_DATE_LEN]; + apr_status_t rv; int status; - apr_pool_t *address_pool; /* is this for us? */ if (proxyhost) { @@ -1003,7 +1010,7 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, proxyhost); return DECLINED; /* proxy connections are via HTTP */ } - if (strncasecmp(url, "ftp:", 4)) { + if (ap_cstr_casecmpn(url, "ftp:", 4)) { ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "declining URL %s - not ftp:", url); return DECLINED; /* only interested in FTP */ @@ -1024,8 +1031,9 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, /* We break the URL into host, port, path-search */ if (r->parsed_uri.hostname == NULL) { if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) { - return ap_proxyerror(r, HTTP_BAD_REQUEST, - apr_psprintf(p, "URI cannot be parsed: %s", url)); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10189) + "URI cannot be parsed: %s", url); + return ap_proxyerror(r, HTTP_BAD_REQUEST, "URI cannot be parsed"); } connectname = uri.hostname; connectport = uri.port; @@ -1074,7 +1082,7 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, * still smaller that the URL is logged regularly. */ if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL - && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 + && ap_cstr_casecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 && (password = ap_pbase64decode(r->pool, password))[0] != ':') { /* Check the decoded string for special characters. */ if (!ftp_check_string(password)) { @@ -1107,61 +1115,35 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01036) "connecting %s to %s:%d", url, connectname, connectport); - if (worker->s->is_address_reusable) { - if (!worker->cp->addr) { - if ((err = PROXY_THREAD_LOCK(worker->balancer)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(01037) "lock"); - return HTTP_INTERNAL_SERVER_ERROR; + /* create space for state information */ + backend = ap_get_module_config(c->conn_config, &proxy_ftp_module); + if (!backend) { + status = ap_proxy_acquire_connection("FTP", &backend, worker, r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection("FTP", backend, r->server); } + return status; } - connect_addr = worker->cp->addr; - address_pool = worker->cp->pool; + ap_set_module_config(c->conn_config, &proxy_ftp_module, backend); } - else - address_pool = r->pool; - /* do a DNS lookup for the destination host */ - if (!connect_addr) - err = apr_sockaddr_info_get(&(connect_addr), - connectname, APR_UNSPEC, - connectport, 0, - address_pool); - if (worker->s->is_address_reusable && !worker->cp->addr) { - worker->cp->addr = connect_addr; - if ((uerr = PROXY_THREAD_UNLOCK(worker->balancer)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(01038) "unlock"); - } - } /* * get all the possible IP addresses for the destname and loop through * them until we get a successful connection */ + err = ap_proxy_determine_address("FTP", backend, connectname, connectport, + 0, r, r->server); if (APR_SUCCESS != err) { - return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, - "DNS lookup failure for: ", - connectname, NULL)); + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error resolving backend address"); } /* check if ProxyBlock directive on this host */ - if (OK != ap_proxy_checkproxyblock2(r, conf, connectname, connect_addr)) { - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); - } - - /* create space for state information */ - backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_ftp_module); - if (!backend) { - status = ap_proxy_acquire_connection("FTP", &backend, worker, r->server); - if (status != OK) { - if (backend) { - backend->close = 1; - ap_proxy_release_connection("FTP", backend, r->server); - } - return status; - } - /* TODO: see if ftp could use determine_connection */ - backend->addr = connect_addr; - ap_set_module_config(c->conn_config, &proxy_ftp_module, backend); + if (OK != ap_proxy_checkproxyblock2(r, conf, connectname, backend->addr)) { + return ftp_proxyerror(r, backend, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); } @@ -1171,21 +1153,15 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, * We have determined who to connect to. Now make the connection. */ - if (ap_proxy_connect_backend("FTP", backend, worker, r->server)) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01039) - "an error occurred creating a new connection to %pI (%s)", - connect_addr, connectname); proxy_ftp_cleanup(r, backend); return HTTP_SERVICE_UNAVAILABLE; } - if (!backend->connection) { - status = ap_proxy_connection_create_ex("FTP", backend, r); - if (status != OK) { - proxy_ftp_cleanup(r, backend); - return status; - } + status = ap_proxy_connection_create_ex("FTP", backend, r); + if (status != OK) { + proxy_ftp_cleanup(r, backend); + return status; } /* Use old naming */ @@ -1203,6 +1179,7 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, * correct directory... */ + bb = apr_brigade_create(p, c->bucket_alloc); /* possible results: */ /* 120 Service ready in nnn minutes. */ @@ -1306,7 +1283,7 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, /* Special handling for leading "%2f": this enforces a "cwd /" * out of the $HOME directory which was the starting point after login */ - if (strncasecmp(path, "%2f", 3) == 0) { + if (ap_cstr_casecmpn(path, "%2f", 3) == 0) { path += 3; while (*path == '/') /* skip leading '/' (after root %2f) */ ++path; @@ -1520,7 +1497,8 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, "PASV contacting host %d.%d.%d.%d:%d", h3, h2, h1, h0, pasvport); - if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { + if ((rv = apr_socket_create(&data_sock, backend->addr->family, + SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01045) "error creating PASV socket"); proxy_ftp_cleanup(r, backend); @@ -1542,7 +1520,14 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, } /* make the connection */ - apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", h3, h2, h1, h0), connect_addr->family, pasvport, 0, p); + err = apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", + h3, h2, h1, h0), + backend->addr->family, pasvport, 0, p); + if (APR_SUCCESS != err) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "DNS lookup failure for: ", + connectname, NULL)); + } rv = apr_socket_connect(data_sock, pasv_addr); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01048) @@ -1565,7 +1550,8 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, apr_port_t local_port; unsigned int h0, h1, h2, h3, p0, p1; - if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { + if ((rv = apr_socket_create(&local_sock, backend->addr->family, + SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01049) "error creating local socket"); proxy_ftp_cleanup(r, backend); @@ -1585,7 +1571,12 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, #endif /* _OSD_POSIX */ } - apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool); + err = apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool); + if (APR_SUCCESS != err) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "DNS lookup failure for: ", + connectname, NULL)); + } if ((rv = apr_socket_bind(local_sock, local_addr)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01051) |