pr_ipbind_t *pr_ipbind_find(const pr_netaddr_t *addr, unsigned int port, unsigned char skip_inactive) { register unsigned int i; pr_ipbind_t *ipbind = NULL; if (addr == NULL) { errno = EINVAL; return NULL; } i = ipbind_hash_addr(addr); for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { pr_signals_handle(); if (skip_inactive && !ipbind->ib_isactive) { continue; } if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0) { if (ipbind->ib_port == 0 || port == 0 || ipbind->ib_port == port) { return ipbind; } } } errno = ENOENT; return NULL; }
int pr_ipbind_create(server_rec *server, pr_netaddr_t *addr, unsigned int port) { pr_ipbind_t *ipbind = NULL; register unsigned int i = 0; if (server == NULL|| addr == NULL) { errno = EINVAL; return -1; } i = ipbind_hash_addr(addr); /* Make sure the address is not already in use */ for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && ipbind->ib_port == port) { /* An ipbind already exists for this IP address */ pr_log_pri(PR_LOG_WARNING, "notice: '%s' (%s:%u) already bound to '%s'", server->ServerName, pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); errno = EADDRINUSE; return -1; } } if (!binding_pool) { binding_pool = make_sub_pool(permanent_pool); pr_pool_tag(binding_pool, "Bindings Pool"); } ipbind = pcalloc(server->pool, sizeof(pr_ipbind_t)); ipbind->ib_server = server; ipbind->ib_addr = addr; ipbind->ib_port = port; ipbind->ib_namebinds = NULL; ipbind->ib_isdefault = FALSE; ipbind->ib_islocalhost = FALSE; ipbind->ib_isactive = FALSE; pr_trace_msg(trace_channel, 8, "created IP binding for %s#%u, server %p", pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port, ipbind->ib_server); /* Add the ipbind to the table. */ if (ipbind_table[i]) { ipbind->ib_next = ipbind_table[i]; } ipbind_table[i] = ipbind; return 0; }
static void dynmasq_refresh(void) { server_rec *s; pr_log_debug(DEBUG2, MOD_DYNMASQ_VERSION ": resolving all MasqueradeAddress directives (could take a little while)"); for (s = (server_rec *) server_list->xas_list; s; s = s->next) { config_rec *c; c = find_config(s->conf, CONF_PARAM, "MasqueradeAddress", FALSE); if (c != NULL) { const char *masq_addr; pr_netaddr_t *na; masq_addr = c->argv[1]; pr_netaddr_clear_ipcache(masq_addr); na = pr_netaddr_get_addr(s->pool, masq_addr, NULL); if (na != NULL) { /* Compare the obtained netaddr with the one already present. * Only update the "live" netaddr if they differ. */ pr_log_debug(DEBUG2, MOD_DYNMASQ_VERSION ": resolved MasqueradeAddress '%s' to IP address %s", masq_addr, pr_netaddr_get_ipstr(na)); if (pr_netaddr_cmp(c->argv[0], na) != 0) { pr_log_pri(PR_LOG_DEBUG, MOD_DYNMASQ_VERSION ": MasqueradeAddress '%s' updated for new address %s (was %s)", masq_addr, pr_netaddr_get_ipstr(na), pr_netaddr_get_ipstr(c->argv[0])); /* Overwrite the old netaddr pointer. Note that this constitutes * a minor memory leak, as there currently isn't a way to free * the memory used by a netaddr object. Hrm. */ c->argv[0] = na; } else { pr_log_debug(DEBUG2, MOD_DYNMASQ_VERSION ": MasqueradeAddress '%s' has not changed addresses", masq_addr); } } else { pr_log_pri(PR_LOG_INFO, MOD_DYNMASQ_VERSION ": unable to resolve '%s', keeping previous address", masq_addr); } } } return; }
pr_ipbind_t *pr_ipbind_find(pr_netaddr_t *addr, unsigned int port, unsigned char skip_inactive) { pr_ipbind_t *ipbind = NULL; register unsigned int i = ipbind_hash_addr(addr); for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (skip_inactive && !ipbind->ib_isactive) { continue; } if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && (!ipbind->ib_port || ipbind->ib_port == port)) return ipbind; } return NULL; }
END_TEST START_TEST (netaddr_cmp_test) { pr_netaddr_t *addr, *addr2; int res; const char *name; res = pr_netaddr_cmp(NULL, NULL); fail_unless(res == 0, "Expected 0, got %d", res); name = "127.0.0.1"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); res = pr_netaddr_cmp(addr, NULL); fail_unless(res == 1, "Expected 1, got %d", res); res = pr_netaddr_cmp(NULL, addr); fail_unless(res == -1, "Expected -1, got %d", res); name = "::1"; addr2 = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr2 != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); res = pr_netaddr_cmp(addr, addr2); fail_unless(res == -1, "Expected -1, got %d", res); res = pr_netaddr_cmp(addr2, addr); fail_unless(res == -1, "Expected -1, got %d", res); name = "::ffff:127.0.0.1"; addr2 = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr2 != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); res = pr_netaddr_cmp(addr, addr2); fail_unless(res == 0, "Expected 0, got %d", res); res = pr_netaddr_cmp(addr2, addr); fail_unless(res == 0, "Expected 0, got %d", res); }
int pr_ipbind_close(pr_netaddr_t *addr, unsigned int port, unsigned char close_namebinds) { int res = 0; register unsigned int i = 0; if (addr) { pr_ipbind_t *ipbind = NULL; unsigned char have_ipbind = FALSE; i = ipbind_hash_addr(addr); if (ipbind_table[i] == NULL) { pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d", pr_netaddr_get_ipstr(addr), port); errno = ENOENT; return -1; } for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && (!ipbind->ib_port || ipbind->ib_port == port)) { have_ipbind = TRUE; break; } } if (!have_ipbind) { pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d", pr_netaddr_get_ipstr(addr), port); errno = ENOENT; return -1; } /* If already closed, exit now. */ if (!ipbind->ib_isactive) { errno = EPERM; return -1; } /* Close the ipbinding's listen connection, if present. The trick * here is determining whether this binding's listen member is * _the_ listening socket for the master daemon, or whether it's * been created for SocketBindTight, and can be closed. * * Actually, it's not that hard. It's only _the_ listening socket * for the master daemon in inetd mode, in which case virtual servers * can't be shutdown via ftpdctl, anyway. */ if (SocketBindTight && ipbind->ib_listener != NULL) { pr_inet_close(ipbind->ib_server->pool, ipbind->ib_listener); ipbind->ib_listener = ipbind->ib_server->listen = NULL; } /* Mark this ipbind as inactive. For SocketBindTight sockets, the * closing of the listening connection will suffice, from the clients' * point of view. However, this covers the non-SocketBindTight case, * and will prevent this binding from returning its server_rec pointer * on future lookup requests via pr_ipbind_get_server(). */ ipbind->ib_isactive = FALSE; if (close_namebinds && ipbind->ib_namebinds) { register unsigned int j = 0; pr_namebind_t **namebinds = NULL; namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; for (j = 0; j < ipbind->ib_namebinds->nelts; j++) { pr_namebind_t *nb = namebinds[j]; PR_CLOSE_NAMEBIND(nb->nb_name, nb->nb_server->addr, nb->nb_server->ServerPort); } } } else { /* A NULL addr has a special meaning: close _all_ ipbinds in the * list. */ for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { pr_ipbind_t *ipbind = NULL; for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (SocketBindTight && ipbind->ib_listener != NULL) { pr_inet_close(main_server->pool, ipbind->ib_listener); ipbind->ib_listener = ipbind->ib_server->listen = NULL; } /* Note: do not need to check if this ipbind was previously closed, * for the NULL addr is a request to shut down all ipbinds, * regardless of their current state. */ ipbind->ib_isactive = FALSE; if (close_namebinds && ipbind->ib_namebinds) { register unsigned int j = 0; pr_namebind_t **namebinds = NULL; namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; for (j = 0; j < ipbind->ib_namebinds->nelts; j++) { pr_namebind_t *nb = namebinds[j]; PR_CLOSE_NAMEBIND(nb->nb_name, nb->nb_server->addr, nb->nb_server->ServerPort); } } } } } return 0; }
/* Returns 1 if there was a positive match, -1 if there was a negative * match, -2 if there was an error, and zero if there was no match at all. */ int pr_netacl_match(const pr_netacl_t *acl, const pr_netaddr_t *addr) { pool *tmp_pool; if (acl == NULL || addr == NULL) { errno = EINVAL; return -2; } tmp_pool = make_sub_pool(permanent_pool); switch (acl->type) { case PR_NETACL_TYPE_ALL: pr_trace_msg(trace_channel, 10, "addr '%s' matched rule 'ALL' ('%s')", pr_netaddr_get_ipstr(addr), pr_netacl_get_str(tmp_pool, acl)); destroy_pool(tmp_pool); return 1; case PR_NETACL_TYPE_NONE: pr_trace_msg(trace_channel, 10, "addr '%s' matched rule 'NONE'", pr_netaddr_get_ipstr(addr)); destroy_pool(tmp_pool); return -1; case PR_NETACL_TYPE_IPMASK: pr_trace_msg(trace_channel, 10, "checking addr '%s' against IP mask rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); if (pr_netaddr_ncmp(addr, acl->addr, acl->masklen) == 0) { pr_trace_msg(trace_channel, 10, "addr '%s' matched IP mask rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_IPMATCH: pr_trace_msg(trace_channel, 10, "checking addr '%s' against IP address rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); if (pr_netaddr_cmp(addr, acl->addr) == 0) { pr_trace_msg(trace_channel, 10, "addr '%s' matched IP address rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_DNSMATCH: pr_trace_msg(trace_channel, 10, "checking addr '%s' against DNS name rule '%s'", pr_netaddr_get_dnsstr(addr), acl->pattern); if (strcmp(pr_netaddr_get_dnsstr(addr), acl->pattern) == 0) { pr_trace_msg(trace_channel, 10, "addr '%s' (%s) matched DNS name rule '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_IPGLOB: pr_trace_msg(trace_channel, 10, "checking addr '%s' against IP glob rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); if (pr_netaddr_fnmatch(addr, acl->pattern, PR_NETADDR_MATCH_IP) == TRUE) { pr_trace_msg(trace_channel, 10, "addr '%s' matched IP glob rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_DNSGLOB: if (ServerUseReverseDNS) { pr_trace_msg(trace_channel, 10, "checking addr '%s' against DNS glob rule '%s'", pr_netaddr_get_dnsstr(addr), acl->pattern); if (pr_netaddr_fnmatch(addr, acl->pattern, PR_NETADDR_MATCH_DNS) == TRUE) { pr_trace_msg(trace_channel, 10, "addr '%s' (%s) matched DNS glob rule '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } } else { pr_trace_msg(trace_channel, 10, "skipping comparing addr '%s' (%s) against DNS glob rule '%s' " "because UseReverseDNS is off", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), acl->aclstr); } break; } destroy_pool(tmp_pool); return 0; }
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; }
static int forward_handle_user_passthru(cmd_rec *cmd, struct proxy_session *proxy_sess, int *successful, int flags) { int res, xerrno; char *user = NULL; cmd_rec *user_cmd = NULL; pr_response_t *resp = NULL; unsigned int resp_nlines = 0; if (flags & PROXY_FORWARD_USER_PASSTHRU_FL_PARSE_DSTADDR) { struct proxy_conn *pconn = NULL; pr_netaddr_t *remote_addr = NULL; array_header *other_addrs = NULL; res = forward_cmd_parse_dst(cmd->tmp_pool, cmd->arg, &user, &pconn); if (res < 0) { errno = EINVAL; return -1; } remote_addr = proxy_conn_get_addr(pconn, &other_addrs); /* Ensure that the requested remote address is NOT (blatantly) ourselves, * i.e. the proxy itself. This prevents easy-to-detect proxy loops. */ if (pr_netaddr_cmp(remote_addr, session.c->local_addr) == 0 && pr_netaddr_get_port(remote_addr) == pr_netaddr_get_port(session.c->local_addr)) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "requested destination %s#%u is local address %s#%u, rejecting", pr_netaddr_get_ipstr(remote_addr), ntohs(pr_netaddr_get_port(remote_addr)), pr_netaddr_get_ipstr(session.c->local_addr), ntohs(pr_netaddr_get_port(session.c->local_addr))); pr_response_send(R_530, _("Unable to connect to %s: %s"), proxy_conn_get_hostport(pconn), strerror(EPERM)); return 1; } proxy_sess->dst_addr = remote_addr; proxy_sess->other_addrs = other_addrs; proxy_sess->dst_pconn = pconn; /* Change the command so that it no longer includes the proxy info. */ user_cmd = pr_cmd_alloc(cmd->pool, 2, C_USER, user); user_cmd->arg = user; } else { user_cmd = cmd; } if (flags & PROXY_FORWARD_USER_PASSTHRU_FL_CONNECT_DSTADDR) { pr_response_t *banner = NULL; unsigned int banner_nlines = 0; res = forward_connect(proxy_pool, proxy_sess, &banner, &banner_nlines); if (res < 0) { xerrno = errno; *successful = FALSE; /* Send a failed USER response to our waiting frontend client, but do * not necessarily close the frontend connection. */ resp = pcalloc(cmd->tmp_pool, sizeof(pr_response_t)); resp->num = R_530; if (banner != NULL) { resp->msg = banner->msg; resp_nlines = banner_nlines; } else { resp->msg = pstrcat(cmd->tmp_pool, "Unable to connect to ", proxy_conn_get_hostport(proxy_sess->dst_pconn), ": ", strerror(xerrno), NULL); resp_nlines = 1; } res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); if (res < 0) { xerrno = errno; pr_response_block(TRUE); errno = xerrno; return -1; } return 1; } } res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, user_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) user_cmd->argv[0], strerror(xerrno)); errno = xerrno; return -1; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) cmd->argv[0], strerror(xerrno)); errno = xerrno; return -1; } if (resp->num[0] == '2' || resp->num[0] == '3') { *successful = TRUE; if (strcmp(resp->num, R_232) == 0) { proxy_sess_state |= PROXY_SESS_STATE_BACKEND_AUTHENTICATED; pr_timer_remove(PR_TIMER_LOGIN, ANY_MODULE); } } /* XXX TODO: Concatenate the banner from the connect with the USER response * message here, and send the entire kit to the frontend client, e.g.: * * Name (gatekeeper:you): [email protected] * 331-(----GATEWAY CONNECTED TO ftp.uu.net----) * 331-(220 ftp.uu.net FTP server (SunOS 4.1) ready. * 331 Guest login ok, send ident as password. * Password: ###### * 230 Guest login ok, access restrictions apply. * ftp> dir */ res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); if (res < 0) { xerrno = errno; pr_response_block(TRUE); errno = xerrno; return -1; } return 1; }