📄 mod_proxy_ftp.c
字号:
/* * RFC2616 states: 14.37 Retry-After * * The Retry-After response-header field can be used with a 503 (Service * Unavailable) response to indicate how long the service is expected * to be unavailable to the requesting client. [...] The value of * this field can be either an HTTP-date or an integer number of * seconds (in decimal) after the time of the response. Retry-After * = "Retry-After" ":" ( HTTP-date | delta-seconds ) */ char *secs_str = ftpmessage; time_t secs; /* Look for a number, preceded by whitespace */ while (*secs_str) if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) && apr_isdigit(secs_str[0])) break; if (*secs_str != '\0') { secs = atol(secs_str); apr_table_add(r->headers_out, "Retry-After", apr_psprintf(p, "%lu", (unsigned long)(60 * secs))); } return ftp_proxyerror(r, backend, HTTP_SERVICE_UNAVAILABLE, ftpmessage); } if (rc != 220) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } rc = proxy_ftp_command(apr_pstrcat(p, "USER ", user, CRLF, NULL), r, origin, bb, &ftpmessage); /* possible results; 230, 331, 332, 421, 500, 501, 530 */ /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ /* 230 User logged in, proceed. */ /* 331 User name okay, need password. */ /* 332 Need account for login. */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ /* (This may include errors such as command line too long.) */ /* 501 Syntax error in parameters or arguments. */ /* 530 Not logged in. */ if (rc == -1 || rc == 421) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); } if (rc == 530) { proxy_ftp_cleanup(r, backend); return ftp_unauthorized(r, 1); /* log it: user name guessing * attempt? */ } if (rc != 230 && rc != 331) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } if (rc == 331) { /* send password */ if (password == NULL) { proxy_ftp_cleanup(r, backend); return ftp_unauthorized(r, 0); } rc = proxy_ftp_command(apr_pstrcat(p, "PASS ", password, CRLF, NULL), r, origin, bb, &ftpmessage); /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ /* 230 User logged in, proceed. */ /* 332 Need account for login. */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ /* 501 Syntax error in parameters or arguments. */ /* 503 Bad sequence of commands. */ /* 530 Not logged in. */ if (rc == -1 || rc == 421) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); } if (rc == 332) { return ftp_proxyerror(r, backend, HTTP_UNAUTHORIZED, apr_pstrcat(p, "Need account for login: ", ftpmessage, NULL)); } /* @@@ questionable -- we might as well return a 403 Forbidden here */ if (rc == 530) { proxy_ftp_cleanup(r, backend); return ftp_unauthorized(r, 1); /* log it: passwd guessing * attempt? */ } if (rc != 230 && rc != 202) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } } apr_table_set(r->notes, "Directory-README", ftpmessage); /* 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) { path += 3; while (*path == '/') /* skip leading '/' (after root %2f) */ ++path; rc = proxy_ftp_command("CWD /" CRLF, r, origin, bb, &ftpmessage); if (rc == -1 || rc == 421) return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); } /* * set the directory (walk directory component by component): this is * what we must do if we don't know the OS type of the remote machine */ for (;;) { strp = strchr(path, '/'); if (strp == NULL) break; *strp = '\0'; len = decodeenc(path); /* Note! This decodes a %2f -> "/" */ if (strchr(path, '/')) { /* are there now any '/' characters? */ return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST, "Use of /%2f is only allowed at the base directory"); } /* NOTE: FTP servers do globbing on the path. * So we need to escape the URI metacharacters. * We use a special glob-escaping routine to escape globbing chars. * We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH */ rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", ftp_escape_globbingchars(p, path), CRLF, NULL), r, origin, bb, &ftpmessage); *strp = '/'; /* responses: 250, 421, 500, 501, 502, 530, 550 */ /* 250 Requested file action okay, completed. */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ /* 501 Syntax error in parameters or arguments. */ /* 502 Command not implemented. */ /* 530 Not logged in. */ /* 550 Requested action not taken. */ if (rc == -1 || rc == 421) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); } if (rc == 550) { return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); } if (rc != 250) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } path = strp + 1; } /* * IV: Make Data Connection? ------------------------- * * Try EPSV, if that fails... try PASV, if that fails... try PORT. *//* this temporarily switches off EPSV/PASV *//*goto bypass;*/ /* set up data connection - EPSV */ { apr_sockaddr_t *data_addr; char *data_ip; apr_port_t data_port; /* * The EPSV command replaces PASV where both IPV4 and IPV6 is * supported. Only the port is returned, the IP address is always the * same as that on the control connection. Example: Entering Extended * Passive Mode (|||6446|) */ rc = proxy_ftp_command("EPSV" CRLF, r, origin, bb, &ftpmessage); /* possible results: 227, 421, 500, 501, 502, 530 */ /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ /* 501 Syntax error in parameters or arguments. */ /* 502 Command not implemented. */ /* 530 Not logged in. */ if (rc == -1 || rc == 421) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); } if (rc != 229 && rc != 500 && rc != 501 && rc != 502) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } else if (rc == 229) { char *pstr; char *tok_cntx; pstr = ftpmessage; pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ if (pstr != NULL) { if (*(pstr + strlen(pstr) + 1) == '=') { pstr += strlen(pstr) + 2; } else { pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & * port params */ if (pstr != NULL) pstr = apr_strtok(NULL, ")", &tok_cntx); } } if (pstr) { apr_sockaddr_t *epsv_addr; data_port = atoi(pstr + 3); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: FTP: EPSV contacting remote host on port %d", data_port); if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: FTP: error creating EPSV socket"); proxy_ftp_cleanup(r, backend); return HTTP_INTERNAL_SERVER_ERROR; }#if !defined (TPF) && !defined(BEOS) if (conf->recv_buffer_size > 0 && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, conf->recv_buffer_size))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); }#endif rv = apr_socket_opt_set(data_sock, APR_TCP_NODELAY, 1); if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_socket_opt_set(APR_TCP_NODELAY): Failed to set"); } /* make the connection */ apr_socket_addr_get(&data_addr, APR_REMOTE, sock); apr_sockaddr_ip_get(&data_ip, data_addr); apr_sockaddr_info_get(&epsv_addr, data_ip, connect_addr->family, data_port, 0, p); rv = apr_socket_connect(data_sock, epsv_addr); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, "proxy: FTP: EPSV attempt to connect to %pI failed - Firewall/NAT?", epsv_addr); return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, "EPSV attempt to connect to %pI failed - firewall/NAT?", epsv_addr)); } else { connect = 1; } } else { /* and try the regular way */ apr_socket_close(data_sock); } } } /* set up data connection - PASV */ if (!connect) { rc = proxy_ftp_command("PASV" CRLF, r, origin, bb, &ftpmessage); /* possible results: 227, 421, 500, 501, 502, 530 */ /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ /* 501 Syntax error in parameters or arguments. */ /* 502 Command not implemented. */ /* 530 Not logged in. */ if (rc == -1 || rc == 421) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); } if (rc != 227 && rc != 502) { return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } else if (rc == 227) { unsigned int h0, h1, h2, h3, p0, p1; char *pstr; char *tok_cntx;/* FIXME: Check PASV against RFC1123 */ pstr = ftpmessage; pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ if (pstr != NULL) { if (*(pstr + strlen(pstr) + 1) == '=') { pstr += strlen(pstr) + 2; } else { pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & * port params */ if (pstr != NULL) pstr = apr_strtok(NULL, ")", &tok_cntx); } }/* FIXME: Only supports IPV4 - fix in RFC2428 */ if (pstr != NULL && (sscanf(pstr, "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { apr_sockaddr_t *pasv_addr; apr_port_t pasvport = (p1 << 8) + p0; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: FTP: 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) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: error creating PASV socket"); proxy_ftp_cleanup(r, backend); return HTTP_INTERNAL_SERVER_ERROR; }#if !defined (TPF) && !defined(BEOS) if (conf->recv_buffer_size > 0 && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, conf->recv_buffer_size))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); }#endif rv = apr_socket_opt_set(data_sock, APR_TCP_NODELAY, 1); if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_socket_opt_set(APR_TCP_NODELAY): Failed to set"); } /* 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); rv = apr_socket_connect(data_sock, pasv_addr); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, "proxy: FTP: PASV attempt to connect to %pI failed - Firewall/NAT?", pasv_addr); return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, "PASV attempt to connect to %pI failed - firewall/NAT?", pasv_addr)); } else { connect = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -