#include "mod_proxy.h"
#include "http_main.h"
#include "http_log.h"
#include "http_core.h"
#define AUTODETECT_PWD
static
int
decodeenc(
char
*x)
{
int
i, j, ch;
if
(x[0] ==
'\0'
)
return
0;
for
(i = 0, j = 0; x[i] !=
'\0'
; i++, j++) {
ch = x[i];
if
(ch ==
'%'
&& ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
ch = ap_proxy_hex2c(&x[i + 1]);
i += 2;
}
x[j] = ch;
}
x[j] =
'\0'
;
return
j;
}
static
int
ftp_check_string(
const
char
*x)
{
int
i, ch;
for
(i = 0; x[i] !=
'\0'
; i++) {
ch = x[i];
if
(ch ==
'%'
&& ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
ch = ap_proxy_hex2c(&x[i + 1]);
i += 2;
}
if
(ch == CR || ch == LF || (OS_ASC(ch) & 0x80))
return
0;
}
return
1;
}
int
ap_proxy_ftp_canon(request_rec *r,
char
*url)
{
char
*user, *password, *host, *path, *parms, *strp, sport[7];
pool *p = r->pool;
const
char
*err;
int
port;
port = DEFAULT_FTP_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;
strp =
strchr
(url,
';'
);
if
(strp != NULL) {
*(strp++) =
'\0'
;
parms = ap_proxy_canonenc(p, strp,
strlen
(strp), enc_parm,
r->proxyreq);
if
(parms == NULL)
return
HTTP_BAD_REQUEST;
}
else
parms =
""
;
path = ap_proxy_canonenc(p, url,
strlen
(url), enc_path, r->proxyreq);
if
(path == NULL)
return
HTTP_BAD_REQUEST;
if
(!ftp_check_string(path))
return
HTTP_BAD_REQUEST;
if
(r->proxyreq == NOT_PROXY && r->args != NULL) {
if
(strp != NULL) {
strp = ap_proxy_canonenc(p, r->args,
strlen
(r->args), enc_parm, STD_PROXY);
if
(strp == NULL)
return
HTTP_BAD_REQUEST;
parms = ap_pstrcat(p, parms,
"?"
, strp, NULL);
}
else
{
strp = ap_proxy_canonenc(p, r->args,
strlen
(r->args), enc_fpath, STD_PROXY);
if
(strp == NULL)
return
HTTP_BAD_REQUEST;
path = ap_pstrcat(p, path,
"?"
, strp, NULL);
}
r->args = NULL;
}
if
(port != DEFAULT_FTP_PORT)
ap_snprintf(sport,
sizeof
(sport),
":%d"
, port);
else
sport[0] =
'\0'
;
r->filename = ap_pstrcat(p,
"proxy:ftp://"
, (user != NULL) ? user :
""
,
(password != NULL) ?
":"
:
""
,
(password != NULL) ? password :
""
,
(user != NULL) ?
"@"
:
""
, host, sport,
"/"
, path,
(parms[0] !=
'\0'
) ?
";"
:
""
, parms, NULL);
return
OK;
}
static
int
ftp_getrc(BUFF *f)
{
int
len, status;
char
linebuff[100], buff[5];
len = ap_bgets(linebuff,
sizeof
linebuff, f);
if
(len == -1)
return
-1;
if
(len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
!ap_isdigit(linebuff[2]) || (linebuff[3] !=
' '
&& linebuff[3] !=
'-'
))
status = 0;
else
status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 *
'0'
;
if
(linebuff[len - 1] !=
'\n'
) {
(
void
)ap_bskiplf(f);
}
if
(linebuff[3] ==
'-'
) {
memcpy
(buff, linebuff, 3);
buff[3] =
' '
;
do
{
len = ap_bgets(linebuff,
sizeof
linebuff, f);
if
(len == -1)
return
-1;
if
(linebuff[len - 1] !=
'\n'
) {
(
void
)ap_bskiplf(f);
}
}
while
(
memcmp
(linebuff, buff, 4) != 0);
}
return
status;
}
static
int
ftp_getrc_msg(BUFF *f,
char
*msgbuf,
int
msglen)
{
int
len, status;
char
linebuff[100], buff[5];
char
*mb = msgbuf,
*me = &msgbuf[msglen];
len = ap_bgets(linebuff,
sizeof
linebuff, f);
if
(len == -1)
return
-1;
if
(len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
!ap_isdigit(linebuff[2]) || (linebuff[3] !=
' '
&& linebuff[3] !=
'-'
))
status = 0;
else
status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 *
'0'
;
mb = ap_cpystrn(mb, linebuff+4, me - mb);
if
(linebuff[len - 1] !=
'\n'
)
(
void
)ap_bskiplf(f);
if
(linebuff[3] ==
'-'
) {
memcpy
(buff, linebuff, 3);
buff[3] =
' '
;
do
{
len = ap_bgets(linebuff,
sizeof
linebuff, f);
if
(len == -1)
return
-1;
if
(linebuff[len - 1] !=
'\n'
) {
(
void
)ap_bskiplf(f);
}
mb = ap_cpystrn(mb, linebuff+4, me - mb);
}
while
(
memcmp
(linebuff, buff, 4) != 0);
}
return
status;
}
static
long
int
send_dir(BUFF *f, request_rec *r, cache_req *c,
char
*cwd)
{
char
buf[IOBUFSIZE];
char
buf2[IOBUFSIZE];
char
*filename;
int
searchidx = 0;
char
*searchptr = NULL;
int
firstfile = 1;
unsigned
long
total_bytes_sent = 0;
register
int
n, o, w;
conn_rec *con = r->connection;
char
*dir, *path, *reldir, *site;
site = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO);
path = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY);
(
void
)decodeenc(path);
path = dir = ap_pstrcat(r->pool, path,
"/"
, NULL);
while
((n =
strlen
(path)) > 1 && path[n-1] ==
'/'
&& path[n-2] ==
'/'
)
path[n-1] =
'\0'
;
n = ap_snprintf(buf,
sizeof
(buf), DOCTYPE_HTML_3_2
"<HTML><HEAD><TITLE>%s%s</TITLE>\n"
"<BASE HREF=\"%s%s\"></HEAD>\n"
"<BODY><H2>Directory of "
"<A HREF=\"/\">%s</A>/"
,
site, path, site, path, site);
total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
while
((dir =
strchr
(dir+1,
'/'
)) != NULL)
{
*dir =
'\0'
;
if
((reldir =
strrchr
(path+1,
'/'
))==NULL)
reldir = path+1;
else
++reldir;
ap_snprintf(buf,
sizeof
(buf),
"<A HREF=\"/%s/\">%s</A>/"
, path+1, reldir);
total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
*dir =
'/'
;
}
if
(cwd == NULL ||
strncmp
(cwd, path,
strlen
(cwd)) == 0) {
ap_snprintf(buf,
sizeof
(buf),
"</H2>\n<HR><PRE>"
);
}
else
{
ap_snprintf(buf,
sizeof
(buf),
"</H2>\n(%s)\n<HR><PRE>"
, cwd);
}
total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
while
(!con->aborted) {
n = ap_bgets(buf,
sizeof
buf, f);
if
(n == -1) {
if
(c != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
"proxy: error reading from %s"
, c->url);
c = ap_proxy_cache_error(c);
}
break
;
}
if
(n == 0)
break
;
if
(buf[0] ==
'l'
&& (filename=
strstr
(buf,
" -> "
)) != NULL) {
char
*link_ptr = filename;
do
{
filename--;
}
while
(filename[0] !=
' '
);
*(filename++) =
'\0'
;
*(link_ptr++) =
'\0'
;
if
((n =
strlen
(link_ptr)) > 1 && link_ptr[n - 1] ==
'\n'
)
link_ptr[n - 1] =
'\0'
;
ap_snprintf(buf2,
sizeof
(buf2),
"%s <A HREF=\"%s\">%s %s</A>\n"
, buf, filename, filename, link_ptr);
ap_cpystrn(buf, buf2,
sizeof
(buf));
n =
strlen
(buf);
}
else
if
(buf[0] ==
'd'
|| buf[0] ==
'-'
|| buf[0] ==
'l'
|| ap_isdigit(buf[0])) {
if
(ap_isdigit(buf[0])) {
searchptr =
strchr
(buf,
'<'
);
if
(searchptr != NULL)
*searchptr =
'['
;
searchptr =
strchr
(buf,
'>'
);
if
(searchptr != NULL)
*searchptr =
']'
;
}
filename =
strrchr
(buf,
' '
);
*(filename++) = 0;
filename[
strlen
(filename) - 1] = 0;
if
(!
strcmp
(filename,
"."
) || !
strcmp
(filename,
".."
) || firstfile) {
firstfile = 0;
searchidx = filename - buf;
}
else
if
(searchidx != 0 && buf[searchidx] != 0) {
*(--filename) =
' '
;
buf[searchidx - 1] = 0;
filename = &buf[searchidx];
}
if
(!
strcmp
(filename,
"."
) || !
strcmp
(filename,
".."
) || buf[0] ==
'd'
) {
ap_snprintf(buf2,
sizeof
(buf2),
"%s <A HREF=\"%s/\">%s</A>\n"
,
buf, filename, filename);
}
else
{
ap_snprintf(buf2,
sizeof
(buf2),
"%s <A HREF=\"%s\">%s</A>\n"
, buf, filename, filename);
}
ap_cpystrn(buf, buf2,
sizeof
(buf));
n =
strlen
(buf);
}
o = 0;
total_bytes_sent += n;
if
(c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
"proxy: error writing to %s"
, c->tempfile);
c = ap_proxy_cache_error(c);
}
while
(n && !r->connection->aborted) {
w = ap_bwrite(con->client, &buf[o], n);
if
(w <= 0)
break
;
ap_reset_timeout(r);
n -= w;
o += w;
}
}
total_bytes_sent += ap_proxy_bputs2(
"</PRE><HR>\n"
, con->client, c);
total_bytes_sent += ap_proxy_bputs2(ap_psignature(
""
, r), con->client, c);
total_bytes_sent += ap_proxy_bputs2(
"</BODY></HTML>\n"
, con->client, c);
ap_bflush(con->client);
return
total_bytes_sent;
}
static
int
ftp_unauthorized (request_rec *r,
int
log_it)
{
r->proxyreq = NOT_PROXY;
if
(log_it)
ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
"proxy: missing or failed auth to %s"
,
ap_unparse_uri_components(r->pool,
&r->parsed_uri, UNP_OMITPATHINFO));
ap_table_setn(r->err_headers_out,
"WWW-Authenticate"
,
ap_pstrcat(r->pool,
"Basic realm=\""
,
ap_unparse_uri_components(r->pool, &r->parsed_uri,
UNP_OMITPASSWORD|UNP_OMITPATHINFO),
"\""
, NULL));
return
HTTP_UNAUTHORIZED;
}
int
ap_proxy_ftp_handler(request_rec *r, cache_req *c,
char
*url)
{
char
*host, *path, *strp, *parms;
char
*cwd = NULL;
char
*user = NULL;
const
char
*password = NULL;
const
char
*err;
int
port, i, j, len, sock, dsock, rc, nocache = 0;
int
csd = 0;
struct
sockaddr_in server;
struct
hostent server_hp;
struct
in_addr destaddr;
table *resp_hdrs;
BUFF *f;
BUFF *data = NULL;
pool *p = r->pool;
int
one = 1;
NET_SIZE_T clen;
void
*sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
struct
noproxy_entry *npent = (
struct
noproxy_entry *) conf->noproxies->elts;
struct
nocache_entry *ncent = (
struct
nocache_entry *) conf->nocaches->elts;
unsigned
int
presult, h0, h1, h2, h3, p0, p1;
unsigned
int
paddr;
unsigned
short
pport;
struct
sockaddr_in data_addr;
int
pasvmode = 0;
char
pasv[64];
char
*pstr;
char
resp[MAX_STRING_LEN];
char
*size = NULL;
if
(r->method_number != M_GET)
return
HTTP_NOT_IMPLEMENTED;
host = r->parsed_uri.hostname;
port = (r->parsed_uri.port != 0)
? r->parsed_uri.port
: ap_default_port_for_request(r);
path = ap_pstrdup(p, r->parsed_uri.path);
path = (path != NULL && path[0] !=
'\0'
) ? &path[1] :
""
;
if
((password = ap_table_get(r->headers_in,
"Authorization"
)) != NULL
&& strcasecmp(ap_getword(r->pool, &password,
' '
),
"Basic"
) == 0
&& (password = ap_pbase64decode(r->pool, password))[0] !=
':'
) {
user = ap_getword_nulls (r->connection->pool, &password,
':'
);
r->connection->ap_auth_type =
"Basic"
;
r->connection->user = r->parsed_uri.user = user;
nocache = 1;
}
else
if
((user = r->parsed_uri.user) != NULL) {
user = ap_pstrdup(p, user);
decodeenc(user);
if
((password = r->parsed_uri.password) != NULL) {
char
*tmp = ap_pstrdup(p, password);
decodeenc(tmp);
password = tmp;
}
nocache = 1;
}
else
{
user =
"anonymous"
;
password =
"apache_proxy@"
;
}
destaddr.s_addr = ap_inet_addr(host);
for
(i = 0; i < conf->noproxies->nelts; i++) {
if
(destaddr.s_addr == npent[i].addr.s_addr ||
(npent[i].name != NULL &&
(npent[i].name[0] ==
'*'
||
strstr
(host, npent[i].name) != NULL)))
return
ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked"
);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: connect to %s:%d"
, host, port);
parms =
strchr
(path,
';'
);
if
(parms != NULL)
*(parms++) =
'\0'
;
memset
(&server, 0,
sizeof
(
struct
sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons((unsigned
short
)port);
err = ap_proxy_host2addr(host, &server_hp);
if
(err != NULL)
return
ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if
(sock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating socket"
);
return
HTTP_INTERNAL_SERVER_ERROR;
}
#if !defined(TPF) && !defined(BEOS)
if
(conf->recv_buffer_size > 0
&& setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
(
const
char
*) &conf->recv_buffer_size,
sizeof
(
int
))
== -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"
);
}
#endif
if
(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (
void
*) &one,
sizeof
(one)) == -1) {
#ifndef _OSD_POSIX /* BS2000 has this option "always on" */
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)"
);
ap_pclosesocket(p, sock);
return
HTTP_INTERNAL_SERVER_ERROR;
#endif /*_OSD_POSIX*/
}
#ifdef SINIX_D_RESOLVER_BUG
{
struct
in_addr *ip_addr = (
struct
in_addr *) *server_hp.h_addr_list;
for
(; ip_addr->s_addr != 0; ++ip_addr) {
memcpy
(&server.sin_addr, ip_addr,
sizeof
(
struct
in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if
(i == 0)
break
;
}
}
#else
j = 0;
while
(server_hp.h_addr_list[j] != NULL) {
memcpy
(&server.sin_addr, server_hp.h_addr_list[j],
sizeof
(
struct
in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if
(i == 0)
break
;
j++;
}
#endif
if
(i == -1) {
ap_pclosesocket(p, sock);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
"Could not connect to remote machine: "
,
strerror
(
errno
), NULL));
}
c->req_time =
time
(NULL);
f = ap_bcreate(p, B_RDWR | B_SOCKET);
ap_bpushfd(f, sock, sock);
#ifdef CHARSET_EBCDIC
ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
#endif /*CHARSET_EBCDIC*/
ap_hard_timeout(
"proxy ftp"
, r);
i = ftp_getrc_msg(f, resp,
sizeof
resp);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
#if 0
if
(i == 120) {
ap_kill_timeout(r);
ap_set_header(
"Retry-After"
, ap_psprintf(p,
"%u"
, 60*wait_mins);
return
ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp);
}
#endif
if
(i != 220) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY, resp);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: connected."
);
ap_bvputs(f,
"USER "
, user, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: USER %s"
, user);
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i == 530) {
ap_kill_timeout(r);
return
ftp_unauthorized (r, 1);
}
if
(i != 230 && i != 331) {
ap_kill_timeout(r);
return
HTTP_BAD_GATEWAY;
}
if
(i == 331) {
if
(password == NULL) {
return
ftp_unauthorized (r, 0);
}
ap_bvputs(f,
"PASS "
, password, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: PASS %s"
, password);
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i == 332) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_UNAUTHORIZED,
"Need account for login"
);
}
if
(i == 530) {
ap_kill_timeout(r);
return
ftp_unauthorized (r, 1);
}
if
(i != 230 && i != 202) {
ap_kill_timeout(r);
return
HTTP_BAD_GATEWAY;
}
}
for
(;;) {
strp =
strchr
(path,
'/'
);
if
(strp == NULL)
break
;
*strp =
'\0'
;
len = decodeenc(path);
ap_bvputs(f,
"CWD "
, path, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: CWD %s"
, path);
*strp =
'/'
;
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i == 550) {
ap_kill_timeout(r);
return
HTTP_NOT_FOUND;
}
if
(i != 250) {
ap_kill_timeout(r);
return
HTTP_BAD_GATEWAY;
}
path = strp + 1;
}
if
(parms != NULL &&
strncmp
(parms,
"type="
, 5) == 0) {
parms += 5;
if
((parms[0] !=
'd'
&& parms[0] !=
'a'
&& parms[0] !=
'i'
) ||
parms[1] !=
'\0'
)
parms =
""
;
}
else
parms =
""
;
if
(parms[0] !=
'a'
) {
ap_bputs(
"TYPE I"
CRLF, f);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: TYPE I"
);
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i != 200 && i != 504) {
ap_kill_timeout(r);
return
HTTP_BAD_GATEWAY;
}
if
(i == 504)
parms[0] =
'\0'
;
}
dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if
(dsock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating PASV socket"
);
ap_bclose(f);
ap_kill_timeout(r);
return
HTTP_INTERNAL_SERVER_ERROR;
}
#if !defined (TPF) && !defined(BEOS)
if
(conf->recv_buffer_size) {
if
(setsockopt(dsock, SOL_SOCKET, SO_RCVBUF,
(
const
char
*) &conf->recv_buffer_size,
sizeof
(
int
)) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"
);
}
}
#endif
ap_bputs(
"PASV"
CRLF, f);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: PASV command issued"
);
i = ap_bgets(pasv,
sizeof
(pasv), f);
if
(i == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
"PASV: control connection is toast"
);
ap_pclosesocket(p, dsock);
ap_bclose(f);
ap_kill_timeout(r);
return
HTTP_INTERNAL_SERVER_ERROR;
}
else
{
pasv[i - 1] =
'\0'
;
pstr =
strtok
(pasv,
" "
);
if
(pstr != NULL) {
presult =
atoi
(pstr);
if
(*(pstr +
strlen
(pstr) + 1) ==
'='
)
pstr +=
strlen
(pstr) + 2;
else
{
pstr =
strtok
(NULL,
"("
);
if
(pstr != NULL)
pstr =
strtok
(NULL,
")"
);
}
}
else
presult =
atoi
(pasv);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, presult);
if
(presult == 227 && pstr != NULL && (
sscanf
(pstr,
"%d,%d,%d,%d,%d,%d"
, &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0;
pport = (p1 << 8) + p0;
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: contacting host %d.%d.%d.%d:%d"
,
h3, h2, h1, h0, pport);
data_addr.sin_family = AF_INET;
data_addr.sin_addr.s_addr = htonl(paddr);
data_addr.sin_port = htons(pport);
i = ap_proxy_doconnect(dsock, &data_addr, r);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
ap_pstrcat(r->pool,
"Could not connect to remote machine: "
,
strerror
(
errno
), NULL));
}
else
{
pasvmode = 1;
}
}
else
ap_pclosesocket(p, dsock);
}
if
(!pasvmode) {
clen =
sizeof
(
struct
sockaddr_in);
if
(getsockname(sock, (
struct
sockaddr *) &server, &clen) < 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error getting socket address"
);
ap_bclose(f);
ap_kill_timeout(r);
return
HTTP_INTERNAL_SERVER_ERROR;
}
dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if
(dsock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating socket"
);
ap_bclose(f);
ap_kill_timeout(r);
return
HTTP_INTERNAL_SERVER_ERROR;
}
if
(setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (
void
*) &one,
sizeof
(one)) == -1) {
#ifndef _OSD_POSIX /* BS2000 has this option "always on" */
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error setting reuseaddr option"
);
ap_pclosesocket(p, dsock);
ap_bclose(f);
ap_kill_timeout(r);
return
HTTP_INTERNAL_SERVER_ERROR;
#endif /*_OSD_POSIX*/
}
if
(bind(dsock, (
struct
sockaddr *) &server,
sizeof
(
struct
sockaddr_in)) == -1) {
char
buff[22];
ap_snprintf(buff,
sizeof
(buff),
"%s:%d"
, inet_ntoa(server.sin_addr), server.sin_port);
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error binding to ftp data socket %s"
, buff);
ap_bclose(f);
ap_pclosesocket(p, dsock);
return
HTTP_INTERNAL_SERVER_ERROR;
}
listen(dsock, 2);
}
len = decodeenc(path);
if
(len == 0) {
parms =
"d"
;
}
else
{
ap_bvputs(f,
"SIZE "
, path, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: SIZE %s"
, path);
i = ftp_getrc_msg(f, resp,
sizeof
resp);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d with response %s"
, i, resp);
if
(i != 500) {
if
(i == 550) {
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: SIZE shows this is a directory"
);
parms =
"d"
;
ap_bvputs(f,
"CWD "
, path, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: CWD %s"
, path);
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
if
(i == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i == 550) {
ap_kill_timeout(r);
return
HTTP_NOT_FOUND;
}
if
(i != 250) {
ap_kill_timeout(r);
return
HTTP_BAD_GATEWAY;
}
path =
""
;
len = 0;
}
else
if
(i == 213) {
for
(j = 0; j <
sizeof
resp && ap_isdigit(resp[j]); j++)
;
resp[j] =
'\0'
;
if
(resp[0] !=
'\0'
)
size = ap_pstrdup(p, resp);
}
}
}
#ifdef AUTODETECT_PWD
ap_bvputs(f,
"PWD"
, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: PWD"
);
i = ftp_getrc_msg(f, resp,
sizeof
resp);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: PWD returned status %d"
, i);
if
(i == -1 || i == 421) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i == 550) {
ap_kill_timeout(r);
return
HTTP_NOT_FOUND;
}
if
(i == 257) {
const
char
*dirp = resp;
cwd = ap_getword_conf(r->pool, &dirp);
}
#endif /*AUTODETECT_PWD*/
if
(parms[0] ==
'd'
) {
if
(len != 0)
ap_bvputs(f,
"LIST "
, path, CRLF, NULL);
else
ap_bputs(
"LIST -lag"
CRLF, f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: LIST %s"
, (len == 0 ?
""
: path));
}
else
{
ap_bvputs(f,
"RETR "
, path, CRLF, NULL);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: RETR %s"
, path);
}
ap_bflush(f);
rc = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, rc);
if
(rc == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(rc == 550) {
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: RETR failed, trying LIST instead"
);
parms =
"d"
;
ap_bvputs(f,
"CWD "
, path, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: CWD %s"
, path);
rc = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, rc);
if
(rc == -1) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(rc == 550) {
ap_kill_timeout(r);
return
HTTP_NOT_FOUND;
}
if
(rc != 250) {
ap_kill_timeout(r);
return
HTTP_BAD_GATEWAY;
}
#ifdef AUTODETECT_PWD
ap_bvputs(f,
"PWD"
, CRLF, NULL);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: PWD"
);
i = ftp_getrc_msg(f, resp,
sizeof
resp);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: PWD returned status %d"
, i);
if
(i == -1 || i == 421) {
ap_kill_timeout(r);
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
if
(i == 550) {
ap_kill_timeout(r);
return
HTTP_NOT_FOUND;
}
if
(i == 257) {
const
char
*dirp = resp;
cwd = ap_getword_conf(r->pool, &dirp);
}
#endif /*AUTODETECT_PWD*/
ap_bputs(
"LIST -lag"
CRLF, f);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: LIST -lag"
);
rc = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, rc);
if
(rc == -1)
return
ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"
);
}
ap_kill_timeout(r);
if
(rc != 125 && rc != 150 && rc != 226 && rc != 250)
return
HTTP_BAD_GATEWAY;
r->status = HTTP_OK;
r->status_line =
"200 OK"
;
resp_hdrs = ap_make_table(p, 2);
c->hdrs = resp_hdrs;
ap_table_setn(resp_hdrs,
"Date"
, ap_gm_timestr_822(r->pool, r->request_time));
ap_table_setn(resp_hdrs,
"Server"
, ap_get_server_version());
if
(parms[0] ==
'd'
) {
ap_table_setn(resp_hdrs,
"Content-Type"
,
"text/html"
);
#ifdef CHARSET_EBCDIC
r->ebcdic.conv_out = 1;
#endif
}
else
{
#ifdef CHARSET_EBCDIC
r->ebcdic.conv_out = 0;
#endif
if
(r->content_type != NULL) {
ap_table_setn(resp_hdrs,
"Content-Type"
, r->content_type);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: Content-Type set to %s"
, r->content_type);
}
else
{
ap_table_setn(resp_hdrs,
"Content-Type"
, ap_default_type(r));
}
if
(parms[0] !=
'a'
&& size != NULL) {
ap_table_set(resp_hdrs,
"Content-Length"
, size);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: Content-Length set to %s"
, size);
}
}
if
(r->content_encoding != NULL && r->content_encoding[0] !=
'\0'
) {
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: Content-Encoding set to %s"
, r->content_encoding);
ap_table_setn(resp_hdrs,
"Content-Encoding"
, r->content_encoding);
}
if
(nocache == 0) {
for
(i = 0; i < conf->nocaches->nelts; i++) {
if
(destaddr.s_addr == ncent[i].addr.s_addr ||
(ncent[i].name != NULL &&
(ncent[i].name[0] ==
'*'
||
strstr
(host, ncent[i].name) != NULL))) {
nocache = 1;
break
;
}
}
}
i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache);
if
(i != DECLINED) {
ap_pclosesocket(p, dsock);
ap_bclose(f);
return
i;
}
if
(!pasvmode) {
ap_hard_timeout(
"proxy ftp data connect"
, r);
clen =
sizeof
(
struct
sockaddr_in);
do
csd = accept(dsock, (
struct
sockaddr *) &server, &clen);
while
(csd == -1 &&
errno
== EINTR);
if
(csd == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: failed to accept data connection"
);
ap_pclosesocket(p, dsock);
ap_bclose(f);
ap_kill_timeout(r);
if
(c != NULL)
c = ap_proxy_cache_error(c);
return
HTTP_BAD_GATEWAY;
}
ap_note_cleanups_for_socket(p, csd);
data = ap_bcreate(p, B_RDWR | B_SOCKET);
ap_bpushfd(data, csd, -1);
ap_kill_timeout(r);
}
else
{
data = ap_bcreate(p, B_RDWR | B_SOCKET);
ap_bpushfd(data, dsock, dsock);
}
ap_hard_timeout(
"proxy receive"
, r);
ap_proxy_write_headers(c, ap_pstrcat(p,
"HTTP/1.1 "
, r->status_line, NULL), resp_hdrs);
ap_overlap_tables(r->headers_out, resp_hdrs, AP_OVERLAP_TABLES_SET);
ap_table_setn(r->headers_out,
"X-Cache"
,
ap_pstrcat(r->pool,
"MISS from "
,
ap_get_server_name(r), NULL));
r->content_type = ap_table_get (r->headers_out,
"Content-Type"
);
ap_send_http_header(r);
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out);
#endif
if
(!r->header_only) {
if
(parms[0] !=
'd'
) {
if
(c != NULL)
c->cache_completion = 0;
ap_proxy_send_fb(data, r, c, -1, 0);
}
else
send_dir(data, r, c, cwd);
if
(rc == 125 || rc == 150)
rc = ftp_getrc(f);
if
(rc != 226 && rc != 250)
c = ap_proxy_cache_error(c);
}
else
{
ap_bputs(
"ABOR"
CRLF, f);
ap_bflush(f);
if
(!pasvmode)
ap_bclose(data);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: ABOR"
);
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: returned status %d"
, i);
}
ap_kill_timeout(r);
ap_proxy_cache_tidy(c);
ap_bputs(
"QUIT"
CRLF, f);
ap_bflush(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: QUIT"
);
i = ftp_getrc(f);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
"FTP: QUIT: status %d"
, i);
if
(pasvmode)
ap_bclose(data);
ap_bclose(f);
ap_rflush(r);
ap_proxy_garbage_coll(r);
return
OK;
}