END_TEST START_TEST (parse_ext_addr_test) { const pr_netaddr_t *addr, *res; const char *msg; res = proxy_ftp_msg_parse_ext_addr(NULL, NULL, NULL, 0, NULL); fail_unless(res == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); res = proxy_ftp_msg_parse_ext_addr(p, NULL, NULL, 0, NULL); fail_unless(res == NULL, "Failed to handle null msg"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "foo"; res = proxy_ftp_msg_parse_ext_addr(p, msg, NULL, 0, NULL); fail_unless(res == NULL, "Failed to handle null addr"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL); fail_unless(addr != NULL, "Failed to get address for 127.0.0.1: %s", strerror(errno)); res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, 0, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); /* EPSV response formats */ res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad EPSV response"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(foo"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad EPSV response"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(foo)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(1)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(|4)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(|0)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(|1)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); /* Where the network protocol matches that of the address... */ msg = "(|1|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle badly formatted message"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(|1|1.2.3.4)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle badly formatted message"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(|1|1.2.3.4|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle badly formatted message"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|1|1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "all"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "1"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(|||5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); fail_unless( strcmp(pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)) == 0, "Expected '%s', got '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)); /* ...and where the network protocol does not match that of the address. */ msg = "(||::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2|1.2.3.4|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); #ifdef PR_USE_IPV6 addr = pr_netaddr_get_addr(p, "::1", NULL); fail_unless(addr != NULL, "Failed to get address for ::1: %s", strerror(errno)); msg = "(|2|1.2.3.4|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|1|::1|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2|::1|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2|::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(|2|::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "ALL"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "2"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(|||5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); fail_unless( strcmp(pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)) == 0, "Expected '%s', got '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); #endif /* PR_USE_IPV6 */ }
const pr_netaddr_t *proxy_ftp_xfer_prepare_passive(int policy_id, cmd_rec *cmd, const char *error_code, struct proxy_session *proxy_sess, int flags) { int res, xerrno = 0; cmd_rec *pasv_cmd; const pr_netaddr_t *remote_addr = NULL; pr_response_t *resp; unsigned int resp_nlines = 0; unsigned short remote_port; char *passive_cmd, *passive_respcode = NULL; if (cmd == NULL || error_code == NULL || proxy_sess == NULL || proxy_sess->backend_ctrl_conn == NULL) { errno = EINVAL; return NULL; } /* Whether we send a PASV (and expect 227) or an EPSV (and expect 229) * need to depend on the policy_id. */ switch (policy_id) { case PR_CMD_PASV_ID: passive_cmd = C_PASV; break; case PR_CMD_EPSV_ID: /* If the remote host does not mention EPSV in its features, fall back * to using PASV. */ passive_cmd = C_EPSV; if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPSV not supported by backend server (via FEAT), using PASV"); if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } passive_cmd = C_PASV; policy_id = PR_CMD_PASV_ID; } break; default: /* In this case, the cmd we were given is the one we should send to * the backend server -- but only if it is either EPSV or PASV. */ if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) != 0 && pr_cmd_cmp(cmd, PR_CMD_PASV_ID) != 0) { pr_trace_msg(trace_channel, 9, "illegal FTP passive transfer command '%s'", (char *) cmd->argv[0]); errno = EINVAL; return NULL; } passive_cmd = cmd->argv[0]; if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) { /* If the remote host does not mention EPSV in its features, fall back * to using PASV. */ if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPSV not supported by backend server (via FEAT), using PASV"); if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } passive_cmd = C_PASV; policy_id = PR_CMD_PASV_ID; } } break; } pasv_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, passive_cmd); switch (pr_cmd_get_id(pasv_cmd->argv[0])) { case PR_CMD_PASV_ID: passive_respcode = R_227; break; case PR_CMD_EPSV_ID: passive_respcode = R_229; break; } res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, pasv_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) pasv_cmd->argv[0], strerror(xerrno)); pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines, flags); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) pasv_cmd->argv[0], strerror(xerrno)); pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } /* We specifically expect a 227 or 229 response code here; anything else is * an error. Right? */ if (strncmp(resp->num, passive_respcode, 4) != 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received response code %s, but expected %s for %s command", resp->num, passive_respcode, (char *) pasv_cmd->argv[0]); if (policy_id == PR_CMD_EPSV_ID) { /* If using EPSV failed, try again using PASV, and switch the * DataTransferPolicy (if EPSV) to be PASV, for future attempts. */ if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { pr_trace_msg(trace_channel, 15, "falling back from EPSV to PASV DataTransferPolicy"); proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } return proxy_ftp_xfer_prepare_passive(PR_CMD_PASV_ID, cmd, error_code, proxy_sess, flags); } res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); errno = EPERM; return NULL; } switch (pr_cmd_get_id(pasv_cmd->argv[0])) { case PR_CMD_PASV_ID: remote_addr = proxy_ftp_msg_parse_addr(cmd->tmp_pool, resp->msg, pr_netaddr_get_family(session.c->local_addr)); break; case PR_CMD_EPSV_ID: remote_addr = proxy_ftp_msg_parse_ext_addr(cmd->tmp_pool, resp->msg, session.c->remote_addr, PR_CMD_EPSV_ID, NULL); break; } if (remote_addr == NULL) { xerrno = errno; pr_trace_msg(trace_channel, 2, "error parsing %s response '%s': %s", (char *) pasv_cmd->argv[0], resp->msg, strerror(xerrno)); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } remote_port = ntohs(pr_netaddr_get_port(remote_addr)); /* Make sure that the given address matches the address to which we * originally connected. */ if (pr_netaddr_cmp(remote_addr, proxy_sess->backend_ctrl_conn->remote_addr) != 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "Refused %s address %s (address mismatch with %s)", (char *) pasv_cmd->argv[0], pr_netaddr_get_ipstr(remote_addr), pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr)); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } if (remote_port < 1024) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "Refused %s port %hu (below 1024)", (char *) pasv_cmd->argv[0], remote_port); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } return remote_addr; }