const pr_netaddr_t *proxy_ftp_msg_parse_addr(pool *p, const char *msg, int addr_family) { int valid_fmt = FALSE; const char *ptr; char *addr_buf; unsigned int h1, h2, h3, h4, p1, p2; unsigned short port; size_t addrlen; pr_netaddr_t *addr; if (p == NULL || msg == NULL) { errno = EINVAL; return NULL; } /* Have to scan the message for the encoded address/port. Note that we may * see some strange formats for PASV responses from FTP servers here. * * We can't predict where the expected address/port numbers start in the * string, so start from the beginning. */ for (ptr = msg; *ptr; ptr++) { if (sscanf(ptr, "%u,%u,%u,%u,%u,%u", &h1, &h2, &h3, &h4, &p1, &p2) == 6) { valid_fmt = TRUE; break; } } if (valid_fmt == FALSE) { pr_trace_msg(trace_channel, 12, "unable to find PORT/PASV address/port format in '%s'", msg); errno = EPERM; return NULL; } if (h1 > 255 || h2 > 255 || h3 > 255 || h4 > 255 || p1 > 255 || p2 > 255 || (h1|h2|h3|h4) == 0 || (p1|p2) == 0) { pr_trace_msg(trace_channel, 9, "message '%s' has invalid address/port value(s)", msg); errno = EINVAL; return NULL; } /* A dotted quad address has a maximum size of 16 bytes: 4 numbers of 3 digits * (max), 3 periods, and 1 terminating NUL. */ addrlen = 16; #ifdef PR_USE_IPV6 /* Allow extra room for any necessary "::ffff:" prefix, for IPv6 sessions. */ addrlen += 7; #endif /* PR_USE_IPV6 */ addr_buf = pcalloc(p, addrlen); #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { if (addr_family == AF_INET6) { snprintf(addr_buf, addrlen-1, "::ffff:%u.%u.%u.%u", h1, h2, h3, h4); } else { snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4); } } else { snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4); } #else snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4); #endif /* PR_USE_IPV6 */ /* XXX Ideally we would NOT be using session pool here, but some other * pool. These objects can't be destroyed (they have no pools of their own), * so they will just clutter up the session pool. Perhaps we could have * a pool of addrs in this API, for reusing. */ addr = (pr_netaddr_t *) pr_netaddr_get_addr(session.pool, addr_buf, NULL); if (addr == NULL) { int xerrno = errno; pr_trace_msg(trace_channel, 7, "unable to resolve '%s' from message '%s': %s", addr_buf, msg, strerror(xerrno)); errno = xerrno; return NULL; } port = (p1 << 8) + p2; pr_netaddr_set_port2(addr, port); return addr; }
struct proxy_conn *proxy_conn_create(pool *p, const char *uri) { int res, use_tls = PROXY_TLS_ENGINE_AUTO; char hostport[512], *proto, *remote_host, *username = NULL, *password = NULL; unsigned int remote_port; struct proxy_conn *pconn; pool *pconn_pool; if (p == NULL || uri == NULL) { errno = EINVAL; return NULL; } res = proxy_uri_parse(p, uri, &proto, &remote_host, &remote_port, &username, &password); if (res < 0) { return NULL; } if (supported_protocol(proto) < 0) { pr_trace_msg(trace_channel, 4, "unsupported protocol '%s' in URI '%.100s'", proto, uri); errno = EPERM; return NULL; } if (strncmp(proto, "ftps", 5) == 0) { /* If the 'ftps' scheme is used, then FTPS is REQUIRED for connections * to this server. */ use_tls = PROXY_TLS_ENGINE_ON; } else if (strncmp(proto, "sftp", 5) == 0) { /* As might be obvious, do not try to use TLS against an SSH2/SFTP * server. */ use_tls = PROXY_TLS_ENGINE_OFF; } memset(hostport, '\0', sizeof(hostport)); snprintf(hostport, sizeof(hostport)-1, "%s:%u", remote_host, remote_port); pconn_pool = pr_pool_create_sz(p, 128); pr_pool_tag(pconn_pool, "proxy connection pool"); pconn = pcalloc(pconn_pool, sizeof(struct proxy_conn)); pconn->pconn_pool = pconn_pool; pconn->pconn_host = pstrdup(pconn_pool, remote_host); pconn->pconn_port = remote_port; pconn->pconn_hostport = pstrdup(pconn_pool, hostport); pconn->pconn_uri = pstrdup(pconn_pool, uri); pconn->pconn_proto = pstrdup(pconn_pool, proto); pconn->pconn_tls = use_tls; if (username != NULL) { pconn->pconn_username = pstrdup(pconn_pool, username); } if (password != NULL) { pconn->pconn_password = pstrdup(pconn_pool, password); } pconn->pconn_addr = pr_netaddr_get_addr(pconn_pool, remote_host, &(pconn->pconn_addrs)); if (pconn->pconn_addr == NULL) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to resolve '%s' from URI '%s'", remote_host, uri); destroy_pool(pconn_pool); errno = EINVAL; return NULL; } if (pr_netaddr_set_port2(pconn->pconn_addr, remote_port) < 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to set port %d from URI '%s': %s", remote_port, uri, strerror(errno)); destroy_pool(pconn_pool); errno = EINVAL; return NULL; } return pconn; }