END_TEST START_TEST (netaddr_clear_test) { pr_netaddr_t *addr; mark_point(); pr_netaddr_clear(NULL); addr = pr_netaddr_alloc(p); addr->na_family = 1; pr_netaddr_clear(addr); fail_unless(addr->na_family == 0, "Failed to clear addr"); }
END_TEST #endif /* PR_USE_IPV6 */ START_TEST (netaddr_get_ipstr_test) { pr_netaddr_t *addr; const char *res; res = pr_netaddr_get_ipstr(NULL); fail_unless(res == NULL, "Failed to handle null argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_get_addr(p, "localhost", NULL); fail_unless(addr != NULL, "Failed to get addr for 'localhost': %s", strerror(errno)); res = pr_netaddr_get_ipstr(addr); fail_unless(res != NULL, "Failed to get IP str for addr: %s", strerror(errno)); fail_unless(strcmp(res, "127.0.0.1") == 0, "Expected '%s', got '%s'", "127.0.0.1", res); fail_unless(addr->na_have_ipstr == 1, "addr should have cached IP str"); pr_netaddr_clear(addr); res = pr_netaddr_get_ipstr(addr); fail_unless(res == NULL, "Expected null, got '%s'", res); }
const pr_netaddr_t *proxy_ftp_msg_parse_ext_addr(pool *p, const char *msg, const pr_netaddr_t *addr, int cmd_id, const char *net_proto) { pr_netaddr_t *res = NULL, na; int family = 0; unsigned short port = 0; char delim, *msg_str, *ptr; size_t msglen; if (p == NULL || msg == NULL || addr == NULL) { errno = EINVAL; return NULL; } if (cmd_id == PR_CMD_EPSV_ID) { /* First, find the opening '(' character. */ ptr = strchr(msg, '('); if (ptr == NULL) { pr_trace_msg(trace_channel, 12, "missing starting '(' character for extended address in '%s'", msg); errno = EINVAL; return NULL; } /* Make sure that the last character is a closing ')'. */ msglen = strlen(ptr); if (ptr[msglen-1] != ')') { pr_trace_msg(trace_channel, 12, "missing ending ')' character for extended address in '%s'", msg); errno = EINVAL; return NULL; } msg_str = pstrndup(p, ptr+1, msglen-2); } else { msg_str = pstrdup(p, msg); } /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order), * where <d> is an arbitrary delimiter character. */ delim = *msg_str++; /* If the network protocol string (e.g. sent by client in EPSV command) is * null, then determine the protocol family from the address family we were * given. */ /* XXX Hack to skip "all", e.g. "EPSV ALL" commands. */ if (net_proto != NULL) { if (strncasecmp(net_proto, "all", 4) == 0) { net_proto = NULL; } } if (net_proto == NULL) { if (*msg_str == delim) { switch (pr_netaddr_get_family(addr)) { case AF_INET: family = 1; break; #ifdef PR_USE_IPV6 case AF_INET6: if (pr_netaddr_use_ipv6()) { family = 2; break; } #endif /* PR_USE_IPV6 */ default: break; } } else { family = atoi(msg_str); } } else { family = atoi(net_proto); } switch (family) { case 1: pr_trace_msg(trace_channel, 19, "parsed IPv4 address from '%s'", msg); break; #ifdef PR_USE_IPV6 case 2: pr_trace_msg(trace_channel, 19, "parsed IPv6 address from '%s'", msg); if (pr_netaddr_use_ipv6()) { break; } #endif /* PR_USE_IPV6 */ default: pr_trace_msg(trace_channel, 12, "unsupported network protocol %d", family); errno = EPROTOTYPE; return NULL; } /* Now, skip past those numeric characters that atoi() used. */ while (PR_ISDIGIT(*msg_str)) { msg_str++; } /* If the next character is not the delimiter, it's a badly formatted * parameter. */ if (*msg_str == delim) { msg_str++; } else { pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'", msg_str); errno = EPERM; return NULL; } pr_netaddr_clear(&na); /* If the next character IS the delimiter, then the address portion is * omitted (which is permissible). */ if (*msg_str == delim) { pr_netaddr_set_family(&na, pr_netaddr_get_family(addr)); pr_netaddr_set_sockaddr(&na, pr_netaddr_get_sockaddr(addr)); msg_str++; } else { ptr = strchr(msg_str, delim); if (ptr == NULL) { /* Badly formatted message. */ errno = EINVAL; return NULL; } /* Twiddle the string so that just the address portion will be processed * by pr_inet_pton(). */ *ptr = '\0'; /* Use pr_inet_pton() to translate the address string into the address * value. */ switch (family) { case 1: { struct sockaddr *sa = NULL; pr_netaddr_set_family(&na, AF_INET); sa = pr_netaddr_get_sockaddr(&na); if (sa) { sa->sa_family = AF_INET; } if (pr_inet_pton(AF_INET, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) { pr_trace_msg(trace_channel, 2, "error converting IPv4 address '%s': %s", msg_str, strerror(errno)); errno = EPERM; return NULL; } break; } case 2: { struct sockaddr *sa = NULL; pr_netaddr_set_family(&na, AF_INET6); sa = pr_netaddr_get_sockaddr(&na); if (sa) { sa->sa_family = AF_INET6; } if (pr_inet_pton(AF_INET6, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) { pr_trace_msg(trace_channel, 2, "error converting IPv6 address '%s': %s", msg_str, strerror(errno)); errno = EPERM; return NULL; } break; } } /* Advance past the address portion of the argument. */ msg_str = ++ptr; } port = atoi(msg_str); while (PR_ISDIGIT(*msg_str)) { msg_str++; } /* If the next character is not the delimiter, it's a badly formatted * parameter. */ if (*msg_str != delim) { pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'", msg_str); errno = EPERM; return NULL; } /* XXX Use a pool other than session.pool here, in the future. */ res = pr_netaddr_dup(session.pool, &na); pr_netaddr_set_port(res, htons(port)); return res; }
/* Initialize a new connection record, also creates a new subpool just for the * new connection. */ static conn_t *init_conn(pool *p, int fd, pr_netaddr_t *bind_addr, int port, int retry_bind, int reporting) { pool *sub_pool = NULL; conn_t *c; pr_netaddr_t na; int addr_family; int res = 0, one = 1, hold_errno; if (!inet_pool) { inet_pool = make_sub_pool(permanent_pool); pr_pool_tag(inet_pool, "Inet Pool"); } /* Initialize the netaddr. */ pr_netaddr_clear(&na); sub_pool = make_sub_pool(p); pr_pool_tag(sub_pool, "init_conn() subpool"); c = (conn_t *) pcalloc(sub_pool, sizeof(conn_t)); c->pool = sub_pool; c->local_port = port; c->rfd = c->wfd = -1; if (bind_addr) { addr_family = pr_netaddr_get_family(bind_addr); } else if (inet_family) { addr_family = inet_family; } else { /* If no default family has been set, then default to IPv6 (if IPv6 * support is enabled), otherwise use IPv4. */ #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) addr_family = AF_INET6; else addr_family = AF_INET; #else addr_family = AF_INET; #endif /* PR_USE_IPV6 */ } /* If fd == -1, there is no currently open socket, so create one. */ if (fd == -1) { socklen_t salen; register unsigned int i = 0; /* Certain versions of Solaris apparently require us to be root * in order to create a socket inside a chroot. * * FreeBSD 2.2.6 (possibly other versions as well), has a security * "feature" which disallows SO_REUSEADDR from working if the socket * owners don't match. The easiest thing to do is simply make sure * the socket is created as root. (Note: this "feature" seems to apply * to _all_ BSDs.) */ #if defined(SOLARIS2) || defined(FREEBSD2) || defined(FREEBSD3) || \ defined(FREEBSD4) || defined(FREEBSD5) || defined(FREEBSD6) || \ defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(DARWIN6) || defined(DARWIN7) || defined(DARWIN8) || \ defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \ defined(SCO3) || defined(CYGWIN) || defined(SYSV4_2MP) || \ defined(SYSV5SCO_SV6) || defined(SYSV5UNIXWARE7) # ifdef SOLARIS2 if (port != INPORT_ANY && port < 1024) { # endif pr_signals_block(); PRIVS_ROOT # ifdef SOLARIS2 }
pr_namebind_t *pr_namebind_find(const char *name, pr_netaddr_t *addr, unsigned int port, unsigned char skip_inactive) { pr_ipbind_t *ipbind = NULL; pr_namebind_t *namebind = NULL; if (name == NULL || addr == NULL) { errno = EINVAL; return NULL; } /* First, find an active ipbind for the given addr/port */ ipbind = pr_ipbind_find(addr, port, skip_inactive); if (ipbind == NULL) { pr_netaddr_t wildcard_addr; int addr_family; /* If not found, look for the wildcard address. */ addr_family = pr_netaddr_get_family(addr); pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, addr_family); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); #ifdef PR_USE_IPV6 if (ipbind == FALSE && addr_family == AF_INET6 && pr_netaddr_use_ipv6()) { /* No IPv6 wildcard address found; try the IPv4 wildcard address. */ pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, AF_INET); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); } #endif /* PR_USE_IPV6 */ } if (ipbind == NULL) { errno = ENOENT; return NULL; } if (!ipbind->ib_namebinds) { return NULL; } else { register unsigned int i = 0; pr_namebind_t **namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; for (i = 0; i < ipbind->ib_namebinds->nelts; i++) { namebind = namebinds[i]; /* Skip inactive namebinds */ if (skip_inactive == TRUE && namebind != NULL && namebind->nb_isactive == FALSE) { continue; } /* At present, this looks for an exactly matching name. In the future, * we may want to have something like Apache's matching scheme, which * looks for the most specific domain to the most general. Note that * that scheme, however, is specific to DNS; should any other naming * scheme be desired, that sort of matching will be unnecessary. */ if (namebind != NULL && namebind->nb_name != NULL) { if (namebind->nb_iswildcard == FALSE) { if (strcasecmp(namebind->nb_name, name) == 0) return namebind; } } else { int match_flags = PR_FNM_NOESCAPE|PR_FNM_CASEFOLD; if (pr_fnmatch(namebind->nb_name, name, match_flags) == 0) { pr_trace_msg(trace_channel, 9, "matched name '%s' against pattern '%s'", name, namebind->nb_name); return namebind; } pr_trace_msg(trace_channel, 9, "failed to match name '%s' against pattern '%s'", name, namebind->nb_name); } } } return NULL; }
int pr_namebind_create(server_rec *server, const char *name, pr_netaddr_t *addr, unsigned int port) { pr_ipbind_t *ipbind = NULL; pr_namebind_t *namebind = NULL, **namebinds = NULL; if (server == NULL || name == NULL) { errno = EINVAL; return -1; } /* First, find the ipbind to hold this namebind. */ ipbind = pr_ipbind_find(addr, port, FALSE); if (ipbind == NULL) { pr_netaddr_t wildcard_addr; int addr_family; /* If not found, look for the wildcard address. */ addr_family = pr_netaddr_get_family(addr); pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, addr_family); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); #ifdef PR_USE_IPV6 if (ipbind == FALSE && addr_family == AF_INET6 && pr_netaddr_use_ipv6()) { /* No IPv6 wildcard address found; try the IPv4 wildcard address. */ pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, AF_INET); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); } #endif /* PR_USE_IPV6 */ } if (ipbind == NULL) { errno = ENOENT; return -1; } /* Make sure we can add this namebind. */ if (!ipbind->ib_namebinds) { ipbind->ib_namebinds = make_array(binding_pool, 0, sizeof(pr_namebind_t *)); } else { register unsigned int i = 0; namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; /* See if there is already a namebind for the given name. */ for (i = 0; i < ipbind->ib_namebinds->nelts; i++) { namebind = namebinds[i]; if (namebind != NULL && namebind->nb_name != NULL) { /* DNS names are case-insensitive, hence the case-insensitive check * here. * * XXX Ideally, we should check whether any existing namebinds which * are globs will match the newly added namebind as well. */ if (strcasecmp(namebind->nb_name, name) == 0) { errno = EEXIST; return -1; } } } } namebind = (pr_namebind_t *) pcalloc(server->pool, sizeof(pr_namebind_t)); namebind->nb_name = name; namebind->nb_server = server; namebind->nb_isactive = FALSE; if (pr_str_is_fnmatch(name) == TRUE) { namebind->nb_iswildcard = TRUE; } pr_trace_msg(trace_channel, 8, "created named binding '%s' for %s#%u, server %p", name, pr_netaddr_get_ipstr(server->addr), server->ServerPort, server->ServerName); /* The given server should already have the following populated: * * server->ServerName * server->ServerAddress * server->ServerFQDN */ /* These TCP socket tweaks will not apply to the control connection (it will * already have been established by the time this named vhost is used), * but WILL apply to any data connections established to this named vhost. */ #if 0 namebind->nb_server->tcp_mss_len = (server->tcp_mss_len ? server->tcp_mss_len : main_server->tcp_mss_len); namebind->nb_server->tcp_rcvbuf_len = (server->tcp_rcvbuf_len ? server->tcp_rcvbuf_len : main_server->tcp_rcvbuf_len); namebind->nb_server->tcp_rcvbuf_override = (server->tcp_rcvbuf_override ? TRUE : main_server->tcp_rcvbuf_override); namebind->nb_server->tcp_sndbuf_len = (server->tcp_sndbuf_len ? server->tcp_sndbuf_len : main_server->tcp_sndbuf_len); namebind->nb_server->tcp_sndbuf_override = (server->tcp_sndbuf_override ? TRUE : main_server->tcp_sndbuf_override); /* XXX Shouldn't need these; the ipbind container handles all of the * connection (listener, port, addr) stuff. */ namebind->nb_server->addr = (server->addr ? server->addr : main_server->addr); namebind->nb_server->ServerPort = (server->ServerPort ? server->ServerPort : main_server->ServerPort); namebind->nb_listener = (server->listen ? server->listen : main_server->listen); #endif *((pr_namebind_t **) push_array(ipbind->ib_namebinds)) = namebind; return 0; }
server_rec *pr_ipbind_get_server(pr_netaddr_t *addr, unsigned int port) { pr_ipbind_t *ipbind = NULL; pr_netaddr_t wildcard_addr; int addr_family; /* If we've got a binding configured for this exact address, return it * straightaway. */ ipbind = pr_ipbind_find(addr, port, TRUE); if (ipbind != NULL) return ipbind->ib_server; /* Look for a vhost bound to the wildcard address (i.e. INADDR_ANY). * * This allows for "<VirtualHost 0.0.0.0>" configurations, where the * IP address to which the client might connect is not known at * configuration time. (Usually happens when the same config file * is deployed to multiple machines.) */ addr_family = pr_netaddr_get_family(addr); pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, addr_family); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE); if (ipbind != NULL) { pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); return ipbind->ib_server; } else { #ifdef PR_USE_IPV6 if (addr_family == AF_INET6 && pr_netaddr_use_ipv6()) { /* The pr_ipbind_find() probably returned NULL because there aren't * any <VirtualHost> sections configured explicitly for the wildcard * IPv6 address of "::", just the IPv4 wildcard "0.0.0.0" address. * * So try the pr_ipbind_find() again, this time using the IPv4 wildcard. */ pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, AF_INET); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE); if (ipbind != NULL) { pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); return ipbind->ib_server; } } #endif /* PR_USE_IPV6 */ } /* Use the default server, if set. */ if (ipbind_default_server && ipbind_default_server->ib_isactive) { pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " "DefaultServer '%s'", pr_netaddr_get_ipstr(addr), port, ipbind_default_server->ib_server->ServerName); return ipbind_default_server->ib_server; } /* Not found in binding list, and no DefaultServer, so see if it's the * loopback address */ if (ipbind_localhost_server && pr_netaddr_is_loopback(addr)) { return ipbind_localhost_server->ib_server; } return NULL; }
END_TEST #ifdef PR_USE_IPV6 START_TEST (netaddr_get_dnsstr_ipv6_test) { pr_netaddr_t *addr; const char *ip, *res; ip = "::1"; res = pr_netaddr_get_dnsstr(NULL); fail_unless(res == NULL, "Failed to handle null argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_get_addr(p, ip, NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", ip, strerror(errno)); pr_netaddr_set_reverse_dns(FALSE); res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); fail_unless(strcmp(res, ip) == 0, "Expected '%s', got '%s'", ip, res); pr_netaddr_set_reverse_dns(TRUE); /* Even though we should expect a DNS name, not an IP address, the * previous call to pr_netaddr_get_dnsstr() cached the IP address. */ res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); fail_unless(strcmp(res, ip) == 0, "Expected '%s', got '%s'", ip, res); pr_netaddr_clear(addr); /* Clearing the address doesn't work, since that removes even the address * info, in addition to the cached strings. */ res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); fail_unless(strcmp(res, "") == 0, "Expected '%s', got '%s'", "", res); /* We need to clear the netaddr internal cache as well. */ pr_netaddr_clear_ipcache(ip); addr = pr_netaddr_get_addr(p, ip, NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", ip, strerror(errno)); mark_point(); fail_unless(addr->na_have_dnsstr == 0, "addr already has cached DNS str"); mark_point(); res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); mark_point(); /* Depending on the contents of /etc/hosts, resolving ::1 could * return either "localhost" or "localhost.localdomain". Perhaps even * other variations, although these should be the most common. */ fail_unless(strcmp(res, "localhost") == 0 || strcmp(res, "localhost.localdomain") == 0 || strcmp(res, "localhost6") == 0 || strcmp(res, "localhost6.localdomain") == 0 || strcmp(res, "ip6-localhost") == 0 || strcmp(res, "ip6-loopback") == 0 || strcmp(res, ip) == 0, "Expected '%s', got '%s'", "localhost, localhost.localdomain et al", res); }