END_TEST START_TEST (inet_connect_ipv6_test) { #ifdef PR_USE_IPV6 int res; conn_t *conn; const pr_netaddr_t *addr; unsigned char use_ipv6; use_ipv6 = pr_netaddr_use_ipv6(); pr_netaddr_enable_ipv6(); pr_inet_set_default_family(p, AF_INET6); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); addr = pr_netaddr_get_addr(p, "::1", NULL); fail_unless(addr != NULL, "Failed to resolve '::1': %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, addr, 80); fail_unless(res < 0, "Connected to 127.0.0.1#80 unexpectedly"); fail_unless(errno == ECONNREFUSED || errno == ENETUNREACH, "Expected ECONNREFUSED (%d) or ENETUNREACH (%d), got %s (%d)", ECONNREFUSED, ENETUNREACH, strerror(errno), errno); proxy_inet_close(p, conn); /* Try connecting to Google's DNS server. */ conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); addr = pr_netaddr_get_addr(p, "2001:4860:4860::8888", NULL); fail_unless(addr != NULL, "Failed to resolve '2001:4860:4860::8888': %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, addr, 53); if (res < 0) { /* This could be expected, e.g. if there's no route. */ fail_unless(errno == EHOSTUNREACH || errno == ENETUNREACH, "Expected EHOSTUNREACH (%d) or ENETUNREACH (%d), got %s (%d)", EHOSTUNREACH, ENETUNREACH, strerror(errno), errno); } mark_point(); proxy_inet_close(p, conn); pr_inet_set_default_family(p, AF_INET); if (use_ipv6 == FALSE) { pr_netaddr_disable_ipv6(); } #endif /* PR_USE_IPV6 */ }
END_TEST START_TEST (inet_close_test) { conn_t *conn; mark_point(); proxy_inet_close(NULL, NULL); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); mark_point(); conn->rfd = conn->wfd = 999; proxy_inet_close(NULL, conn); }
END_TEST START_TEST (inet_openrw_test) { conn_t *res, *conn; const pr_netaddr_t *addr; res = proxy_inet_openrw(NULL, NULL, NULL, PR_NETIO_STRM_CTRL, -1, -1, -1, FALSE); fail_unless(res == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); res = proxy_inet_openrw(p, NULL, NULL, PR_NETIO_STRM_CTRL, -1, -1, -1, FALSE); fail_unless(res == NULL, "Failed to handle null conn"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); conn = pr_inet_create_conn(p, -2, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); res = proxy_inet_openrw(p, conn, NULL, PR_NETIO_STRM_OTHR, -1, -1, -1, FALSE); fail_unless(res == NULL, "Failed to handle null addr"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); proxy_inet_close(p, conn); addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL); fail_unless(addr != NULL, "Failed to resolve '127.0.0.1': %s", strerror(errno)); res = proxy_inet_openrw(p, conn, addr, PR_NETIO_STRM_OTHR, -1, -1, -1, FALSE); fail_unless(res != NULL, "Failed to open rw conn: %s", strerror(errno)); proxy_inet_close(p, conn); }
END_TEST START_TEST (inet_connect_ipv4_test) { int res; conn_t *conn; const pr_netaddr_t *addr; mark_point(); res = proxy_inet_connect(NULL, NULL, NULL, 0); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); res = proxy_inet_connect(p, NULL, NULL, 0); fail_unless(res < 0, "Failed to handle null conn"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, NULL, 0); fail_unless(res < 0, "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 resolve '127.0.0.1': %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, addr, 80); fail_unless(res < 0, "Connected to 127.0.0.1#80 unexpectedly"); fail_unless(errno == ECONNREFUSED, "Expected ECONNREFUSED (%d), got '%s' (%d)", ECONNREFUSED, strerror(errno), errno); proxy_inet_close(p, conn); /* Try connecting to Google's DNS server. */ addr = pr_netaddr_get_addr(p, "8.8.8.8", NULL); fail_unless(addr != NULL, "Failed to resolve '8.8.8.8': %s", strerror(errno)); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, addr, 53); fail_if(res < 0, "Failed to connect to 8.8.8.8#53: %s", strerror(errno)); mark_point(); proxy_inet_close(p, conn); /* Now start supplying in/out streams. */ conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD); fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, addr, 53); fail_if(res < 0, "Failed to connect to 8.8.8.8#53: %s", strerror(errno)); mark_point(); proxy_inet_close(p, conn); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD); fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s", strerror(errno)); conn->outstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, -1, PR_NETIO_IO_WR); fail_unless(conn->outstrm != NULL, "Failed to open othr writing stream: %s", strerror(errno)); mark_point(); res = proxy_inet_connect(p, conn, addr, 53); fail_if(res < 0, "Failed to connect to 8.8.8.8#53: %s", strerror(errno)); mark_point(); proxy_inet_close(p, conn); }
END_TEST START_TEST (inet_listen_test) { int res; conn_t *conn; mark_point(); res = proxy_inet_listen(NULL, NULL, 0, 0); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = proxy_inet_listen(p, NULL, 0, 0); fail_unless(res < 0, "Failed to handle null conn"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); mark_point(); res = proxy_inet_listen(p, conn, 5, 0); fail_unless(res == 0, "Failed to listen on conn: %s", strerror(errno)); mark_point(); proxy_inet_close(p, conn); /* Now start providing in/out streams. */ conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD); fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s", strerror(errno)); mark_point(); res = proxy_inet_listen(p, conn, 5, 0); fail_unless(res == 0, "Failed to listen on conn: %s", strerror(errno)); mark_point(); proxy_inet_close(p, conn); conn = pr_inet_create_conn(p, -1, NULL, INPORT_ANY, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); conn->instrm = pr_netio_open(p, PR_NETIO_STRM_CTRL, -1, PR_NETIO_IO_RD); fail_unless(conn->instrm != NULL, "Failed to open ctrl reading stream: %s", strerror(errno)); conn->outstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, -1, PR_NETIO_IO_WR); fail_unless(conn->outstrm != NULL, "Failed to open othr writing stream: %s", strerror(errno)); mark_point(); res = proxy_inet_listen(p, conn, 5, 0); fail_unless(res == 0, "Failed to listen on conn: %s", strerror(errno)); mark_point(); proxy_inet_close(p, conn); }
int proxy_ftp_xfer_prepare_active(int policy_id, cmd_rec *cmd, const char *error_code, struct proxy_session *proxy_sess, int flags) { int backend_family, bind_family, res, xerrno = 0; cmd_rec *actv_cmd; const pr_netaddr_t *bind_addr = NULL; pr_response_t *resp; unsigned int resp_nlines = 0; conn_t *data_conn = NULL; char *active_cmd; const char *resp_msg = NULL; if (cmd == NULL || error_code == NULL || proxy_sess == NULL || proxy_sess->backend_ctrl_conn == NULL) { errno = EINVAL; return -1; } switch (policy_id) { case PR_CMD_PORT_ID: active_cmd = C_PORT; break; case PR_CMD_EPRT_ID: /* If the remote host does not mention EPRT in its features, fall back * to using PORT. */ active_cmd = C_EPRT; if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPRT not supported by backend server (via FEAT), using PORT"); if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } active_cmd = C_PORT; policy_id = PR_CMD_PORT_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 EPRT or PORT. */ if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 && pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0) { pr_trace_msg(trace_channel, 9, "illegal FTP active transfer command '%s'", (char *) cmd->argv[0]); errno = EINVAL; return -1; } active_cmd = cmd->argv[0]; if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0) { /* If the remote host does not mention EPRT in its features, fall back * to using PORT. */ if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPRT not supported by backend server (via FEAT), using PORT"); if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } active_cmd = C_PORT; policy_id = PR_CMD_PORT_ID; } } break; } bind_addr = proxy_sess->src_addr; if (bind_addr == NULL) { bind_addr = session.c->local_addr; } if (pr_netaddr_is_loopback(bind_addr) == TRUE && pr_netaddr_is_loopback(proxy_sess->backend_ctrl_conn->remote_addr) != TRUE) { const char *local_name; const pr_netaddr_t *local_addr; local_name = pr_netaddr_get_localaddr_str(cmd->pool); local_addr = pr_netaddr_get_addr(cmd->pool, local_name, NULL); if (local_addr != NULL) { pr_trace_msg(trace_channel, 14, "%s is a loopback address, using %s instead", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(local_addr)); bind_addr = local_addr; } } /* Need to check the family of the bind addr against the family of the * backend server. */ backend_family = pr_netaddr_get_family(proxy_sess->backend_ctrl_conn->remote_addr); bind_family = pr_netaddr_get_family(bind_addr); if (bind_family == backend_family) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { /* Make sure that the family is NOT IPv6, even though the local and * remote families match. The PORT command cannot be used for IPv6 * addresses -- but EPRT CAN be used for IPv6 addresses. */ if (bind_family == AF_INET6 && policy_id == PR_CMD_PORT_ID) { pr_netaddr_t *mapped_addr; mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr); if (mapped_addr != NULL) { pr_trace_msg(trace_channel, 9, "converting local IPv6 address '%s' to IPv4 address '%s' for " "active transfer using PORT", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(mapped_addr)); bind_addr = mapped_addr; } } } #endif /* PR_USE_IPV6 */ } else { if (backend_family == AF_INET) { pr_netaddr_t *mapped_addr; /* In this scenario, the remote peer is an IPv4 (or IPv4-mapped IPv6) * peer, so make sure we use an IPv4 local address. */ mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr); if (mapped_addr != NULL) { pr_trace_msg(trace_channel, 9, "converting local IPv6 address '%s' to IPv4 address '%s' for " "active transfer with IPv4 peer", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(mapped_addr)); bind_addr = mapped_addr; } } } if (proxy_sess->backend_data_conn != NULL) { /* Make sure that we only have one backend data connection. */ proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; } data_conn = proxy_ftp_conn_listen(cmd->tmp_pool, bind_addr, FALSE); if (data_conn == NULL) { xerrno = errno; pr_response_add_err(error_code, _("Unable to build data connection: Internal error")); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } proxy_sess->backend_data_conn = data_conn; switch (pr_cmd_get_id(active_cmd)) { case PR_CMD_PORT_ID: resp_msg = proxy_ftp_msg_fmt_addr(cmd->tmp_pool, data_conn->local_addr, data_conn->local_port, FALSE); break; case PR_CMD_EPRT_ID: resp_msg = proxy_ftp_msg_fmt_ext_addr(cmd->tmp_pool, data_conn->local_addr, data_conn->local_port, PR_CMD_EPRT_ID, FALSE); break; } actv_cmd = pr_cmd_alloc(cmd->tmp_pool, 2, active_cmd, resp_msg); actv_cmd->arg = (char *) resp_msg; pr_cmd_clear_cache(actv_cmd); res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, actv_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) actv_cmd->argv[0], strerror(xerrno)); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } 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 *) actv_cmd->argv[0], strerror(xerrno)); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } if (resp->num[0] != '2') { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received non-2xx response from backend for %s: %s %s", (char *) actv_cmd->argv[0], resp->num, resp->msg); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; if (policy_id == PR_CMD_EPRT_ID) { /* If using EPRT failed, try again using PORT, and switch the * DataTransferPolicy (if EPRT) to be PORT, for future attempts. */ if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { pr_trace_msg(trace_channel, 15, "falling back from EPRT to PORT DataTransferPolicy"); proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } return proxy_ftp_xfer_prepare_active(PR_CMD_PORT_ID, cmd, error_code, proxy_sess, flags); } pr_response_add_err(error_code, "%s", resp->msg); pr_response_flush(&resp_err_list); errno = EINVAL; return -1; } return 0; }
static int forward_connect(pool *p, struct proxy_session *proxy_sess, pr_response_t **resp, unsigned int *resp_nlines) { conn_t *server_conn = NULL; int banner_ok = TRUE, use_tls, xerrno = 0; pr_netaddr_t *dst_addr; array_header *other_addrs = NULL; dst_addr = proxy_sess->dst_addr; other_addrs = proxy_sess->other_addrs; server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr); if (server_conn == NULL) { xerrno = errno; if (other_addrs != NULL) { register unsigned int i; /* Try the other IP addresses for the requested name (if any) as well. */ for (i = 0; i < other_addrs->nelts; i++) { dst_addr = ((pr_netaddr_t **) other_addrs->elts)[i]; pr_trace_msg(trace_channel, 8, "attempting to connect to other address #%u (%s) for requested " "URI '%.100s'", i+1, pr_netaddr_get_ipstr(dst_addr), proxy_conn_get_uri(proxy_sess->dst_pconn)); server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr); if (server_conn != NULL) { proxy_sess->dst_addr = dst_addr; break; } } } if (server_conn == NULL) { xerrno = errno; /* EINVALs lead to strange-looking error responses; change them to * EPERM. */ if (xerrno == EINVAL) { xerrno = EPERM; } } errno = xerrno; return -1; } /* XXX Support/send a CLNT command of our own? Configurable via e.g. * "UserAgent" string? */ proxy_sess->frontend_ctrl_conn = session.c; proxy_sess->backend_ctrl_conn = server_conn; /* Read the response from the backend server. */ *resp = proxy_ftp_ctrl_recv_resp(p, proxy_sess->backend_ctrl_conn, resp_nlines); if (*resp == NULL) { xerrno = errno; pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to read banner from server %s:%u: %s", pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr), ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)), strerror(xerrno)); errno = EPERM; return -1; } if ((*resp)->num[0] != '2') { banner_ok = FALSE; } (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received banner from backend %s:%u%s: %s %s", pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr), ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)), banner_ok ? "" : ", DISCONNECTING", (*resp)->num, (*resp)->msg); if (banner_ok == FALSE) { pr_inet_close(p, proxy_sess->backend_ctrl_conn); proxy_sess->backend_ctrl_conn = NULL; errno = EPERM; return -1; } /* Get the features supported by the backend server */ if (proxy_ftp_sess_get_feat(p, proxy_sess) < 0) { if (errno != EPERM) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to determine features of backend server: %s", strerror(errno)); } } use_tls = proxy_tls_use_tls(); if (use_tls != PROXY_TLS_ENGINE_OFF) { if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error enabling TLS on control connection to backend server: %s", strerror(xerrno)); pr_inet_close(p, proxy_sess->backend_ctrl_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } } if (proxy_netio_postopen(server_conn->instrm) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "postopen error for backend control connection input stream: %s", strerror(xerrno)); proxy_inet_close(session.pool, server_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } if (proxy_netio_postopen(server_conn->outstrm) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "postopen error for backend control connection output stream: %s", strerror(xerrno)); proxy_inet_close(session.pool, server_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } if (use_tls != PROXY_TLS_ENGINE_OFF) { if (proxy_sess_state & PROXY_SESS_STATE_BACKEND_HAS_CTRL_TLS) { /* NOTE: should this be a fatal error? */ (void) proxy_ftp_sess_send_pbsz_prot(p, proxy_sess); } } (void) proxy_ftp_sess_send_host(p, proxy_sess); proxy_sess_state |= PROXY_SESS_STATE_CONNECTED; return 0; }