Skip to content

Commit

Permalink
Merge pull request #181 from Castaglia/proxy-abor-issue78
Browse files Browse the repository at this point in the history
Issue #78: Properly handle/proxy any ABOR commands from frontend to b…
  • Loading branch information
Castaglia authored Oct 31, 2020
2 parents 8fa323d + c0f1afd commit 9520536
Show file tree
Hide file tree
Showing 5 changed files with 550 additions and 154 deletions.
3 changes: 2 additions & 1 deletion include/proxy/ftp/ctrl.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy FTP control conn API
* Copyright (c) 2012-2016 TJ Saunders
* Copyright (c) 2012-2020 TJ Saunders
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -35,6 +35,7 @@ int proxy_ftp_ctrl_handle_async(pool *p, conn_t *backend_conn,

pr_response_t *proxy_ftp_ctrl_recv_resp(pool *p, conn_t *ctrl_conn,
unsigned int *resp_nlines, int flags);
int proxy_ftp_ctrl_send_abort(pool *p, conn_t *ctrl_conn, cmd_rec *cmd);
int proxy_ftp_ctrl_send_cmd(pool *p, conn_t *ctrl_conn, cmd_rec *cmd);
int proxy_ftp_ctrl_send_resp(pool *p, conn_t *ctrl_conn, pr_response_t *resp,
unsigned int resp_nlines);
Expand Down
81 changes: 80 additions & 1 deletion lib/proxy/ftp/ctrl.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy FTP control conn routines
* Copyright (c) 2012-2016 TJ Saunders
* Copyright (c) 2012-2020 TJ Saunders
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -26,6 +26,7 @@

#include "proxy/netio.h"
#include "proxy/ftp/ctrl.h"
#include "proxy/tls.h"

static const char *trace_channel = "proxy.ftp.ctrl";

Expand Down Expand Up @@ -366,6 +367,84 @@ pr_response_t *proxy_ftp_ctrl_recv_resp(pool *p, conn_t *ctrl_conn,
return resp;
}

#ifndef TELNET_DM
# define TELNET_DM 242
#endif /* TELNET_DM */

#ifndef TELNET_IAC
# define TELNET_IAC 255
#endif /* TELNET_IAC */

#ifndef TELNET_IP
# define TELNET_IP 244
#endif /* TELNET_IP */

int proxy_ftp_ctrl_send_abort(pool *p, conn_t *ctrl_conn, cmd_rec *cmd) {
int fd, res, use_tls, xerrno;
unsigned char buf[7];

if (p == NULL ||
ctrl_conn == NULL ||
cmd == NULL) {
errno = EINVAL;
return -1;
}

/* If we are proxying the ABOR command, preface it with the Telnet "Sync"
* mechanism, using OOB data. If the receiving server supports this, it can
* generate a signal to interrupt any IO occurring on the backend server
* (such as when sendfile(2) is used).
*
* Note that such Telnet codes can only be used if we are NOT using TLS
* on the backend control connection.
*/
use_tls = proxy_tls_using_tls();
if (use_tls != PROXY_TLS_ENGINE_OFF) {
return proxy_ftp_ctrl_send_cmd(p, ctrl_conn, cmd);
}

fd = PR_NETIO_FD(ctrl_conn->outstrm);

buf[0] = TELNET_IAC;
buf[1] = TELNET_IP;
buf[2] = TELNET_IAC;

pr_trace_msg(trace_channel, 9,
"sending Telnet abort code out-of-band to backend");
res = send(fd, &buf, 3, MSG_OOB);
xerrno = errno;

if (res < 0) {
pr_trace_msg(trace_channel, 1,
"error sending Telnet abort code out-of-band to backend: %s",
strerror(xerrno));
errno = xerrno;
return -1;
}

buf[0] = TELNET_DM;
buf[1] = 'A';
buf[2] = 'B';
buf[3] = 'O';
buf[4] = 'R';
buf[5] = '\r';
buf[6] = '\n';

pr_trace_msg(trace_channel, 9,
"proxied %s command from frontend to backend", (char *) cmd->argv[0]);
res = send(fd, &buf, 7, 0);
xerrno = errno;

if (res < 0) {
pr_trace_msg(trace_channel, 1,
"error sending Telnet DM code to backend: %s", strerror(xerrno));
errno = xerrno;
return -1;
}

return 0;
}

int proxy_ftp_ctrl_send_cmd(pool *p, conn_t *ctrl_conn, cmd_rec *cmd) {
int res;

Expand Down
139 changes: 112 additions & 27 deletions mod_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,28 +84,12 @@ static void proxy_timeoutidle_ev(const void *, void *);
static void proxy_timeoutnoxfer_ev(const void *, void *);
static void proxy_timeoutstalled_ev(const void *, void *);

MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
static int recv_resp(cmd_rec *cmd, struct proxy_session *proxy_sess,
pr_response_t **rp) {
int res, xerrno = 0;
pr_response_t *resp;
unsigned int resp_nlines = 0;

res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
cmd);
if (res < 0) {
xerrno = errno;
(void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
"error sending %s to backend: %s", (char *) cmd->argv[0],
strerror(xerrno));

pr_response_add_err(R_500, _("%s: %s"), (char *) cmd->argv[0],
strerror(xerrno));
pr_response_flush(&resp_err_list);

errno = xerrno;
return PR_ERROR(cmd);
}

resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
&resp_nlines, 0);
if (resp == NULL) {
Expand Down Expand Up @@ -133,7 +117,7 @@ MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
pr_response_flush(&resp_err_list);

errno = xerrno;
return PR_ERROR(cmd);
return -1;
}

res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn,
Expand All @@ -143,15 +127,90 @@ MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,

pr_response_block(TRUE);
errno = xerrno;
return -1;
}

if (rp != NULL) {
*rp = resp;
}

return 0;
}

MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
pr_response_t **rp) {
int res, xerrno = 0;

res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
cmd);
xerrno = errno;

if (res < 0) {
xerrno = errno;
(void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
"error sending %s to backend: %s", (char *) cmd->argv[0],
strerror(xerrno));

pr_response_add_err(R_500, _("%s: %s"), (char *) cmd->argv[0],
strerror(xerrno));
pr_response_flush(&resp_err_list);

errno = xerrno;
return PR_ERROR(cmd);
}

if (recv_resp(cmd, proxy_sess, rp) < 0) {
return PR_ERROR(cmd);
}

pr_response_block(TRUE);
return PR_HANDLED(cmd);
}

if (rp != NULL) {
*rp = resp;
MODRET proxy_abort(cmd_rec *cmd, struct proxy_session *proxy_sess,
pr_response_t **rp) {
int res, xerrno = 0;

res = proxy_ftp_ctrl_send_abort(cmd->tmp_pool,
proxy_sess->backend_ctrl_conn, cmd);
xerrno = errno;

if (res < 0) {
(void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
"error sending %s to backend: %s", (char *) cmd->argv[0],
strerror(xerrno));

pr_response_add_err(R_500, _("%s: %s"), (char *) cmd->argv[0],
strerror(xerrno));
pr_response_flush(&resp_err_list);

errno = xerrno;
return PR_ERROR(cmd);
}

if (proxy_sess->backend_data_conn != NULL) {
pr_trace_msg(trace_channel, 19, "received ABOR on frontend connection, "
"closing backend data connection");
proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
proxy_sess->backend_data_conn = NULL;
}

if (recv_resp(cmd, proxy_sess, rp) < 0) {
return PR_ERROR(cmd);
}

/* The ABOR command might have two responses, as when there is a data
* transfer in progress: one response for the data transfer, and one for
* handling the ABOR command itself.
*/
if ((proxy_sess->frontend_sess_flags & SF_XFER) ||
(proxy_sess->backend_sess_flags & SF_XFER)) {
if (recv_resp(cmd, proxy_sess, rp) < 0) {
return PR_ERROR(cmd);
}
}

pr_response_block(TRUE);
return PR_HANDLED(cmd);
}

Expand Down Expand Up @@ -2525,19 +2584,28 @@ MODRET proxy_data(struct proxy_session *proxy_sess, cmd_rec *cmd) {
continue;
}

#if 0
/* Any commands from the frontend client take priority */

/* NOTE: This is temporarily disabled, until I can better handle an
* ABOR command on the frontend control connection whilst in the middle
* of a data transfer.
*/
if (frontend_ctrlfd >= 0 &&
FD_ISSET(frontend_ctrlfd, &rfds)) {
proxy_process_cmd();
pr_response_block(FALSE);

/* Check for closed frontend/backend data connections, as from an ABOR
* command.
*/
if (session.d == NULL ||
proxy_sess->backend_data_conn == NULL) {
if (pr_data_get_timeout(PR_DATA_TIMEOUT_STALLED) > 0) {
pr_timer_remove(PR_TIMER_STALLED, ANY_MODULE);
}

pr_throttle_pause(bytes_transferred, TRUE);
pr_response_clear(&resp_list);
pr_response_clear(&resp_err_list);

return PR_HANDLED(cmd);
}
}
#endif

if (src_data_conn != NULL &&
datafd >= 0 &&
Expand Down Expand Up @@ -4357,6 +4425,23 @@ MODRET proxy_any(cmd_rec *cmd) {
return PR_DECLINED(cmd);
}
break;

case PR_CMD_ABOR_ID:
mr = proxy_abort(cmd, proxy_sess, NULL);
if ((proxy_sess->frontend_sess_flags & SF_XFER) ||
(proxy_sess->backend_sess_flags & SF_XFER)) {
pr_trace_msg(trace_channel, 19, "received ABOR on frontend connection, "
"closing frontend data connection");

if (session.d != NULL) {
pr_inet_close(session.pool, proxy_sess->frontend_data_conn);
proxy_sess->frontend_data_conn = session.d = NULL;
}

proxy_sess->frontend_sess_flags &= ~SF_XFER;
proxy_sess->backend_sess_flags &= ~SF_XFER;
}
return mr;
}

/* If we are not connected to a backend server, then don't try to proxy
Expand Down
Loading

0 comments on commit 9520536

Please sign in to comment.