Apache HTTPD
mod_proxy_ftp.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* FTP routines for Apache proxy */
18
19#define APR_WANT_BYTEFUNC
20#include "mod_proxy.h"
21#if APR_HAVE_TIME_H
22#include <time.h>
23#endif
24#include "apr_version.h"
25
26#define AUTODETECT_PWD
27/* Automatic timestamping (Last-Modified header) based on MDTM is used if:
28 * 1) the FTP server supports the MDTM command and
29 * 2) HAVE_TIMEGM (preferred) or HAVE_GMTOFF is available at compile time
30 */
31#define USE_MDTM
32
33
34module AP_MODULE_DECLARE_DATA proxy_ftp_module;
35
43
45{
48
49 /* Put these in the dir config so they work inside <Location> */
50 new->ftp_list_on_wildcard = 1;
51 new->ftp_escape_wildcards = 1;
52
53 return (void *) new;
54}
55
56static void *merge_proxy_ftp_dir_config(apr_pool_t *p, void *basev, void *addv)
57{
61
62 /* Put these in the dir config so they work inside <Location> */
65 base->ftp_list_on_wildcard;
66 new->ftp_list_on_wildcard_set = add->ftp_list_on_wildcard_set ?
67 1 :
68 base->ftp_list_on_wildcard_set;
69 new->ftp_escape_wildcards = add->ftp_escape_wildcards_set ?
71 base->ftp_escape_wildcards;
72 new->ftp_escape_wildcards_set = add->ftp_escape_wildcards_set ?
73 1 :
74 base->ftp_escape_wildcards_set;
75 new->ftp_directory_charset = add->ftp_directory_charset ?
77 base->ftp_directory_charset;
78 return new;
79}
80
81static const char *set_ftp_list_on_wildcard(cmd_parms *cmd, void *dconf,
82 int flag)
83{
84 proxy_ftp_dir_conf *conf = dconf;
85
88 return NULL;
89}
90
91static const char *set_ftp_escape_wildcards(cmd_parms *cmd, void *dconf,
92 int flag)
93{
94 proxy_ftp_dir_conf *conf = dconf;
95
98 return NULL;
99}
100
101static const char *set_ftp_directory_charset(cmd_parms *cmd, void *dconf,
102 const char *arg)
103{
104 proxy_ftp_dir_conf *conf = dconf;
105
107 return NULL;
108}
109
110/*
111 * Decodes a '%' escaped string, and returns the number of characters
112 */
113static int decodeenc(char *x)
114{
115 int i, j, ch;
116
117 if (x[0] == '\0')
118 return 0; /* special case for no characters */
119 for (i = 0, j = 0; x[i] != '\0'; i++, j++) {
120 /* decode it if not already done */
121 ch = x[i];
122 if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) {
123 ch = ap_proxy_hex2c(&x[i + 1]);
124 i += 2;
125 }
126 x[j] = ch;
127 }
128 x[j] = '\0';
129 return j;
130}
131
132/*
133 * Escape the globbing characters in a path used as argument to
134 * the FTP commands (SIZE, CWD, RETR, MDTM, ...).
135 * ftpd assumes '\\' as a quoting character to escape special characters.
136 * Just returns the original string if ProxyFtpEscapeWildcards has been
137 * configured "off".
138 * Returns: escaped string
139 */
140#define FTP_GLOBBING_CHARS "*?[{~"
141static const char *ftp_escape_globbingchars(apr_pool_t *p, const char *path, proxy_ftp_dir_conf *dconf)
142{
143 char *ret;
144 char *d;
145
146 if (!dconf->ftp_escape_wildcards) {
147 return path;
148 }
149
150 ret = apr_palloc(p, 2*strlen(path)+sizeof(""));
151 for (d = ret; *path; ++path) {
153 *d++ = '\\';
154 *d++ = *path;
155 }
156 *d = '\0';
157 return ret;
158}
159
160/*
161 * Check for globbing characters in a path used as argument to
162 * the FTP commands (SIZE, CWD, RETR, MDTM, ...).
163 * ftpd assumes '\\' as a quoting character to escape special characters.
164 * Returns: 0 (no globbing chars, or all globbing chars escaped), 1 (globbing chars)
165 */
166static int ftp_check_globbingchars(const char *path)
167{
168 for ( ; *path; ++path) {
169 if (*path == '\\')
170 ++path;
171 if (*path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL)
172 return TRUE;
173 }
174 return FALSE;
175}
176
177/*
178 * checks an encoded ftp string for bad characters, namely, CR, LF or
179 * non-ascii character
180 */
181static int ftp_check_string(const char *x)
182{
183 int i, ch = 0;
184#if APR_CHARSET_EBCDIC
185 char buf[1];
186#endif
187
188 for (i = 0; x[i] != '\0'; i++) {
189 ch = x[i];
190 if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) {
191 ch = ap_proxy_hex2c(&x[i + 1]);
192 i += 2;
193 }
194#if !APR_CHARSET_EBCDIC
195 if (ch == '\015' || ch == '\012' || (ch & 0x80))
196#else /* APR_CHARSET_EBCDIC */
197 if (ch == '\r' || ch == '\n')
198 return 0;
199 buf[0] = ch;
201 if (buf[0] & 0x80)
202#endif /* APR_CHARSET_EBCDIC */
203 return 0;
204 }
205 return 1;
206}
207
208/*
209 * converts a series of buckets into a string
210 * XXX: BillS says this function performs essentially the same function as
211 * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline()
212 * instead? I think ftp_string_read() will not work properly on non ASCII
213 * (EBCDIC) machines either.
214 */
217{
218 apr_bucket *e;
219 apr_status_t rv;
220 char *pos = buff;
221 char *response;
222 int found = 0;
224
225 /* start with an empty string */
226 buff[0] = 0;
227 *eos = 0;
228 *outlen = 0;
229
230 /* loop through each brigade */
231 while (!found) {
232 /* get brigade from network one line at a time */
233 if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb,
236 0))) {
237 return rv;
238 }
239 /* loop through each bucket */
240 while (!found) {
241 if (*eos || APR_BRIGADE_EMPTY(bb)) {
242 /* The connection aborted or timed out */
243 return APR_ECONNABORTED;
244 }
245 e = APR_BRIGADE_FIRST(bb);
246 if (APR_BUCKET_IS_EOS(e)) {
247 *eos = 1;
248 }
249 else {
250 if (APR_SUCCESS != (rv = apr_bucket_read(e,
251 (const char **)&response,
252 &len,
253 APR_BLOCK_READ))) {
254 return rv;
255 }
256 /*
257 * is string LF terminated?
258 * XXX: This check can be made more efficient by simply checking
259 * if the last character in the 'response' buffer is an ASCII_LF.
260 * See ap_rgetline() for an example.
261 */
262 if (memchr(response, APR_ASCII_LF, len)) {
263 found = 1;
264 }
265 /* concat strings until buff is full - then throw the data away */
266 if (len > ((bufflen-1)-(pos-buff))) {
267 len = (bufflen-1)-(pos-buff);
268 }
269 if (len > 0) {
270 memcpy(pos, response, len);
271 pos += len;
272 *outlen += len;
273 }
274 }
276 }
277 *pos = '\0';
278 }
279
280 return APR_SUCCESS;
281}
282
283/*
284 * Canonicalise ftp URLs.
285 */
286static int proxy_ftp_canon(request_rec *r, char *url)
287{
288 char *user, *password, *host, *path, *parms, *strp, sport[7];
289 apr_pool_t *p = r->pool;
290 const char *err;
293 int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0;
294
295 /* */
296 if (ap_cstr_casecmpn(url, "ftp:", 4) == 0) {
297 url += 4;
298 }
299 else {
300 return DECLINED;
301 }
303
304 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
305
306 port = def_port;
307 err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port);
308 if (err)
309 return HTTP_BAD_REQUEST;
310 if (user != NULL && !ftp_check_string(user))
311 return HTTP_BAD_REQUEST;
313 return HTTP_BAD_REQUEST;
314
315 /* now parse path/parameters args, according to rfc1738 */
316 /*
317 * N.B. if this isn't a true proxy request, then the URL path (but not
318 * query args) has already been decoded. This gives rise to the problem
319 * of a ; being decoded into the path.
320 */
321 strp = strchr(url, ';');
322 if (strp != NULL) {
323 *(strp++) = '\0';
324 parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, 0,
325 r->proxyreq);
326 if (parms == NULL)
327 return HTTP_BAD_REQUEST;
328 }
329 else
330 parms = "";
331
333 r->proxyreq);
334 if (path == NULL)
335 return HTTP_BAD_REQUEST;
337 return HTTP_BAD_REQUEST;
338
339 if (r->proxyreq && r->args != NULL) {
340 if (strp != NULL) {
341 strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1, r->proxyreq);
342 if (strp == NULL)
343 return HTTP_BAD_REQUEST;
344 parms = apr_pstrcat(p, parms, "?", strp, NULL);
345 }
346 else {
347 strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1, r->proxyreq);
348 if (strp == NULL)
349 return HTTP_BAD_REQUEST;
350 path = apr_pstrcat(p, path, "?", strp, NULL);
351 }
352 r->args = NULL;
353 }
354
355/* now, rebuild URL */
356
357 if (port != def_port)
358 apr_snprintf(sport, sizeof(sport), ":%d", port);
359 else
360 sport[0] = '\0';
361
362 if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
363 host = apr_pstrcat(p, "[", host, "]", NULL);
364 }
365 r->filename = apr_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "",
366 (password != NULL) ? ":" : "",
367 (password != NULL) ? password : "",
368 (user != NULL) ? "@" : "", host, sport, "/", path,
369 (parms[0] != '\0') ? ";" : "", parms, NULL);
370
371 return OK;
372}
373
374/* we chop lines longer than 80 characters */
375#define MAX_LINE_LEN 80
376
377/*
378 * Reads response lines, returns both the ftp status code and
379 * remembers the response message in the supplied buffer
380 */
382{
383 int status;
384 char response[MAX_LINE_LEN];
385 char buff[5];
386 char *mb = msgbuf, *me = &msgbuf[msglen];
387 apr_status_t rv;
389
390 int eos;
391
392 if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) {
393 return -1;
394 }
395/*
396 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(03233)
397 "<%s", response);
398*/
399 if (nread < 4) {
400 ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(10229) "Malformed FTP response '%s'", response);
401 *mb = '\0';
402 return -1;
403 }
404
405 if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) ||
406 !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-'))
407 status = 0;
408 else
409 status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0';
410
411 mb = apr_cpystrn(mb, response + 4, me - mb);
412
413 if (response[3] == '-') { /* multi-line reply "123-foo\nbar\n123 baz" */
414 memcpy(buff, response, 3);
415 buff[3] = ' ';
416 do {
417 if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) {
418 return -1;
419 }
420 mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb);
421 } while (memcmp(response, buff, 4) != 0);
422 }
423
424 return status;
425}
426
427/* this is a filter that turns a raw ASCII directory listing into pretty HTML */
428
429/* ideally, mod_proxy should simply send the raw directory list up the filter
430 * stack to mod_autoindex, which in theory should turn the raw ascii into
431 * pretty html along with all the bells and whistles it provides...
432 *
433 * all in good time...! :)
434 */
435
436typedef struct {
439 enum {
440 HEADER, BODY, FOOTER
441 } state;
443
444/* fallback regex for ls -s1; ($0..$2) == 3 */
445#define LS_REG_PATTERN "^ *([0-9]+) +([^ ]+)$"
446#define LS_REG_MATCH 3
448
451{
452 request_rec *r = f->r;
454 apr_pool_t *p = r->pool;
455 apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc);
456 apr_status_t rv;
457
458 int n;
459 char *dir, *path, *reldir, *site, *str, *type;
460
461 const char *pwd = apr_table_get(r->notes, "Directory-PWD");
462 const char *readme = apr_table_get(r->notes, "Directory-README");
463
464 proxy_dir_ctx_t *ctx = f->ctx;
465
466 if (!ctx) {
467 f->ctx = ctx = apr_pcalloc(p, sizeof(*ctx));
468 ctx->in = apr_brigade_create(p, c->bucket_alloc);
469 ctx->buffer[0] = 0;
470 ctx->state = HEADER;
471 }
472
473 /* combine the stored and the new */
475
476 if (HEADER == ctx->state) {
477
478 /* basedir is either "", or "/%2f" for the "squid %2f hack" */
479 const char *basedir = ""; /* By default, path is relative to the $HOME dir */
480 char *wildcard = NULL;
481 const char *escpath;
482
483 /*
484 * In the reverse proxy case we need to construct our site string
485 * via ap_construct_url. For non anonymous sites apr_uri_unparse would
486 * only supply us with 'username@' which leads to the construction of
487 * an invalid base href later on. Losing the username part of the URL
488 * is no problem in the reverse proxy case as the browser sents the
489 * credentials anyway once entered.
490 */
491 if (r->proxyreq == PROXYREQ_REVERSE) {
492 site = ap_construct_url(p, "", r);
493 }
494 else {
495 /* Save "scheme://site" prefix without password */
496 site = apr_uri_unparse(p, &f->r->parsed_uri,
499 }
500
501 /* ... and path without query args */
503
504 /* If path began with /%2f, change the basedir */
505 if (ap_cstr_casecmpn(path, "/%2f", 4) == 0) {
506 basedir = "/%2f";
507 }
508
509 /* Strip off a type qualifier. It is ignored for dir listings */
510 if ((type = strstr(path, ";type=")) != NULL)
511 *type++ = '\0';
512
513 (void)decodeenc(path);
514
515 while (path[1] == '/') /* collapse multiple leading slashes to one */
516 ++path;
517
518 reldir = strrchr(path, '/');
520 wildcard = &reldir[1];
521 reldir[0] = '\0'; /* strip off the wildcard suffix */
522 }
523
524 /* Copy path, strip (all except the last) trailing slashes */
525 /* (the trailing slash is needed for the dir component loop below) */
526 path = dir = apr_pstrcat(p, path, "/", NULL);
527 for (n = strlen(path); n > 1 && path[n - 1] == '/' && path[n - 2] == '/'; --n)
528 path[n - 1] = '\0';
529
530 /* Add a link to the root directory (if %2f hack was used) */
531 str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : "";
532
533 /* print "ftp://host/" */
536 "<html>\n <head>\n <title>%s%s%s</title>\n"
537 "<base href=\"%s%s%s\">\n"
538 " </head>\n"
539 " <body>\n <h2>Directory of "
540 "<a href=\"/\">%s</a>/%s",
543 ap_escape_uri(p, site), str);
544
546 p, c->bucket_alloc));
547
548 for (dir = path+1; (dir = strchr(dir, '/')) != NULL; )
549 {
550 *dir = '\0';
551 if ((reldir = strrchr(path+1, '/'))==NULL) {
552 reldir = path+1;
553 }
554 else
555 ++reldir;
556 /* print "path/" component */
557 str = apr_psprintf(p, "<a href=\"%s%s/\">%s</a>/", basedir,
560 *dir = '/';
561 while (*dir == '/')
562 ++dir;
564 strlen(str), p,
565 c->bucket_alloc));
566 }
567 if (wildcard != NULL) {
570 strlen(wildcard), p,
571 c->bucket_alloc));
572 }
573
574 /* If the caller has determined the current directory, and it differs */
575 /* from what the client requested, then show the real name */
576 if (pwd == NULL || strncmp(pwd, path, strlen(pwd)) == 0) {
577 str = apr_psprintf(p, "</h2>\n\n <hr />\n\n<pre>");
578 }
579 else {
580 str = apr_psprintf(p, "</h2>\n\n(%s)\n\n <hr />\n\n<pre>",
582 }
584 p, c->bucket_alloc));
585
586 /* print README */
587 if (readme) {
588 str = apr_psprintf(p, "%s\n</pre>\n\n<hr />\n\n<pre>\n",
589 ap_escape_html(p, readme));
590
592 strlen(str), p,
593 c->bucket_alloc));
594 }
595
596 /* make sure page intro gets sent out */
598 if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
599 return rv;
600 }
602
603 ctx->state = BODY;
604 }
605
606 /* loop through each line of directory */
607 while (BODY == ctx->state) {
608 char *filename;
609 int found = 0;
610 int eos = 0;
612
613 /* get a complete line */
614 /* if the buffer overruns - throw data away */
615 while (!found && !APR_BRIGADE_EMPTY(ctx->in)) {
616 char *pos, *response;
618 apr_bucket *e;
619
620 e = APR_BRIGADE_FIRST(ctx->in);
621 if (APR_BUCKET_IS_EOS(e)) {
622 eos = 1;
623 break;
624 }
625 if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) {
626 return rv;
627 }
628 pos = memchr(response, APR_ASCII_LF, len);
629 if (pos != NULL) {
630 if ((response + len) != (pos + 1)) {
631 len = pos - response + 1;
632 apr_bucket_split(e, pos - response + 1);
633 }
634 found = 1;
635 }
636 max = sizeof(ctx->buffer) - strlen(ctx->buffer) - 1;
637 if (len > max) {
638 len = max;
639 }
640
641 /* len+1 to leave space for the trailing nil char */
642 apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len+1);
643
645 }
646
647 /* EOS? jump to footer */
648 if (eos) {
649 ctx->state = FOOTER;
650 break;
651 }
652
653 /* not complete? leave and try get some more */
654 if (!found) {
655 return APR_SUCCESS;
656 }
657
658 {
659 apr_size_t n = strlen(ctx->buffer);
660 if (ctx->buffer[n-1] == CRLF[1]) /* strip trailing '\n' */
661 ctx->buffer[--n] = '\0';
662 if (ctx->buffer[n-1] == CRLF[0]) /* strip trailing '\r' if present */
663 ctx->buffer[--n] = '\0';
664 }
665
666 /* a symlink? */
667 if (ctx->buffer[0] == 'l' && (filename = strstr(ctx->buffer, " -> ")) != NULL) {
668 char *link_ptr = filename;
669
670 do {
671 filename--;
672 } while (filename[0] != ' ' && filename > ctx->buffer);
673 if (filename > ctx->buffer)
674 *(filename++) = '\0';
675 *(link_ptr++) = '\0';
676 str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n",
677 ap_escape_html(p, ctx->buffer),
681 }
682
683 /* a directory/file? */
684 else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) {
685 int searchidx = 0;
686 char *searchptr = NULL;
687 int firstfile = 1;
688 if (apr_isdigit(ctx->buffer[0])) { /* handle DOS dir */
689 searchptr = strchr(ctx->buffer, '<');
690 if (searchptr != NULL)
691 *searchptr = '[';
692 searchptr = strchr(ctx->buffer, '>');
693 if (searchptr != NULL)
694 *searchptr = ']';
695 }
696
697 filename = strrchr(ctx->buffer, ' ');
698 if (filename == NULL) {
699 /* Line is broken. Ignore it. */
701 "proxy_ftp: could not parse line %s",
702 ctx->buffer);
703 /* erase buffer for next time around */
704 ctx->buffer[0] = 0;
705 continue; /* while state is BODY */
706 }
707 *(filename++) = '\0';
708
709 /* handle filenames with spaces in 'em */
710 if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
711 firstfile = 0;
712 searchidx = filename - ctx->buffer;
713 }
714 else if (searchidx != 0 && ctx->buffer[searchidx] != 0) {
715 *(--filename) = ' ';
716 ctx->buffer[searchidx - 1] = '\0';
717 filename = &ctx->buffer[searchidx];
718 }
719
720 /* Append a slash to the HREF link for directories */
721 if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') {
722 str = apr_psprintf(p, "%s <a href=\"%s/\">%s</a>\n",
723 ap_escape_html(p, ctx->buffer),
726 }
727 else {
728 str = apr_psprintf(p, "%s <a href=\"%s\">%s</a>\n",
729 ap_escape_html(p, ctx->buffer),
732 }
733 }
734 /* Try a fallback for listings in the format of "ls -s1" */
735 else if (0 == ap_regexec(ls_regex, ctx->buffer, LS_REG_MATCH, re_result, 0)) {
736 /*
737 * We don't need to check for rm_eo == rm_so == -1 here since ls_regex
738 * is such that $2 cannot be unset if we have a match.
739 */
740 filename = apr_pstrndup(p, &ctx->buffer[re_result[2].rm_so], re_result[2].rm_eo - re_result[2].rm_so);
741
742 str = apr_pstrcat(p, ap_escape_html(p, apr_pstrndup(p, ctx->buffer, re_result[2].rm_so)),
743 "<a href=\"", ap_escape_uri(p, filename), "\">",
744 ap_escape_html(p, filename), "</a>\n", NULL);
745 }
746 else {
747 strcat(ctx->buffer, "\n"); /* re-append the newline */
748 str = ap_escape_html(p, ctx->buffer);
749 }
750
751 /* erase buffer for next time around */
752 ctx->buffer[0] = 0;
753
755 c->bucket_alloc));
757 if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
758 return rv;
759 }
761
762 }
763
764 if (FOOTER == ctx->state) {
765 str = apr_psprintf(p, "</pre>\n\n <hr />\n\n %s\n\n </body>\n</html>\n", ap_psignature("", r));
767 c->bucket_alloc));
770 if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
771 return rv;
772 }
774 }
775
776 return APR_SUCCESS;
777}
778
779/* Parse EPSV reply and return port, or zero on error. */
781{
782 const char *p;
783 char *ep;
784 long port;
785
786 /* Reply syntax per RFC 2428: "229 blah blah (|||port|)" where '|'
787 * can be any character in ASCII from 33-126, obscurely. Verify
788 * the syntax. */
789 p = ap_strchr_c(reply, '(');
790 if (p == NULL || !p[1] || p[1] != p[2] || p[1] != p[3]
791 || p[4] == p[1]) {
792 return 0;
793 }
794
795 errno = 0;
796 port = strtol(p + 4, &ep, 10);
797 if (errno || port < 1 || port > 65535 || ep[0] != p[1] || ep[1] != ')') {
798 return 0;
799 }
800
801 return (apr_port_t)port;
802}
803
804/*
805 * Generic "send FTP command to server" routine, using the control socket.
806 * Returns the FTP returncode (3 digit code)
807 * Allows for tracing the FTP protocol (in LogLevel debug)
808 */
809static int
811 apr_bucket_brigade *bb, char **pmessage)
812{
813 char *crlf;
814 int rc;
816
817 /* If cmd == NULL, we retrieve the next ftp response line */
818 if (cmd != NULL) {
820 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(cmd, strlen(cmd), r->pool, c->bucket_alloc));
822 ap_pass_brigade(ftp_ctrl->output_filters, bb);
823
824 if (APLOGrtrace2(r)) {
825 /* strip off the CRLF for logging */
826 apr_cpystrn(message, cmd, sizeof(message));
827 if ((crlf = strchr(message, '\r')) != NULL ||
828 (crlf = strchr(message, '\n')) != NULL)
829 *crlf = '\0';
830 if (strncmp(message,"PASS ", 5) == 0)
831 strcpy(&message[5], "****");
833 }
834 }
835
836 rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof(message));
837 if (rc == -1 || rc == 421)
838 strcpy(message,"<unable to read result>");
839 if ((crlf = strchr(message, '\r')) != NULL ||
840 (crlf = strchr(message, '\n')) != NULL)
841 *crlf = '\0';
842 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "<%3.3u %s", rc, message);
843
844 if (pmessage != NULL)
846
847 return rc;
848}
849
850/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */
852 apr_bucket_brigade *bb, char **pmessage)
853{
854 char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */
855 int ret = HTTP_OK;
856 int rc;
857
858 /* set desired type */
859 old_type[0] = xfer_type;
860
862 r, ftp_ctrl, bb, pmessage);
863/* responses: 200, 421, 500, 501, 504, 530 */
864 /* 200 Command okay. */
865 /* 421 Service not available, closing control connection. */
866 /* 500 Syntax error, command unrecognized. */
867 /* 501 Syntax error in parameters or arguments. */
868 /* 504 Command not implemented for that parameter. */
869 /* 530 Not logged in. */
870 if (rc == -1 || rc == 421) {
872 "Error reading from remote server");
873 }
874 else if (rc != 200 && rc != 504) {
876 "Unable to set transfer type");
877 }
878/* Allow not implemented */
879 else if (rc == 504) {
880 /* ignore it silently */
881 }
882
883 return ret;
884}
885
886
887/* Return the current directory which we have selected on the FTP server, or NULL */
889{
890 char *cwd = NULL;
891 char *ftpmessage = NULL;
892
893 /* responses: 257, 500, 501, 502, 421, 550 */
894 /* 257 "<directory-name>" <commentary> */
895 /* 421 Service not available, closing control connection. */
896 /* 500 Syntax error, command unrecognized. */
897 /* 501 Syntax error in parameters or arguments. */
898 /* 502 Command not implemented. */
899 /* 550 Requested action not taken. */
900 switch (proxy_ftp_command("PWD" CRLF, r, ftp_ctrl, bb, &ftpmessage)) {
901 case -1:
902 case 421:
903 case 550:
905 "Failed to read PWD on ftp server");
906 break;
907
908 case 257: {
909 const char *dirp = ftpmessage;
911 }
912 }
913 return cwd;
914}
915
916
917/* Common routine for failed authorization (i.e., missing or wrong password)
918 * to an ftp service. This causes most browsers to retry the request
919 * with username and password (which was presumably queried from the user)
920 * supplied in the Authorization: header.
921 * Note that we "invent" a realm name which consists of the
922 * ftp://user@host part of the request (sans password -if supplied but invalid-)
923 */
925{
927 /*
928 * Log failed requests if they supplied a password (log username/password
929 * guessing attempts)
930 */
931 if (log_it)
933 "missing or failed auth to %s",
936
937 apr_table_setn(r->err_headers_out, "WWW-Authenticate",
938 apr_pstrcat(r->pool, "Basic realm=\"",
941 "\"", NULL));
942
943 return HTTP_UNAUTHORIZED;
944}
945
946static
948{
949
950 backend->close = 1;
951 ap_set_module_config(r->connection->conn_config, &proxy_ftp_module, NULL);
952 ap_proxy_release_connection("FTP", backend, r->server);
953
954 return OK;
955}
956
957static
959{
960 proxy_ftp_cleanup(r, conn);
962}
963/*
964 * Handles direct access of ftp:// URLs
965 * Original (Non-PASV) version from
966 * Troy Morrison <[email protected]>
967 * PASV added by Chuck
968 * Filters by [Graham Leggett <[email protected]>]
969 */
971 proxy_server_conf *conf, char *url,
972 const char *proxyhost, apr_port_t proxyport)
973{
974 apr_pool_t *p = r->pool;
976 proxy_conn_rec *backend;
978 conn_rec *origin, *data = NULL;
981 char *buf, *connectname;
983 char *ftpmessage = NULL;
984 char *path, *strp, *type_suffix, *cwd = NULL;
986 char *user = NULL;
987/* char *account = NULL; how to supply an account in a URL? */
988 const char *password = NULL;
989 int len, rc;
990 int one = 1;
991 char *size = NULL;
992 char xfer_type = 'A'; /* after ftp login, the default is ASCII */
993 int dirlisting = 0;
994#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF))
995 apr_time_t mtime = 0L;
996#endif
998 &proxy_ftp_module);
999
1000 /* stuff for PASV mode */
1001 int connect = 0, use_port = 0;
1003 apr_status_t rv;
1004 int status;
1005
1006 /* is this for us? */
1007 if (proxyhost) {
1009 "declining URL %s - proxyhost %s specified:", url,
1010 proxyhost);
1011 return DECLINED; /* proxy connections are via HTTP */
1012 }
1013 if (ap_cstr_casecmpn(url, "ftp:", 4)) {
1015 "declining URL %s - not ftp:", url);
1016 return DECLINED; /* only interested in FTP */
1017 }
1018 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "serving URL %s", url);
1019
1020
1021 /*
1022 * I: Who Do I Connect To? -----------------------
1023 *
1024 * Break up the URL to determine the host to connect to
1025 */
1026
1027 /* we only support GET and HEAD */
1028 if (r->method_number != M_GET)
1029 return HTTP_NOT_IMPLEMENTED;
1030
1031 /* We break the URL into host, port, path-search */
1032 if (r->parsed_uri.hostname == NULL) {
1033 if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) {
1035 "URI cannot be parsed: %s", url);
1036 return ap_proxyerror(r, HTTP_BAD_REQUEST, "URI cannot be parsed");
1037 }
1038 connectname = uri.hostname;
1039 connectport = uri.port;
1040 path = apr_pstrdup(p, uri.path);
1041 }
1042 else {
1046 }
1047 if (connectport == 0) {
1049 }
1050 path = (path != NULL && path[0] != '\0') ? &path[1] : "";
1051
1052 type_suffix = strchr(path, ';');
1053 if (type_suffix != NULL)
1054 *(type_suffix++) = '\0';
1055
1056 if (type_suffix != NULL && strncmp(type_suffix, "type=", 5) == 0
1057 && apr_isalpha(type_suffix[5])) {
1058 /* "type=d" forces a dir listing.
1059 * The other types (i|a|e) are directly used for the ftp TYPE command
1060 */
1061 if ( ! (dirlisting = (apr_tolower(type_suffix[5]) == 'd')))
1063
1064 /* Check valid types, rather than ignoring invalid types silently: */
1065 if (strchr("AEI", xfer_type) == NULL)
1067 "ftp proxy supports only types 'a', 'i', or 'e': \"",
1068 type_suffix, "\" is invalid.", NULL));
1069 }
1070 else {
1071 /* make binary transfers the default */
1072 xfer_type = 'I';
1073 }
1074
1075
1076 /*
1077 * The "Authorization:" header must be checked first. We allow the user
1078 * to "override" the URL-coded user [ & password ] in the Browsers'
1079 * User&Password Dialog. NOTE that this is only marginally more secure
1080 * than having the password travel in plain as part of the URL, because
1081 * Basic Auth simply uuencodes the plain text password. But chances are
1082 * still smaller that the URL is logged regularly.
1083 */
1084 if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL
1085 && ap_cstr_casecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0
1086 && (password = ap_pbase64decode(r->pool, password))[0] != ':') {
1087 /* Check the decoded string for special characters. */
1088 if (!ftp_check_string(password)) {
1090 "user credentials contained invalid character");
1091 }
1092 /*
1093 * Note that this allocation has to be made from r->connection->pool
1094 * because it has the lifetime of the connection. The other
1095 * allocations are temporary and can be tossed away any time.
1096 */
1097 user = ap_getword_nulls(r->connection->pool, &password, ':');
1098 r->ap_auth_type = "Basic";
1099 r->user = r->parsed_uri.user = user;
1100 }
1101 else if ((user = r->parsed_uri.user) != NULL) {
1102 user = apr_pstrdup(p, user);
1103 decodeenc(user);
1104 if ((password = r->parsed_uri.password) != NULL) {
1105 char *tmp = apr_pstrdup(p, password);
1106 decodeenc(tmp);
1107 password = tmp;
1108 }
1109 }
1110 else {
1111 user = "anonymous";
1112 password = "apache-proxy@";
1113 }
1114
1116 "connecting %s to %s:%d", url, connectname, connectport);
1117
1118 /* create space for state information */
1119 backend = ap_get_module_config(c->conn_config, &proxy_ftp_module);
1120 if (!backend) {
1121 status = ap_proxy_acquire_connection("FTP", &backend, worker, r->server);
1122 if (status != OK) {
1123 if (backend) {
1124 backend->close = 1;
1125 ap_proxy_release_connection("FTP", backend, r->server);
1126 }
1127 return status;
1128 }
1129 ap_set_module_config(c->conn_config, &proxy_ftp_module, backend);
1130 }
1131
1132 /*
1133 * get all the possible IP addresses for the destname and loop through
1134 * them until we get a successful connection
1135 */
1137 0, r, r->server);
1138 if (APR_SUCCESS != err) {
1139 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1140 "Error resolving backend address");
1141 }
1142
1143 /* check if ProxyBlock directive on this host */
1144 if (OK != ap_proxy_checkproxyblock2(r, conf, connectname, backend->addr)) {
1145 return ftp_proxyerror(r, backend, HTTP_FORBIDDEN,
1146 "Connect to remote machine blocked");
1147 }
1148
1149
1150 /*
1151 * II: Make the Connection -----------------------
1152 *
1153 * We have determined who to connect to. Now make the connection.
1154 */
1155
1156 if (ap_proxy_connect_backend("FTP", backend, worker, r->server)) {
1157 proxy_ftp_cleanup(r, backend);
1159 }
1160
1161 status = ap_proxy_connection_create_ex("FTP", backend, r);
1162 if (status != OK) {
1163 proxy_ftp_cleanup(r, backend);
1164 return status;
1165 }
1166
1167 /* Use old naming */
1168 origin = backend->connection;
1169 sock = backend->sock;
1170
1172 "control connection complete");
1173
1174
1175 /*
1176 * III: Send Control Request -------------------------
1177 *
1178 * Log into the ftp server, send the username & password, change to the
1179 * correct directory...
1180 */
1181
1182 bb = apr_brigade_create(p, c->bucket_alloc);
1183
1184 /* possible results: */
1185 /* 120 Service ready in nnn minutes. */
1186 /* 220 Service ready for new user. */
1187 /* 421 Service not available, closing control connection. */
1188 rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage);
1189 if (rc == -1 || rc == 421) {
1190 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server");
1191 }
1192 if (rc == 120) {
1193 /*
1194 * RFC2616 states: 14.37 Retry-After
1195 *
1196 * The Retry-After response-header field can be used with a 503 (Service
1197 * Unavailable) response to indicate how long the service is expected
1198 * to be unavailable to the requesting client. [...] The value of
1199 * this field can be either an HTTP-date or an integer number of
1200 * seconds (in decimal) after the time of the response. Retry-After
1201 * = "Retry-After" ":" ( HTTP-date | delta-seconds )
1202 */
1203 char *secs_str = ftpmessage;
1204 time_t secs;
1205
1206 /* Look for a number, preceded by whitespace */
1207 while (*secs_str)
1208 if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) &&
1210 break;
1211 if (*secs_str != '\0') {
1212 secs = atol(secs_str);
1213 apr_table_addn(r->headers_out, "Retry-After",
1214 apr_psprintf(p, "%lu", (unsigned long)(60 * secs)));
1215 }
1217 }
1218 if (rc != 220) {
1219 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1220 }
1221
1222 rc = proxy_ftp_command(apr_pstrcat(p, "USER ", user, CRLF, NULL),
1223 r, origin, bb, &ftpmessage);
1224 /* possible results; 230, 331, 332, 421, 500, 501, 530 */
1225 /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
1226 /* 230 User logged in, proceed. */
1227 /* 331 User name okay, need password. */
1228 /* 332 Need account for login. */
1229 /* 421 Service not available, closing control connection. */
1230 /* 500 Syntax error, command unrecognized. */
1231 /* (This may include errors such as command line too long.) */
1232 /* 501 Syntax error in parameters or arguments. */
1233 /* 530 Not logged in. */
1234 if (rc == -1 || rc == 421) {
1235 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server");
1236 }
1237 if (rc == 530) {
1238 proxy_ftp_cleanup(r, backend);
1239 return ftp_unauthorized(r, 1); /* log it: user name guessing
1240 * attempt? */
1241 }
1242 if (rc != 230 && rc != 331) {
1243 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1244 }
1245
1246 if (rc == 331) { /* send password */
1247 if (password == NULL) {
1248 proxy_ftp_cleanup(r, backend);
1249 return ftp_unauthorized(r, 0);
1250 }
1251
1253 r, origin, bb, &ftpmessage);
1254 /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
1255 /* 230 User logged in, proceed. */
1256 /* 332 Need account for login. */
1257 /* 421 Service not available, closing control connection. */
1258 /* 500 Syntax error, command unrecognized. */
1259 /* 501 Syntax error in parameters or arguments. */
1260 /* 503 Bad sequence of commands. */
1261 /* 530 Not logged in. */
1262 if (rc == -1 || rc == 421) {
1263 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1264 "Error reading from remote server");
1265 }
1266 if (rc == 332) {
1267 return ftp_proxyerror(r, backend, HTTP_UNAUTHORIZED,
1268 apr_pstrcat(p, "Need account for login: ", ftpmessage, NULL));
1269 }
1270 /* @@@ questionable -- we might as well return a 403 Forbidden here */
1271 if (rc == 530) {
1272 proxy_ftp_cleanup(r, backend);
1273 return ftp_unauthorized(r, 1); /* log it: passwd guessing
1274 * attempt? */
1275 }
1276 if (rc != 230 && rc != 202) {
1277 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1278 }
1279 }
1280 apr_table_set(r->notes, "Directory-README", ftpmessage);
1281
1282
1283 /* Special handling for leading "%2f": this enforces a "cwd /"
1284 * out of the $HOME directory which was the starting point after login
1285 */
1286 if (ap_cstr_casecmpn(path, "%2f", 3) == 0) {
1287 path += 3;
1288 while (*path == '/') /* skip leading '/' (after root %2f) */
1289 ++path;
1290
1291 rc = proxy_ftp_command("CWD /" CRLF, r, origin, bb, &ftpmessage);
1292 if (rc == -1 || rc == 421)
1293 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1294 "Error reading from remote server");
1295 }
1296
1297 /*
1298 * set the directory (walk directory component by component): this is
1299 * what we must do if we don't know the OS type of the remote machine
1300 */
1301 for (;;) {
1302 strp = strchr(path, '/');
1303 if (strp == NULL)
1304 break;
1305 *strp = '\0';
1306
1307 decodeenc(path); /* Note! This decodes a %2f -> "/" */
1308
1309 if (strchr(path, '/')) { /* are there now any '/' characters? */
1310 return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST,
1311 "Use of /%2f is only allowed at the base directory");
1312 }
1313
1314 /* NOTE: FTP servers do globbing on the path.
1315 * So we need to escape the URI metacharacters.
1316 * We use a special glob-escaping routine to escape globbing chars.
1317 * We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH
1318 */
1321 r, origin, bb, &ftpmessage);
1322 *strp = '/';
1323 /* responses: 250, 421, 500, 501, 502, 530, 550 */
1324 /* 250 Requested file action okay, completed. */
1325 /* 421 Service not available, closing control connection. */
1326 /* 500 Syntax error, command unrecognized. */
1327 /* 501 Syntax error in parameters or arguments. */
1328 /* 502 Command not implemented. */
1329 /* 530 Not logged in. */
1330 /* 550 Requested action not taken. */
1331 if (rc == -1 || rc == 421) {
1332 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1333 "Error reading from remote server");
1334 }
1335 if (rc == 550) {
1336 return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage);
1337 }
1338 if (rc != 250) {
1339 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1340 }
1341
1342 path = strp + 1;
1343 }
1344
1345 /*
1346 * IV: Make Data Connection? -------------------------
1347 *
1348 * Try EPSV, if that fails... try PASV, if that fails... try PORT.
1349 */
1350/* this temporarily switches off EPSV/PASV */
1351/*goto bypass;*/
1352
1353 /* set up data connection - EPSV */
1354 {
1356
1357 /*
1358 * The EPSV command replaces PASV where both IPV4 and IPV6 is
1359 * supported. Only the port is returned, the IP address is always the
1360 * same as that on the control connection. Example: Entering Extended
1361 * Passive Mode (|||6446|)
1362 */
1363 rc = proxy_ftp_command("EPSV" CRLF,
1364 r, origin, bb, &ftpmessage);
1365 /* possible results: 227, 421, 500, 501, 502, 530 */
1366 /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
1367 /* 421 Service not available, closing control connection. */
1368 /* 500 Syntax error, command unrecognized. */
1369 /* 501 Syntax error in parameters or arguments. */
1370 /* 502 Command not implemented. */
1371 /* 530 Not logged in. */
1372 if (rc == -1 || rc == 421) {
1373 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1374 "Error reading from remote server");
1375 }
1376 if (rc != 229 && rc != 500 && rc != 501 && rc != 502) {
1377 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1378 }
1379 else if (rc == 229) {
1380 /* Parse the port out of the EPSV reply. */
1382
1383 if (data_port) {
1384 apr_sockaddr_t *remote_addr, epsv_addr;
1385
1387 "EPSV contacting remote host on port %d", data_port);
1388
1389 /* Retrieve the client's address. */
1390 rv = apr_socket_addr_get(&remote_addr, APR_REMOTE, sock);
1391 if (rv == APR_SUCCESS) {
1392 /* Take a shallow copy of the server address to
1393 * modify; the _addr_get function gives back a
1394 * pointer to the socket's internal structure.
1395 * This is awkward given current APR network
1396 * interfaces. */
1397 epsv_addr = *remote_addr;
1399#if APR_HAVE_IPV6
1400 if (epsv_addr.family == APR_INET6) {
1401 epsv_addr.sa.sin6.sin6_port = htons(data_port);
1402 }
1403 else
1404#endif
1405 {
1406 epsv_addr.sa.sin.sin_port = htons(data_port);
1407 }
1409 }
1410
1411 if (rv != APR_SUCCESS) {
1413 "could not establish socket for client data connection");
1414 proxy_ftp_cleanup(r, backend);
1416 }
1417
1418 if (conf->recv_buffer_size > 0
1420 conf->recv_buffer_size))) {
1422 "apr_socket_opt_set(SO_RCVBUF): Failed to "
1423 "set ProxyReceiveBufferSize, using default");
1424 }
1425
1427 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
1429 "apr_socket_opt_set(APR_TCP_NODELAY): "
1430 "Failed to set");
1431 }
1432
1434 if (rv != APR_SUCCESS) {
1436 "EPSV attempt to connect to %pI failed - "
1437 "Firewall/NAT?", &epsv_addr);
1439 "EPSV attempt to connect to %pI failed - firewall/NAT?", &epsv_addr));
1440 }
1441 else {
1443 "connected data socket to %pI", &epsv_addr);
1444 connect = 1;
1445 }
1446 }
1447 }
1448 }
1449
1450 /* set up data connection - PASV */
1451 if (!connect) {
1452 rc = proxy_ftp_command("PASV" CRLF,
1453 r, origin, bb, &ftpmessage);
1454 /* possible results: 227, 421, 500, 501, 502, 530 */
1455 /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
1456 /* 421 Service not available, closing control connection. */
1457 /* 500 Syntax error, command unrecognized. */
1458 /* 501 Syntax error in parameters or arguments. */
1459 /* 502 Command not implemented. */
1460 /* 530 Not logged in. */
1461 if (rc == -1 || rc == 421) {
1462 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1463 "Error reading from remote server");
1464 }
1465 if (rc != 227 && rc != 502) {
1466 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1467 }
1468 else if (rc == 227) {
1469 unsigned int h0, h1, h2, h3, p0, p1;
1470 char *pstr;
1471 char *tok_cntx;
1472
1473/* FIXME: Check PASV against RFC1123 */
1474
1475 pstr = ftpmessage;
1476 pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */
1477 if (pstr != NULL) {
1478 if (*(pstr + strlen(pstr) + 1) == '=') {
1479 pstr += strlen(pstr) + 2;
1480 }
1481 else {
1482 pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address &
1483 * port params */
1484 if (pstr != NULL)
1485 pstr = apr_strtok(NULL, ")", &tok_cntx);
1486 }
1487 }
1488
1489/* FIXME: Only supports IPV4 - fix in RFC2428 */
1490
1491 if (pstr != NULL && (sscanf(pstr,
1492 "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
1493
1495 apr_port_t pasvport = (p1 << 8) + p0;
1497 "PASV contacting host %d.%d.%d.%d:%d",
1498 h3, h2, h1, h0, pasvport);
1499
1500 if ((rv = apr_socket_create(&data_sock, backend->addr->family,
1501 SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
1503 "error creating PASV socket");
1504 proxy_ftp_cleanup(r, backend);
1506 }
1507
1508 if (conf->recv_buffer_size > 0
1510 conf->recv_buffer_size))) {
1512 "apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
1513 }
1514
1516 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
1518 "apr_socket_opt_set(APR_TCP_NODELAY): "
1519 "Failed to set");
1520 }
1521
1522 /* make the connection */
1524 h3, h2, h1, h0),
1525 backend->addr->family, pasvport, 0, p);
1526 if (APR_SUCCESS != err) {
1527 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1528 apr_pstrcat(p, "DNS lookup failure for: ",
1529 connectname, NULL));
1530 }
1532 if (rv != APR_SUCCESS) {
1534 "PASV attempt to connect to %pI failed - Firewall/NAT?", pasv_addr);
1536 "PASV attempt to connect to %pI failed - firewall/NAT?", pasv_addr));
1537 }
1538 else {
1539 connect = 1;
1540 }
1541 }
1542 }
1543 }
1544/*bypass:*/
1545
1546 /* set up data connection - PORT */
1547 if (!connect) {
1548 apr_sockaddr_t *local_addr;
1549 char *local_ip;
1551 unsigned int h0, h1, h2, h3, p0, p1;
1552
1553 if ((rv = apr_socket_create(&local_sock, backend->addr->family,
1554 SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
1556 "error creating local socket");
1557 proxy_ftp_cleanup(r, backend);
1559 }
1560 apr_socket_addr_get(&local_addr, APR_LOCAL, sock);
1561 local_port = local_addr->port;
1562 apr_sockaddr_ip_get(&local_ip, local_addr);
1563
1565 != APR_SUCCESS) {
1566#ifndef _OSD_POSIX /* BS2000 has this option "always on" */
1568 "error setting reuseaddr option");
1569 proxy_ftp_cleanup(r, backend);
1571#endif /* _OSD_POSIX */
1572 }
1573
1574 err = apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool);
1575 if (APR_SUCCESS != err) {
1576 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1577 apr_pstrcat(p, "DNS lookup failure for: ",
1578 connectname, NULL));
1579 }
1580
1581 if ((rv = apr_socket_bind(local_sock, local_addr)) != APR_SUCCESS) {
1583 "error binding to ftp data socket %pI", local_addr);
1584 proxy_ftp_cleanup(r, backend);
1586 }
1587
1588 /* only need a short queue */
1589 if ((rv = apr_socket_listen(local_sock, 2)) != APR_SUCCESS) {
1591 "error listening to ftp data socket %pI", local_addr);
1592 proxy_ftp_cleanup(r, backend);
1594 }
1595
1596/* FIXME: Sent PORT here */
1597
1598 if (local_ip && (sscanf(local_ip,
1599 "%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) {
1600 p1 = (local_port >> 8);
1601 p0 = (local_port & 0xFF);
1602
1603 rc = proxy_ftp_command(apr_psprintf(p, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0),
1604 r, origin, bb, &ftpmessage);
1605 /* possible results: 200, 421, 500, 501, 502, 530 */
1606 /* 200 Command okay. */
1607 /* 421 Service not available, closing control connection. */
1608 /* 500 Syntax error, command unrecognized. */
1609 /* 501 Syntax error in parameters or arguments. */
1610 /* 502 Command not implemented. */
1611 /* 530 Not logged in. */
1612 if (rc == -1 || rc == 421) {
1613 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1614 "Error reading from remote server");
1615 }
1616 if (rc != 200) {
1617 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1618 }
1619
1620 /* signal that we must use the EPRT/PORT loop */
1621 use_port = 1;
1622 }
1623 else {
1624/* IPV6 FIXME:
1625 * The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first
1626 * number (1,2) indicates the protocol type. Examples:
1627 * EPRT |1|132.235.1.2|6275|
1628 * EPRT |2|1080::8:800:200C:417A|5282|
1629 */
1630 return ftp_proxyerror(r, backend, HTTP_NOT_IMPLEMENTED,
1631 "Connect to IPV6 ftp server using EPRT not supported. Enable EPSV.");
1632 }
1633 }
1634
1635
1636 /*
1637 * V: Set The Headers -------------------
1638 *
1639 * Get the size of the request, set up the environment for HTTP.
1640 */
1641
1642 /* set request; "path" holds last path component */
1643 len = decodeenc(path);
1644
1645 if (strchr(path, '/')) { /* are there now any '/' characters? */
1646 return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST,
1647 "Use of /%2f is only allowed at the base directory");
1648 }
1649
1650 /* If len == 0 then it must be a directory (you can't RETR nothing)
1651 * Also, don't allow to RETR by wildcard. Instead, create a dirlisting,
1652 * unless ProxyFtpListOnWildcard is off.
1653 */
1654 if (len == 0 || (ftp_check_globbingchars(path) && fdconf->ftp_list_on_wildcard)) {
1655 dirlisting = 1;
1656 }
1657 else {
1658 /* (from FreeBSD ftpd):
1659 * SIZE is not in RFC959, but Postel has blessed it and
1660 * it will be in the updated RFC.
1661 *
1662 * Return size of file in a format suitable for
1663 * using with RESTART (we just count bytes).
1664 */
1665 /* from draft-ietf-ftpext-mlst-14.txt:
1666 * This value will
1667 * change depending on the current STRUcture, MODE and TYPE of the data
1668 * connection, or a data connection which would be created were one
1669 * created now. Thus, the result of the SIZE command is dependent on
1670 * the currently established STRU, MODE and TYPE parameters.
1671 */
1672 /* Therefore: switch to binary if the user did not specify ";type=a" */
1673 ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage);
1674 rc = proxy_ftp_command(apr_pstrcat(p, "SIZE ",
1676 r, origin, bb, &ftpmessage);
1677 if (rc == -1 || rc == 421) {
1678 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1679 "Error reading from remote server");
1680 }
1681 else if (rc == 213) {/* Size command ok */
1682 int j;
1683 for (j = 0; apr_isdigit(ftpmessage[j]); j++)
1684 ;
1685 ftpmessage[j] = '\0';
1686 if (ftpmessage[0] != '\0')
1687 size = ftpmessage; /* already pstrdup'ed: no copy necessary */
1688 }
1689 else if (rc == 550) { /* Not a regular file */
1691 "SIZE shows this is a directory");
1692 dirlisting = 1;
1695 r, origin, bb, &ftpmessage);
1696 /* possible results: 250, 421, 500, 501, 502, 530, 550 */
1697 /* 250 Requested file action okay, completed. */
1698 /* 421 Service not available, closing control connection. */
1699 /* 500 Syntax error, command unrecognized. */
1700 /* 501 Syntax error in parameters or arguments. */
1701 /* 502 Command not implemented. */
1702 /* 530 Not logged in. */
1703 /* 550 Requested action not taken. */
1704 if (rc == -1 || rc == 421) {
1705 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1706 "Error reading from remote server");
1707 }
1708 if (rc == 550) {
1709 return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage);
1710 }
1711 if (rc != 250) {
1712 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1713 }
1714 path = "";
1715 len = 0;
1716 }
1717 }
1718
1719 cwd = ftp_get_PWD(r, origin, bb);
1720 if (cwd != NULL) {
1721 apr_table_set(r->notes, "Directory-PWD", cwd);
1722 }
1723
1724 if (dirlisting) {
1725 ftp_set_TYPE('A', r, origin, bb, NULL);
1726 /* If the current directory contains no slash, we are talking to
1727 * a non-unix ftp system. Try LIST instead of "LIST -lag", it
1728 * should return a long listing anyway (unlike NLST).
1729 * Some exotic FTP servers might choke on the "-lag" switch.
1730 */
1731 /* Note that we do not escape the path here, to allow for
1732 * queries like: ftp://user@host/apache/src/server/http_*.c
1733 */
1734 if (len != 0)
1735 buf = apr_pstrcat(p, "LIST ", path, CRLF, NULL);
1736 else if (cwd == NULL || strchr(cwd, '/') != NULL)
1737 buf = "LIST -lag" CRLF;
1738 else
1739 buf = "LIST" CRLF;
1740 }
1741 else {
1742 /* switch to binary if the user did not specify ";type=a" */
1743 ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage);
1744#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF))
1745 /* from draft-ietf-ftpext-mlst-14.txt:
1746 * The FTP command, MODIFICATION TIME (MDTM), can be used to determine
1747 * when a file in the server NVFS was last modified. <..>
1748 * The syntax of a time value is:
1749 * time-val = 14DIGIT [ "." 1*DIGIT ] <..>
1750 * Symbolically, a time-val may be viewed as
1751 * YYYYMMDDHHMMSS.sss
1752 * The "." and subsequent digits ("sss") are optional. <..>
1753 * Time values are always represented in UTC (GMT)
1754 */
1756 r, origin, bb, &ftpmessage);
1757 /* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */
1758 if (rc == 213) {
1759 struct {
1760 char YYYY[4+1];
1761 char MM[2+1];
1762 char DD[2+1];
1763 char hh[2+1];
1764 char mm[2+1];
1765 char ss[2+1];
1766 } time_val;
1767 if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]",
1768 time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) {
1769 struct tm tms;
1770 memset (&tms, '\0', sizeof tms);
1771 tms.tm_year = atoi(time_val.YYYY) - 1900;
1772 tms.tm_mon = atoi(time_val.MM) - 1;
1773 tms.tm_mday = atoi(time_val.DD);
1774 tms.tm_hour = atoi(time_val.hh);
1775 tms.tm_min = atoi(time_val.mm);
1776 tms.tm_sec = atoi(time_val.ss);
1777#ifdef HAVE_TIMEGM /* Does system have timegm()? */
1778 mtime = timegm(&tms);
1780#elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */
1781 /* mktime will subtract the local timezone, which is not what we want.
1782 * Add it again because the MDTM string is GMT
1783 */
1784 mtime = mktime(&tms);
1785 mtime += tms.tm_gmtoff;
1787#else
1788 mtime = 0L;
1789#endif
1790 }
1791 }
1792#endif /* USE_MDTM */
1793/* FIXME: Handle range requests - send REST */
1795 }
1796 rc = proxy_ftp_command(buf, r, origin, bb, &ftpmessage);
1797 /* rc is an intermediate response for the LIST or RETR commands */
1798
1799 /*
1800 * RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530,
1801 * 550 NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502,
1802 * 530
1803 */
1804 /* 110 Restart marker reply. */
1805 /* 125 Data connection already open; transfer starting. */
1806 /* 150 File status okay; about to open data connection. */
1807 /* 226 Closing data connection. */
1808 /* 250 Requested file action okay, completed. */
1809 /* 421 Service not available, closing control connection. */
1810 /* 425 Can't open data connection. */
1811 /* 426 Connection closed; transfer aborted. */
1812 /* 450 Requested file action not taken. */
1813 /* 451 Requested action aborted. Local error in processing. */
1814 /* 500 Syntax error, command unrecognized. */
1815 /* 501 Syntax error in parameters or arguments. */
1816 /* 530 Not logged in. */
1817 /* 550 Requested action not taken. */
1818 if (rc == -1 || rc == 421) {
1819 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1820 "Error reading from remote server");
1821 }
1822 if (rc == 550) {
1824 "RETR failed, trying LIST instead");
1825
1826 /* Directory Listings should always be fetched in ASCII mode */
1827 dirlisting = 1;
1828 ftp_set_TYPE('A', r, origin, bb, NULL);
1829
1832 r, origin, bb, &ftpmessage);
1833 /* possible results: 250, 421, 500, 501, 502, 530, 550 */
1834 /* 250 Requested file action okay, completed. */
1835 /* 421 Service not available, closing control connection. */
1836 /* 500 Syntax error, command unrecognized. */
1837 /* 501 Syntax error in parameters or arguments. */
1838 /* 502 Command not implemented. */
1839 /* 530 Not logged in. */
1840 /* 550 Requested action not taken. */
1841 if (rc == -1 || rc == 421) {
1842 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1843 "Error reading from remote server");
1844 }
1845 if (rc == 550) {
1846 return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage);
1847 }
1848 if (rc != 250) {
1849 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1850 }
1851
1852 /* Update current directory after CWD */
1853 cwd = ftp_get_PWD(r, origin, bb);
1854 if (cwd != NULL) {
1855 apr_table_set(r->notes, "Directory-PWD", cwd);
1856 }
1857
1858 /* See above for the "LIST" vs. "LIST -lag" discussion. */
1859 rc = proxy_ftp_command((cwd == NULL || strchr(cwd, '/') != NULL)
1860 ? "LIST -lag" CRLF : "LIST" CRLF,
1861 r, origin, bb, &ftpmessage);
1862
1863 /* rc is an intermediate response for the LIST command (125 transfer starting, 150 opening data connection) */
1864 if (rc == -1 || rc == 421)
1865 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
1866 "Error reading from remote server");
1867 }
1868 if (rc != 125 && rc != 150 && rc != 226 && rc != 250) {
1869 return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
1870 }
1871
1872 r->status = HTTP_OK;
1873 r->status_line = "200 OK";
1874
1876 apr_table_setn(r->headers_out, "Date", dates);
1878
1879 /* set content-type */
1880 if (dirlisting) {
1881 ap_set_content_type(r, apr_pstrcat(p, "text/html;charset=",
1882 fdconf->ftp_directory_charset ?
1883 fdconf->ftp_directory_charset :
1884 "ISO-8859-1", NULL));
1885 }
1886 else {
1887 if (xfer_type != 'A' && size != NULL) {
1888 /* We "trust" the ftp server to really serve (size) bytes... */
1889 apr_table_setn(r->headers_out, "Content-Length", size);
1891 "Content-Length set to %s", size);
1892 }
1893 }
1894 if (r->content_type) {
1895 apr_table_setn(r->headers_out, "Content-Type", r->content_type);
1897 "Content-Type set to %s", r->content_type);
1898 }
1899
1900#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF))
1901 if (mtime != 0L) {
1904 apr_table_set(r->headers_out, "Last-Modified", datestr);
1906 "Last-Modified set to %s", datestr);
1907 }
1908#endif /* USE_MDTM */
1909
1910 /* If an encoding has been set by mistake, delete it.
1911 * @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz,
1912 * @@@ the encoding is currently set to x-gzip)
1913 */
1914 if (dirlisting && r->content_encoding != NULL)
1916
1917 /* set content-encoding (not for dir listings, they are uncompressed)*/
1918 if (r->content_encoding != NULL && r->content_encoding[0] != '\0') {
1920 "Content-Encoding set to %s", r->content_encoding);
1921 apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
1922 }
1923
1924 /* wait for connection */
1925 if (use_port) {
1926 for (;;) {
1928 if (APR_STATUS_IS_EINTR(rv)) {
1929 continue;
1930 }
1931 else if (rv == APR_SUCCESS) {
1932 break;
1933 }
1934 else {
1936 "failed to accept data connection");
1937 proxy_ftp_cleanup(r, backend);
1938 return HTTP_BAD_GATEWAY;
1939 }
1940 }
1941 }
1942
1943 /* the transfer socket is now open, create a new connection */
1945 r->connection->sbh, c->bucket_alloc);
1946 if (!data) {
1947 /*
1948 * the peer reset the connection already; ap_run_create_connection() closed
1949 * the socket
1950 */
1952 "an error occurred creating the transfer connection");
1953 proxy_ftp_cleanup(r, backend);
1955 }
1956
1957 /*
1958 * We do not do SSL over the data connection, even if the virtual host we
1959 * are in might have SSL enabled
1960 */
1962 /* set up the connection filters */
1964 if (rc != OK && rc != DONE) {
1966 "pre_connection setup failed (%d)", rc);
1967 data->aborted = 1;
1968 proxy_ftp_cleanup(r, backend);
1969 return rc;
1970 }
1971
1972 /*
1973 * VI: Receive the Response ------------------------
1974 *
1975 * Get response from the remote ftp socket, and pass it up the filter chain.
1976 */
1977
1978 /* send response */
1979 r->sent_bodyct = 1;
1980
1981 if (dirlisting) {
1982 /* insert directory filter */
1983 ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection);
1984 }
1985
1986 /* send body */
1987 if (!r->header_only) {
1988 apr_bucket *e;
1989 int finish = FALSE;
1990
1991 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "start body send");
1992
1993 /* read the body, pass it to the output filters */
1994 while (ap_get_brigade(data->input_filters,
1995 bb,
1998 conf->io_buffer_size) == APR_SUCCESS) {
1999#if DEBUGGING
2000 {
2004 "proxy: readbytes: %#x", readbytes);
2005 }
2006#endif
2007 /* sanity check */
2008 if (APR_BRIGADE_EMPTY(bb)) {
2009 break;
2010 }
2011
2012 /* found the last brigade? */
2014 /* if this is the last brigade, cleanup the
2015 * backend connection first to prevent the
2016 * backend server from hanging around waiting
2017 * for a slow client to eat these bytes
2018 */
2020 if (data_sock) {
2022 }
2023 data_sock = NULL;
2025 "data connection closed");
2026 /* signal that we must leave */
2027 finish = TRUE;
2028 }
2029
2030 /* if no EOS yet, then we must flush */
2031 if (FALSE == finish) {
2032 e = apr_bucket_flush_create(c->bucket_alloc);
2034 }
2035
2036 /* try send what we read */
2038 || c->aborted) {
2039 /* Ack! Phbtt! Die! User aborted! */
2040 finish = TRUE;
2041 }
2042
2043 /* make sure we always clean up after ourselves */
2045
2046 /* if we are done, leave */
2047 if (TRUE == finish) {
2048 break;
2049 }
2050 }
2051 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "end body send");
2052
2053 }
2054 if (data_sock) {
2057 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01058) "data connection closed");
2058 }
2059
2060 /* Retrieve the final response for the RETR or LIST commands */
2061 proxy_ftp_command(NULL, r, origin, bb, &ftpmessage);
2063
2064 /*
2065 * VII: Clean Up -------------
2066 *
2067 * If there are no KeepAlives, or if the connection has been signalled to
2068 * close, close the socket and clean up
2069 */
2070
2071 /* finish */
2072 proxy_ftp_command("QUIT" CRLF, r, origin, bb, &ftpmessage);
2073 /* responses: 221, 500 */
2074 /* 221 Service closing control connection. */
2075 /* 500 Syntax error, command unrecognized. */
2076 ap_flush_conn(origin);
2077 proxy_ftp_cleanup(r, backend);
2078
2080 return OK;
2081}
2082
2084{
2085 /* hooks */
2088 /* filters */
2091 /* Compile the output format of "ls -s1" as a fallback for non-unix ftp listings */
2094}
2095
2097{
2098 AP_INIT_FLAG("ProxyFtpListOnWildcard", set_ftp_list_on_wildcard, NULL,
2099 RSRC_CONF|ACCESS_CONF, "Whether wildcard characters in a path cause mod_proxy_ftp to list the files instead of trying to get them. Defaults to on."),
2100 AP_INIT_FLAG("ProxyFtpEscapeWildcards", set_ftp_escape_wildcards, NULL,
2101 RSRC_CONF|ACCESS_CONF, "Whether the proxy should escape wildcards in paths before sending them to the FTP server. Defaults to on, but most FTP servers will need it turned off if you need to manage paths that contain wildcard characters."),
2102 AP_INIT_TAKE1("ProxyFtpDirCharset", set_ftp_directory_charset, NULL,
2103 RSRC_CONF|ACCESS_CONF, "Define the character set for proxied FTP listings"),
2104 {NULL}
2105};
2106
2107
2110 create_proxy_ftp_dir_config,/* create per-directory config structure */
2111 merge_proxy_ftp_dir_config, /* merge per-directory config structures */
2112 NULL, /* create per-server config structure */
2113 NULL, /* merge per-server config structures */
2114 proxy_ftp_cmds, /* command apr_table_t */
2115 ap_proxy_ftp_register_hook /* register hooks */
2116};
int n
Definition ap_regex.h:278
const char * buff
Definition ap_regex.h:186
const char apr_size_t len
Definition ap_regex.h:187
#define AP_REG_EXTENDED
Definition ap_regex.h:78
#define TRUE
Definition abts.h:38
#define FALSE
Definition abts.h:35
#define connect
char * strstr(char *s1, char *s2)
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Versioning Interface.
int ap_run_pre_connection(conn_rec *c, void *csd)
Definition connection.c:43
conn_rec * ap_run_create_connection(apr_pool_t *p, server_rec *server, apr_socket_t *csd, long conn_id, void *sbh, apr_bucket_alloc_t *alloc)
Definition connection.c:41
void ap_flush_conn(conn_rec *c)
Definition connection.c:93
return found
Definition core.c:2840
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
#define ap_set_module_config(v, m, val)
request_rec int int apr_table_t const char * path
request_rec * r
#define HUGE_STRING_LEN
Definition httpd.h:303
#define MAX_STRING_LEN
Definition httpd.h:300
#define DOCTYPE_HTML_3_2
Definition httpd.h:238
const char * ap_get_server_description(void)
Definition core.c:3587
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DONE
Definition httpd.h:458
#define ap_xlate_proto_to_ascii(x, y)
Definition util_ebcdic.h:80
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
ap_filter_rec_t * ap_register_output_filter(const char *name, ap_out_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
ap_filter_t * ap_add_output_filter(const char *name, void *ctx, request_rec *r, conn_rec *c)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
@ AP_FTYPE_RESOURCE
#define ap_get_core_module_config(v)
Definition http_core.h:383
char * ap_construct_url(apr_pool_t *p, const char *uri, request_rec *r)
Definition core.c:1246
#define APLOGrtrace2(r)
Definition http_log.h:247
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_TRACE4
Definition http_log.h:75
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_TRACE3
Definition http_log.h:74
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
const unsigned char * buf
Definition util_md5.h:50
void ap_set_content_type(request_rec *r, const char *ct)
const char apr_port_t port
Definition http_vhost.h:125
void * dummy
Definition http_vhost.h:62
const char * host
Definition http_vhost.h:124
void const char * arg
Definition http_vhost.h:63
#define CRLF
Definition httpd.h:724
#define APR_ENOTIMPL
Definition apr_errno.h:476
#define APR_ECONNABORTED
Definition apr_errno.h:769
#define APR_STATUS_IS_EINTR(s)
Definition apr_errno.h:1281
apr_file_t * f
#define APR_BRIGADE_LAST(b)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define apr_bucket_split(e, point)
apr_bucket * e
#define APR_BRIGADE_CONCAT(a, b)
#define APR_BRIGADE_EMPTY(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char * url
Definition apr_escape.h:120
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
apr_redis_t * rc
Definition apr_redis.h:173
#define APR_URI_UNP_OMITQUERY
Definition apr_uri.h:77
const char * uri
Definition apr_uri.h:159
#define APR_URI_UNP_OMITPATHINFO
Definition apr_uri.h:75
#define APR_URI_UNP_OMITPASSWORD
Definition apr_uri.h:68
#define APR_URI_UNP_OMITSITEPART
Definition apr_uri.h:64
#define ACCESS_CONF
#define RSRC_CONF
#define HTTP_OK
Definition httpd.h:490
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_SERVICE_UNAVAILABLE
Definition httpd.h:538
#define HTTP_BAD_GATEWAY
Definition httpd.h:537
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_NOT_FOUND
Definition httpd.h:512
#define HTTP_UNAUTHORIZED
Definition httpd.h:509
#define HTTP_NOT_IMPLEMENTED
Definition httpd.h:536
int ap_proxy_ssl_engine(conn_rec *c, ap_conf_vector_t *per_dir_config, int enable)
Definition mod_proxy.c:3061
int ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, server_rec *s)
char * ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, char **passwordp, char **hostp, apr_port_t *port)
Definition proxy_util.c:342
int ap_proxy_release_connection(const char *proxy_function, proxy_conn_rec *conn, server_rec *s)
int ap_proxy_acquire_connection(const char *proxy_function, proxy_conn_rec **conn, proxy_worker *worker, server_rec *s)
apr_status_t ap_proxy_determine_address(const char *proxy_function, proxy_conn_rec *conn, const char *hostname, apr_port_t hostport, unsigned int flags, request_rec *r, server_rec *s)
int ap_proxy_hex2c(const char *x)
Definition proxy_util.c:118
char * ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, enum enctype t, int flags, int proxyreq)
Definition proxy_util.c:220
int ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf, const char *hostname, apr_sockaddr_t *addr)
Definition proxy_util.c:824
#define PROXY_CANONENC_NOENCODEDSLASHENCODING
Definition mod_proxy.h:81
void proxy_hook_canon_handler(proxy_HOOK_canon_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3412
int ap_proxy_connection_create_ex(const char *proxy_function, proxy_conn_rec *conn, request_rec *r)
int ap_proxyerror(request_rec *r, int statuscode, const char *message)
Definition proxy_util.c:432
void proxy_hook_scheme_handler(proxy_HOOK_scheme_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3406
char * ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, int forcedec, int proxyreq)
Definition proxy_util.c:321
@ enc_path
Definition mod_proxy.h:76
@ enc_fpath
Definition mod_proxy.h:76
@ enc_parm
Definition mod_proxy.h:76
#define M_GET
Definition httpd.h:592
#define STANDARD20_MODULE_STUFF
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
const char * ap_psignature(const char *prefix, request_rec *r)
Definition core.c:3516
char * ap_getword(apr_pool_t *p, const char **line, char stop)
Definition util.c:723
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
#define ap_strchr_c(s, c)
Definition httpd.h:2353
#define PROXYREQ_NONE
Definition httpd.h:1133
char * ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
Definition util.c:2477
char * ap_getword_nulls(apr_pool_t *p, const char **line, char stop)
Definition util.c:779
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define ap_escape_html(p, s)
Definition httpd.h:1860
#define ap_assert(exp)
Definition httpd.h:2271
#define PROXYREQ_REVERSE
Definition httpd.h:1135
ap_regex_t * ap_pregcomp(apr_pool_t *p, const char *pattern, int cflags)
Definition util.c:262
char * ap_getword_conf(apr_pool_t *p, const char **line)
Definition util.c:833
apr_size_t size
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_toupper(c)
Definition apr_lib.h:233
#define apr_isdigit(c)
Definition apr_lib.h:209
#define apr_isxdigit(c)
Definition apr_lib.h:229
#define apr_isalpha(c)
Definition apr_lib.h:205
#define apr_tolower(c)
Definition apr_lib.h:231
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char apr_int32_t flag
void * data
int type
apr_time_t mtime
char * buffer
void * memchr(const void *s, int c, size_t n)
#define APR_ASCII_LF
Definition apr_general.h:63
const apr_hash_t * h1
Definition apr_hash.h:232
const apr_hash_t const apr_hash_t * h2
Definition apr_hash.h:233
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_socket_t * sock
apr_uint16_t apr_port_t
#define APR_UNSPEC
@ APR_LOCAL
@ APR_REMOTE
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_dir_t * dir
apr_size_t const char * filename
Definition apr_shm.h:72
#define APR_TCP_NODELAY
#define APR_SO_RCVBUF
#define APR_SO_REUSEADDR
const char const char * password
apr_int32_t apr_int32_t apr_int32_t err
apr_int32_t in
apr_cmdtype_e cmd
int int status
#define APR_RFC822_DATE_LEN
Definition apr_time.h:186
apr_size_t apr_size_t const char apr_time_exp_t * tm
Definition apr_time.h:221
#define APR_USEC_PER_SEC
Definition apr_time.h:60
apr_size_t apr_size_t max
Definition apr_time.h:220
apr_int64_t apr_time_t
Definition apr_time.h:45
apr_pool_t * p
Definition md_event.c:32
static apr_file_t * out
Definition mod_info.c:85
Proxy Extension Module for Apache.
static apr_status_t proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *in)
static const char * set_ftp_list_on_wildcard(cmd_parms *cmd, void *dconf, int flag)
#define MAX_LINE_LEN
static int decodeenc(char *x)
static const char * set_ftp_directory_charset(cmd_parms *cmd, void *dconf, const char *arg)
static const char * ftp_escape_globbingchars(apr_pool_t *p, const char *path, proxy_ftp_dir_conf *dconf)
static int ftp_check_string(const char *x)
static apr_status_t proxy_ftp_cleanup(request_rec *r, proxy_conn_rec *backend)
static char * ftp_get_PWD(request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb)
static int ftp_set_TYPE(char xfer_type, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char **pmessage)
static int ftp_unauthorized(request_rec *r, int log_it)
static void ap_proxy_ftp_register_hook(apr_pool_t *p)
static void * create_proxy_ftp_dir_config(apr_pool_t *p, char *dummy)
static int proxy_ftp_canon(request_rec *r, char *url)
static const char * set_ftp_escape_wildcards(cmd_parms *cmd, void *dconf, int flag)
static int ftp_check_globbingchars(const char *path)
static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, proxy_server_conf *conf, char *url, const char *proxyhost, apr_port_t proxyport)
#define LS_REG_PATTERN
#define LS_REG_MATCH
static ap_regex_t * ls_regex
#define FTP_GLOBBING_CHARS
static int proxy_ftp_command(const char *cmd, request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char **pmessage)
static apr_port_t parse_epsv_reply(const char *reply)
static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbuf, int msglen)
static const command_rec proxy_ftp_cmds[]
static int ftp_proxyerror(request_rec *r, proxy_conn_rec *conn, int statuscode, const char *message)
static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, apr_size_t bufflen, int *eos, apr_size_t *outlen)
static void * merge_proxy_ftp_dir_config(apr_pool_t *p, void *basev, void *addv)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
The representation of a filter chain.
apr_int32_t family
char * user
Definition apr_uri.h:91
char * path
Definition apr_uri.h:99
apr_port_t port
Definition apr_uri.h:109
char * password
Definition apr_uri.h:93
char * hostname
Definition apr_uri.h:95
Structure to store things which are per connection.
Definition httpd.h:1152
apr_pool_t * pool
Definition httpd.h:1154
struct ap_conf_vector_t * conn_config
Definition httpd.h:1190
void * sbh
Definition httpd.h:1199
long id
Definition httpd.h:1187
Per-directory configuration.
Definition http_core.h:527
apr_sockaddr_t * addr
Definition mod_proxy.h:276
apr_socket_t * sock
Definition mod_proxy.h:278
unsigned int close
Definition mod_proxy.h:284
conn_rec * connection
Definition mod_proxy.h:270
apr_bucket_brigade * in
const char * ftp_directory_charset
apr_size_t recv_buffer_size
Definition mod_proxy.h:176
apr_size_t io_buffer_size
Definition mod_proxy.h:177
A structure that represents the current request.
Definition httpd.h:845
char * user
Definition httpd.h:1005
int status
Definition httpd.h:891
const char * content_type
Definition httpd.h:992
struct ap_filter_t * output_filters
Definition httpd.h:1070
int header_only
Definition httpd.h:875
apr_table_t * notes
Definition httpd.h:985
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
apr_time_t request_time
Definition httpd.h:886
apr_uri_t parsed_uri
Definition httpd.h:1092
int proxyreq
Definition httpd.h:873
conn_rec * connection
Definition httpd.h:849
apr_table_t * err_headers_out
Definition httpd.h:981
apr_table_t * headers_in
Definition httpd.h:976
apr_off_t sent_bodyct
Definition httpd.h:929
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * status_line
Definition httpd.h:889
char * args
Definition httpd.h:1026
const char * content_encoding
Definition httpd.h:997
char * ap_auth_type
Definition httpd.h:1007
apr_table_t * headers_out
Definition httpd.h:978
apr_status_t apr_socket_accept(apr_socket_t **new, apr_socket_t *sock, apr_pool_t *connection_context)
Definition sockets.c:247
apr_status_t apr_socket_listen(apr_socket_t *sock, apr_int32_t backlog)
Definition sockets.c:239
apr_status_t apr_socket_bind(apr_socket_t *sock, apr_sockaddr_t *sa)
Definition sockets.c:216
apr_status_t apr_socket_close(apr_socket_t *thesocket)
Definition sockets.c:211
apr_status_t apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa)
Definition sockets.c:388
apr_status_t apr_socket_create(apr_socket_t **new, int ofamily, int type, int protocol, apr_pool_t *cont)
Definition sockets.c:116
apr_status_t apr_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on)
Definition sockopt.c:113
apr_status_t apr_rfc822_date(char *date_str, apr_time_t t)
Definition timestr.c:42
#define str
@ AP_MODE_READBYTES
Definition util_filter.h:43
@ AP_MODE_GETLINE
Definition util_filter.h:48