Lines 34-42
Link Here
|
34 |
*/ |
34 |
*/ |
35 |
#define USE_MDTM |
35 |
#define USE_MDTM |
36 |
|
36 |
|
|
|
37 |
/* The state machine states.*/ |
38 |
enum ftp_states { |
39 |
ftp_connect = 0, /* Init socket connection (wait for welcome)*/ |
40 |
ftp_send_user, |
41 |
ftp_send_pass, |
42 |
ftp_choose_action, |
43 |
ftp_check_type, |
44 |
ftp_check_trans_mode, |
45 |
ftp_check_transfer, |
46 |
ftp_connect_data_port, |
47 |
ftp_send_retr, |
48 |
ftp_send_stor, |
49 |
ftp_send_list, |
50 |
ftp_start_response, |
51 |
ftp_transfer_stream, |
52 |
ftp_finish_response, |
53 |
ftp_cleanup, |
54 |
ftp_error |
55 |
}; |
37 |
|
56 |
|
|
|
57 |
typedef struct { |
58 |
char *ip; |
59 |
int port; |
60 |
apr_socket_t *sock; |
61 |
apr_socket_t *local_sock; |
62 |
} data_conn; |
63 |
|
64 |
|
65 |
/* The local data is kept different from the normal ftp_conn_data |
66 |
* for the future where we will be able to reuse the connection to |
67 |
* an ftp server. The data here is relevant for a single request |
68 |
* while the data in ftp_conn_data is relevant for a full connection |
69 |
* period.(may span multiple requests).*/ |
70 |
typedef struct { |
71 |
proxy_server_conf *conf; |
72 |
apr_sockaddr_t *data_addr; |
73 |
data_conn data; |
74 |
int rc; |
75 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
76 |
apr_time_t mtime; |
77 |
#endif |
78 |
|
79 |
} ftp_local_data; |
80 |
|
81 |
/* modes of data connection (EPORT is not implemented right now) */ |
82 |
enum ftp_connection_modes { |
83 |
EPSV = 0, |
84 |
PASV, |
85 |
PORT, |
86 |
EPORT |
87 |
}; |
88 |
|
89 |
/*The possible actions that the user can specify Currently STOR is not |
90 |
* implemented but is meant for the immediate future. We should perhaps |
91 |
* look at allowing arbitrary actions either in a header or in the url*/ |
92 |
enum ftp_action { |
93 |
LISTING = 0, |
94 |
RETR, |
95 |
STOR |
96 |
}; |
97 |
|
98 |
typedef struct { |
99 |
enum ftp_connection_modes mode; |
100 |
char xfer_type; |
101 |
enum ftp_states state; |
102 |
proxy_conn_rec *p_conn; |
103 |
const char* user; |
104 |
const char* password; |
105 |
char* msg; |
106 |
char* size; |
107 |
char* path; |
108 |
char* url; |
109 |
apr_uri_t *uri; |
110 |
enum ftp_action action; |
111 |
ftp_local_data cur; /* The data that change in each request*/ |
112 |
} ftp_conn_data; |
113 |
|
114 |
|
38 |
module AP_MODULE_DECLARE_DATA proxy_ftp_module; |
115 |
module AP_MODULE_DECLARE_DATA proxy_ftp_module; |
39 |
|
116 |
|
|
|
117 |
const char *proxy_function = "FTP"; |
40 |
/* |
118 |
/* |
41 |
* Decodes a '%' escaped string, and returns the number of characters |
119 |
* Decodes a '%' escaped string, and returns the number of characters |
42 |
*/ |
120 |
*/ |
Lines 632-703
Link Here
|
632 |
return rc; |
710 |
return rc; |
633 |
} |
711 |
} |
634 |
|
712 |
|
635 |
/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */ |
|
|
636 |
static int ftp_set_TYPE(char xfer_type, request_rec *r, conn_rec *ftp_ctrl, |
637 |
apr_bucket_brigade *bb, char **pmessage) |
638 |
{ |
639 |
char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */ |
640 |
int ret = HTTP_OK; |
641 |
int rc; |
642 |
|
643 |
/* set desired type */ |
644 |
old_type[0] = xfer_type; |
645 |
|
646 |
rc = proxy_ftp_command(apr_pstrcat(r->pool, "TYPE ", old_type, CRLF, NULL), |
647 |
r, ftp_ctrl, bb, pmessage); |
648 |
/* responses: 200, 421, 500, 501, 504, 530 */ |
649 |
/* 200 Command okay. */ |
650 |
/* 421 Service not available, closing control connection. */ |
651 |
/* 500 Syntax error, command unrecognized. */ |
652 |
/* 501 Syntax error in parameters or arguments. */ |
653 |
/* 504 Command not implemented for that parameter. */ |
654 |
/* 530 Not logged in. */ |
655 |
if (rc == -1 || rc == 421) { |
656 |
ret = ap_proxyerror(r, HTTP_BAD_GATEWAY, |
657 |
"Error reading from remote server"); |
658 |
} |
659 |
else if (rc != 200 && rc != 504) { |
660 |
ret = ap_proxyerror(r, HTTP_BAD_GATEWAY, |
661 |
"Unable to set transfer type"); |
662 |
} |
663 |
/* Allow not implemented */ |
664 |
else if (rc == 504) |
665 |
/* ignore it silently */; |
666 |
|
667 |
return ret; |
668 |
} |
669 |
|
670 |
|
671 |
/* Return the current directory which we have selected on the FTP server, or NULL */ |
672 |
static char *ftp_get_PWD(request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
673 |
{ |
674 |
char *cwd = NULL; |
675 |
char *ftpmessage = NULL; |
676 |
|
677 |
/* responses: 257, 500, 501, 502, 421, 550 */ |
678 |
/* 257 "<directory-name>" <commentary> */ |
679 |
/* 421 Service not available, closing control connection. */ |
680 |
/* 500 Syntax error, command unrecognized. */ |
681 |
/* 501 Syntax error in parameters or arguments. */ |
682 |
/* 502 Command not implemented. */ |
683 |
/* 550 Requested action not taken. */ |
684 |
switch (proxy_ftp_command("PWD" CRLF, r, ftp_ctrl, bb, &ftpmessage)) { |
685 |
case -1: |
686 |
case 421: |
687 |
case 550: |
688 |
ap_proxyerror(r, HTTP_BAD_GATEWAY, |
689 |
"Failed to read PWD on ftp server"); |
690 |
break; |
691 |
|
692 |
case 257: { |
693 |
const char *dirp = ftpmessage; |
694 |
cwd = ap_getword_conf(r->pool, &dirp); |
695 |
} |
696 |
} |
697 |
return cwd; |
698 |
} |
699 |
|
700 |
|
701 |
/* Common routine for failed authorization (i.e., missing or wrong password) |
713 |
/* Common routine for failed authorization (i.e., missing or wrong password) |
702 |
* to an ftp service. This causes most browsers to retry the request |
714 |
* to an ftp service. This causes most browsers to retry the request |
703 |
* with username and password (which was presumably queried from the user) |
715 |
* with username and password (which was presumably queried from the user) |
Lines 714-845
Link Here
|
714 |
*/ |
726 |
*/ |
715 |
if (log_it) |
727 |
if (log_it) |
716 |
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, |
728 |
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, |
717 |
"proxy: missing or failed auth to %s", |
729 |
"proxy: missing or failed auth to %s", |
718 |
apr_uri_unparse(r->pool, |
730 |
apr_uri_unparse(r->pool, |
719 |
&r->parsed_uri, APR_URI_UNP_OMITPATHINFO)); |
731 |
&r->parsed_uri, APR_URI_UNP_OMITPATHINFO)); |
720 |
|
732 |
|
721 |
apr_table_setn(r->err_headers_out, "WWW-Authenticate", |
733 |
apr_table_setn(r->err_headers_out, "WWW-Authenticate", |
722 |
apr_pstrcat(r->pool, "Basic realm=\"", |
734 |
apr_pstrcat(r->pool, "Basic realm=\"", |
723 |
apr_uri_unparse(r->pool, &r->parsed_uri, |
735 |
apr_uri_unparse(r->pool, &r->parsed_uri, |
724 |
APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO), |
736 |
APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO), |
725 |
"\"", NULL)); |
737 |
"\"", NULL)); |
726 |
|
|
|
727 |
return HTTP_UNAUTHORIZED; |
728 |
} |
738 |
} |
729 |
|
739 |
|
730 |
static |
740 |
static |
731 |
apr_status_t proxy_ftp_cleanup(request_rec *r, proxy_conn_rec *backend) |
741 |
int proxyerror(request_rec *r, proxy_conn_rec *conn, int statuscode, const char *message) |
732 |
{ |
742 |
{ |
733 |
|
743 |
conn->close = 1; |
734 |
backend->close = 1; |
|
|
735 |
ap_set_module_config(r->connection->conn_config, &proxy_ftp_module, NULL); |
744 |
ap_set_module_config(r->connection->conn_config, &proxy_ftp_module, NULL); |
736 |
ap_proxy_release_connection("FTP", backend, r->server); |
745 |
ap_proxy_release_connection(proxy_function, conn, r->server); |
737 |
|
746 |
|
738 |
return OK; |
747 |
return ap_proxyerror(r, statuscode, message); |
739 |
} |
748 |
} |
740 |
|
749 |
|
741 |
static |
750 |
static |
742 |
int ftp_proxyerror(request_rec *r, proxy_conn_rec *conn, int statuscode, const char *message) |
751 |
void parse_ftp_auth(ftp_conn_data *ftp_data, request_rec *r) |
743 |
{ |
752 |
{ |
744 |
proxy_ftp_cleanup(r, conn); |
753 |
/* char *account = NULL; how to supply an account in a URL? */ |
745 |
return ap_proxyerror(r, statuscode, message); |
|
|
746 |
} |
747 |
/* |
748 |
* Handles direct access of ftp:// URLs |
749 |
* Original (Non-PASV) version from |
750 |
* Troy Morrison <spiffnet@zoom.com> |
751 |
* PASV added by Chuck |
752 |
* Filters by [Graham Leggett <minfrin@sharp.fm>] |
753 |
*/ |
754 |
static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, |
755 |
proxy_server_conf *conf, char *url, |
756 |
const char *proxyhost, apr_port_t proxyport) |
757 |
{ |
758 |
apr_pool_t *p = r->pool; |
759 |
conn_rec *c = r->connection; |
760 |
proxy_conn_rec *backend; |
761 |
apr_socket_t *sock, *local_sock, *data_sock = NULL; |
762 |
apr_sockaddr_t *connect_addr = NULL; |
763 |
apr_status_t rv; |
764 |
conn_rec *origin, *data = NULL; |
765 |
apr_status_t err = APR_SUCCESS; |
766 |
apr_status_t uerr = APR_SUCCESS; |
767 |
apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); |
768 |
char *buf, *connectname; |
769 |
apr_port_t connectport; |
770 |
char buffer[MAX_STRING_LEN]; |
771 |
char *ftpmessage = NULL; |
772 |
char *path, *strp, *type_suffix, *cwd = NULL; |
773 |
apr_uri_t uri; |
774 |
char *user = NULL; |
754 |
char *user = NULL; |
775 |
/* char *account = NULL; how to supply an account in a URL? */ |
|
|
776 |
const char *password = NULL; |
755 |
const char *password = NULL; |
777 |
int len, rc; |
756 |
|
778 |
int one = 1; |
757 |
/* |
779 |
char *size = NULL; |
758 |
* The "Authorization:" header must be checked first. We allow the user |
780 |
char xfer_type = 'A'; /* after ftp login, the default is ASCII */ |
759 |
* to "override" the URL-coded user [ & password ] in the Browsers' |
781 |
int dirlisting = 0; |
760 |
* User&Password Dialog. NOTE that this is only marginally more secure |
782 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
761 |
* than having the password travel in plain as part of the URL, because |
783 |
apr_time_t mtime = 0L; |
762 |
* Basic Auth simply uuencodes the plain text password. But chances are |
784 |
#endif |
763 |
* still smaller that the URL is logged regularly. |
785 |
|
764 |
*/ |
786 |
/* stuff for PASV mode */ |
765 |
if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL |
787 |
int connect = 0, use_port = 0; |
766 |
&& strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 |
788 |
char dates[APR_RFC822_DATE_LEN]; |
767 |
&& (password = ap_pbase64decode(r->pool, password))[0] != ':') { |
789 |
int status; |
768 |
/* |
790 |
apr_pool_t *address_pool; |
769 |
* Note that this allocation has to be made from p_conn->pool |
791 |
|
770 |
* because it has the lifetime of the connection. The other |
792 |
/* is this for us? */ |
771 |
* allocations are temporary and can be tossed away any time. |
793 |
if (proxyhost) { |
772 |
*/ |
794 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
773 |
user = ap_getword_nulls(ftp_data->p_conn->pool, &password, ':'); |
795 |
"proxy: FTP: declining URL %s - proxyhost %s specified:", url, proxyhost); |
774 |
r->ap_auth_type = "Basic"; |
796 |
return DECLINED; /* proxy connections are via HTTP */ |
775 |
r->user = r->parsed_uri.user = user; |
797 |
} |
776 |
} |
798 |
if (strncasecmp(url, "ftp:", 4)) { |
777 |
else if ((user = r->parsed_uri.user) != NULL) { |
799 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
778 |
user = apr_pstrdup(ftp_data->p_conn->pool, user); |
800 |
"proxy: FTP: declining URL %s - not ftp:", url); |
779 |
decodeenc(user); |
801 |
return DECLINED; /* only interested in FTP */ |
780 |
if ((password = r->parsed_uri.password) != NULL) { |
|
|
781 |
char *tmp = apr_pstrdup(ftp_data->p_conn->pool, password); |
782 |
decodeenc(tmp); |
783 |
password = tmp; |
784 |
} |
802 |
} |
785 |
} |
|
|
786 |
else { |
787 |
user = "anonymous"; |
788 |
password = "apache-proxy@"; |
789 |
} |
790 |
ftp_data->user = user; |
791 |
ftp_data->password = password; |
792 |
} |
793 |
|
794 |
static |
795 |
void ftp_debug(request_rec *r, char* fn, char* msg) |
796 |
{ |
803 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
797 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
804 |
"proxy: FTP: serving URL %s", url); |
798 |
"proxy:<FTP: [%s] %s", fn, msg); |
|
|
799 |
} |
805 |
|
800 |
|
|
|
801 |
static |
802 |
void set_ftp_error(ftp_conn_data* ftp_data, int rc, char* msg) |
803 |
{ |
804 |
ftp_data->cur.rc = rc; |
805 |
ftp_data->msg = msg; |
806 |
ftp_data->state = ftp_error; |
807 |
} |
806 |
|
808 |
|
807 |
/* |
809 |
static |
808 |
* I: Who Do I Connect To? ----------------------- |
810 |
int on_init(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
809 |
* |
811 |
{ |
810 |
* Break up the URL to determine the host to connect to |
812 |
char *path = 0; |
811 |
*/ |
813 |
char xfer_type = 'A'; /* after ftp login, the default is ASCII */ |
|
|
814 |
int dirlisting = 0; |
815 |
apr_pool_t *p = r->pool; |
816 |
char *type_suffix = 0; |
812 |
|
817 |
|
813 |
/* we only support GET and HEAD */ |
818 |
path = ftp_data->uri->path; |
814 |
if (r->method_number != M_GET) |
|
|
815 |
return HTTP_NOT_IMPLEMENTED; |
816 |
|
819 |
|
817 |
/* We break the URL into host, port, path-search */ |
|
|
818 |
if (r->parsed_uri.hostname == NULL) { |
819 |
if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) { |
820 |
return ap_proxyerror(r, HTTP_BAD_REQUEST, |
821 |
apr_psprintf(p, "URI cannot be parsed: %s", url)); |
822 |
} |
823 |
connectname = uri.hostname; |
824 |
connectport = uri.port; |
825 |
path = apr_pstrdup(p, uri.path); |
826 |
} |
827 |
else { |
828 |
connectname = r->parsed_uri.hostname; |
829 |
connectport = r->parsed_uri.port; |
830 |
path = apr_pstrdup(p, r->parsed_uri.path); |
831 |
} |
832 |
if (connectport == 0) { |
833 |
connectport = apr_uri_port_of_scheme("ftp"); |
834 |
} |
835 |
path = (path != NULL && path[0] != '\0') ? &path[1] : ""; |
820 |
path = (path != NULL && path[0] != '\0') ? &path[1] : ""; |
|
|
821 |
ftp_data->path = apr_pstrdup(p, path); |
836 |
|
822 |
|
837 |
type_suffix = strchr(path, ';'); |
823 |
type_suffix = strchr(path, ';'); |
838 |
if (type_suffix != NULL) |
824 |
if (type_suffix != NULL) |
839 |
*(type_suffix++) = '\0'; |
825 |
*(type_suffix++) = '\0'; |
840 |
|
826 |
|
841 |
if (type_suffix != NULL && strncmp(type_suffix, "type=", 5) == 0 |
827 |
if (type_suffix != NULL && strncmp(type_suffix, "type=", 5) == 0 |
842 |
&& apr_isalpha(type_suffix[5])) { |
828 |
&& apr_isalpha(type_suffix[5])) { |
843 |
/* "type=d" forces a dir listing. |
829 |
/* "type=d" forces a dir listing. |
844 |
* The other types (i|a|e) are directly used for the ftp TYPE command |
830 |
* The other types (i|a|e) are directly used for the ftp TYPE command |
845 |
*/ |
831 |
*/ |
Lines 849-1666
Link Here
|
849 |
/* Check valid types, rather than ignoring invalid types silently: */ |
835 |
/* Check valid types, rather than ignoring invalid types silently: */ |
850 |
if (strchr("AEI", xfer_type) == NULL) |
836 |
if (strchr("AEI", xfer_type) == NULL) |
851 |
return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool, |
837 |
return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool, |
852 |
"ftp proxy supports only types 'a', 'i', or 'e': \"", |
838 |
"ftp proxy supports only types 'a', 'i', or 'e': \"", |
853 |
type_suffix, "\" is invalid.", NULL)); |
839 |
type_suffix, "\" is invalid.", NULL)); |
854 |
} |
840 |
} |
855 |
else { |
841 |
else { |
856 |
/* make binary transfers the default */ |
842 |
/* make binary transfers the default */ |
857 |
xfer_type = 'I'; |
843 |
xfer_type = 'I'; |
858 |
} |
844 |
} |
|
|
845 |
ftp_data->xfer_type = xfer_type; |
846 |
return OK; |
847 |
} |
859 |
|
848 |
|
|
|
849 |
/* Possible results: |
850 |
* 120 Service ready in nnn minutes. |
851 |
* 220 server.com FTP server (Version vvv) ready. |
852 |
* 421 Service not available, closing control connection. |
853 |
* */ |
854 |
static |
855 |
int on_connect(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
856 |
{ |
857 |
char *msg = NULL; |
858 |
int rc = proxy_ftp_command(0, r, ftp_ctrl, bb, &msg); |
859 |
ftp_debug(r, "on_connect", msg); |
860 |
switch (rc / 100) { |
861 |
case 1: |
862 |
/* |
863 |
* RFC2616 states: 14.37 Retry-After |
864 |
* |
865 |
* The Retry-After response-header field can be used with a 503 (Service |
866 |
* Unavailable) response to indicate how long the service is expected |
867 |
* to be unavailable to the requesting client. [...] The value of |
868 |
* this field can be either an HTTP-date or an integer number of |
869 |
* seconds (in decimal) after the time of the response. Retry-After |
870 |
* = "Retry-After" ":" ( HTTP-date | delta-seconds ) |
871 |
*/ |
872 |
{ |
873 |
char *secs_str = msg; |
874 |
time_t secs; |
860 |
|
875 |
|
861 |
/* |
876 |
/* Look for a number, preceded by whitespace */ |
862 |
* The "Authorization:" header must be checked first. We allow the user |
877 |
while (*secs_str) |
863 |
* to "override" the URL-coded user [ & password ] in the Browsers' |
878 |
if ((secs_str==msg || apr_isspace(secs_str[-1])) && |
864 |
* User&Password Dialog. NOTE that this is only marginally more secure |
879 |
apr_isdigit(secs_str[0])) |
865 |
* than having the password travel in plain as part of the URL, because |
880 |
break; |
866 |
* Basic Auth simply uuencodes the plain text password. But chances are |
881 |
if (*secs_str != '\0') { |
867 |
* still smaller that the URL is logged regularly. |
882 |
secs = atol(secs_str); |
868 |
*/ |
883 |
apr_table_add(r->headers_out, "Retry-After", |
869 |
if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL |
884 |
apr_psprintf(r->connection->pool, "%lu", (unsigned long)(60 * secs))); |
870 |
&& strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 |
885 |
} |
871 |
&& (password = ap_pbase64decode(r->pool, password))[0] != ':') { |
886 |
} |
872 |
/* |
887 |
set_ftp_error(ftp_data, rc, msg); |
873 |
* Note that this allocation has to be made from r->connection->pool |
888 |
return HTTP_SERVICE_UNAVAILABLE; |
874 |
* because it has the lifetime of the connection. The other |
889 |
|
875 |
* allocations are temporary and can be tossed away any time. |
890 |
case 2: |
876 |
*/ |
891 |
ftp_data->state = ftp_send_user; |
877 |
user = ap_getword_nulls(r->connection->pool, &password, ':'); |
892 |
return OK; |
878 |
r->ap_auth_type = "Basic"; |
893 |
|
879 |
r->user = r->parsed_uri.user = user; |
894 |
default: |
|
|
895 |
set_ftp_error(ftp_data, rc, msg); |
896 |
return HTTP_BAD_GATEWAY; |
880 |
} |
897 |
} |
881 |
else if ((user = r->parsed_uri.user) != NULL) { |
898 |
} |
882 |
user = apr_pstrdup(p, user); |
|
|
883 |
decodeenc(user); |
884 |
if ((password = r->parsed_uri.password) != NULL) { |
885 |
char *tmp = apr_pstrdup(p, password); |
886 |
decodeenc(tmp); |
887 |
password = tmp; |
888 |
} |
889 |
} |
890 |
else { |
891 |
user = "anonymous"; |
892 |
password = "apache-proxy@"; |
893 |
} |
894 |
|
899 |
|
895 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
900 |
/* possible results; 230, 331, 332, 421, 500, 501, 530 |
896 |
"proxy: FTP: connecting %s to %s:%d", url, connectname, connectport); |
901 |
* states: 1 - error, 2 - success; 3 - send password, 4,5 fail |
|
|
902 |
* 230 User logged in, proceed. |
903 |
* 331 User name okay, need password. |
904 |
* 332 Need account for login. |
905 |
* 421 Service not available, closing control connection. |
906 |
* 500 Syntax error, command unrecognized. |
907 |
* (This may include errors such as command line too long.) |
908 |
* 501 Syntax error in parameters or arguments. |
909 |
* 530 Not logged in. |
910 |
**/ |
911 |
static |
912 |
int send_user(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
913 |
{ |
914 |
int rc; |
915 |
char *msg = NULL; |
897 |
|
916 |
|
898 |
if (worker->is_address_reusable) { |
917 |
parse_ftp_auth(ftp_data, r); |
899 |
if (!worker->cp->addr) { |
918 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "USER ", ftp_data->user, CRLF, NULL), |
900 |
if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) { |
919 |
r, ftp_ctrl, bb, &msg); |
901 |
ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, |
920 |
ftp_debug(r, "user", msg); |
902 |
"proxy: FTP: lock"); |
921 |
switch (rc / 100) { |
903 |
return HTTP_INTERNAL_SERVER_ERROR; |
922 |
case 2: |
|
|
923 |
apr_table_set(r->notes, "Directory-README", msg); |
924 |
ftp_data->state = ftp_choose_action; |
925 |
return OK; |
926 |
case 3: |
927 |
ftp_data->state = ftp_send_pass; |
928 |
return OK; |
929 |
case 5: |
930 |
if (rc == 530 ) { |
931 |
ftp_unauthorized(r, 1); /* log it: user name guessing attempt?*/ |
932 |
return HTTP_UNAUTHORIZED; |
904 |
} |
933 |
} |
905 |
} |
934 |
default: |
906 |
connect_addr = worker->cp->addr; |
935 |
set_ftp_error(ftp_data, rc, msg); |
907 |
address_pool = worker->cp->pool; |
936 |
return HTTP_BAD_GATEWAY; |
908 |
} |
937 |
} |
909 |
else |
938 |
} |
910 |
address_pool = r->pool; |
|
|
911 |
|
939 |
|
912 |
/* do a DNS lookup for the destination host */ |
940 |
/* possible results 202, 230, 332, 421, 500, 501, 503, 530 |
913 |
if (!connect_addr) |
941 |
* 230 User logged in, proceed. |
914 |
err = apr_sockaddr_info_get(&(connect_addr), |
942 |
* 332 Need account for login. |
915 |
connectname, APR_UNSPEC, |
943 |
* 421 Service not available, closing control connection. |
916 |
connectport, 0, |
944 |
* 500 Syntax error, command unrecognized. |
917 |
address_pool); |
945 |
* 501 Syntax error in parameters or arguments. |
918 |
if (worker->is_address_reusable && !worker->cp->addr) { |
946 |
* 503 Bad sequence of commands. |
919 |
worker->cp->addr = connect_addr; |
947 |
* 530 Not logged in. */ |
920 |
if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { |
948 |
static |
921 |
ap_log_error(APLOG_MARK, APLOG_ERR, uerr, r->server, |
949 |
int send_pass(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
922 |
"proxy: FTP: unlock"); |
950 |
{ |
923 |
} |
951 |
int rc; |
924 |
} |
952 |
char *msg = NULL; |
925 |
/* |
|
|
926 |
* get all the possible IP addresses for the destname and loop through |
927 |
* them until we get a successful connection |
928 |
*/ |
929 |
if (APR_SUCCESS != err) { |
930 |
return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, |
931 |
"DNS lookup failure for: ", |
932 |
connectname, NULL)); |
933 |
} |
934 |
|
953 |
|
935 |
/* check if ProxyBlock directive on this host */ |
954 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "PASS ", ftp_data->password, CRLF, NULL), |
936 |
if (OK != ap_proxy_checkproxyblock(r, conf, connect_addr)) { |
955 |
r, ftp_ctrl, bb, &msg); |
937 |
return ap_proxyerror(r, HTTP_FORBIDDEN, |
956 |
ftp_debug(r, "pass", msg); |
938 |
"Connect to remote machine blocked"); |
957 |
switch (rc / 100) { |
939 |
} |
958 |
case 2: |
940 |
|
959 |
apr_table_set(r->notes, "Directory-README", msg); |
941 |
/* create space for state information */ |
960 |
ftp_data->state = ftp_choose_action; |
942 |
backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_ftp_module); |
961 |
return OK; |
943 |
if (!backend) { |
962 |
case 3: |
944 |
status = ap_proxy_acquire_connection("FTP", &backend, worker, r->server); |
963 |
apr_pstrcat(r->connection->pool, "Need account for login: ", msg, NULL); |
945 |
if (status != OK) { |
964 |
return HTTP_UNAUTHORIZED; |
946 |
if (backend) { |
965 |
case 5: |
947 |
backend->close = 1; |
966 |
if (rc == 530) { |
948 |
ap_proxy_release_connection("FTP", backend, r->server); |
967 |
ftp_unauthorized(r, 1); /* log it: user name guessing attempt?*/ |
|
|
968 |
return HTTP_UNAUTHORIZED; |
949 |
} |
969 |
} |
950 |
return status; |
970 |
default: |
951 |
} |
971 |
set_ftp_error(ftp_data, rc, msg); |
952 |
/* TODO: see if ftp could use determine_connection */ |
972 |
return HTTP_BAD_GATEWAY; |
953 |
backend->addr = connect_addr; |
|
|
954 |
ap_set_module_config(c->conn_config, &proxy_ftp_module, backend); |
955 |
} |
973 |
} |
|
|
974 |
} |
956 |
|
975 |
|
|
|
976 |
/* possible results: 250, 421, 500, 501, 502, 530, 550 |
977 |
* 250 Requested file action okay, completed. |
978 |
* 421 Service not available, closing control connection. |
979 |
* 500 Syntax error, command unrecognized. |
980 |
* 501 Syntax error in parameters or arguments. |
981 |
* 502 Command not implemented. |
982 |
* 530 Not logged in. |
983 |
* 550 Requested action not taken. */ |
957 |
|
984 |
|
958 |
/* |
985 |
/* (from FreeBSD ftpd): |
959 |
* II: Make the Connection ----------------------- |
986 |
* SIZE is not in RFC959, but Postel has blessed it and |
960 |
* |
987 |
* it will be in the updated RFC. |
961 |
* We have determined who to connect to. Now make the connection. |
988 |
* |
962 |
*/ |
989 |
* Return size of file in a format suitable for |
|
|
990 |
* using with RESTART (we just count bytes). |
991 |
*/ |
992 |
/* from draft-ietf-ftpext-mlst-14.txt: |
993 |
* This value will |
994 |
* change depending on the current STRUcture, MODE and TYPE of the data |
995 |
* connection, or a data connection which would be created were one |
996 |
* created now. Thus, the result of the SIZE command is dependent on |
997 |
* the currently established STRU, MODE and TYPE parameters. |
998 |
*/ |
963 |
|
999 |
|
|
|
1000 |
static |
1001 |
int send_size(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1002 |
{ |
1003 |
int rc; |
1004 |
char *msg = NULL; |
964 |
|
1005 |
|
965 |
if (ap_proxy_connect_backend("FTP", backend, worker, r->server)) { |
1006 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "SIZE ", ftp_data->path, CRLF, NULL), |
966 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1007 |
r, ftp_ctrl, bb, &msg); |
967 |
"proxy: FTP: an error occurred creating a new connection to %pI (%s)", |
1008 |
ftp_debug(r, "size", msg); |
968 |
connect_addr, connectname); |
1009 |
switch (rc / 100) { |
969 |
proxy_ftp_cleanup(r, backend); |
1010 |
case 2: |
970 |
return HTTP_SERVICE_UNAVAILABLE; |
1011 |
{ |
|
|
1012 |
int j; |
1013 |
for (j = 0; apr_isdigit(msg[j]); j++); |
1014 |
msg[j] = '\0'; |
1015 |
if (msg[0] != '\0') |
1016 |
ftp_data->size = msg; |
1017 |
} |
1018 |
ftp_data->action = RETR; |
1019 |
ftp_data->state = ftp_check_type; |
1020 |
return OK; |
1021 |
case 5: |
1022 |
if (rc == 550) { |
1023 |
ftp_data->action = LISTING; |
1024 |
return OK; |
1025 |
} |
1026 |
default: |
1027 |
set_ftp_error(ftp_data, rc, msg); |
1028 |
return HTTP_BAD_GATEWAY; |
971 |
} |
1029 |
} |
|
|
1030 |
} |
972 |
|
1031 |
|
973 |
if (!backend->connection) { |
1032 |
/* responses: 250, 421, 500, 501, 502, 530, 550 |
974 |
status = ap_proxy_connection_create("FTP", backend, c, r->server); |
1033 |
* 250 Requested file action okay, completed. |
975 |
if (status != OK) { |
1034 |
* 421 Service not available, closing control connection. |
976 |
proxy_ftp_cleanup(r, backend); |
1035 |
* 500 Syntax error, command unrecognized. |
977 |
return status; |
1036 |
* 501 Syntax error in parameters or arguments. |
978 |
} |
1037 |
* 502 Command not implemented. |
|
|
1038 |
* 530 Not logged in. |
1039 |
* 550 Requested action not taken. */ |
1040 |
static |
1041 |
int send_cwd(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1042 |
{ |
1043 |
int rc; |
1044 |
char *msg = NULL; |
1045 |
|
1046 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "CWD ", ftp_data->path, CRLF, NULL), |
1047 |
r, ftp_ctrl, bb, &msg); |
1048 |
ftp_debug(r, "cwd", msg); |
1049 |
switch (rc / 100) { |
1050 |
case 2: |
1051 |
ftp_data->state = ftp_check_type; |
1052 |
ftp_data->action = LISTING; |
1053 |
return OK; |
1054 |
case 5: |
1055 |
if (rc == 550) { |
1056 |
ftp_data->action = RETR; |
1057 |
return OK; |
1058 |
} |
1059 |
default: |
1060 |
set_ftp_error(ftp_data, rc, msg); |
1061 |
return HTTP_BAD_GATEWAY; |
979 |
} |
1062 |
} |
|
|
1063 |
} |
980 |
|
1064 |
|
981 |
/* Use old naming */ |
1065 |
static |
982 |
origin = backend->connection; |
1066 |
int choose_action(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
983 |
sock = backend->sock; |
1067 |
{ |
|
|
1068 |
char* path = ftp_data->path; |
1069 |
enum ftp_action action = LISTING; /* May be*/ |
1070 |
int fail = 1; |
1071 |
int len = 0; |
1072 |
int rc = 0; |
1073 |
/* Special handling for leading "%2f": this enforces a "cwd /" |
1074 |
* out of the $HOME directory which was the starting point after login |
1075 |
*/ |
1076 |
if (strncasecmp(path, "%2f", 3) == 0) { |
1077 |
path += 2; |
1078 |
path[0] = '/'; |
1079 |
} |
984 |
|
1080 |
|
985 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1081 |
decodeenc(path); /* Note! This decodes a %2f -> "/" */ |
986 |
"proxy: FTP: control connection complete"); |
|
|
987 |
|
1082 |
|
988 |
|
1083 |
/* NOTE: FTP servers do globbing on the path. |
989 |
/* |
1084 |
* So we need to escape the URI metacharacters. |
990 |
* III: Send Control Request ------------------------- |
1085 |
* We use a special glob-escaping routine to escape globbing chars. |
991 |
* |
1086 |
* We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH |
992 |
* Log into the ftp server, send the username & password, change to the |
|
|
993 |
* correct directory... |
994 |
*/ |
1087 |
*/ |
|
|
1088 |
path = ftp_escape_globbingchars(r->connection->pool, path); |
1089 |
ftp_data->path = path; |
1090 |
ftp_data->state = ftp_check_type; |
995 |
|
1091 |
|
|
|
1092 |
if (r-> method_number == M_PUT) { |
1093 |
ftp_data->action = STOR; |
1094 |
set_ftp_error(ftp_data, rc, "Not implemented"); |
1095 |
return HTTP_BAD_REQUEST; /* Not implemented*/ |
1096 |
} |
996 |
|
1097 |
|
997 |
/* possible results: */ |
1098 |
|
998 |
/* 120 Service ready in nnn minutes. */ |
1099 |
len = strlen(ftp_data->path); |
999 |
/* 220 Service ready for new user. */ |
1100 |
|
1000 |
/* 421 Service not available, closing control connection. */ |
1101 |
/* If len == 0 then it must be a directory (you can't RETR nothing)*/ |
1001 |
rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage); |
1102 |
if (!len) { |
1002 |
if (rc == -1 || rc == 421) { |
1103 |
ftp_data->action = LISTING; |
1003 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); |
1104 |
return OK; |
1004 |
} |
1105 |
} |
1005 |
if (rc == 120) { |
|
|
1006 |
/* |
1007 |
* RFC2616 states: 14.37 Retry-After |
1008 |
* |
1009 |
* The Retry-After response-header field can be used with a 503 (Service |
1010 |
* Unavailable) response to indicate how long the service is expected |
1011 |
* to be unavailable to the requesting client. [...] The value of |
1012 |
* this field can be either an HTTP-date or an integer number of |
1013 |
* seconds (in decimal) after the time of the response. Retry-After |
1014 |
* = "Retry-After" ":" ( HTTP-date | delta-seconds ) |
1015 |
*/ |
1016 |
char *secs_str = ftpmessage; |
1017 |
time_t secs; |
1018 |
|
1106 |
|
1019 |
/* Look for a number, preceded by whitespace */ |
1107 |
/* Do we look like a file?*/ |
1020 |
while (*secs_str) |
1108 |
if (path[len - 1] != '/') |
1021 |
if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) && |
1109 |
action = RETR; /*May be.*/ |
1022 |
apr_isdigit(secs_str[0])) |
1110 |
else { |
1023 |
break; |
1111 |
/* No files with an '/' end possible*/ |
1024 |
if (*secs_str != '\0') { |
1112 |
ftp_data->action = LISTING; |
1025 |
secs = atol(secs_str); |
1113 |
return send_cwd(ftp_data, r, ftp_ctrl, bb); |
1026 |
apr_table_add(r->headers_out, "Retry-After", |
|
|
1027 |
apr_psprintf(p, "%lu", (unsigned long)(60 * secs))); |
1028 |
} |
1029 |
return ftp_proxyerror(r, backend, HTTP_SERVICE_UNAVAILABLE, ftpmessage); |
1030 |
} |
1114 |
} |
1031 |
if (rc != 220) { |
1115 |
|
1032 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1116 |
/* Also, don't allow to RETR by wildcard. Instead, create a dirlisting */ |
|
|
1117 |
if (ftp_check_globbingchars(ftp_data->path)) { |
1118 |
ftp_data->action = LISTING; |
1119 |
return send_cwd(ftp_data, r, ftp_ctrl, bb); |
1033 |
} |
1120 |
} |
1034 |
|
1121 |
|
1035 |
rc = proxy_ftp_command(apr_pstrcat(p, "USER ", user, CRLF, NULL), |
1122 |
|
1036 |
r, origin, bb, &ftpmessage); |
1123 |
/* verify our previous guesses */ |
1037 |
/* possible results; 230, 331, 332, 421, 500, 501, 530 */ |
1124 |
switch (action) { |
1038 |
/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ |
1125 |
case LISTING: |
1039 |
/* 230 User logged in, proceed. */ |
1126 |
return send_cwd(ftp_data, r, ftp_ctrl, bb); |
1040 |
/* 331 User name okay, need password. */ |
1127 |
case RETR: |
1041 |
/* 332 Need account for login. */ |
1128 |
return send_size(ftp_data, r, ftp_ctrl, bb); |
1042 |
/* 421 Service not available, closing control connection. */ |
1129 |
default: |
1043 |
/* 500 Syntax error, command unrecognized. */ |
1130 |
set_ftp_error(ftp_data, rc, "Not implemented"); |
1044 |
/* (This may include errors such as command line too long.) */ |
1131 |
return HTTP_BAD_REQUEST; /* Not implemented*/ |
1045 |
/* 501 Syntax error in parameters or arguments. */ |
|
|
1046 |
/* 530 Not logged in. */ |
1047 |
if (rc == -1 || rc == 421) { |
1048 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); |
1049 |
} |
1132 |
} |
1050 |
if (rc == 530) { |
1133 |
} |
1051 |
proxy_ftp_cleanup(r, backend); |
1134 |
|
1052 |
return ftp_unauthorized(r, 1); /* log it: user name guessing |
1135 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
1053 |
* attempt? */ |
1136 |
/* from draft-ietf-ftpext-mlst-14.txt: |
|
|
1137 |
* The FTP command, MODIFICATION TIME (MDTM), can be used to determine |
1138 |
* when a file in the server NVFS was last modified. <..> |
1139 |
* The syntax of a time value is: |
1140 |
* time-val = 14DIGIT [ "." 1*DIGIT ] <..> |
1141 |
* Symbolically, a time-val may be viewed as |
1142 |
* YYYYMMDDHHMMSS.sss |
1143 |
* The "." and subsequent digits ("sss") are optional. <..> |
1144 |
* Time values are always represented in UTC (GMT) |
1145 |
*/ |
1146 |
static |
1147 |
int send_mdtm(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1148 |
{ |
1149 |
int rc; |
1150 |
char *msg = NULL; |
1151 |
apr_time_t mtime = 0L; |
1152 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "MDTM ", ftp_data->path, CRLF, NULL), |
1153 |
r, ftp_ctrl, bb, &msg); |
1154 |
ftp_debug(r, "mdtm", msg); |
1155 |
/* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */ |
1156 |
switch (rc / 100) { |
1157 |
case 2: |
1158 |
struct { |
1159 |
char YYYY[4+1]; |
1160 |
char MM[2+1]; |
1161 |
char DD[2+1]; |
1162 |
char hh[2+1]; |
1163 |
char mm[2+1]; |
1164 |
char ss[2+1]; |
1165 |
} time_val; |
1166 |
if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]", |
1167 |
time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) { |
1168 |
struct tm tms; |
1169 |
memset (&tms, '\0', sizeof tms); |
1170 |
tms.tm_year = atoi(time_val.YYYY) - 1900; |
1171 |
tms.tm_mon = atoi(time_val.MM) - 1; |
1172 |
tms.tm_mday = atoi(time_val.DD); |
1173 |
tms.tm_hour = atoi(time_val.hh); |
1174 |
tms.tm_min = atoi(time_val.mm); |
1175 |
tms.tm_sec = atoi(time_val.ss); |
1176 |
#ifdef HAVE_TIMEGM /* Does system have timegm()? */ |
1177 |
mtime = timegm(&tms); |
1178 |
mtime *= APR_USEC_PER_SEC; |
1179 |
#elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */ |
1180 |
/* mktime will subtract the local timezone, which is not what we want. |
1181 |
* Add it again because the MDTM string is GMT |
1182 |
*/ |
1183 |
mtime = mktime(&tms); |
1184 |
mtime += tms.tm_gmtoff; |
1185 |
mtime *= APR_USEC_PER_SEC; |
1186 |
#else |
1187 |
mtime = 0L; |
1188 |
#endif |
1189 |
} |
1190 |
default: |
1191 |
mtime = 0L; |
1054 |
} |
1192 |
} |
1055 |
if (rc != 230 && rc != 331) { |
1193 |
ftp_data->cur.mtime = mtime; |
1056 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1194 |
return OK; |
|
|
1195 |
} |
1196 |
#endif |
1197 |
|
1198 |
|
1199 |
|
1200 |
/* responses: 221, 500 |
1201 |
* 221 Service closing control connection. |
1202 |
* 500 Syntax error, command unrecognized. */ |
1203 |
static |
1204 |
int send_quit(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1205 |
{ |
1206 |
int rc; |
1207 |
char *msg = NULL; |
1208 |
|
1209 |
rc = proxy_ftp_command("QUIT" CRLF, |
1210 |
r, ftp_ctrl, bb, &msg); |
1211 |
ftp_debug(r, "quit", msg); |
1212 |
switch (rc / 100) { |
1213 |
default: |
1214 |
ftp_data->msg = msg; |
1215 |
return rc; |
1057 |
} |
1216 |
} |
|
|
1217 |
} |
1058 |
|
1218 |
|
1059 |
if (rc == 331) { /* send password */ |
1219 |
static |
1060 |
if (password == NULL) { |
1220 |
int send_stor(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1061 |
proxy_ftp_cleanup(r, backend); |
1221 |
{ |
1062 |
return ftp_unauthorized(r, 0); |
1222 |
int rc; |
1063 |
} |
1223 |
char *msg = NULL; |
1064 |
|
1224 |
|
1065 |
rc = proxy_ftp_command(apr_pstrcat(p, "PASS ", password, CRLF, NULL), |
1225 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "STOR ", ftp_data->path, CRLF, NULL), |
1066 |
r, origin, bb, &ftpmessage); |
1226 |
r, ftp_ctrl, bb, &msg); |
1067 |
/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ |
1227 |
ftp_debug(r, "stor", msg); |
1068 |
/* 230 User logged in, proceed. */ |
1228 |
switch (rc / 100) { |
1069 |
/* 332 Need account for login. */ |
1229 |
case 1: |
1070 |
/* 421 Service not available, closing control connection. */ |
1230 |
ftp_data->state = ftp_start_response; |
1071 |
/* 500 Syntax error, command unrecognized. */ |
1231 |
return OK; |
1072 |
/* 501 Syntax error in parameters or arguments. */ |
1232 |
default: |
1073 |
/* 503 Bad sequence of commands. */ |
1233 |
set_ftp_error(ftp_data, rc, msg); |
1074 |
/* 530 Not logged in. */ |
1234 |
return HTTP_BAD_GATEWAY; |
1075 |
if (rc == -1 || rc == 421) { |
|
|
1076 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1077 |
"Error reading from remote server"); |
1078 |
} |
1079 |
if (rc == 332) { |
1080 |
return ftp_proxyerror(r, backend, HTTP_UNAUTHORIZED, |
1081 |
apr_pstrcat(p, "Need account for login: ", ftpmessage, NULL)); |
1082 |
} |
1083 |
/* @@@ questionable -- we might as well return a 403 Forbidden here */ |
1084 |
if (rc == 530) { |
1085 |
proxy_ftp_cleanup(r, backend); |
1086 |
return ftp_unauthorized(r, 1); /* log it: passwd guessing |
1087 |
* attempt? */ |
1088 |
} |
1089 |
if (rc != 230 && rc != 202) { |
1090 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1091 |
} |
1092 |
} |
1235 |
} |
1093 |
apr_table_set(r->notes, "Directory-README", ftpmessage); |
1236 |
} |
|
|
1237 |
/* intermediate response for the LIST or RETR commands |
1094 |
|
1238 |
|
|
|
1239 |
* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, |
1240 |
* 550 NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, |
1241 |
* 530 |
1095 |
|
1242 |
|
1096 |
/* Special handling for leading "%2f": this enforces a "cwd /" |
1243 |
* 110 Restart marker reply. |
1097 |
* out of the $HOME directory which was the starting point after login |
1244 |
* 125 Data connection already open; transfer starting. |
1098 |
*/ |
1245 |
* 150 File status okay; about to open data connection. |
1099 |
if (strncasecmp(path, "%2f", 3) == 0) { |
1246 |
* 226 Closing data connection. |
1100 |
path += 3; |
1247 |
* 250 Requested file action okay, completed. |
1101 |
while (*path == '/') /* skip leading '/' (after root %2f) */ |
1248 |
* 421 Service not available, closing control connection. |
1102 |
++path; |
1249 |
* 425 Can't open data connection. |
|
|
1250 |
* 426 Connection closed; transfer aborted. |
1251 |
* 450 Requested file action not taken. |
1252 |
* 451 Requested action aborted. Local error in processing. |
1253 |
* 500 Syntax error, command unrecognized. |
1254 |
* 501 Syntax error in parameters or arguments. |
1255 |
* 530 Not logged in. |
1256 |
* 550 Requested action not taken. */ |
1257 |
static |
1258 |
int send_retr(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1259 |
{ |
1260 |
int rc; |
1261 |
char *msg = NULL; |
1103 |
|
1262 |
|
1104 |
rc = proxy_ftp_command("CWD /" CRLF, r, origin, bb, &ftpmessage); |
1263 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "RETR ", ftp_data->path, CRLF, NULL), |
1105 |
if (rc == -1 || rc == 421) |
1264 |
r, ftp_ctrl, bb, &msg); |
1106 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1265 |
ftp_debug(r, "retr", msg); |
1107 |
"Error reading from remote server"); |
1266 |
switch (rc / 100) { |
|
|
1267 |
case 1: |
1268 |
ftp_data->state = ftp_start_response; |
1269 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
1270 |
send_mdtm(ftp_data, r, ftp_ctrl, bb); |
1271 |
#endif |
1272 |
return OK; |
1273 |
default: |
1274 |
set_ftp_error(ftp_data, rc, msg); |
1275 |
return HTTP_BAD_GATEWAY; |
1108 |
} |
1276 |
} |
|
|
1277 |
} |
1109 |
|
1278 |
|
1110 |
/* |
|
|
1111 |
* set the directory (walk directory component by component): this is |
1112 |
* what we must do if we don't know the OS type of the remote machine |
1113 |
*/ |
1114 |
for (;;) { |
1115 |
strp = strchr(path, '/'); |
1116 |
if (strp == NULL) |
1117 |
break; |
1118 |
*strp = '\0'; |
1119 |
|
1279 |
|
1120 |
len = decodeenc(path); /* Note! This decodes a %2f -> "/" */ |
1280 |
/* intermediate response for the LIST command |
|
|
1281 |
* 125 transfer starting, |
1282 |
* 150 opening data connection */ |
1283 |
static |
1284 |
int send_list(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1285 |
{ |
1286 |
int rc; |
1287 |
char *msg = NULL; |
1121 |
|
1288 |
|
1122 |
if (strchr(path, '/')) { /* are there now any '/' characters? */ |
1289 |
rc = proxy_ftp_command("LIST" CRLF, |
1123 |
return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST, |
1290 |
r, ftp_ctrl, bb, &msg); |
1124 |
"Use of /%2f is only allowed at the base directory"); |
1291 |
ftp_debug(r, "list", msg); |
1125 |
} |
1292 |
switch (rc / 100) { |
|
|
1293 |
case 1: |
1294 |
ftp_data->state = ftp_start_response; |
1295 |
return OK; |
1296 |
default: |
1297 |
set_ftp_error(ftp_data, rc, msg); |
1298 |
return HTTP_BAD_GATEWAY; |
1299 |
} |
1300 |
} |
1126 |
|
1301 |
|
1127 |
/* NOTE: FTP servers do globbing on the path. |
1302 |
static |
1128 |
* So we need to escape the URI metacharacters. |
1303 |
int send_type(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1129 |
* We use a special glob-escaping routine to escape globbing chars. |
1304 |
{ |
1130 |
* We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH |
1305 |
int rc; |
1131 |
*/ |
1306 |
char *msg = NULL; |
1132 |
rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", |
1307 |
char xtype[2] = {'A', 0}; |
1133 |
ftp_escape_globbingchars(p, path), CRLF, NULL), |
1308 |
xtype[0] = ftp_data->xfer_type; |
1134 |
r, origin, bb, &ftpmessage); |
|
|
1135 |
*strp = '/'; |
1136 |
/* responses: 250, 421, 500, 501, 502, 530, 550 */ |
1137 |
/* 250 Requested file action okay, completed. */ |
1138 |
/* 421 Service not available, closing control connection. */ |
1139 |
/* 500 Syntax error, command unrecognized. */ |
1140 |
/* 501 Syntax error in parameters or arguments. */ |
1141 |
/* 502 Command not implemented. */ |
1142 |
/* 530 Not logged in. */ |
1143 |
/* 550 Requested action not taken. */ |
1144 |
if (rc == -1 || rc == 421) { |
1145 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1146 |
"Error reading from remote server"); |
1147 |
} |
1148 |
if (rc == 550) { |
1149 |
return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); |
1150 |
} |
1151 |
if (rc != 250) { |
1152 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1153 |
} |
1154 |
|
1309 |
|
1155 |
path = strp + 1; |
1310 |
rc = proxy_ftp_command(apr_pstrcat(r->connection->pool, "TYPE ", xtype, CRLF, NULL), |
|
|
1311 |
r, ftp_ctrl, bb, &msg); |
1312 |
ftp_debug(r, "type", msg); |
1313 |
switch (rc / 100) { |
1314 |
case 2: |
1315 |
ftp_data->state = ftp_check_trans_mode; |
1316 |
return OK; |
1317 |
default: |
1318 |
set_ftp_error(ftp_data, rc, msg); |
1319 |
return HTTP_BAD_GATEWAY; |
1156 |
} |
1320 |
} |
|
|
1321 |
} |
1157 |
|
1322 |
|
1158 |
/* |
1323 |
static |
1159 |
* IV: Make Data Connection? ------------------------- |
1324 |
int check_type(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1160 |
* |
1325 |
{ |
1161 |
* Try EPSV, if that fails... try PASV, if that fails... try PORT. |
1326 |
/*There are no binary list retrivals. Force xfer_type to ASCII if it is a list*/ |
1162 |
*/ |
1327 |
if (ftp_data->action == LISTING) |
1163 |
/* this temporarily switches off EPSV/PASV */ |
1328 |
ftp_data->xfer_type = 'A'; |
1164 |
/*goto bypass;*/ |
1329 |
/* Check if the type in ftp_data is same as that of the cur, |
|
|
1330 |
* if it is we do not have to send a type*/ |
1331 |
if (ftp_data->xfer_type == 'A') { |
1332 |
ftp_data->state = ftp_check_trans_mode; |
1333 |
return OK; |
1334 |
} |
1165 |
|
1335 |
|
1166 |
/* set up data connection - EPSV */ |
1336 |
return send_type(ftp_data, r, ftp_ctrl, bb); |
1167 |
{ |
1337 |
} |
1168 |
apr_sockaddr_t *data_addr; |
|
|
1169 |
char *data_ip; |
1170 |
apr_port_t data_port; |
1171 |
|
1338 |
|
1172 |
/* |
|
|
1173 |
* The EPSV command replaces PASV where both IPV4 and IPV6 is |
1174 |
* supported. Only the port is returned, the IP address is always the |
1175 |
* same as that on the control connection. Example: Entering Extended |
1176 |
* Passive Mode (|||6446|) |
1177 |
*/ |
1178 |
rc = proxy_ftp_command("EPSV" CRLF, |
1179 |
r, origin, bb, &ftpmessage); |
1180 |
/* possible results: 227, 421, 500, 501, 502, 530 */ |
1181 |
/* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ |
1182 |
/* 421 Service not available, closing control connection. */ |
1183 |
/* 500 Syntax error, command unrecognized. */ |
1184 |
/* 501 Syntax error in parameters or arguments. */ |
1185 |
/* 502 Command not implemented. */ |
1186 |
/* 530 Not logged in. */ |
1187 |
if (rc == -1 || rc == 421) { |
1188 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1189 |
"Error reading from remote server"); |
1190 |
} |
1191 |
if (rc != 229 && rc != 500 && rc != 501 && rc != 502) { |
1192 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1193 |
} |
1194 |
else if (rc == 229) { |
1195 |
char *pstr; |
1196 |
char *tok_cntx; |
1197 |
|
1339 |
|
1198 |
pstr = ftpmessage; |
1340 |
/* |
1199 |
pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ |
1341 |
* The EPSV command replaces PASV where both IPV4 and IPV6 is |
1200 |
if (pstr != NULL) { |
1342 |
* supported. Only the port is returned, the IP address is always the |
1201 |
if (*(pstr + strlen(pstr) + 1) == '=') { |
1343 |
* same as that on the control connection. Example: Entering Extended |
1202 |
pstr += strlen(pstr) + 2; |
1344 |
* Passive Mode (|||6446|) |
1203 |
} |
1345 |
* |
1204 |
else { |
1346 |
* possible results: 227, 421, 500, 501, 502, 530 |
1205 |
pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & |
1347 |
* 229 Entering Extended Passive Mode (port). |
1206 |
* port params */ |
1348 |
* 421 Service not available, closing control connection. |
1207 |
if (pstr != NULL) |
1349 |
* 500 Syntax error, command unrecognized. |
1208 |
pstr = apr_strtok(NULL, ")", &tok_cntx); |
1350 |
* 501 Syntax error in parameters or arguments. |
1209 |
} |
1351 |
* 502 Command not implemented. |
1210 |
} |
1352 |
* 530 Not logged in. */ |
1211 |
|
1353 |
|
1212 |
if (pstr) { |
1354 |
static |
1213 |
apr_sockaddr_t *epsv_addr; |
1355 |
int send_epsv(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1214 |
data_port = atoi(pstr + 3); |
1356 |
{ |
|
|
1357 |
int rc; |
1358 |
char *msg = NULL; |
1359 |
|
1360 |
rc = proxy_ftp_command("EPSV" CRLF, |
1361 |
r, ftp_ctrl, bb, &msg); |
1362 |
ftp_debug(r, "epsv", msg); |
1363 |
switch (rc / 100) { |
1364 |
case 2: |
1365 |
{ |
1366 |
char *pstr; |
1367 |
char *tok_cntx; |
1368 |
pstr = msg; |
1369 |
pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ |
1370 |
if (pstr != NULL) { |
1371 |
if (*(pstr + strlen(pstr) + 1) == '=') |
1372 |
pstr += strlen(pstr) + 2; |
1373 |
else { |
1374 |
pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & |
1375 |
* port params */ |
1376 |
if (pstr != NULL) |
1377 |
pstr = apr_strtok(NULL, ")", &tok_cntx); |
1378 |
} |
1379 |
if (pstr != 0 && strlen(pstr) > 3 ) { |
1380 |
ftp_data->cur.data.port = atoi(pstr + 3); |
1215 |
|
1381 |
|
1216 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1382 |
apr_sockaddr_t *data_addr; |
1217 |
"proxy: FTP: EPSV contacting remote host on port %d", |
1383 |
char *data_ip; |
1218 |
data_port); |
1384 |
apr_socket_addr_get(&data_addr, APR_REMOTE, ftp_data->p_conn->sock); |
|
|
1385 |
apr_sockaddr_ip_get(&data_ip, data_addr); |
1386 |
ftp_data->cur.data.ip = data_ip; |
1219 |
|
1387 |
|
1220 |
if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { |
1388 |
ftp_data->mode = EPSV; |
1221 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1389 |
ftp_data->state = ftp_connect_data_port; |
1222 |
"proxy: FTP: error creating EPSV socket"); |
1390 |
return OK; |
1223 |
proxy_ftp_cleanup(r, backend); |
1391 |
} |
1224 |
return HTTP_INTERNAL_SERVER_ERROR; |
|
|
1225 |
} |
1392 |
} |
|
|
1393 |
} |
1226 |
|
1394 |
|
1227 |
#if !defined (TPF) && !defined(BEOS) |
1395 |
msg = "Invalid epassive response."; |
1228 |
if (conf->recv_buffer_size > 0 |
|
|
1229 |
&& (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, |
1230 |
conf->recv_buffer_size))) { |
1231 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1232 |
"proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); |
1233 |
} |
1234 |
#endif |
1235 |
|
1396 |
|
1236 |
/* make the connection */ |
1397 |
default: |
1237 |
apr_socket_addr_get(&data_addr, APR_REMOTE, sock); |
1398 |
set_ftp_error(ftp_data, rc, msg); |
1238 |
apr_sockaddr_ip_get(&data_ip, data_addr); |
1399 |
return HTTP_BAD_GATEWAY; |
1239 |
apr_sockaddr_info_get(&epsv_addr, data_ip, connect_addr->family, data_port, 0, p); |
|
|
1240 |
rv = apr_socket_connect(data_sock, epsv_addr); |
1241 |
if (rv != APR_SUCCESS) { |
1242 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, |
1243 |
"proxy: FTP: EPSV attempt to connect to %pI failed - Firewall/NAT?", epsv_addr); |
1244 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, |
1245 |
"EPSV attempt to connect to %pI failed - firewall/NAT?", epsv_addr)); |
1246 |
} |
1247 |
else { |
1248 |
connect = 1; |
1249 |
} |
1250 |
} |
1251 |
else { |
1252 |
/* and try the regular way */ |
1253 |
apr_socket_close(data_sock); |
1254 |
} |
1255 |
} |
1256 |
} |
1400 |
} |
|
|
1401 |
} |
1257 |
|
1402 |
|
1258 |
/* set up data connection - PASV */ |
1403 |
static |
1259 |
if (!connect) { |
1404 |
int send_pasv(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1260 |
rc = proxy_ftp_command("PASV" CRLF, |
1405 |
{ |
1261 |
r, origin, bb, &ftpmessage); |
1406 |
int rc; |
1262 |
/* possible results: 227, 421, 500, 501, 502, 530 */ |
1407 |
char *msg = NULL; |
1263 |
/* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ |
1408 |
|
1264 |
/* 421 Service not available, closing control connection. */ |
1409 |
rc = proxy_ftp_command("PASV" CRLF, |
1265 |
/* 500 Syntax error, command unrecognized. */ |
1410 |
r, ftp_ctrl, bb, &msg); |
1266 |
/* 501 Syntax error in parameters or arguments. */ |
1411 |
ftp_debug(r, "pasv", msg); |
1267 |
/* 502 Command not implemented. */ |
1412 |
switch (rc / 100) { |
1268 |
/* 530 Not logged in. */ |
1413 |
case 2: |
1269 |
if (rc == -1 || rc == 421) { |
1414 |
{ |
1270 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1415 |
unsigned int h0, h1, h2, h3, p0, p1; |
1271 |
"Error reading from remote server"); |
1416 |
char *pstr; |
1272 |
} |
1417 |
char *tok_cntx; |
1273 |
if (rc != 227 && rc != 502) { |
|
|
1274 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1275 |
} |
1276 |
else if (rc == 227) { |
1277 |
unsigned int h0, h1, h2, h3, p0, p1; |
1278 |
char *pstr; |
1279 |
char *tok_cntx; |
1280 |
|
1418 |
|
1281 |
/* FIXME: Check PASV against RFC1123 */ |
1419 |
/*TODO: Check PASV against RFC1123 */ |
1282 |
|
1420 |
|
1283 |
pstr = ftpmessage; |
1421 |
pstr = msg; |
1284 |
pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ |
1422 |
pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ |
1285 |
if (pstr != NULL) { |
1423 |
if (pstr != NULL) { |
1286 |
if (*(pstr + strlen(pstr) + 1) == '=') { |
1424 |
if (*(pstr + strlen(pstr) + 1) == '=') { |
1287 |
pstr += strlen(pstr) + 2; |
1425 |
pstr += strlen(pstr) + 2; |
|
|
1426 |
} |
1427 |
else { |
1428 |
pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & |
1429 |
* port params */ |
1430 |
if (pstr != NULL) |
1431 |
pstr = apr_strtok(NULL, ")", &tok_cntx); |
1432 |
} |
1433 |
|
1434 |
if (pstr != NULL && (sscanf(pstr, "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { |
1435 |
ftp_data->cur.data.port = (p1 << 8) + p0; |
1436 |
char* data_ip = apr_psprintf(r->connection->pool, "%d.%d.%d.%d", h3, h2, h1, h0); |
1437 |
ftp_data->cur.data.ip = data_ip; |
1438 |
|
1439 |
ftp_data->mode = PASV; |
1440 |
ftp_data->state = ftp_connect_data_port; |
1441 |
return OK; |
1442 |
} |
1288 |
} |
1443 |
} |
1289 |
else { |
|
|
1290 |
pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & |
1291 |
* port params */ |
1292 |
if (pstr != NULL) |
1293 |
pstr = apr_strtok(NULL, ")", &tok_cntx); |
1294 |
} |
1295 |
} |
1444 |
} |
1296 |
|
1445 |
|
1297 |
/* FIXME: Only supports IPV4 - fix in RFC2428 */ |
1446 |
msg = "Invalid passive response."; |
1298 |
|
1447 |
|
1299 |
if (pstr != NULL && (sscanf(pstr, |
1448 |
default: |
1300 |
"%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { |
1449 |
set_ftp_error(ftp_data, rc, msg); |
|
|
1450 |
return HTTP_BAD_GATEWAY; |
1451 |
} |
1452 |
} |
1301 |
|
1453 |
|
1302 |
apr_sockaddr_t *pasv_addr; |
|
|
1303 |
apr_port_t pasvport = (p1 << 8) + p0; |
1304 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1305 |
"proxy: FTP: PASV contacting host %d.%d.%d.%d:%d", |
1306 |
h3, h2, h1, h0, pasvport); |
1307 |
|
1454 |
|
1308 |
if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { |
1455 |
static |
1309 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1456 |
int setup_local_port(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1310 |
"proxy: error creating PASV socket"); |
1457 |
{ |
1311 |
proxy_ftp_cleanup(r, backend); |
1458 |
int rv = 0; |
1312 |
return HTTP_INTERNAL_SERVER_ERROR; |
1459 |
apr_sockaddr_t *local_addr; |
1313 |
} |
1460 |
char *local_ip; |
|
|
1461 |
apr_port_t local_port; |
1462 |
apr_socket_t *local_sock, *sock; |
1463 |
apr_sockaddr_t *connect_addr = ftp_data->p_conn->addr; |
1314 |
|
1464 |
|
1315 |
#if !defined (TPF) && !defined(BEOS) |
1465 |
if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { |
1316 |
if (conf->recv_buffer_size > 0 |
1466 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1317 |
&& (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, |
1467 |
"proxy: FTP: error creating local socket"); |
1318 |
conf->recv_buffer_size))) { |
1468 |
return HTTP_INTERNAL_SERVER_ERROR; |
1319 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1469 |
} |
1320 |
"proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); |
1470 |
apr_socket_addr_get(&local_addr, APR_LOCAL, sock); |
1321 |
} |
1471 |
local_port = local_addr->port; |
1322 |
#endif |
1472 |
apr_sockaddr_ip_get(&local_ip, local_addr); |
1323 |
|
1473 |
|
1324 |
/* make the connection */ |
1474 |
if ((rv = apr_socket_opt_set(local_sock, APR_SO_REUSEADDR, 1)) |
1325 |
apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", h3, h2, h1, h0), connect_addr->family, pasvport, 0, p); |
1475 |
!= APR_SUCCESS) { |
1326 |
rv = apr_socket_connect(data_sock, pasv_addr); |
1476 |
#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ |
1327 |
if (rv != APR_SUCCESS) { |
1477 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1328 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, |
1478 |
"proxy: FTP: error setting reuseaddr option"); |
1329 |
"proxy: FTP: PASV attempt to connect to %pI failed - Firewall/NAT?", pasv_addr); |
1479 |
return HTTP_INTERNAL_SERVER_ERROR; |
1330 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, |
1480 |
#endif /* _OSD_POSIX */ |
1331 |
"PASV attempt to connect to %pI failed - firewall/NAT?", pasv_addr)); |
|
|
1332 |
} |
1333 |
else { |
1334 |
connect = 1; |
1335 |
} |
1336 |
} |
1337 |
else { |
1338 |
/* and try the regular way */ |
1339 |
apr_socket_close(data_sock); |
1340 |
} |
1341 |
} |
1342 |
} |
1481 |
} |
1343 |
/*bypass:*/ |
|
|
1344 |
|
1482 |
|
1345 |
/* set up data connection - PORT */ |
1483 |
apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool); |
1346 |
if (!connect) { |
|
|
1347 |
apr_sockaddr_t *local_addr; |
1348 |
char *local_ip; |
1349 |
apr_port_t local_port; |
1350 |
unsigned int h0, h1, h2, h3, p0, p1; |
1351 |
|
1484 |
|
1352 |
if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { |
1485 |
if ((rv = apr_socket_bind(local_sock, local_addr)) != APR_SUCCESS) { |
1353 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1486 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1354 |
"proxy: FTP: error creating local socket"); |
1487 |
"proxy: FTP: error binding to ftp data socket %pI", local_addr); |
1355 |
proxy_ftp_cleanup(r, backend); |
1488 |
return HTTP_INTERNAL_SERVER_ERROR; |
1356 |
return HTTP_INTERNAL_SERVER_ERROR; |
1489 |
} |
1357 |
} |
|
|
1358 |
apr_socket_addr_get(&local_addr, APR_LOCAL, sock); |
1359 |
local_port = local_addr->port; |
1360 |
apr_sockaddr_ip_get(&local_ip, local_addr); |
1361 |
|
1490 |
|
1362 |
if ((rv = apr_socket_opt_set(local_sock, APR_SO_REUSEADDR, one)) |
1491 |
/* only need a short queue */ |
1363 |
!= APR_SUCCESS) { |
1492 |
if ((rv = apr_socket_listen(local_sock, 2)) != APR_SUCCESS) { |
1364 |
#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ |
1493 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1365 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1494 |
"proxy: FTP: error listening to ftp data socket %pI", local_addr); |
1366 |
"proxy: FTP: error setting reuseaddr option"); |
1495 |
return HTTP_INTERNAL_SERVER_ERROR; |
1367 |
proxy_ftp_cleanup(r, backend); |
1496 |
} |
1368 |
return HTTP_INTERNAL_SERVER_ERROR; |
|
|
1369 |
#endif /* _OSD_POSIX */ |
1370 |
} |
1371 |
|
1497 |
|
1372 |
apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool); |
1498 |
ftp_data->cur.data.ip = local_ip; |
|
|
1499 |
ftp_data->cur.data.port = local_port; |
1500 |
ftp_data->cur.data.local_sock = local_sock; |
1501 |
return 0; |
1502 |
} |
1373 |
|
1503 |
|
1374 |
if ((rv = apr_socket_bind(local_sock, local_addr)) != APR_SUCCESS) { |
1504 |
/* possible results: 200, 421, 500, 501, 502, 530 |
1375 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1505 |
* 200 Command okay. |
1376 |
"proxy: FTP: error binding to ftp data socket %pI", local_addr); |
1506 |
* 421 Service not available, closing control connection. |
1377 |
proxy_ftp_cleanup(r, backend); |
1507 |
* 500 Syntax error, command unrecognized. |
1378 |
return HTTP_INTERNAL_SERVER_ERROR; |
1508 |
* 501 Syntax error in parameters or arguments. |
1379 |
} |
1509 |
* 502 Command not implemented. |
|
|
1510 |
* 530 Not logged in. */ |
1511 |
static |
1512 |
int send_port(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1513 |
{ |
1514 |
unsigned int h0, h1, h2, h3, p0, p1; |
1515 |
char* msg; |
1380 |
|
1516 |
|
1381 |
/* only need a short queue */ |
1517 |
int rc = setup_local_port(ftp_data, r, ftp_ctrl, bb); |
1382 |
if ((rv = apr_socket_listen(local_sock, 2)) != APR_SUCCESS) { |
1518 |
if (rc) { |
1383 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1519 |
ftp_data->msg = "Unable to establish server connection."; |
1384 |
"proxy: FTP: error listening to ftp data socket %pI", local_addr); |
1520 |
return rc; |
1385 |
proxy_ftp_cleanup(r, backend); |
1521 |
} |
1386 |
return HTTP_INTERNAL_SERVER_ERROR; |
1522 |
char* local_ip = ftp_data->cur.data.ip; |
1387 |
} |
1523 |
int local_port = ftp_data->cur.data.port; |
|
|
1524 |
if (local_ip && (sscanf(local_ip, "%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) { |
1525 |
p1 = (local_port >> 8); |
1526 |
p0 = (local_port & 0xFF); |
1527 |
} else { |
1528 |
/* IPV6 FIXME: |
1529 |
* The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first |
1530 |
* number (1,2) indicates the protocol type. Examples: |
1531 |
* EPRT |1|132.235.1.2|6275| |
1532 |
* EPRT |2|1080::8:800:200C:417A|5282| |
1533 |
*/ |
1534 |
ftp_data->msg = "Connect to IPV6 ftp server using EPRT not supported. Enable EPSV."; |
1535 |
return HTTP_NOT_IMPLEMENTED; |
1536 |
} |
1388 |
|
1537 |
|
1389 |
/* FIXME: Sent PORT here */ |
|
|
1390 |
|
1538 |
|
1391 |
if (local_ip && (sscanf(local_ip, |
1539 |
rc = proxy_ftp_command(apr_psprintf(r->connection->pool, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0), |
1392 |
"%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) { |
1540 |
r, ftp_ctrl, bb, &msg); |
1393 |
p1 = (local_port >> 8); |
1541 |
switch ( rc / 100) { |
1394 |
p0 = (local_port & 0xFF); |
1542 |
case 2: |
|
|
1543 |
ftp_data->mode = PORT; |
1544 |
ftp_data->state = ftp_check_transfer; |
1545 |
return OK; |
1395 |
|
1546 |
|
1396 |
rc = proxy_ftp_command(apr_psprintf(p, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0), |
1547 |
default: |
1397 |
r, origin, bb, &ftpmessage); |
1548 |
set_ftp_error(ftp_data, rc, msg); |
1398 |
/* possible results: 200, 421, 500, 501, 502, 530 */ |
1549 |
return HTTP_BAD_GATEWAY; |
1399 |
/* 200 Command okay. */ |
1550 |
} |
1400 |
/* 421 Service not available, closing control connection. */ |
1551 |
} |
1401 |
/* 500 Syntax error, command unrecognized. */ |
|
|
1402 |
/* 501 Syntax error in parameters or arguments. */ |
1403 |
/* 502 Command not implemented. */ |
1404 |
/* 530 Not logged in. */ |
1405 |
if (rc == -1 || rc == 421) { |
1406 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1407 |
"Error reading from remote server"); |
1408 |
} |
1409 |
if (rc != 200) { |
1410 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, buffer); |
1411 |
} |
1412 |
|
1552 |
|
1413 |
/* signal that we must use the EPRT/PORT loop */ |
1553 |
static |
1414 |
use_port = 1; |
1554 |
int check_trans_mode(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1415 |
} |
1555 |
{ |
1416 |
else { |
1556 |
int rc; |
1417 |
/* IPV6 FIXME: |
1557 |
switch (ftp_data->mode) { |
1418 |
* The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first |
1558 |
case EPSV: |
1419 |
* number (1,2) indicates the protocol type. Examples: |
1559 |
rc = send_epsv(ftp_data, r, ftp_ctrl, bb); |
1420 |
* EPRT |1|132.235.1.2|6275| |
1560 |
if (rc == OK) |
1421 |
* EPRT |2|1080::8:800:200C:417A|5282| |
1561 |
break; |
1422 |
*/ |
1562 |
case PASV: |
1423 |
return ftp_proxyerror(r, backend, HTTP_NOT_IMPLEMENTED, |
1563 |
rc = send_pasv(ftp_data, r, ftp_ctrl, bb); |
1424 |
"Connect to IPV6 ftp server using EPRT not supported. Enable EPSV."); |
1564 |
if (rc == OK) |
1425 |
} |
1565 |
break; |
|
|
1566 |
case PORT: |
1567 |
rc = send_port(ftp_data, r, ftp_ctrl, bb); |
1568 |
if (rc == OK) |
1569 |
break; |
1570 |
default: |
1571 |
return rc; |
1426 |
} |
1572 |
} |
|
|
1573 |
return OK; |
1574 |
} |
1427 |
|
1575 |
|
1428 |
|
1576 |
|
1429 |
/* |
1577 |
static |
1430 |
* V: Set The Headers ------------------- |
1578 |
int connect_data_port(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1431 |
* |
1579 |
{ |
1432 |
* Get the size of the request, set up the environment for HTTP. |
1580 |
apr_socket_t *data_sock = NULL; |
1433 |
*/ |
1581 |
apr_sockaddr_t *connect_addr = ftp_data->p_conn->addr; |
1434 |
|
1582 |
|
1435 |
/* set request; "path" holds last path component */ |
1583 |
int data_port = ftp_data->cur.data.port; |
1436 |
len = decodeenc(path); |
1584 |
int rv; |
1437 |
|
1585 |
|
1438 |
if (strchr(path, '/')) { /* are there now any '/' characters? */ |
1586 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1439 |
return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST, |
1587 |
"proxy: FTP: EPSV contacting remote host on port %d", |
1440 |
"Use of /%2f is only allowed at the base directory"); |
1588 |
data_port); |
|
|
1589 |
|
1590 |
if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { |
1591 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1592 |
"proxy: FTP: error creating EPSV socket"); |
1593 |
return HTTP_INTERNAL_SERVER_ERROR; |
1441 |
} |
1594 |
} |
1442 |
|
1595 |
|
1443 |
/* If len == 0 then it must be a directory (you can't RETR nothing) |
1596 |
#if !defined (TPF) && !defined(BEOS) |
1444 |
* Also, don't allow to RETR by wildcard. Instead, create a dirlisting |
1597 |
if (ftp_data->cur.conf->recv_buffer_size > 0 |
1445 |
*/ |
1598 |
&& (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, |
1446 |
if (len == 0 || ftp_check_globbingchars(path)) { |
1599 |
ftp_data->cur.conf->recv_buffer_size))) { |
1447 |
dirlisting = 1; |
1600 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
|
|
1601 |
"proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); |
1448 |
} |
1602 |
} |
1449 |
else { |
1603 |
#endif |
1450 |
/* (from FreeBSD ftpd): |
1604 |
{ |
1451 |
* SIZE is not in RFC959, but Postel has blessed it and |
1605 |
/* make the connection */ |
1452 |
* it will be in the updated RFC. |
1606 |
apr_sockaddr_t *d_addr; |
1453 |
* |
1607 |
apr_sockaddr_info_get(&d_addr, ftp_data->cur.data.ip, connect_addr->family, data_port, 0, ftp_data->p_conn->pool); |
1454 |
* Return size of file in a format suitable for |
1608 |
rv = apr_socket_connect(data_sock, d_addr); |
1455 |
* using with RESTART (we just count bytes). |
1609 |
if (rv != APR_SUCCESS) { |
1456 |
*/ |
1610 |
ftp_data->msg = apr_psprintf(r->pool, |
1457 |
/* from draft-ietf-ftpext-mlst-14.txt: |
1611 |
"EPSV attempt to connect to %pI failed - firewall/NAT?", d_addr); |
1458 |
* This value will |
1612 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, ftp_data->msg); |
1459 |
* change depending on the current STRUcture, MODE and TYPE of the data |
1613 |
return HTTP_BAD_GATEWAY; |
1460 |
* connection, or a data connection which would be created were one |
|
|
1461 |
* created now. Thus, the result of the SIZE command is dependent on |
1462 |
* the currently established STRU, MODE and TYPE parameters. |
1463 |
*/ |
1464 |
/* Therefore: switch to binary if the user did not specify ";type=a" */ |
1465 |
ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage); |
1466 |
rc = proxy_ftp_command(apr_pstrcat(p, "SIZE ", |
1467 |
ftp_escape_globbingchars(p, path), CRLF, NULL), |
1468 |
r, origin, bb, &ftpmessage); |
1469 |
if (rc == -1 || rc == 421) { |
1470 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1471 |
"Error reading from remote server"); |
1472 |
} |
1614 |
} |
1473 |
else if (rc == 213) {/* Size command ok */ |
|
|
1474 |
int j; |
1475 |
for (j = 0; apr_isdigit(ftpmessage[j]); j++) |
1476 |
; |
1477 |
ftpmessage[j] = '\0'; |
1478 |
if (ftpmessage[0] != '\0') |
1479 |
size = ftpmessage; /* already pstrdup'ed: no copy necessary */ |
1480 |
} |
1481 |
else if (rc == 550) { /* Not a regular file */ |
1482 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1483 |
"proxy: FTP: SIZE shows this is a directory"); |
1484 |
dirlisting = 1; |
1485 |
rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", |
1486 |
ftp_escape_globbingchars(p, path), CRLF, NULL), |
1487 |
r, origin, bb, &ftpmessage); |
1488 |
/* possible results: 250, 421, 500, 501, 502, 530, 550 */ |
1489 |
/* 250 Requested file action okay, completed. */ |
1490 |
/* 421 Service not available, closing control connection. */ |
1491 |
/* 500 Syntax error, command unrecognized. */ |
1492 |
/* 501 Syntax error in parameters or arguments. */ |
1493 |
/* 502 Command not implemented. */ |
1494 |
/* 530 Not logged in. */ |
1495 |
/* 550 Requested action not taken. */ |
1496 |
if (rc == -1 || rc == 421) { |
1497 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1498 |
"Error reading from remote server"); |
1499 |
} |
1500 |
if (rc == 550) { |
1501 |
return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); |
1502 |
} |
1503 |
if (rc != 250) { |
1504 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1505 |
} |
1506 |
path = ""; |
1507 |
len = 0; |
1508 |
} |
1509 |
} |
1615 |
} |
|
|
1616 |
ftp_data->cur.data.sock = data_sock; |
1617 |
ftp_data->state = ftp_check_transfer; |
1618 |
return OK; |
1619 |
} |
1510 |
|
1620 |
|
1511 |
cwd = ftp_get_PWD(r, origin, bb); |
|
|
1512 |
if (cwd != NULL) { |
1513 |
apr_table_set(r->notes, "Directory-PWD", cwd); |
1514 |
} |
1515 |
|
1621 |
|
1516 |
if (dirlisting) { |
|
|
1517 |
ftp_set_TYPE('A', r, origin, bb, NULL); |
1518 |
/* If the current directory contains no slash, we are talking to |
1519 |
* a non-unix ftp system. Try LIST instead of "LIST -lag", it |
1520 |
* should return a long listing anyway (unlike NLST). |
1521 |
* Some exotic FTP servers might choke on the "-lag" switch. |
1522 |
*/ |
1523 |
/* Note that we do not escape the path here, to allow for |
1524 |
* queries like: ftp://user@host/apache/src/server/http_*.c |
1525 |
*/ |
1526 |
if (len != 0) |
1527 |
buf = apr_pstrcat(p, "LIST ", path, CRLF, NULL); |
1528 |
else if (cwd == NULL || strchr(cwd, '/') != NULL) |
1529 |
buf = apr_pstrcat(p, "LIST -lag", CRLF, NULL); |
1530 |
else |
1531 |
buf = "LIST" CRLF; |
1532 |
} |
1533 |
else { |
1534 |
/* switch to binary if the user did not specify ";type=a" */ |
1535 |
ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage); |
1536 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
1537 |
/* from draft-ietf-ftpext-mlst-14.txt: |
1538 |
* The FTP command, MODIFICATION TIME (MDTM), can be used to determine |
1539 |
* when a file in the server NVFS was last modified. <..> |
1540 |
* The syntax of a time value is: |
1541 |
* time-val = 14DIGIT [ "." 1*DIGIT ] <..> |
1542 |
* Symbolically, a time-val may be viewed as |
1543 |
* YYYYMMDDHHMMSS.sss |
1544 |
* The "." and subsequent digits ("sss") are optional. <..> |
1545 |
* Time values are always represented in UTC (GMT) |
1546 |
*/ |
1547 |
rc = proxy_ftp_command(apr_pstrcat(p, "MDTM ", ftp_escape_globbingchars(p, path), CRLF, NULL), |
1548 |
r, origin, bb, &ftpmessage); |
1549 |
/* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */ |
1550 |
if (rc == 213) { |
1551 |
struct { |
1552 |
char YYYY[4+1]; |
1553 |
char MM[2+1]; |
1554 |
char DD[2+1]; |
1555 |
char hh[2+1]; |
1556 |
char mm[2+1]; |
1557 |
char ss[2+1]; |
1558 |
} time_val; |
1559 |
if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]", |
1560 |
time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) { |
1561 |
struct tm tms; |
1562 |
memset (&tms, '\0', sizeof tms); |
1563 |
tms.tm_year = atoi(time_val.YYYY) - 1900; |
1564 |
tms.tm_mon = atoi(time_val.MM) - 1; |
1565 |
tms.tm_mday = atoi(time_val.DD); |
1566 |
tms.tm_hour = atoi(time_val.hh); |
1567 |
tms.tm_min = atoi(time_val.mm); |
1568 |
tms.tm_sec = atoi(time_val.ss); |
1569 |
#ifdef HAVE_TIMEGM /* Does system have timegm()? */ |
1570 |
mtime = timegm(&tms); |
1571 |
mtime *= APR_USEC_PER_SEC; |
1572 |
#elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */ |
1573 |
/* mktime will subtract the local timezone, which is not what we want. |
1574 |
* Add it again because the MDTM string is GMT |
1575 |
*/ |
1576 |
mtime = mktime(&tms); |
1577 |
mtime += tms.tm_gmtoff; |
1578 |
mtime *= APR_USEC_PER_SEC; |
1579 |
#else |
1580 |
mtime = 0L; |
1581 |
#endif |
1582 |
} |
1583 |
} |
1584 |
#endif /* USE_MDTM */ |
1585 |
/* FIXME: Handle range requests - send REST */ |
1586 |
buf = apr_pstrcat(p, "RETR ", ftp_escape_globbingchars(p, path), CRLF, NULL); |
1587 |
} |
1588 |
rc = proxy_ftp_command(buf, r, origin, bb, &ftpmessage); |
1589 |
/* rc is an intermediate response for the LIST or RETR commands */ |
1590 |
|
1622 |
|
1591 |
/* |
1623 |
static |
1592 |
* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, |
1624 |
int check_transfer(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1593 |
* 550 NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, |
1625 |
{ |
1594 |
* 530 |
1626 |
switch (ftp_data->action) { |
1595 |
*/ |
1627 |
case STOR: |
1596 |
/* 110 Restart marker reply. */ |
1628 |
ftp_data->state = ftp_send_stor; |
1597 |
/* 125 Data connection already open; transfer starting. */ |
1629 |
break; |
1598 |
/* 150 File status okay; about to open data connection. */ |
1630 |
case LISTING: |
1599 |
/* 226 Closing data connection. */ |
1631 |
ftp_data->state = ftp_send_list; |
1600 |
/* 250 Requested file action okay, completed. */ |
1632 |
break; |
1601 |
/* 421 Service not available, closing control connection. */ |
1633 |
default: |
1602 |
/* 425 Can't open data connection. */ |
1634 |
case RETR: |
1603 |
/* 426 Connection closed; transfer aborted. */ |
1635 |
ftp_data->state = ftp_send_retr; |
1604 |
/* 450 Requested file action not taken. */ |
1636 |
break; |
1605 |
/* 451 Requested action aborted. Local error in processing. */ |
|
|
1606 |
/* 500 Syntax error, command unrecognized. */ |
1607 |
/* 501 Syntax error in parameters or arguments. */ |
1608 |
/* 530 Not logged in. */ |
1609 |
/* 550 Requested action not taken. */ |
1610 |
if (rc == -1 || rc == 421) { |
1611 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1612 |
"Error reading from remote server"); |
1613 |
} |
1637 |
} |
1614 |
if (rc == 550) { |
1638 |
return OK; |
1615 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1639 |
} |
1616 |
"proxy: FTP: RETR failed, trying LIST instead"); |
|
|
1617 |
|
1640 |
|
1618 |
/* Directory Listings should always be fetched in ASCII mode */ |
1641 |
static |
1619 |
dirlisting = 1; |
1642 |
int start_response(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1620 |
ftp_set_TYPE('A', r, origin, bb, NULL); |
1643 |
{ |
|
|
1644 |
char *msg = NULL; |
1645 |
char dates[APR_RFC822_DATE_LEN]; |
1621 |
|
1646 |
|
1622 |
rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", |
1647 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
1623 |
ftp_escape_globbingchars(p, path), CRLF, NULL), |
1648 |
apr_time_t mtime = ftp_data->mtime; |
1624 |
r, origin, bb, &ftpmessage); |
1649 |
#endif |
1625 |
/* possible results: 250, 421, 500, 501, 502, 530, 550 */ |
|
|
1626 |
/* 250 Requested file action okay, completed. */ |
1627 |
/* 421 Service not available, closing control connection. */ |
1628 |
/* 500 Syntax error, command unrecognized. */ |
1629 |
/* 501 Syntax error in parameters or arguments. */ |
1630 |
/* 502 Command not implemented. */ |
1631 |
/* 530 Not logged in. */ |
1632 |
/* 550 Requested action not taken. */ |
1633 |
if (rc == -1 || rc == 421) { |
1634 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1635 |
"Error reading from remote server"); |
1636 |
} |
1637 |
if (rc == 550) { |
1638 |
return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); |
1639 |
} |
1640 |
if (rc != 250) { |
1641 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1642 |
} |
1643 |
|
1650 |
|
1644 |
/* Update current directory after CWD */ |
|
|
1645 |
cwd = ftp_get_PWD(r, origin, bb); |
1646 |
if (cwd != NULL) { |
1647 |
apr_table_set(r->notes, "Directory-PWD", cwd); |
1648 |
} |
1649 |
|
1650 |
/* See above for the "LIST" vs. "LIST -lag" discussion. */ |
1651 |
rc = proxy_ftp_command((cwd == NULL || strchr(cwd, '/') != NULL) |
1652 |
? "LIST -lag" CRLF : "LIST" CRLF, |
1653 |
r, origin, bb, &ftpmessage); |
1654 |
|
1655 |
/* rc is an intermediate response for the LIST command (125 transfer starting, 150 opening data connection) */ |
1656 |
if (rc == -1 || rc == 421) |
1657 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, |
1658 |
"Error reading from remote server"); |
1659 |
} |
1660 |
if (rc != 125 && rc != 150 && rc != 226 && rc != 250) { |
1661 |
return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); |
1662 |
} |
1663 |
|
1664 |
r->status = HTTP_OK; |
1651 |
r->status = HTTP_OK; |
1665 |
r->status_line = "200 OK"; |
1652 |
r->status_line = "200 OK"; |
1666 |
|
1653 |
|
Lines 1669-1695
Link Here
|
1669 |
apr_table_setn(r->headers_out, "Server", ap_get_server_banner()); |
1656 |
apr_table_setn(r->headers_out, "Server", ap_get_server_banner()); |
1670 |
|
1657 |
|
1671 |
/* set content-type */ |
1658 |
/* set content-type */ |
1672 |
if (dirlisting) { |
1659 |
if (ftp_data->action == LISTING) { |
1673 |
ap_set_content_type(r, "text/html"); |
1660 |
ap_set_content_type(r, "text/html"); |
1674 |
} |
1661 |
} |
1675 |
else { |
1662 |
else { |
1676 |
if (r->content_type) { |
1663 |
if (r->content_type) { |
1677 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1664 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1678 |
"proxy: FTP: Content-Type set to %s", r->content_type); |
1665 |
"proxy: FTP: Content-Type set to %s", r->content_type); |
1679 |
} |
1666 |
} |
1680 |
else { |
1667 |
else { |
1681 |
ap_set_content_type(r, ap_default_type(r)); |
1668 |
ap_set_content_type(r, ap_default_type(r)); |
1682 |
} |
1669 |
} |
1683 |
if (xfer_type != 'A' && size != NULL) { |
1670 |
if (ftp_data->xfer_type != 'A' ) { |
1684 |
/* We "trust" the ftp server to really serve (size) bytes... */ |
1671 |
apr_table_setn(r->headers_out, "Content-Length", ftp_data->size); |
1685 |
apr_table_setn(r->headers_out, "Content-Length", size); |
|
|
1686 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1672 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1687 |
"proxy: FTP: Content-Length set to %s", size); |
1673 |
"proxy: FTP: Content-Length set to %s", ftp_data->size); |
1688 |
} |
1674 |
} |
1689 |
} |
1675 |
} |
1690 |
apr_table_setn(r->headers_out, "Content-Type", r->content_type); |
1676 |
apr_table_setn(r->headers_out, "Content-Type", r->content_type); |
1691 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1677 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1692 |
"proxy: FTP: Content-Type set to %s", r->content_type); |
1678 |
"proxy: FTP: Content-Type set to %s", r->content_type); |
1693 |
|
1679 |
|
1694 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
1680 |
#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) |
1695 |
if (mtime != 0L) { |
1681 |
if (mtime != 0L) { |
Lines 1697-1703
Link Here
|
1697 |
apr_rfc822_date(datestr, mtime); |
1683 |
apr_rfc822_date(datestr, mtime); |
1698 |
apr_table_set(r->headers_out, "Last-Modified", datestr); |
1684 |
apr_table_set(r->headers_out, "Last-Modified", datestr); |
1699 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1685 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1700 |
"proxy: FTP: Last-Modified set to %s", datestr); |
1686 |
"proxy: FTP: Last-Modified set to %s", datestr); |
1701 |
} |
1687 |
} |
1702 |
#endif /* USE_MDTM */ |
1688 |
#endif /* USE_MDTM */ |
1703 |
|
1689 |
|
Lines 1705-1722
Link Here
|
1705 |
* @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz, |
1691 |
* @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz, |
1706 |
* @@@ the encoding is currently set to x-gzip) |
1692 |
* @@@ the encoding is currently set to x-gzip) |
1707 |
*/ |
1693 |
*/ |
1708 |
if (dirlisting && r->content_encoding != NULL) |
1694 |
if ((ftp_data->action == LISTING) && r->content_encoding != NULL) |
1709 |
r->content_encoding = NULL; |
1695 |
r->content_encoding = NULL; |
1710 |
|
1696 |
|
1711 |
/* set content-encoding (not for dir listings, they are uncompressed)*/ |
1697 |
/* set content-encoding (not for dir listings, they are uncompressed)*/ |
1712 |
if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { |
1698 |
if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { |
1713 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1699 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1714 |
"proxy: FTP: Content-Encoding set to %s", r->content_encoding); |
1700 |
"proxy: FTP: Content-Encoding set to %s", r->content_encoding); |
1715 |
apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); |
1701 |
apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); |
1716 |
} |
1702 |
} |
1717 |
|
1703 |
|
|
|
1704 |
ftp_data->state = ftp_transfer_stream; |
1705 |
return OK; |
1706 |
} |
1707 |
|
1708 |
static |
1709 |
int transfer_stream(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1710 |
{ |
1711 |
int rc; |
1712 |
char *msg = NULL; |
1713 |
|
1714 |
apr_status_t rv; |
1715 |
apr_socket_t *local_sock = ftp_data->cur.data.local_sock; |
1716 |
apr_socket_t *data_sock = ftp_data->cur.data.sock; |
1717 |
conn_rec *c = r->connection; |
1718 |
char dates[APR_RFC822_DATE_LEN]; |
1719 |
conn_rec *data = NULL; |
1720 |
|
1718 |
/* wait for connection */ |
1721 |
/* wait for connection */ |
1719 |
if (use_port) { |
1722 |
if (ftp_data->mode == PORT) { |
1720 |
for (;;) { |
1723 |
for (;;) { |
1721 |
rv = apr_socket_accept(&data_sock, local_sock, r->pool); |
1724 |
rv = apr_socket_accept(&data_sock, local_sock, r->pool); |
1722 |
if (rv == APR_EINTR) { |
1725 |
if (rv == APR_EINTR) { |
Lines 1727-1750
Link Here
|
1727 |
} |
1730 |
} |
1728 |
else { |
1731 |
else { |
1729 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1732 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, |
1730 |
"proxy: FTP: failed to accept data connection"); |
1733 |
"proxy: FTP: failed to accept data connection"); |
1731 |
proxy_ftp_cleanup(r, backend); |
|
|
1732 |
return HTTP_BAD_GATEWAY; |
1734 |
return HTTP_BAD_GATEWAY; |
1733 |
} |
1735 |
} |
1734 |
} |
1736 |
} |
1735 |
} |
1737 |
} |
1736 |
|
1738 |
|
1737 |
/* the transfer socket is now open, create a new connection */ |
1739 |
/* the transfer socket is now open, create a new connection */ |
1738 |
data = ap_run_create_connection(p, r->server, data_sock, r->connection->id, |
1740 |
data = ap_run_create_connection(r->pool, r->server, data_sock, r->connection->id, |
1739 |
r->connection->sbh, c->bucket_alloc); |
1741 |
r->connection->sbh, c->bucket_alloc); |
1740 |
if (!data) { |
1742 |
if (!data) { |
1741 |
/* |
1743 |
/* |
1742 |
* the peer reset the connection already; ap_run_create_connection() closed |
1744 |
* the peer reset the connection already; ap_run_create_connection() closed |
1743 |
* the socket |
1745 |
* the socket |
1744 |
*/ |
1746 |
*/ |
1745 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1747 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1746 |
"proxy: FTP: an error occurred creating the transfer connection"); |
1748 |
"proxy: FTP: an error occurred creating the transfer connection"); |
1747 |
proxy_ftp_cleanup(r, backend); |
|
|
1748 |
return HTTP_INTERNAL_SERVER_ERROR; |
1749 |
return HTTP_INTERNAL_SERVER_ERROR; |
1749 |
} |
1750 |
} |
1750 |
|
1751 |
|
Lines 1752-1761
Link Here
|
1752 |
rc = ap_run_pre_connection(data, data_sock); |
1753 |
rc = ap_run_pre_connection(data, data_sock); |
1753 |
if (rc != OK && rc != DONE) { |
1754 |
if (rc != OK && rc != DONE) { |
1754 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1755 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1755 |
"proxy: FTP: pre_connection setup failed (%d)", |
1756 |
"proxy: FTP: pre_connection setup failed (%d)", |
1756 |
rc); |
1757 |
rc); |
1757 |
data->aborted = 1; |
1758 |
data->aborted = 1; |
1758 |
proxy_ftp_cleanup(r, backend); |
|
|
1759 |
return rc; |
1759 |
return rc; |
1760 |
} |
1760 |
} |
1761 |
|
1761 |
|
Lines 1768-1774
Link Here
|
1768 |
/* send response */ |
1768 |
/* send response */ |
1769 |
r->sent_bodyct = 1; |
1769 |
r->sent_bodyct = 1; |
1770 |
|
1770 |
|
1771 |
if (dirlisting) { |
1771 |
if (ftp_data->action == LISTING) { |
1772 |
/* insert directory filter */ |
1772 |
/* insert directory filter */ |
1773 |
ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection); |
1773 |
ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection); |
1774 |
} |
1774 |
} |
Lines 1779-1799
Link Here
|
1779 |
int finish = FALSE; |
1779 |
int finish = FALSE; |
1780 |
|
1780 |
|
1781 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1781 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1782 |
"proxy: FTP: start body send"); |
1782 |
"proxy: FTP: start body send"); |
1783 |
|
1783 |
|
1784 |
/* read the body, pass it to the output filters */ |
1784 |
/* read the body, pass it to the output filters */ |
1785 |
while (ap_get_brigade(data->input_filters, |
1785 |
while (ap_get_brigade(data->input_filters, |
1786 |
bb, |
1786 |
bb, |
1787 |
AP_MODE_READBYTES, |
1787 |
AP_MODE_READBYTES, |
1788 |
APR_BLOCK_READ, |
1788 |
APR_BLOCK_READ, |
1789 |
conf->io_buffer_size) == APR_SUCCESS) { |
1789 |
ftp_data->cur.conf->io_buffer_size) == APR_SUCCESS) { |
1790 |
#if DEBUGGING |
1790 |
#if DEBUGGING |
1791 |
{ |
1791 |
{ |
1792 |
apr_off_t readbytes; |
1792 |
apr_off_t readbytes; |
1793 |
apr_brigade_length(bb, 0, &readbytes); |
1793 |
apr_brigade_length(bb, 0, &readbytes); |
1794 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, |
1794 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, |
1795 |
r->server, "proxy (PID %d): readbytes: %#x", |
1795 |
r->server, "proxy (PID %d): readbytes: %#x", |
1796 |
getpid(), readbytes); |
1796 |
getpid(), readbytes); |
1797 |
} |
1797 |
} |
1798 |
#endif |
1798 |
#endif |
1799 |
/* sanity check */ |
1799 |
/* sanity check */ |
Lines 1813-1819
Link Here
|
1813 |
apr_socket_close(data_sock); |
1813 |
apr_socket_close(data_sock); |
1814 |
data_sock = NULL; |
1814 |
data_sock = NULL; |
1815 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1815 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1816 |
"proxy: FTP: data connection closed"); |
1816 |
"proxy: FTP: data connection closed"); |
1817 |
/* signal that we must leave */ |
1817 |
/* signal that we must leave */ |
1818 |
finish = TRUE; |
1818 |
finish = TRUE; |
1819 |
} |
1819 |
} |
Lines 1826-1832
Link Here
|
1826 |
|
1826 |
|
1827 |
/* try send what we read */ |
1827 |
/* try send what we read */ |
1828 |
if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS |
1828 |
if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS |
1829 |
|| c->aborted) { |
1829 |
|| c->aborted) { |
1830 |
/* Ack! Phbtt! Die! User aborted! */ |
1830 |
/* Ack! Phbtt! Die! User aborted! */ |
1831 |
finish = TRUE; |
1831 |
finish = TRUE; |
1832 |
} |
1832 |
} |
Lines 1840-1857
Link Here
|
1840 |
} |
1840 |
} |
1841 |
} |
1841 |
} |
1842 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1842 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1843 |
"proxy: FTP: end body send"); |
1843 |
"proxy: FTP: end body send"); |
1844 |
|
1844 |
|
1845 |
} |
1845 |
} |
1846 |
if (data_sock) { |
|
|
1847 |
ap_flush_conn(data); |
1848 |
apr_socket_close(data_sock); |
1849 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1850 |
"proxy: FTP: data connection closed"); |
1851 |
} |
1852 |
|
1846 |
|
|
|
1847 |
ap_flush_conn(data); |
1848 |
ftp_data->state = ftp_finish_response; |
1849 |
return OK; |
1850 |
} |
1851 |
|
1852 |
static |
1853 |
int finish_response(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1854 |
{ |
1855 |
int rc; |
1856 |
char* msg; |
1857 |
apr_socket_close(ftp_data->cur.data.sock); |
1858 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1859 |
"proxy: FTP: data connection closed"); |
1860 |
|
1853 |
/* Retrieve the final response for the RETR or LIST commands */ |
1861 |
/* Retrieve the final response for the RETR or LIST commands */ |
1854 |
rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage); |
1862 |
rc = proxy_ftp_command(NULL, r, ftp_ctrl, bb, &msg); |
|
|
1863 |
ftp_data->state = ftp_cleanup; |
1864 |
return OK; |
1865 |
} |
1866 |
|
1867 |
|
1868 |
static |
1869 |
int cleanup(ftp_conn_data *ftp_data, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) |
1870 |
{ |
1871 |
int rc; |
1855 |
apr_brigade_cleanup(bb); |
1872 |
apr_brigade_cleanup(bb); |
1856 |
|
1873 |
|
1857 |
/* |
1874 |
/* |
Lines 1860-1879
Link Here
|
1860 |
* If there are no KeepAlives, or if the connection has been signalled to |
1877 |
* If there are no KeepAlives, or if the connection has been signalled to |
1861 |
* close, close the socket and clean up |
1878 |
* close, close the socket and clean up |
1862 |
*/ |
1879 |
*/ |
|
|
1880 |
rc = send_quit(ftp_data, r, ftp_ctrl, bb); |
1881 |
ftp_data->p_conn->close = 1; |
1863 |
|
1882 |
|
1864 |
/* finish */ |
1883 |
ap_flush_conn(ftp_ctrl); |
1865 |
rc = proxy_ftp_command("QUIT" CRLF, |
|
|
1866 |
r, origin, bb, &ftpmessage); |
1867 |
/* responses: 221, 500 */ |
1868 |
/* 221 Service closing control connection. */ |
1869 |
/* 500 Syntax error, command unrecognized. */ |
1870 |
ap_flush_conn(origin); |
1871 |
proxy_ftp_cleanup(r, backend); |
1872 |
|
1873 |
apr_brigade_destroy(bb); |
1884 |
apr_brigade_destroy(bb); |
|
|
1885 |
ap_set_module_config(r->connection->conn_config, &proxy_ftp_module, NULL); |
1874 |
return OK; |
1886 |
return OK; |
1875 |
} |
1887 |
} |
1876 |
|
1888 |
|
|
|
1889 |
static |
1890 |
ftp_conn_data* init_ftp_data(proxy_conn_rec* p_conn) |
1891 |
{ |
1892 |
ftp_conn_data *ftp_data = apr_pcalloc(p_conn->pool, sizeof(ftp_conn_data)); |
1893 |
ftp_data->xfer_type = 'A'; |
1894 |
ftp_data->path = ""; |
1895 |
ftp_data->user = "anonymous"; |
1896 |
ftp_data->password = "apache-proxy@"; |
1897 |
ftp_data->p_conn = p_conn; |
1898 |
return ftp_data; |
1899 |
} |
1900 |
|
1901 |
static |
1902 |
apr_status_t ap_proxy_ftp_request(apr_pool_t *p, request_rec *r, |
1903 |
proxy_conn_rec *p_conn, conn_rec *origin, |
1904 |
proxy_server_conf *conf, |
1905 |
apr_uri_t *uri, |
1906 |
char *url, char *server_portstr) |
1907 |
{ |
1908 |
int rc = 0; |
1909 |
apr_bucket_brigade *bb = apr_brigade_create(p, r->connection->bucket_alloc); |
1910 |
|
1911 |
/* see if we have any ftp state info (ie if this conn was initialized before) |
1912 |
* if not, alloc data that needs to be alive for the entire length of persistant |
1913 |
* connection. |
1914 |
*/ |
1915 |
|
1916 |
ftp_conn_data *ftp_data; |
1917 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1918 |
"proxy: FTP: create new connection %s", url); |
1919 |
ftp_data = init_ftp_data(p_conn); |
1920 |
ftp_data->cur.conf = conf; |
1921 |
|
1922 |
ftp_data->url = url; |
1923 |
ftp_data->uri = uri; |
1924 |
|
1925 |
/* parse the path first*/ |
1926 |
rc = on_init(ftp_data, r, origin, bb); |
1927 |
|
1928 |
/* State machine. */ |
1929 |
for(;;) { |
1930 |
switch (ftp_data->state) { |
1931 |
case ftp_connect: |
1932 |
/* The socket will already be connected, just fetch the welcome message*/ |
1933 |
rc = on_connect(ftp_data, r, origin, bb); |
1934 |
break; |
1935 |
case ftp_send_user: |
1936 |
rc = send_user(ftp_data, r, origin, bb); |
1937 |
break; |
1938 |
case ftp_send_pass: |
1939 |
rc = send_pass(ftp_data, r, origin, bb); |
1940 |
break; |
1941 |
case ftp_choose_action: |
1942 |
rc = choose_action(ftp_data, r, origin, bb); |
1943 |
break; |
1944 |
case ftp_check_type: |
1945 |
rc = check_type(ftp_data, r, origin, bb); |
1946 |
break; |
1947 |
case ftp_check_trans_mode: |
1948 |
rc = check_trans_mode(ftp_data, r, origin, bb); |
1949 |
break; |
1950 |
case ftp_connect_data_port: |
1951 |
rc = connect_data_port(ftp_data, r, origin, bb); |
1952 |
break; |
1953 |
case ftp_check_transfer: |
1954 |
rc = check_transfer(ftp_data, r, origin, bb); |
1955 |
break; |
1956 |
case ftp_send_retr: |
1957 |
rc = send_retr(ftp_data, r, origin, bb); |
1958 |
break; |
1959 |
case ftp_send_stor: |
1960 |
rc = send_stor(ftp_data, r, origin, bb); |
1961 |
break; |
1962 |
case ftp_send_list: |
1963 |
rc = send_list(ftp_data, r, origin, bb); |
1964 |
break; |
1965 |
case ftp_start_response: |
1966 |
rc = start_response(ftp_data, r, origin, bb); |
1967 |
break; |
1968 |
case ftp_transfer_stream: |
1969 |
rc = transfer_stream(ftp_data, r, origin, bb); |
1970 |
break; |
1971 |
case ftp_finish_response: |
1972 |
rc = finish_response(ftp_data, r, origin, bb); |
1973 |
break; |
1974 |
case ftp_cleanup: |
1975 |
cleanup(ftp_data, r, origin, bb); |
1976 |
return OK; |
1977 |
|
1978 |
default: |
1979 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
1980 |
"proxy: FTP: Unknown switch [%d] last rc:[%d]", ftp_data->state, ftp_data->cur.rc); |
1981 |
case ftp_error: |
1982 |
proxyerror(r, p_conn, rc, ftp_data->msg); |
1983 |
} |
1984 |
|
1985 |
/* break if there was an error*/ |
1986 |
if (rc != OK) |
1987 |
break; |
1988 |
} |
1989 |
|
1990 |
return rc; |
1991 |
} |
1992 |
|
1993 |
static |
1994 |
apr_status_t ap_proxy_ftp_cleanup(request_rec *r, |
1995 |
proxy_conn_rec *backend) |
1996 |
{ |
1997 |
ap_proxy_release_connection(proxy_function, backend, r->server); |
1998 |
return OK; |
1999 |
} |
2000 |
|
2001 |
/* |
2002 |
* Handles direct access of ftp:// URLs |
2003 |
* Original (Non-PASV) version from |
2004 |
* Troy Morrison <spiffnet@zoom.com> |
2005 |
* PASV added by Chuck |
2006 |
* Filters by [Graham Leggett <minfrin@sharp.fm>] |
2007 |
*/ |
2008 |
static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, |
2009 |
proxy_server_conf *conf, |
2010 |
char *url, const char *proxyname, |
2011 |
apr_port_t proxyport) |
2012 |
{ |
2013 |
int status; |
2014 |
char server_portstr[32]; |
2015 |
char *scheme; |
2016 |
const char *u; |
2017 |
proxy_conn_rec *backend = NULL; |
2018 |
|
2019 |
/* Note: Memory pool allocation. |
2020 |
* A downstream keepalive connection is always connected to the existence |
2021 |
* (or not) of an upstream keepalive connection. If this is not done then |
2022 |
* load balancing against multiple backend servers breaks (one backend |
2023 |
* server ends up taking 100% of the load), and the risk is run of |
2024 |
* downstream keepalive connections being kept open unnecessarily. This |
2025 |
* keeps webservers busy and ties up resources. |
2026 |
* |
2027 |
* As a result, we allocate all sockets out of the upstream connection |
2028 |
* pool, and when we want to reuse a socket, we check first whether the |
2029 |
* connection ID of the current upstream connection is the same as that |
2030 |
* of the connection when the socket was opened. |
2031 |
*/ |
2032 |
apr_pool_t *p = r->connection->pool; |
2033 |
conn_rec *c = r->connection; |
2034 |
apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri)); |
2035 |
|
2036 |
/* find the scheme */ |
2037 |
u = strchr(url, ':'); |
2038 |
if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0') |
2039 |
return DECLINED; |
2040 |
scheme = apr_pstrndup(c->pool, url, u - url); |
2041 |
/* scheme is lowercase */ |
2042 |
ap_str_tolower(scheme); |
2043 |
/* is it for us? */ |
2044 |
if (strcmp(scheme, "ftp") || proxyname) { |
2045 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
2046 |
"proxy: FTP: declining URL %s", url); |
2047 |
return DECLINED; /* only interested in direct FTP */ |
2048 |
} |
2049 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, |
2050 |
"proxy: FTP: serving URL %s", url); |
2051 |
|
2052 |
|
2053 |
/* create space for state information */ |
2054 |
if ((status = ap_proxy_acquire_connection(proxy_function, &backend, |
2055 |
worker, r->server)) != OK) |
2056 |
goto cleanup; |
2057 |
|
2058 |
|
2059 |
/* Step One: Determine Who To Connect To */ |
2060 |
if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend, |
2061 |
uri, &url, 0, |
2062 |
0, server_portstr, |
2063 |
sizeof(server_portstr))) != OK) |
2064 |
goto cleanup; |
2065 |
|
2066 |
/* Step Two: Make the Connection */ |
2067 |
if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) { |
2068 |
status = HTTP_SERVICE_UNAVAILABLE; |
2069 |
goto cleanup; |
2070 |
} |
2071 |
|
2072 |
/* Step Three: Create conn_rec */ |
2073 |
if (!backend->connection) { |
2074 |
if ((status = ap_proxy_connection_create(proxy_function, backend, |
2075 |
c, r->server)) != OK) |
2076 |
goto cleanup; |
2077 |
} |
2078 |
|
2079 |
/* Step Four: Send the Request */ |
2080 |
if ((status = ap_proxy_ftp_request(p, r, backend, backend->connection, |
2081 |
conf, uri, url, server_portstr)) != OK) |
2082 |
goto cleanup; |
2083 |
|
2084 |
/* Step Five: Receive the Response -- is included in previous step */ |
2085 |
|
2086 |
/* Step Six: Clean Up */ |
2087 |
|
2088 |
cleanup: |
2089 |
if (backend) { |
2090 |
if (status != OK) |
2091 |
backend->close = 1; |
2092 |
ap_proxy_ftp_cleanup(r, backend); |
2093 |
} |
2094 |
return status; |
2095 |
|
2096 |
} |
2097 |
|
1877 |
static void ap_proxy_ftp_register_hook(apr_pool_t *p) |
2098 |
static void ap_proxy_ftp_register_hook(apr_pool_t *p) |
1878 |
{ |
2099 |
{ |
1879 |
/* hooks */ |
2100 |
/* hooks */ |
Lines 1893-1895
Link Here
|
1893 |
NULL, /* command apr_table_t */ |
2114 |
NULL, /* command apr_table_t */ |
1894 |
ap_proxy_ftp_register_hook /* register hooks */ |
2115 |
ap_proxy_ftp_register_hook /* register hooks */ |
1895 |
}; |
2116 |
}; |
|
|
2117 |
|