From 07e7e196f4e0ab1edf3172f1a128c4253c20b504 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Thu, 12 Apr 2001 01:57:48 +0000 Subject: [PATCH] FTP directory filter works now. Many FIXME notes added. Much overhauling of proxy_ftp.c PR: Obtained from: Reviewed by: git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88817 13f79535-47bb-0310-9956-ffa450edef68 --- modules/proxy/mod_proxy.c | 4 +- modules/proxy/mod_proxy.h | 10 +- modules/proxy/proxy_ftp.c | 446 +++++++++++++++++++++++++------------ modules/proxy/proxy_http.c | 6 +- modules/proxy/proxy_util.c | 7 +- 5 files changed, 314 insertions(+), 159 deletions(-) diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index dd9153a77e..5d2cf97009 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -410,8 +410,6 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s) ps->noproxies = ap_make_array(p, 10, sizeof(struct noproxy_entry)); ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry)); ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int)); -/* ps->client_socket = NULL;*/ - ps->connection = NULL; ps->domain = NULL; ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */ ps->viaopt_set = 0; /* 0 means default */ @@ -721,7 +719,7 @@ static void register_hooks(apr_pool_t *p) /* filename-to-URI translation */ ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST); /* filters */ - ap_register_output_filter("PROXY_SEND_DIR", ap_proxy_send_dir_filter, AP_FTYPE_CONNECTION); + ap_register_output_filter("PROXY_SEND_DIR", ap_proxy_send_dir_filter, AP_FTYPE_CONTENT); /* fixups */ ap_hook_fixups(proxy_fixup, NULL, NULL, APR_HOOK_FIRST); /* post read_request handling */ diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 7a8f6eab31..9e32cda9e7 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -167,11 +167,6 @@ typedef struct { apr_array_header_t *noproxies; apr_array_header_t *dirconn; apr_array_header_t *allowed_connect_ports; - long id; - const char *connectname; - apr_port_t connectport; -/* apr_socket_t *client_socket;*/ - conn_rec *connection; const char *domain; /* domain name to use in absence of a domain name in the request */ int req; /* true if proxy requests are enabled */ char req_set; @@ -215,7 +210,8 @@ int ap_proxy_connect_handler(request_rec *r, char *url, int ap_proxy_ftp_canon(request_rec *r, char *url); int ap_proxy_ftp_handler(request_rec *r, char *url); -apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *bb); +apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, + apr_bucket_brigade *bb); /* proxy_http.c */ @@ -248,7 +244,7 @@ int ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p); int ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p); int ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr); int ap_proxy_pre_http_connection(conn_rec *c, request_rec *r); -apr_status_t ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen); +apr_status_t ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos); void ap_proxy_reset_output_filters(conn_rec *c); #endif /*MOD_PROXY_H*/ diff --git a/modules/proxy/proxy_ftp.c b/modules/proxy/proxy_ftp.c index 83c53ba701..5c089a1907 100644 --- a/modules/proxy/proxy_ftp.c +++ b/modules/proxy/proxy_ftp.c @@ -198,9 +198,9 @@ static int ftp_getrc_msg(conn_rec *c, apr_bucket_brigade *bb, char *msgbuf, int char *mb = msgbuf, *me = &msgbuf[msglen]; apr_status_t rv; + int eos; - - if (APR_SUCCESS != (rv = ap_proxy_string_read(c, bb, response, sizeof(response)))) { + if (APR_SUCCESS != (rv = ap_proxy_string_read(c, bb, response, sizeof(response), &eos))) { return -1; } if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || @@ -215,7 +215,7 @@ static int ftp_getrc_msg(conn_rec *c, apr_bucket_brigade *bb, char *msgbuf, int memcpy(buff, response, 3); buff[3] = ' '; do { - if (APR_SUCCESS != (rv = ap_proxy_string_read(c, bb, response, sizeof(response)))) { + if (APR_SUCCESS != (rv = ap_proxy_string_read(c, bb, response, sizeof(response), &eos))) { return -1; } mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); @@ -234,81 +234,156 @@ static int ftp_getrc_msg(conn_rec *c, apr_bucket_brigade *bb, char *msgbuf, int * all in good time...! :) */ -apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *bb) +typedef struct { + apr_bucket_brigade *in; + char buffer[MAX_STRING_LEN]; + enum {HEADER, BODY, FOOTER} state; +} proxy_dir_ctx_t; + +apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *in) { - conn_rec *c = f->r->connection; - apr_pool_t *p = f->r->pool; + request_rec *r = f->r; + apr_pool_t *p = r->pool; apr_bucket *e; - char buf[MAX_STRING_LEN]; - char buf2[MAX_STRING_LEN]; + apr_bucket_brigade *out = apr_brigade_create(p); + apr_status_t rv; - char *filename; - int searchidx = 0; - char *searchptr = NULL; - int firstfile = 1; register int n; - char *dir, *path, *reldir, *site; + char *dir, *path, *reldir, *site, *str; - char *cwd = NULL; + const char *pwd = apr_table_get(r->notes, "Directory-PWD"); + const char *readme = apr_table_get(r->notes, "Directory-README"); + proxy_dir_ctx_t *ctx = f->ctx; + if (!ctx) { + f->ctx = ctx = apr_pcalloc(p, sizeof(*ctx)); + ctx->in = apr_brigade_create(p); + ctx->buffer[0] = 0; + ctx->state = HEADER; + } - /* Save "scheme://site" prefix without password */ - site = ap_unparse_uri_components(p, &f->r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO); - /* ... and path without query args */ - path = ap_unparse_uri_components(p, &f->r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY); - (void)decodeenc(path); + /* combine the stored and the new */ + APR_BRIGADE_CONCAT(ctx->in, in); + + if (HEADER == ctx->state) { + + /* Save "scheme://site" prefix without password */ + site = ap_unparse_uri_components(p, &f->r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO); + /* ... and path without query args */ + path = ap_unparse_uri_components(p, &f->r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY); + (void)decodeenc(path); + + /* Copy path, strip (all except the last) trailing slashes */ + path = dir = apr_pstrcat(p, path, "/", NULL); + while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/') + path[n-1] = '\0'; + + /* print "ftp://host/" */ + str = apr_psprintf(p, DOCTYPE_HTML_3_2 + "\n\n\n\n%s%s\n" + "\n\n\n" + "\n\n

Directory of " + "%s/", + site, path, site, path, site); + + e = apr_bucket_pool_create(str, strlen(str), p); + APR_BRIGADE_INSERT_TAIL(out, e); + + while ((dir = strchr(dir+1, '/')) != NULL) + { + *dir = '\0'; + if ((reldir = strrchr(path+1, '/'))==NULL) + reldir = path+1; + else + ++reldir; + /* print "path/" component */ + str = apr_psprintf(p, "%s/", path+1, reldir); + e = apr_bucket_pool_create(str, strlen(str), p); + APR_BRIGADE_INSERT_TAIL(out, e); + *dir = '/'; + } + /* If the caller has determined the current directory, and it differs */ + /* from what the client requested, then show the real name */ + if (pwd == NULL || strncmp (pwd, path, strlen(pwd)) == 0) { + str = apr_psprintf(p, "

\n\n
\n\n
");
+	} else {
+	    str = apr_psprintf(p, "\n\n(%s)\n\n
\n\n
", pwd);
+	}
+	e = apr_bucket_pool_create(str, strlen(str), p);
+	APR_BRIGADE_INSERT_TAIL(out, e);
 
-    /* Copy path, strip (all except the last) trailing slashes */
-    path = dir = apr_pstrcat(p, path, "/", NULL);
-    while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/')
-	path[n-1] = '\0';
+	/* print README */
+	if (readme) {
+	    str = apr_psprintf(p, "%s\n
\n\n
\n\n
\n",
+			     readme);
 
-    /* print "ftp://host/" */
-    n = apr_snprintf(buf, sizeof(buf), DOCTYPE_HTML_3_2
-		"%s%s\n"
-		"\n"
-		"

Directory of " - "%s/", - site, path, site, path, site); + e = apr_bucket_pool_create(str, strlen(str), p); + APR_BRIGADE_INSERT_TAIL(out, e); + } - e = apr_bucket_pool_create(buf, n, p); - APR_BRIGADE_INSERT_TAIL(bb, e); + /* make sure page intro gets sent out */ + e = apr_bucket_flush_create(); + APR_BRIGADE_INSERT_TAIL(out, e); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_cleanup(out); - while ((dir = strchr(dir+1, '/')) != NULL) - { - *dir = '\0'; - if ((reldir = strrchr(path+1, '/'))==NULL) - reldir = path+1; - else - ++reldir; - /* print "path/" component */ - n = apr_snprintf(buf, sizeof(buf), "%s/", path+1, reldir); - e = apr_bucket_pool_create(buf, n, p); - APR_BRIGADE_INSERT_TAIL(bb, e); - *dir = '/'; - } - /* If the caller has determined the current directory, and it differs */ - /* from what the client requested, then show the real name */ - if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) { - n = apr_snprintf(buf, sizeof(buf), "

\n
");
-    } else {
-	n = apr_snprintf(buf, sizeof(buf), "\n(%s)\n
", cwd);
+	ctx->state = BODY;
     }
-    e = apr_bucket_pool_create(buf, n, p);
-    APR_BRIGADE_INSERT_TAIL(bb, e);
 
-    e = apr_bucket_flush_create();
-    APR_BRIGADE_INSERT_TAIL(bb, e);
+    /* loop through each line of directory */
+    while (BODY == ctx->state) {
+	char *filename;
+	int found = 0;
+	int eos = 0;
+	ctx->buffer[0] = 0;
+
+	/* get a complete line */
+	while (!found && !APR_BRIGADE_EMPTY(ctx->in)) {
+	    char *pos, *response;
+	    apr_size_t len, max;
+	    e = APR_BRIGADE_FIRST(ctx->in);
+	    if (APR_BUCKET_IS_EOS(e)) {
+		eos = 1;
+		break;
+	    }
+	    if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) {
+		return rv;
+	    }
+	    pos = memchr(response, APR_ASCII_LF, len);
+	    if (pos != NULL) {
+		if ((pos - response + 1) != len) {
+		    apr_bucket_split(e, pos - response + 1);
+		}
+		found = 1;
+	    }
+	    max = sizeof(ctx->buffer)-strlen(ctx->buffer)-1;
+	    if (len > max) {
+		len = max;
+	    }
+	    apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len);
+	    APR_BUCKET_REMOVE(e);
+	    apr_bucket_destroy(e);
+	}
 
-    while (!c->aborted) {
-	n = ap_getline(buf, sizeof(buf), f->r, 0);
-	if (n == -1) {		/* input error */
+	/* EOS? jump to footer */
+	if (eos) {
+	    ctx->state = FOOTER;
 	    break;
 	}
-	if (n == 0) {
-	    break;		/* EOF */
+
+	/* not complete? leave and try get some more */
+	if (!found) {
+	    return APR_SUCCESS;
 	}
-	if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
+
+ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "proxy: directory line: [%s]", ctx->buffer);
+
+
+	/* a symlink? */
+	if (ctx->buffer[0] == 'l' && (filename=strstr(ctx->buffer, " -> ")) != NULL) {
 	    char *link_ptr = filename;
 
 	    do {
@@ -318,65 +393,76 @@ apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *bb)
 	    *(link_ptr++) = '\0';
 	    if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
 	      link_ptr[n - 1] = '\0';
-	    apr_snprintf(buf2, sizeof(buf2), "%s %s %s\n", buf, filename, filename, link_ptr);
-	    apr_cpystrn(buf, buf2, sizeof(buf));
-	    n = strlen(buf);
+	    str = apr_psprintf(p, "%s %s %s\n", ctx->buffer, filename, filename, link_ptr);
 	}
-	else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || apr_isdigit(buf[0])) {
-	    if (apr_isdigit(buf[0])) {	/* handle DOS dir */
-		searchptr = strchr(buf, '<');
+
+	/* a directory/file? */
+	else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) {
+	    int searchidx = 0;
+	    char *searchptr = NULL;
+	    int firstfile = 1;
+	    if (apr_isdigit(ctx->buffer[0])) {	/* handle DOS dir */
+		searchptr = strchr(ctx->buffer, '<');
 		if (searchptr != NULL)
 		    *searchptr = '[';
-		searchptr = strchr(buf, '>');
+		searchptr = strchr(ctx->buffer, '>');
 		if (searchptr != NULL)
 		    *searchptr = ']';
 	    }
 
-	    filename = strrchr(buf, ' ');
+	    filename = strrchr(ctx->buffer, ' ');
 	    *(filename++) = 0;
 	    filename[strlen(filename) - 1] = 0;
 
 	    /* handle filenames with spaces in 'em */
 	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
 		firstfile = 0;
-		searchidx = filename - buf;
+		searchidx = filename - ctx->buffer;
 	    }
-	    else if (searchidx != 0 && buf[searchidx] != 0) {
+	    else if (searchidx != 0 && ctx->buffer[searchidx] != 0) {
 		*(--filename) = ' ';
-		buf[searchidx - 1] = 0;
-		filename = &buf[searchidx];
+		ctx->buffer[searchidx - 1] = 0;
+		filename = &ctx->buffer[searchidx];
 	    }
 
 	    /* Special handling for '.' and '..' */
-	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
-		apr_snprintf(buf2, sizeof(buf2), "%s %s\n",
-		    buf, filename, filename);
+	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') {
+		str = apr_psprintf(p, "%s %s\n",
+		    ctx->buffer, filename, filename);
 	    }
 	    else {
-		apr_snprintf(buf2, sizeof(buf2), "%s %s\n", buf, filename, filename);
+		str = apr_psprintf(p, "%s %s\n",
+		    ctx->buffer, filename, filename);
 	    }
-	    apr_cpystrn(buf, buf2, sizeof(buf));
-	    n = strlen(buf);
 	}
 
-	e = apr_bucket_pool_create(buf, n, p);
-	APR_BRIGADE_INSERT_TAIL(bb, e);
+	e = apr_bucket_pool_create(str, strlen(str), p);
+	APR_BRIGADE_INSERT_TAIL(out, e);
 	e = apr_bucket_flush_create();
-	APR_BRIGADE_INSERT_TAIL(bb, e);
+	APR_BRIGADE_INSERT_TAIL(out, e);
+	if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
+	    return rv;
+	}
+	apr_brigade_cleanup(out);
 
     }
 
-    n = apr_snprintf(buf, sizeof(buf), "

\n%s\n", ap_psignature("", f->r)); - e = apr_bucket_pool_create(buf, n, p); - APR_BRIGADE_INSERT_TAIL(bb, e); + if (FOOTER == ctx->state) { + str = apr_psprintf(p, "
\n\n
\n\n%s\n\n\n\n", ap_psignature("", r)); + e = apr_bucket_pool_create(str, strlen(str), p); + APR_BRIGADE_INSERT_TAIL(out, e); - e = apr_bucket_eos_create(); - APR_BRIGADE_INSERT_TAIL(bb, e); + e = apr_bucket_flush_create(); + APR_BRIGADE_INSERT_TAIL(out, e); -/* probably not necessary */ -/* e = apr_bucket_flush_create(); - APR_BRIGADE_INSERT_TAIL(bb, e); -*/ + e = apr_bucket_eos_create(); + APR_BRIGADE_INSERT_TAIL(out, e); + + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_destroy(out); + } return APR_SUCCESS; } @@ -420,6 +506,7 @@ static int ftp_unauthorized (request_rec *r, int log_it) int ap_proxy_ftp_handler(request_rec *r, char *url) { apr_pool_t *p = r->pool; + conn_rec *c = r->connection; apr_socket_t *sock, *local_sock, *remote_sock; apr_sockaddr_t *connect_addr; apr_status_t rv; @@ -432,7 +519,6 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) apr_port_t connectport; char buffer[MAX_STRING_LEN]; char *path, *strp, *parms; - char *cwd = NULL; char *user = NULL; /* char *account = NULL; how to supply an account in a URL? */ const char *password = NULL; @@ -448,6 +534,16 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + proxy_conn_rec *backend = + (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_module); + if (!backend) { + backend = ap_pcalloc(c->pool, sizeof(proxy_conn_rec)); + backend->connection = NULL; + backend->hostname = NULL; + backend->port = 0; + ap_set_module_config(c->conn_config, &proxy_module, backend); + } + /* * I: Who Do I Connect To? @@ -616,15 +712,10 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* if a keepalive connection is floating around, close it first! */ /* we might support ftp keepalives later, but not now... */ - if ((conf->id == r->connection->id) && conf->connection) { - apr_socket_close(conf->connection->client_socket); - conf->connection = NULL; + if (backend->connection) { + apr_socket_close(backend->connection->client_socket); + backend->connection = NULL; } - conf->id = r->connection->id; - /* allocate this out of the connection pool - the check on r->connection->id makes - * sure that this string does not get accessed past the connection lifetime */ - conf->connectname = apr_pstrdup(r->connection->pool, connectname); - conf->connectport = connectport; ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: connection complete"); @@ -647,7 +738,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 421 Service not available, closing control connection. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: FTP: initial connect returned status %d", i); + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); } @@ -695,7 +786,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 530 Not logged in. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: FTP: returned status %d", i); + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); } @@ -728,8 +819,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 503 Bad sequence of commands. */ /* 530 Not logged in. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d [%s]", i, buffer); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -776,8 +867,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 530 Not logged in. */ /* 550 Requested action not taken. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -791,6 +882,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) path = strp + 1; } + apr_table_set(r->notes, "Directory-README", buffer); if (parms != NULL && strncasecmp(parms, "type=a", 6) == 0) { parms = "A"; @@ -817,8 +909,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 504 Command not implemented for that parameter. */ /* 530 Not logged in. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -838,16 +930,23 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) * * Try PASV, if that fails try normally. */ +/* this temporarily switches off PASV */ /*goto bypass;*/ /* try to set up PASV data connection first */ +/* IPV6 FIXME: + * The EPSV command replaces PASV where both IPV4 and IPV6 is supported. Only + * the port is returned, the IP address is always the same as that on the + * control connection. Example: + * Entering Extended Passive Mode (|||6446|) + */ buf = apr_pstrcat(p, "PASV", CRLF, NULL); e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); e = apr_bucket_flush_create(); APR_BRIGADE_INSERT_TAIL(bb, e); ap_pass_brigade(origin->output_filters, bb); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: FTP: PASV"); /* possible results: 227, 421, 500, 501, 502, 530 */ /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ @@ -857,8 +956,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 502 Command not implemented. */ /* 530 Not logged in. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -870,6 +969,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) unsigned int h0, h1, h2, h3, p0, p1; char *pstr; +/* FIXME: Check PASV against RFC1123 */ + pstr = apr_pstrdup(p, buffer); pstr = strtok(pstr, " "); /* separate result code */ if (pstr != NULL) { @@ -883,7 +984,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) } } -/* FIXME: Only supports IPV4 */ +/* FIXME: Only supports IPV4 - fix in RFC2428 */ if (pstr != NULL && (sscanf(pstr, "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { @@ -932,13 +1033,14 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) apr_sockaddr_t *local_addr; char *local_ip; apr_port_t local_port; + unsigned int h0, h1, h2, h3, p0, p1; if ((apr_socket_create(&local_sock, APR_INET, SOCK_STREAM, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: FTP: error creating local socket"); return HTTP_INTERNAL_SERVER_ERROR; } - apr_socket_addr_get(&local_addr, APR_LOCAL, local_sock); + apr_socket_addr_get(&local_addr, APR_LOCAL, sock); apr_sockaddr_port_get(&local_port, local_addr); apr_sockaddr_ip_get(&local_ip, local_addr); @@ -950,7 +1052,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) #endif /*_OSD_POSIX*/ } - if (apr_sockaddr_info_get(&local_addr, local_ip, APR_INET, + if (apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: FTP: error creating local socket address"); @@ -965,6 +1067,49 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* only need a short queue */ apr_listen(local_sock, 2); + +/* FIXME: Sent PORT here */ + + if (local_ip && (sscanf(local_ip, + "%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) { + p1 = (local_port >> 8); + p0 = (local_port & 0xFF); + + buf = apr_psprintf(p, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0); + e = apr_bucket_pool_create(buf, strlen(buf), p); + APR_BRIGADE_INSERT_TAIL(bb, e); + e = apr_bucket_flush_create(); + APR_BRIGADE_INSERT_TAIL(bb, e); + ap_pass_brigade(origin->output_filters, bb); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: PORT %d,%d,%d,%d,%d,%d", h3, h2, h1, h0, p1, p0); + /* possible results: 200, 421, 500, 501, 502, 530 */ + /* 200 Command okay. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + rc = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", rc, buffer); + if (rc == -1) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 200) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, buffer); + } + } + else { +/* IPV6 FIXME: + * The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first + * number (1,2) indicates the protocol type. Examples: + * EPRT |1|132.235.1.2|6275| + * EPRT |2|1080::8:800:200C:417A|5282| + */ + return ap_proxyerror(r, HTTP_NOT_IMPLEMENTED, "Connect to IPV6 ftp server not supported"); + } } @@ -993,7 +1138,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: FTP: SIZE %s", path); i = ftp_getrc_msg(origin, cbb, buffer, sizeof buffer); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: FTP: returned status %d with response %s", i, buffer); if (i != 500) { /* Size command not recognized */ if (i == 550) { /* Not a regular file */ @@ -1017,8 +1162,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 502 Command not implemented. */ /* 530 Not logged in. */ /* 550 Requested action not taken. */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -1060,8 +1205,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 502 Command not implemented. */ /* 550 Requested action not taken. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: PWD returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1 || i == 421) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -1071,7 +1216,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) } if (i == 257) { const char *dirp = buffer; - cwd = ap_getword_conf(r->pool, &dirp); + apr_table_set(r->notes, "Directory-PWD", ap_getword_conf(r->pool, &dirp)); } #endif /*AUTODETECT_PWD*/ @@ -1084,6 +1229,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) "proxy: FTP: LIST %s", (len == 0 ? "-lag" : path)); } else { +/* FIXME: Handle range requests - send REST */ buf = apr_pstrcat(p, "RETR ", path, CRLF, NULL); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: FTP: RETR %s", path); @@ -1110,8 +1256,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 530 Not logged in. */ /* 550 Requested action not taken. */ rc = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", rc); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", rc, buffer); if (rc == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -1137,8 +1283,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 530 Not logged in. */ /* 550 Requested action not taken. */ rc = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", rc); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", rc, buffer); if (rc == -1) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -1167,8 +1313,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 502 Command not implemented. */ /* 550 Requested action not taken. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: PWD returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); if (i == -1 || i == 421) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -1178,7 +1324,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) } if (i == 257) { const char *dirp = buffer; - cwd = ap_getword_conf(r->pool, &dirp); + apr_table_set(r->notes, "Directory-PWD", ap_getword_conf(r->pool, &dirp)); } #endif /*AUTODETECT_PWD*/ @@ -1188,11 +1334,11 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) e = apr_bucket_flush_create(); APR_BRIGADE_INSERT_TAIL(bb, e); ap_pass_brigade(origin->output_filters, bb); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: FTP: LIST -lag"); rc = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", rc); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", rc, buffer); if (rc == -1) return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); @@ -1208,8 +1354,10 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) apr_table_setn(r->headers_out, "Date", dates); apr_table_setn(r->headers_out, "Server", ap_get_server_version()); - if (parms[0] == 'd') - apr_table_setn(r->headers_out, "Content-Type", "text/plain"); + if (parms[0] == 'd') { + r->content_type = "text/html"; + apr_table_setn(r->headers_out, "Content-Type", r->content_type); + } else { if (r->content_type != NULL) { apr_table_setn(r->headers_out, "Content-Type", r->content_type); @@ -1226,9 +1374,11 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) "proxy: FTP: Content-Length set to %s", size); } } +ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: Content-Type set to %s", r->content_type); if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: Content-Encoding set to %s", r->content_encoding); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: Content-Encoding set to %s", r->content_encoding); apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); } @@ -1236,6 +1386,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) if (!pasvmode) { for(;;) { +/* FIXME: this does not return, despite the incoming connection being accepted */ switch(apr_accept(&remote_sock, local_sock, r->pool)) { case APR_EINTR: @@ -1250,6 +1401,9 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) } } + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "proxy: FTP: ready to suck data"); + /* the transfer socket is now open, create a new connection */ remote = ap_new_connection(p, r->server, remote_sock, r->connection->id); if (!remote) { @@ -1263,8 +1417,6 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* set up the connection filters */ ap_proxy_pre_http_connection(remote, NULL); -/* XXX temporary end here while testing */ -/*return HTTP_NOT_IMPLEMENTED;*/ /* * VI: Receive the Response @@ -1277,12 +1429,13 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* send response */ r->sent_bodyct = 1; -#ifdef FTP_FILTER - if (parms[0] == 'd') { + /* read till EOF */ + c->remain = -1; + + if (parms[0] == 'd') { /* insert directory filter */ ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection); - } -#endif + } /* send body */ if (!r->header_only) { @@ -1293,6 +1446,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* read the body, pass it to the output filters */ while (ap_get_brigade(remote->input_filters, bb, AP_MODE_BLOCKING) == APR_SUCCESS) { if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + e = apr_bucket_flush_create(); + APR_BRIGADE_INSERT_TAIL(bb, e); ap_pass_brigade(r->output_filters, bb); break; } @@ -1301,8 +1456,7 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) } apr_brigade_cleanup(bb); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: FTP end body send"); - + "proxy: FTP: end body send"); } else { @@ -1324,8 +1478,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 501 Syntax error in parameters or arguments. */ /* 502 Command not implemented. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: returned status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); } @@ -1350,8 +1504,8 @@ int ap_proxy_ftp_handler(request_rec *r, char *url) /* 221 Service closing control connection. */ /* 500 Syntax error, command unrecognized. */ i = ftp_getrc_msg(origin, cbb, buffer, sizeof(buffer)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, - "proxy: FTP: QUIT: status %d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: FTP: %d %s", i, buffer); apr_brigade_destroy(bb); return OK; diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c index c69acb69d6..27387c9985 100644 --- a/modules/proxy/proxy_http.c +++ b/modules/proxy/proxy_http.c @@ -175,7 +175,7 @@ int ap_proxy_http_handler(request_rec *r, char *url, apr_sockaddr_t *connect_addr; char server_portstr[32]; apr_socket_t *sock; - int i, len, backasswards, close=0, failed=0, new=0; + int i, len, backasswards, eos, close=0, failed=0, new=0; apr_status_t err, rv; apr_array_header_t *headers_in_array; apr_table_entry_t *headers_in; @@ -587,7 +587,7 @@ int ap_proxy_http_handler(request_rec *r, char *url, apr_brigade_cleanup(bb); - if (APR_SUCCESS != (rv = ap_proxy_string_read(origin, bb, buffer, sizeof(buffer)))) { + if (APR_SUCCESS != (rv = ap_proxy_string_read(origin, bb, buffer, sizeof(buffer), &eos))) { apr_socket_close(sock); backend->connection = NULL; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -733,6 +733,8 @@ int ap_proxy_http_handler(request_rec *r, char *url, /* read the body, pass it to the output filters */ while (ap_get_brigade(rp->input_filters, bb, AP_MODE_BLOCKING) == APR_SUCCESS) { if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + e = apr_bucket_flush_create(); + APR_BRIGADE_INSERT_TAIL(bb, e); ap_pass_brigade(r->output_filters, bb); break; } diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 42c45648a3..8a99573764 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1082,7 +1082,8 @@ int ap_proxy_pre_http_connection(conn_rec *c, request_rec *r) } /* converts a series of buckets into a string */ -apr_status_t ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen) +apr_status_t ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, + char *buff, size_t bufflen, int *eos) { apr_bucket *e; apr_status_t rv; @@ -1093,6 +1094,7 @@ apr_status_t ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buf /* start with an empty string */ buff[0] = 0; + *eos = 0; /* get line-at-a-time */ c->remain = 0; @@ -1108,6 +1110,9 @@ apr_status_t ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buf /* loop through each bucket */ while (!found && !APR_BRIGADE_EMPTY(bb)) { e = APR_BRIGADE_FIRST(bb); + if (APR_BUCKET_IS_EOS(e)) { + *eos = 1; + } if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) { return rv; } -- 2.40.0