📄 proxy_ftp.c
字号:
strerror(errno), NULL))); } /* record request_time for HTTP/1.1 age calculation */ c->req_time = time(NULL); ctrl = ap_bcreate(p, B_RDWR | B_SOCKET); ap_bpushfd(ctrl, sock, sock);/* shouldn't we implement telnet control options here? */#ifdef CHARSET_EBCDIC ap_bsetflag(ctrl, B_ASCII2EBCDIC | B_EBCDIC2ASCII, 1);#endif /* CHARSET_EBCDIC */ /* possible results: */ /* 120 Service ready in nnn minutes. */ /* 220 Service ready for new user. */ /* 421 Service not available, closing control connection. */ ap_hard_timeout("proxy ftp", r); i = ftp_getrc_msg(ctrl, resp, sizeof resp); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1 || i == 421) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server")); }#if 0 if (i == 120) { /* * RFC2068 states: 14.38 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 ) *//**INDENT** Error@756: Unbalanced parens */ ap_set_header("Retry-After", ap_psprintf(p, "%u", 60 * wait_mins); return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp)); }#endif if (i != 220) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, resp)); } ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: connected."); ap_bvputs(ctrl, "USER ", user, CRLF, NULL); ap_bflush(ctrl); /* capture any errors */ ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: USER %s", user); /* 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. */ i = ftp_getrc(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1 || i == 421) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server")); } if (i == 530) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ftp_unauthorized(r, 1)); } if (i != 230 && i != 331) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, HTTP_BAD_GATEWAY); } if (i == 331) { /* send password */ if (password == NULL) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ftp_unauthorized(r, 0)); } ap_bvputs(ctrl, "PASS ", password, CRLF, NULL); ap_bflush(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: PASS %s", password); /* 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. */ i = ftp_getrc(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1 || i == 421) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server")); } if (i == 332) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_UNAUTHORIZED, "Need account for login")); } /* @@@ questionable -- we might as well return a 403 Forbidden here */ if (i == 530) /* log it: passwd guessing attempt? */ return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ftp_unauthorized(r, 1)); if (i != 230 && i != 202) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, HTTP_BAD_GATEWAY); } /* * 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; ap_bputs("CWD /" CRLF, ctrl); ap_bflush(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: CWD /"); /* possible results: 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. */ i = ftp_getrc(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1 || i == 421) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server")); else if (i == 550) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, HTTP_NOT_FOUND); else if (i != 250) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, HTTP_BAD_GATEWAY); }/* 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, '/')) != NULL; path = strp + 1) { char *slash = strp; *slash = '\0'; /* Skip multiple '/' (or trailing '/') to avoid 500 errors */ while (strp[1] == '/') ++strp; if (strp[1] == '\0') break; len = decodeenc(path); /* Note! This decodes a %2f -> "/" */ if (strchr(path, '/')) /* were there any '/' characters? */ return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_REQUEST, "Use of %2F is only allowed at the base directory")); ap_bvputs(ctrl, "CWD ", path, CRLF, NULL); ap_bflush(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: CWD %s", path); *slash = '/';/* 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. */ i = ftp_getrc(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1 || i == 421) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server")); if (i == 550) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, HTTP_NOT_FOUND); if (i == 500 || i == 501) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_REQUEST, "Syntax error in filename (reported by ftp server)")); if (i != 250) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, HTTP_BAD_GATEWAY); } if (parms != NULL && strncmp(parms, "type=", 5) == 0 && ap_isalpha(parms[5])) { /* * "type=d" forces a dir listing. The other types (i|a|e) are * directly used for the ftp TYPE command */ if (!(get_dirlisting = (parms[5] == 'd'))) xfer_type = ap_toupper(parms[5]); /* Check valid types, rather than ignoring invalid types silently: */ if (strchr("AEI", xfer_type) == NULL) return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_REQUEST, ap_pstrcat(r->pool, "ftp proxy supports only types 'a', 'i', or 'e': \"", parms, "\" is invalid.", NULL))); } else { /* make binary transfers the default */ xfer_type = 'I'; }/* try to set up PASV data connection first */ dsock = ap_psocket_ex(p, PF_INET, SOCK_STREAM, IPPROTO_TCP, 1); if (dsock == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, "proxy: error creating PASV socket")); }#if !defined (TPF) && !defined(BEOS) if (conf->recv_buffer_size) { if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF, (const char *)&conf->recv_buffer_size, sizeof(int)) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); } }#endif ap_bputs("PASV" CRLF, ctrl); ap_bflush(ctrl); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: PASV command issued");/* 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. */ i = ap_bgets(pasv, sizeof(pasv), ctrl); if (i == -1 || i == 421) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, "proxy: PASV: control connection is toast")); } else { pasv[i - 1] = '\0'; pstr = strtok(pasv, " "); /* separate result code */ if (pstr != NULL) { presult = atoi(pstr); if (*(pstr + strlen(pstr) + 1) == '=') pstr += strlen(pstr) + 2; else { pstr = strtok(NULL, "("); /* separate address & port * params */ if (pstr != NULL) pstr = strtok(NULL, ")"); } } else presult = atoi(pasv); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: returned status %d", presult); if (presult == 227 && pstr != NULL && (sscanf(pstr, "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { /* pardon the parens, but it makes gcc happy */ paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0; pport = (p1 << 8) + p0; ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: contacting host %d.%d.%d.%d:%d", h3, h2, h1, h0, pport); data_addr.sin_family = AF_INET; data_addr.sin_addr.s_addr = htonl(paddr); data_addr.sin_port = htons(pport); i = ap_proxy_doconnect(dsock, &data_addr, r); if (i == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, "Could not connect to remote machine: ", strerror(errno), NULL))); } pasvmode = 1; } else { ap_pclosesocket(p, dsock); /* and try the regular way */ dsock = -1; } } if (!pasvmode) { /* set up data connection */ clen = sizeof(struct sockaddr_in); if (getsockname(sock, (struct sockaddr *)&server, &clen) < 0) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, "proxy: error getting socket address")); } dsock = ap_psocket_ex(p, PF_INET, SOCK_STREAM, IPPROTO_TCP, 1); if (dsock == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, "proxy: error creating socket")); } if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) == -1) {#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, "proxy: error setting reuseaddr option"));#endif /* _OSD_POSIX */ } if (bind(dsock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_psprintf(p, "proxy: error binding to ftp data socket %s:%d", inet_ntoa(server.sin_addr), server.sin_port))); } listen(dsock, 2); /* only need a short queue */ }/* set request; "path" holds last path component */ len = decodeenc(path); if (strchr(path, '/')) /* were there any '/' characters? */ return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_REQUEST, "Use of %2F is only allowed at the base directory")); /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -