END_TEST START_TEST (netaddr_set_family_test) { pr_netaddr_t *addr; int res; res = pr_netaddr_set_family(NULL, 0); fail_unless(res == -1, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL); fail_unless(addr != NULL, "Failed to get addr for '127.0.0.1': %s", strerror(errno)); res = pr_netaddr_set_family(addr, -1); fail_unless(res == -1, "Failed to handle bad family"); #ifdef EAFNOSUPPORT fail_unless(errno == EAFNOSUPPORT, "Failed to set errno to EAFNOSUPPORT"); #else fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); #endif res = pr_netaddr_set_family(addr, AF_INET); fail_unless(res == 0, "Failed to set family to AF_INET: %s", strerror(errno)); }
/* Copy a connection structure, also creates a sub pool for the new * connection. */ conn_t *pr_inet_copy_conn(pool *p, conn_t *c) { conn_t *res = NULL; pool *sub_pool = NULL; if (p == NULL || c == NULL) { errno = EINVAL; return NULL; } sub_pool = make_sub_pool(p); pr_pool_tag(sub_pool, "inet_copy_conn pool"); res = (conn_t *) pcalloc(sub_pool, sizeof(conn_t)); memcpy(res, c, sizeof(conn_t)); res->pool = sub_pool; res->instrm = res->outstrm = NULL; if (c->local_addr) { res->local_addr = pr_netaddr_alloc(res->pool); if (pr_netaddr_set_family(res->local_addr, pr_netaddr_get_family(c->local_addr)) < 0) { destroy_pool(res->pool); return NULL; } pr_netaddr_set_sockaddr(res->local_addr, pr_netaddr_get_sockaddr(c->local_addr)); } if (c->remote_addr) { res->remote_addr = pr_netaddr_alloc(res->pool); if (pr_netaddr_set_family(res->remote_addr, pr_netaddr_get_family(c->remote_addr)) < 0) { destroy_pool(res->pool); return NULL; } pr_netaddr_set_sockaddr(res->remote_addr, pr_netaddr_get_sockaddr(c->remote_addr)); } if (c->remote_name) { res->remote_name = pstrdup(res->pool, c->remote_name); } register_cleanup(res->pool, (void *) res, conn_cleanup_cb, conn_cleanup_cb); return res; }
void pr_netaddr_set_sess_addrs(void) { pr_netaddr_set_family(&sess_local_addr, pr_netaddr_get_family(session.c->local_addr)); pr_netaddr_set_sockaddr(&sess_local_addr, pr_netaddr_get_sockaddr(session.c->local_addr)); have_sess_local_addr = TRUE; pr_netaddr_set_family(&sess_remote_addr, pr_netaddr_get_family(session.c->remote_addr)); pr_netaddr_set_sockaddr(&sess_remote_addr, pr_netaddr_get_sockaddr(session.c->remote_addr)); memset(sess_remote_name, '\0', sizeof(sess_remote_name)); sstrncpy(sess_remote_name, session.c->remote_name, sizeof(sess_remote_name)); have_sess_remote_addr = TRUE; }
END_TEST START_TEST (netaddr_dup_test) { pr_netaddr_t *res, *addr; res = pr_netaddr_dup(NULL, NULL); fail_unless(res == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); res = pr_netaddr_dup(p, NULL); fail_unless(res == NULL, "Failed to handle null addr"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_alloc(p); pr_netaddr_set_family(addr, AF_INET); res = pr_netaddr_dup(NULL, addr); fail_unless(res == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); res = pr_netaddr_dup(p, addr); fail_unless(res != NULL, "Failed to dup netaddr: %s", strerror(errno)); fail_unless(res->na_family == addr->na_family, "Expected family %d, got %d", addr->na_family, res->na_family); }
pr_netaddr_t *pr_netaddr_dup(pool *p, pr_netaddr_t *na) { pr_netaddr_t *dup_na; if (!p || !na) { errno = EINVAL; return NULL; } dup_na = pr_netaddr_alloc(p); pr_netaddr_set_family(dup_na, pr_netaddr_get_family(na)); pr_netaddr_set_sockaddr(dup_na, pr_netaddr_get_sockaddr(na)); return dup_na; }
pr_netacl_t *pr_netacl_dup(pool *p, const pr_netacl_t *acl) { pr_netacl_t *acl2; if (p == NULL || acl == NULL) { errno = EINVAL; return NULL; } acl2 = pcalloc(p, sizeof(pr_netacl_t)); /* A simple memcpy(3) won't suffice; we need a deep copy. */ acl2->type = acl->type; if (acl->pattern != NULL) { acl2->pattern = pstrdup(p, acl->pattern); } acl2->negated = acl->negated; if (acl->addr != NULL) { pr_netaddr_t *addr; addr = pr_netaddr_alloc(p); pr_netaddr_set_family(addr, pr_netaddr_get_family(acl->addr)); pr_netaddr_set_sockaddr(addr, pr_netaddr_get_sockaddr(acl->addr)); acl2->addr = addr; } acl2->masklen = acl->masklen; if (acl->aclstr != NULL) { acl2->aclstr = pstrdup(p, acl->aclstr); } return acl2; }
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; }
int pr_netaddr_ncmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2, unsigned int bitlen) { pool *tmp_pool = NULL; pr_netaddr_t *a, *b; unsigned int nbytes, nbits; const unsigned char *in1, *in2; if (na1 && !na2) return 1; if (!na1 && na2) return -1; if (!na1 && !na2) return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { /* Cannot compare addresses from different families, unless one * of the netaddrs has an AF_INET family, and the other has an * AF_INET6 family AND is an IPv4-mapped IPv6 address. */ if (pr_netaddr_is_v4mappedv6(na1) != TRUE && pr_netaddr_is_v4mappedv6(na2) != TRUE) { errno = EINVAL; return -1; } if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na1 is an IPv4-mapped IPv6 address, and * na2 is an IPv4 address. */ a = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(a, AF_INET); pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), sizeof(struct in_addr)); b = (pr_netaddr_t *) na2; pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(b), pr_netaddr_get_ipstr(a)); } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na is an IPv4 address, and na2 is an * IPv4-mapped IPv6 address. */ a = (pr_netaddr_t *) na1; b = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(b, AF_INET); pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), sizeof(struct in_addr)); pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(a), pr_netaddr_get_ipstr(b)); } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } switch (pr_netaddr_get_family(a)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). */ if (bitlen > 32) { errno = EINVAL; return -1; } break; } #ifdef PR_USE_IPV6 case AF_INET6: { if (use_ipv6) { /* Make sure that the given number of bits is not more than supported * for IPv6 addresses (128). */ if (bitlen > 128) { errno = EINVAL; return -1; } break; } } #endif /* PR_USE_IPV6 */ default: errno = EPERM; return -1; } /* Retrieve pointers to the contained in_addrs. */ in1 = (const unsigned char *) pr_netaddr_get_inaddr(a); in2 = (const unsigned char *) pr_netaddr_get_inaddr(b); /* Determine the number of bytes, and leftover bits, in the given * bit length. */ nbytes = bitlen / 8; nbits = bitlen % 8; /* Compare bytes, using memcmp(3), first. */ if (nbytes > 0) { int res = memcmp(in1, in2, nbytes); /* No need to continue comparing the addresses if they differ already. */ if (res != 0) { if (tmp_pool) destroy_pool(tmp_pool); return res; } } /* Next, compare the remaining bits in the addresses. */ if (nbits > 0) { unsigned char mask; /* Get the bytes in the addresses that have not yet been compared. */ unsigned char in1byte = in1[nbytes]; unsigned char in2byte = in2[nbytes]; /* Build up a mask covering the bits left to be checked. */ mask = (0xff << (8 - nbits)) & 0xff; if ((in1byte & mask) > (in2byte & mask)) { if (tmp_pool) destroy_pool(tmp_pool); return 1; } if ((in1byte & mask) < (in2byte & mask)) { if (tmp_pool) destroy_pool(tmp_pool); return -1; } } if (tmp_pool) destroy_pool(tmp_pool); /* If we've made it this far, the addresses match, for the given bit * length. */ return 0; }
int pr_netaddr_cmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2) { pool *tmp_pool = NULL; pr_netaddr_t *a, *b; int res; if (na1 && !na2) return 1; if (!na1 && na2) return -1; if (!na1 && !na2) return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { /* Cannot compare addresses from different families, unless one * of the netaddrs has an AF_INET family, and the other has an * AF_INET6 family AND is an IPv4-mapped IPv6 address. */ if (pr_netaddr_is_v4mappedv6(na1) != TRUE && pr_netaddr_is_v4mappedv6(na2) != TRUE) { errno = EINVAL; return -1; } if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na1 is an IPv4-mapped IPv6 address, and * na2 is an IPv4 address. */ a = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(a, AF_INET); pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), sizeof(struct in_addr)); b = (pr_netaddr_t *) na2; pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(b), pr_netaddr_get_ipstr(a)); } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na is an IPv4 address, and na2 is an * IPv4-mapped IPv6 address. */ a = (pr_netaddr_t *) na1; b = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(b, AF_INET); pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), sizeof(struct in_addr)); pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(a), pr_netaddr_get_ipstr(b)); } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } switch (pr_netaddr_get_family(a)) { case AF_INET: res = memcmp(&a->na_addr.v4.sin_addr, &b->na_addr.v4.sin_addr, sizeof(struct in_addr)); if (tmp_pool) destroy_pool(tmp_pool); return res; #ifdef PR_USE_IPV6 case AF_INET6: if (use_ipv6) { res = memcmp(&a->na_addr.v6.sin6_addr, &b->na_addr.v6.sin6_addr, sizeof(struct in6_addr)); if (tmp_pool) destroy_pool(tmp_pool); return res; } #endif /* PR_USE_IPV6 */ } if (tmp_pool) destroy_pool(tmp_pool); errno = EPERM; return -1; }
pr_netaddr_t *pr_netaddr_get_addr(pool *p, const char *name, array_header **addrs) { struct sockaddr_in v4; pr_netaddr_t *na = NULL; int res; if (p == NULL || name == NULL) { errno = EINVAL; return NULL; } pr_trace_msg(trace_channel, 10, "resolving name '%s' to IP address", name); /* Attempt to translate the given name into a pr_netaddr_t using * pr_inet_pton() first. * * First, if IPv6 support is enabled, we try to translate the name using * pr_inet_pton(AF_INET6) on the hopes that the given string is a valid * representation of an IPv6 address. If that fails, or if IPv6 support * is not enabled, we try with pr_inet_pton(AF_INET). If that fails, we * assume that the given name is a DNS name, and we call pr_getaddrinfo(). */ na = (pr_netaddr_t *) pcalloc(p, sizeof(pr_netaddr_t)); #ifdef PR_USE_IPV6 if (use_ipv6) { struct sockaddr_in6 v6; memset(&v6, 0, sizeof(v6)); v6.sin6_family = AF_INET6; # ifdef SIN6_LEN v6.sin6_len = sizeof(struct sockaddr_in6); # endif /* SIN6_LEN */ res = pr_inet_pton(AF_INET6, name, &v6.sin6_addr); if (res > 0) { pr_netaddr_set_family(na, AF_INET6); pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v6); if (addrs) *addrs = NULL; pr_trace_msg(trace_channel, 7, "'%s' resolved to IPv6 address %s", name, pr_netaddr_get_ipstr(na)); return na; } } #endif /* PR_USE_IPV6 */ memset(&v4, 0, sizeof(v4)); v4.sin_family = AF_INET; # ifdef SIN_LEN v4.sin_len = sizeof(struct sockaddr_in); # endif /* SIN_LEN */ res = pr_inet_pton(AF_INET, name, &v4.sin_addr); if (res > 0) { pr_netaddr_set_family(na, AF_INET); pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v4); if (addrs) *addrs = NULL; pr_trace_msg(trace_channel, 7, "'%s' resolved to IPv4 address %s", name, pr_netaddr_get_ipstr(na)); return na; } else if (res == 0) { /* If pr_inet_pton() returns 0, it means that name does not represent a * valid network address in the specified address family. Usually, * this means that name is actually a DNS name, not an IP address * string. So we treat it as a DNS name, and use getaddrinfo(3) to * resolve that name to its IP address(es). */ struct addrinfo hints, *info = NULL; int gai_res = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; pr_trace_msg(trace_channel, 7, "attempting to resolve '%s' to IPv4 address via DNS", name); gai_res = pr_getaddrinfo(name, NULL, &hints, &info); if (gai_res != 0) { if (gai_res != EAI_SYSTEM) { pr_trace_msg(trace_channel, 1, "IPv4 getaddrinfo '%s' error: %s", name, pr_gai_strerror(gai_res)); } else { pr_trace_msg(trace_channel, 1, "IPv4 getaddrinfo '%s' system error: [%d] %s", name, errno, strerror(errno)); } return NULL; } if (info) { /* Copy the first returned addr into na, as the return value. */ pr_netaddr_set_family(na, info->ai_family); pr_netaddr_set_sockaddr(na, info->ai_addr); pr_trace_msg(trace_channel, 7, "resolved '%s' to %s address %s", name, info->ai_family == AF_INET ? "IPv4" : "IPv6", pr_netaddr_get_ipstr(na)); pr_freeaddrinfo(info); } #ifdef PR_USE_IPV6 if (use_ipv6 && addrs) { /* Do the call again, this time for IPv6 addresses. * * We make two separate getaddrinfo(3) calls, rather than one * with a hint of AF_UNSPEC, because of certain bugs where the use * of AF_UNSPEC does not function as advertised. (I suspect this * bug was caused by proftpd's calling pattern, but as I could * not track it down, and as there are reports of AF_UNSPEC not * being as fast as AF_INET/AF_INET6, it just seemed easier to * do it this way.) */ gai_res = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; pr_trace_msg(trace_channel, 7, "attempting to resolve '%s' to IPv6 address via DNS", name); gai_res = pr_getaddrinfo(name, NULL, &hints, &info); if (gai_res != 0) { if (gai_res != EAI_SYSTEM) { pr_trace_msg(trace_channel, 1, "IPv6 getaddrinfo '%s' error: %s", name, pr_gai_strerror(gai_res)); } else { pr_trace_msg(trace_channel, 1, "IPv6 getaddrinfo '%s' system error: [%d] %s", name, errno, strerror(errno)); } return na; } if (info) { pr_netaddr_t **elt; *addrs = make_array(p, 0, sizeof(pr_netaddr_t *)); elt = push_array(*addrs); *elt = pcalloc(p, sizeof(pr_netaddr_t)); pr_netaddr_set_family(*elt, info->ai_family); pr_netaddr_set_sockaddr(*elt, info->ai_addr); pr_trace_msg(trace_channel, 7, "resolved '%s' to %s address %s", name, info->ai_family == AF_INET ? "IPv4" : "IPv6", pr_netaddr_get_ipstr(*elt)); pr_freeaddrinfo(info); } } #endif /* PR_USE_IPV6 */ return na; } pr_trace_msg(trace_channel, 8, "failed to resolve '%s' to an IP address", name); return NULL; }
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; }
pr_netaddr_t *pr_netaddr_get_addr(pool *p, const char *name, array_header **addrs) { struct sockaddr_in v4; #ifdef PR_USE_IPV6 struct sockaddr_in6 v6; #endif /* PR_USE_IPV6 */ pr_netaddr_t *na = NULL; int res; if (p == NULL || name == NULL) { errno = EINVAL; return NULL; } /* Attempt to translate the given name into a pr_netaddr_t using * pr_inet_pton() first. * * First, if IPv6 support is enabled, we try to translate the name using * pr_inet_pton(AF_INET6) on the hopes that the given string is a valid * representation of an IPv6 address. If that fails, or if IPv6 support * is not enabled, we try with pr_inet_pton(AF_INET). If that fails, we * assume that the given name is a DNS name, and we call pr_getaddrinfo(). */ na = (pr_netaddr_t *) pcalloc(p, sizeof(pr_netaddr_t)); #ifdef PR_USE_IPV6 memset(&v6, 0, sizeof(v6)); v6.sin6_family = AF_INET6; # ifdef SIN6_LEN v6.sin6_len = sizeof(struct sockaddr_in6); # endif /* SIN6_LEN */ res = pr_inet_pton(AF_INET6, name, &v6.sin6_addr); if (res > 0) { pr_netaddr_set_family(na, AF_INET6); pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v6); if (addrs) *addrs = NULL; pr_log_debug(DEBUG10, "'%s' resolved to an IPv6 address", name); return na; } #endif memset(&v4, 0, sizeof(v4)); v4.sin_family = AF_INET; # ifdef SIN_LEN v4.sin_len = sizeof(struct sockaddr_in); # endif /* SIN_LEN */ res = pr_inet_pton(AF_INET, name, &v4.sin_addr); if (res > 0) { pr_netaddr_set_family(na, AF_INET); pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v4); if (addrs) *addrs = NULL; pr_log_debug(DEBUG10, "'%s' resolved to an IPv4 address", name); return na; } else if (res == 0) { /* If pr_inet_pton() returns 0, it means that name does not represent a * valid network address in the specified address family. Usually, * this means that name is actually a DNS name, not an IP address * string. So we treat it as a DNS name, and use getaddrinfo(3) to * resolve that name to its IP address(es). */ struct addrinfo hints, *info = NULL; int gai_res = 0; memset(&hints, 0, sizeof(hints)); #ifdef PR_USE_IPV6 /* This looks up both IPv4 (as IPv6-mapped) and IPv6 addresses. */ hints.ai_family = AF_UNSPEC; #else hints.ai_family = AF_INET; #endif /* PR_USE_IPV6 */ hints.ai_socktype = SOCK_STREAM; pr_log_debug(DEBUG10, "attempting to resolve '%s' via DNS", name); gai_res = pr_getaddrinfo(name, NULL, &hints, &info); if (gai_res != 0) { pr_log_pri(PR_LOG_INFO, "getaddrinfo '%s' error: %s", name, res != EAI_SYSTEM ? pr_gai_strerror(gai_res) : strerror(errno)); return NULL; } if (info) { pr_log_debug(DEBUG10, "resolved '%s' to an %s address", name, info->ai_family == AF_INET ? "IPv4" : "IPv6"); /* Copy the first returned addr into na, as the return value. */ pr_netaddr_set_family(na, info->ai_family); pr_netaddr_set_sockaddr(na, info->ai_addr); /* If the caller provided a pointer for any additional addresses, * then we cycle through the rest of getaddrinfo(3)'s results and * build a list to return to the caller. */ if (addrs) { struct addrinfo *ai; *addrs = make_array(p, 0, sizeof(pr_netaddr_t *)); for (ai = info->ai_next; ai; ai = ai->ai_next) { pr_netaddr_t **elt = push_array(*addrs); *elt = pcalloc(p, sizeof(pr_netaddr_t)); pr_netaddr_set_family(*elt, ai->ai_family); pr_netaddr_set_sockaddr(*elt, ai->ai_addr); } } pr_freeaddrinfo(info); return na; } } pr_log_debug(DEBUG10, "failed to resolve '%s' to an IP address", name); return NULL; }
static int data_active_open(char *reason, off_t size) { conn_t *c; int bind_port, rev; pr_netaddr_t *bind_addr; unsigned char *root_revoke = NULL; if (!reason && session.xfer.filename) reason = session.xfer.filename; if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) { bind_addr = session.c->local_addr; } else { /* In this scenario, the server has an IPv6 socket, but the remote client * is an IPv4 (or IPv4-mapped IPv6) peer. */ bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr); } /* Default source port to which to bind for the active transfer, as * per RFC959. */ bind_port = session.c->local_port-1; /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and * 2 indicates 'NonCompliantActiveTransfer'. We change the source port for * a RootRevoke value of 2. */ root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE); if (root_revoke != NULL && *root_revoke == 2) { bind_port = INPORT_ANY; } session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE); /* Default remote address to which to connect for an active transfer, * if the client has not specified a different address via PORT/EPRT, * as per RFC 959. */ if (pr_netaddr_get_family(&session.data_addr) == AF_UNSPEC) { pr_log_debug(DEBUG6, "Client has not sent previous PORT/EPRT command, " "defaulting to %s#%u for active transfer", pr_netaddr_get_ipstr(session.c->remote_addr), session.c->remote_port); pr_netaddr_set_family(&session.data_addr, pr_netaddr_get_family(session.c->remote_addr)); pr_netaddr_set_sockaddr(&session.data_addr, pr_netaddr_get_sockaddr(session.c->remote_addr)); } /* Set the "stalled" timer, if any, to prevent the connection * open from taking too long */ if (timeout_stalled) { pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb, "TimeoutStalled"); } rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS); /* Protocol and socket options should be set before handshaking. */ if (session.xfer.direction == PR_NETIO_IO_RD) { pr_inet_set_socket_opts(session.d->pool, session.d, (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0, main_server->tcp_keepalive); } else { pr_inet_set_socket_opts(session.d->pool, session.d, 0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0), main_server->tcp_keepalive); } /* Make sure that the necessary socket options are set on the socket prior * to the call to connect(2). */ pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0, IPTOS_THROUGHPUT, 1); pr_inet_generate_socket_event("core.data-connect", main_server, session.d->local_addr, session.d->listen_fd); if (pr_inet_connect(session.d->pool, session.d, &session.data_addr, session.data_port) == -1) { pr_log_debug(DEBUG6, "Error connecting to %s#%u for active data transfer: %s", pr_netaddr_get_ipstr(&session.data_addr), session.data_port, strerror(session.d->xerrno)); pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); errno = session.d->xerrno; destroy_pool(session.d->pool); session.d = NULL; return -1; } c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA, session.d->listen_fd, -1, -1, TRUE); pr_netaddr_set_reverse_dns(rev); if (c) { pr_log_debug(DEBUG4, "active data connection opened - local : %s:%d", pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port); pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d", pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port); if (session.xfer.xfer_type != STOR_UNIQUE) { if (size) { pr_response_send(R_150, _("Opening %s mode data connection for %s " "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size); } else { pr_response_send(R_150, _("Opening %s mode data connection for %s"), MODE_STRING, reason); } } else { /* Format of 150 responses for STOU is explicitly dictated by * RFC 1123: * * 4.1.2.9 STOU Command: RFC-959 Section 4.1.3 * * The STOU command stores into a uniquely named file. When it * receives an STOU command, a Server-FTP MUST return the * actual file name in the "125 Transfer Starting" or the "150 * Opening Data Connection" message that precedes the transfer * (the 250 reply code mentioned in RFC-959 is incorrect). The * exact format of these messages is hereby defined to be as * follows: * * 125 FILE: pppp * 150 FILE: pppp * * where pppp represents the unique pathname of the file that * will be written. */ pr_response_send(R_150, "FILE: %s", reason); } pr_inet_close(session.pool, session.d); pr_inet_set_nonblock(session.pool, session.d); session.d = c; return 0; } pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); errno = session.d->xerrno; destroy_pool(session.d->pool); session.d = NULL; return -1; }