📄 mod_proxy_ftp.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* FTP routines for Apache proxy */#include "mod_proxy.h"#if APR_HAVE_TIME_H#include <time.h>#endif#include "apr_version.h"#if (APR_MAJOR_VERSION < 1)#undef apr_socket_create#define apr_socket_create apr_socket_create_ex#endif#define AUTODETECT_PWD/* Automatic timestamping (Last-Modified header) based on MDTM is used if: * 1) the FTP server supports the MDTM command and * 2) HAVE_TIMEGM (preferred) or HAVE_GMTOFF is available at compile time */#define USE_MDTMmodule AP_MODULE_DECLARE_DATA proxy_ftp_module;/* * Decodes a '%' escaped string, and returns the number of characters */static int decodeenc(char *x){ int i, j, ch; if (x[0] == '\0') return 0; /* special case for no characters */ for (i = 0, j = 0; x[i] != '\0'; i++, j++) { /* decode it if not already done */ ch = x[i]; if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) { ch = ap_proxy_hex2c(&x[i + 1]); i += 2; } x[j] = ch; } x[j] = '\0'; return j;}/* * Escape the globbing characters in a path used as argument to * the FTP commands (SIZE, CWD, RETR, MDTM, ...). * ftpd assumes '\\' as a quoting character to escape special characters. * Returns: escaped string */#define FTP_GLOBBING_CHARS "*?[{~"static char *ftp_escape_globbingchars(apr_pool_t *p, const char *path){ char *ret = apr_palloc(p, 2*strlen(path)+sizeof("")); char *d; for (d = ret; *path; ++path) { if (strchr(FTP_GLOBBING_CHARS, *path) != NULL) *d++ = '\\'; *d++ = *path; } *d = '\0'; return ret;}/* * Check for globbing characters in a path used as argument to * the FTP commands (SIZE, CWD, RETR, MDTM, ...). * ftpd assumes '\\' as a quoting character to escape special characters. * Returns: 0 (no globbing chars, or all globbing chars escaped), 1 (globbing chars) */static int ftp_check_globbingchars(const char *path){ for ( ; *path; ++path) { if (*path == '\\') ++path; if (*path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL) return TRUE; } return FALSE;}/* * checks an encoded ftp string for bad characters, namely, CR, LF or * non-ascii character */static int ftp_check_string(const char *x){ int i, ch = 0;#if APR_CHARSET_EBCDIC char buf[1];#endif for (i = 0; x[i] != '\0'; i++) { ch = x[i]; if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) { ch = ap_proxy_hex2c(&x[i + 1]); i += 2; }#if !APR_CHARSET_EBCDIC if (ch == '\015' || ch == '\012' || (ch & 0x80))#else /* APR_CHARSET_EBCDIC */ if (ch == '\r' || ch == '\n') return 0; buf[0] = ch; ap_xlate_proto_to_ascii(buf, 1); if (buf[0] & 0x80)#endif /* APR_CHARSET_EBCDIC */ return 0; } return 1;}/* * Canonicalise ftp URLs. */static int proxy_ftp_canon(request_rec *r, char *url){ char *user, *password, *host, *path, *parms, *strp, sport[7]; apr_pool_t *p = r->pool; const char *err; apr_port_t port, def_port; /* */ if (strncasecmp(url, "ftp:", 4) == 0) { url += 4; } else { return DECLINED; } def_port = apr_uri_port_of_scheme("ftp"); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: FTP: canonicalising URL %s", url); port = def_port; err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port); if (err) return HTTP_BAD_REQUEST; if (user != NULL && !ftp_check_string(user)) return HTTP_BAD_REQUEST; if (password != NULL && !ftp_check_string(password)) return HTTP_BAD_REQUEST; /* now parse path/parameters args, according to rfc1738 */ /* * N.B. if this isn't a true proxy request, then the URL path (but not * query args) has already been decoded. This gives rise to the problem * of a ; being decoded into the path. */ strp = strchr(url, ';'); if (strp != NULL) { *(strp++) = '\0'; parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, 0, r->proxyreq); if (parms == NULL) return HTTP_BAD_REQUEST; } else parms = ""; path = ap_proxy_canonenc(p, url, strlen(url), enc_path, 0, r->proxyreq); if (path == NULL) return HTTP_BAD_REQUEST; if (!ftp_check_string(path)) return HTTP_BAD_REQUEST; if (r->proxyreq && r->args != NULL) { if (strp != NULL) { strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1, r->proxyreq); if (strp == NULL) return HTTP_BAD_REQUEST; parms = apr_pstrcat(p, parms, "?", strp, NULL); } else { strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1, r->proxyreq); if (strp == NULL) return HTTP_BAD_REQUEST; path = apr_pstrcat(p, path, "?", strp, NULL); } r->args = NULL; }/* now, rebuild URL */ if (port != def_port) apr_snprintf(sport, sizeof(sport), ":%d", port); else sport[0] = '\0'; if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ host = apr_pstrcat(p, "[", host, "]", NULL); } r->filename = apr_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "", (password != NULL) ? ":" : "", (password != NULL) ? password : "", (user != NULL) ? "@" : "", host, sport, "/", path, (parms[0] != '\0') ? ";" : "", parms, NULL); return OK;}/* we chop lines longer than 80 characters */#define MAX_LINE_LEN 80/* * Reads response lines, returns both the ftp status code and * remembers the response message in the supplied buffer */static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbuf, int msglen){ int status; char response[MAX_LINE_LEN]; char buff[5]; char *mb = msgbuf, *me = &msgbuf[msglen]; apr_status_t rv; int eos; if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { return -1; }/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "proxy: <FTP: %s", response);*/ if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) status = 0; else status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0'; mb = apr_cpystrn(mb, response + 4, me - mb); if (response[3] == '-') { memcpy(buff, response, 3); buff[3] = ' '; do { if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { return -1; } mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); } while (memcmp(response, buff, 4) != 0); } return status;}/* this is a filter that turns a raw ASCII directory listing into pretty HTML *//* ideally, mod_proxy should simply send the raw directory list up the filter * stack to mod_autoindex, which in theory should turn the raw ascii into * pretty html along with all the bells and whistles it provides... * * all in good time...! :) */typedef struct { apr_bucket_brigade *in; char buffer[MAX_STRING_LEN]; enum { HEADER, BODY, FOOTER } state;} proxy_dir_ctx_t;/* fallback regex for ls -s1; ($0..$2) == 3 */#define LS_REG_PATTERN "^ *([0-9]+) +([^ ]+)$"#define LS_REG_MATCH 3static apr_status_t proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *in){ request_rec *r = f->r; conn_rec *c = r->connection; apr_pool_t *p = r->pool; apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc); apr_status_t rv; register int n; char *dir, *path, *reldir, *site, *str, *type; 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, c->bucket_alloc); ctx->buffer[0] = 0; ctx->state = HEADER; } /* combine the stored and the new */ APR_BRIGADE_CONCAT(ctx->in, in); if (HEADER == ctx->state) { /* basedir is either "", or "/%2f" for the "squid %2f hack" */ const char *basedir = ""; /* By default, path is relative to the $HOME dir */ char *wildcard = NULL; const char *escpath; /* Save "scheme://site" prefix without password */ site = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO); /* * In the reverse proxy case we usually have no site. So contruct * one. */ if ((*site == '\0') && (r->proxyreq == PROXYREQ_REVERSE)) { site = ap_construct_url(p, "", r); } /* ... and path without query args */ path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY); /* If path began with /%2f, change the basedir */ if (strncasecmp(path, "/%2f", 4) == 0) { basedir = "/%2f"; } /* Strip off a type qualifier. It is ignored for dir listings */ if ((type = strstr(path, ";type=")) != NULL) *type++ = '\0'; (void)decodeenc(path);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -