END_TEST START_TEST (netaddr_is_v4mappedv6_test) { int res; const char *name; pr_netaddr_t *addr; res = pr_netaddr_is_v4mappedv6(NULL); fail_unless(res == -1, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); name = "127.0.0.1"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", name, strerror(errno)); res = pr_netaddr_is_v4mappedv6(addr); fail_unless(res == -1, "Expected -1 for IPv4 address '%s', got %d", name, res); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL; got %d [%s]", errno, strerror(errno)); name = "::1"; addr = pr_netaddr_get_addr(p, name, NULL); #ifdef PR_USE_IPV6 fail_unless(addr != NULL, "Failed to get addr for '%s': %s", name, strerror(errno)); res = pr_netaddr_is_v4mappedv6(addr); fail_unless(res == FALSE, "Expected 'false' for IPv6 address '%s', got %d", name, res); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL; got %d [%s]", errno, strerror(errno)); #else fail_unless(addr == NULL, "IPv6 support disabled, should not be able to get addr for '%s'", name); #endif /* PR_USE_IPV6 */ name = "::ffff:127.0.0.1"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", name, strerror(errno)); res = pr_netaddr_is_v4mappedv6(addr); #ifdef PR_USE_IPV6 fail_unless(res == TRUE, "Expected 'true' for IPv4-mapped IPv6 address '%s', got %d", name, res); #else fail_unless(res == -1, "Expected -1 for IPv4-mapped IPv6 address '%s' (--disable-ipv6 used)"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL; got %d [%s]", errno, strerror(errno)); #endif /* PR_USE_IPV6 */ }
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; }
/* This differs from pr_netaddr_get_ipstr() in that pr_netaddr_get_ipstr() * returns a string of the numeric form of the given network address, whereas * this function returns a string of the DNS name (if present). */ const char *pr_netaddr_get_dnsstr(pr_netaddr_t *na) { char *name = NULL; char buf[256]; if (!na) { errno = EINVAL; return NULL; } /* If this pr_netaddr_t has already been resolved to an DNS string, return the * cached string. */ if (na->na_have_dnsstr) return na->na_dnsstr; if (reverse_dns) { int res = 0; pr_trace_msg(trace_channel, 3, "verifying DNS name for IP address %s via reverse DNS lookup", pr_netaddr_get_ipstr(na)); memset(buf, '\0', sizeof(buf)); res = pr_getnameinfo(pr_netaddr_get_sockaddr(na), pr_netaddr_get_sockaddr_len(na), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); buf[sizeof(buf)-1] = '\0'; if (res == 0) { char **checkaddr; struct hostent *hent = NULL; unsigned char ok = FALSE; int family = pr_netaddr_get_family(na); void *inaddr = pr_netaddr_get_inaddr(na); #ifdef HAVE_GETHOSTBYNAME2 if (pr_netaddr_is_v4mappedv6(na) == TRUE) { family = AF_INET; inaddr = get_v4inaddr(na); } hent = gethostbyname2(buf, family); #else hent = gethostbyname(buf); #endif /* HAVE_GETHOSTBYNAME2 */ if (hent != NULL) { char **alias; pr_trace_msg(trace_channel, 10, "checking addresses associated with host '%s'", hent->h_name ? hent->h_name : "(null)"); for (alias = hent->h_aliases; *alias; ++alias) { pr_trace_msg(trace_channel, 10, "host '%s' has alias '%s'", hent->h_name ? hent->h_name : "(null)", *alias); } switch (hent->h_addrtype) { case AF_INET: if (family == AF_INET) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #ifdef PR_USE_IPV6 case AF_INET6: if (use_ipv6 && family == AF_INET6) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #endif /* PR_USE_IPV6 */ } if (ok) { name = buf; pr_trace_msg(trace_channel, 8, "using DNS name '%s' for IP address '%s'", name, pr_netaddr_get_ipstr(na)); } else { name = NULL; pr_trace_msg(trace_channel, 8, "unable to verify any DNS names for IP address '%s'", pr_netaddr_get_ipstr(na)); } } else pr_log_debug(DEBUG1, "notice: unable to resolve '%s': %s", buf, hstrerror(errno)); } } else pr_log_debug(DEBUG10, "UseReverseDNS off, returning IP address instead of DNS name"); if (name) { name = pr_inet_validate(name); } else { name = (char *) pr_netaddr_get_ipstr(na); } /* Copy the string into the pr_netaddr_t cache as well, so we only * have to do this once for this pr_netaddr_t. */ memset(na->na_dnsstr, '\0', sizeof(na->na_dnsstr)); sstrncpy(na->na_dnsstr, name, sizeof(na->na_dnsstr)); na->na_have_dnsstr = TRUE; return na->na_dnsstr; }
/* This differs from pr_netaddr_get_ipstr() in that pr_netaddr_get_ipstr() * returns a string of the numeric form of the given network address, whereas * this function returns a string of the DNS name (if present). */ const char *pr_netaddr_get_dnsstr(pr_netaddr_t *na) { char *name = NULL; char buf[256]; if (!na) { errno = EINVAL; return NULL; } /* If this pr_netaddr_t has already been resolved to an DNS string, return the * cached string. */ if (na->na_have_dnsstr) return na->na_dnsstr; if (reverse_dns) { int res = 0; memset(buf, '\0', sizeof(buf)); res = pr_getnameinfo(pr_netaddr_get_sockaddr(na), pr_netaddr_get_sockaddr_len(na), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); if (res == 0) { char **checkaddr; struct hostent *hent = NULL; unsigned char ok = FALSE; int family = pr_netaddr_get_family(na); void *inaddr = pr_netaddr_get_inaddr(na); #ifdef HAVE_GETHOSTBYNAME2 if (pr_netaddr_is_v4mappedv6(na) == TRUE) { family = AF_INET; inaddr = get_v4inaddr(na); } hent = gethostbyname2(buf, family); #else hent = gethostbyname(buf); #endif /* HAVE_GETHOSTBYNAME2 */ if (hent != NULL) { switch (hent->h_addrtype) { case AF_INET: if (family == AF_INET) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #ifdef PR_USE_IPV6 case AF_INET6: if (family == AF_INET6) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #endif /* PR_USE_IPV6 */ } name = ok ? buf : NULL; } else pr_log_debug(DEBUG1, "notice: unable to resolve '%s': %s", buf, hstrerror(errno)); } } if (!name) name = (char *) pr_netaddr_get_ipstr(na); name = pr_inet_validate(name); /* Copy the string into the pr_netaddr_t cache as well, so we only * have to do this once for this pr_netaddr_t. */ memset(na->na_dnsstr, '\0', sizeof(na->na_dnsstr)); sstrncpy(na->na_dnsstr, name, sizeof(na->na_dnsstr)); na->na_have_dnsstr = TRUE; return na->na_dnsstr; }
pr_netacl_t *pr_netacl_create(pool *p, char *aclstr) { pr_netacl_t *acl; char *cp, *aclstr_dup; if (p == NULL || aclstr == NULL) { errno = EINVAL; return NULL; } if (strlen(aclstr) == 0) { errno = EINVAL; return NULL; } /* Parse the given rule into a netacl object. */ acl = pcalloc(p, sizeof(pr_netacl_t)); aclstr_dup = pstrdup(p, aclstr); if (strncasecmp(aclstr, "all", 4) == 0) { aclstr_dup = pstrdup(p, "all"); acl->type = PR_NETACL_TYPE_ALL; } else if (strncasecmp(aclstr, "none", 5) == 0) { aclstr_dup = pstrdup(p, "none"); acl->type = PR_NETACL_TYPE_NONE; } else if ((cp = strchr(aclstr, '/')) != NULL) { char *tmp; acl->type = PR_NETACL_TYPE_IPMASK; *cp = '\0'; /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* We have some type of IP/mask, either IPv4 or IPv6. We know that colons * will only appear in IPv6 addresses, so... */ if (strspn(aclstr, "0123456789ABCDEFabcdef.:") != strlen(aclstr)) { errno = EINVAL; return NULL; } acl->addr = pr_netaddr_get_addr(p, aclstr, NULL); if (acl->addr == NULL) { return NULL; } /* Determine what the given bitmask is. */ acl->masklen = strtol(cp + 1, &tmp, 10); if (tmp && *tmp) { /* Invalid bitmask syntax. */ errno = EINVAL; return NULL; } *cp = '/'; /* Make sure the given mask length is appropriate for the address. */ switch (pr_netaddr_get_family(acl->addr)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). */ if (acl->masklen > 32) { errno = EINVAL; return NULL; } break; } #ifdef PR_USE_IPV6 case AF_INET6: { if (pr_netaddr_use_ipv6()) { if (acl->masklen > 128) { errno = EINVAL; return NULL; } else if (pr_netaddr_is_v4mappedv6(acl->addr) == TRUE && acl->masklen > 32) { /* The admin may be trying to use IPv6-style masks on IPv4-mapped * IPv6 addresses, which of course will not work as expected. * If the mask is 32 bits or more, warn the admin. */ pr_log_pri(PR_LOG_WARNING, "warning: possibly using IPv6-style netmask on IPv4-mapped IPv6 address, which will not work as expected"); pr_trace_msg(trace_channel, 1, "possibly using IPv6-style netmask on IPv4-mapped IPv6 address (%s), which will not work as expected", aclstr); break; } } } #endif /* PR_USE_IPV6 */ default: break; } #ifdef PR_USE_IPV6 } else if (pr_netaddr_use_ipv6() && strspn(aclstr, "0123456789ABCDEFabcdef.:!") != strlen(aclstr)) { #else } else if (strspn(aclstr, "0123456789.!") != strlen(aclstr)) { #endif /* PR_USE_IPV6 */ /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or if the * first character is a '.', then treat the rule as a glob. */ if (strpbrk(aclstr, "{[*?")) { register unsigned int i; size_t aclstr_len = strlen(aclstr); pr_netacl_type_t netacl_type = PR_NETACL_TYPE_IPGLOB; /* Is this a DNS glob, or an IP address glob? To find out, see if there * are any non-IP characters (i.e. alphabetical characters, taking IPv6 * into account). */ for (i = 0; i < aclstr_len; i++) { if (PR_ISALPHA(aclstr[i])) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { if (aclstr[i] == 'A' || aclstr[i] == 'a' || aclstr[i] == 'B' || aclstr[i] == 'b' || aclstr[i] == 'C' || aclstr[i] == 'c' || aclstr[i] == 'D' || aclstr[i] == 'd' || aclstr[i] == 'E' || aclstr[i] == 'e' || aclstr[i] == 'F' || aclstr[i] == 'f') { continue; } netacl_type = PR_NETACL_TYPE_DNSGLOB; break; } else { netacl_type = PR_NETACL_TYPE_DNSGLOB; break; } #else netacl_type = PR_NETACL_TYPE_DNSGLOB; break; #endif /* PR_USE_IPV6 */ } } acl->type = netacl_type; acl->pattern = pstrdup(p, aclstr); } else if (*aclstr == '.') { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrcat(p, "*", aclstr, NULL); } else { acl->type = PR_NETACL_TYPE_DNSMATCH; acl->pattern = pstrdup(p, aclstr); } } else if (strchr(aclstr, '.') == NULL) { /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or if the * first character is a '.', then treat the rule as a glob. */ if (strpbrk(aclstr, "{[*?")) { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrdup(p, aclstr); } else { acl->type = PR_NETACL_TYPE_DNSMATCH; acl->pattern = pstrdup(p, aclstr); } } else { /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* If the last character is a '.', then treat the rule as an IP glob. */ if (aclstr[strlen(aclstr)-1] == '.') { acl->type = PR_NETACL_TYPE_IPGLOB; acl->pattern = pstrcat(p, aclstr, "*", NULL); } else { register unsigned int i; int use_glob = FALSE, use_dns = FALSE; size_t aclstr_len; /* Is this a DNS glob, DNS match, IP glob, or IP match? * * First, check for any glob characters. After that, determine whether * it's a DNS or IP type ACL. */ /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or * if the first character is a '.', then treat the rule as a glob. */ use_glob = (strpbrk(aclstr, "{[*?") != NULL); aclstr_len = strlen(aclstr); for (i = 0; i < aclstr_len; i++) { if (PR_ISALPHA(aclstr[i])) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { if (aclstr[i] == 'A' || aclstr[i] == 'a' || aclstr[i] == 'B' || aclstr[i] == 'b' || aclstr[i] == 'C' || aclstr[i] == 'c' || aclstr[i] == 'D' || aclstr[i] == 'd' || aclstr[i] == 'E' || aclstr[i] == 'e' || aclstr[i] == 'F' || aclstr[i] == 'f') { continue; } use_dns = TRUE; break; } else { use_dns = TRUE; break; } #else use_dns = TRUE; break; #endif /* PR_USE_IPV6 */ } } if (!use_dns) { acl->type = use_glob ? PR_NETACL_TYPE_IPGLOB : PR_NETACL_TYPE_IPMATCH; acl->addr = pr_netaddr_get_addr(p, aclstr, NULL); if (acl->addr == NULL) { return NULL; } } else { if (use_glob) { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrdup(p, aclstr); } else if (*aclstr == '.') { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrcat(p, "*", aclstr, NULL); } else { acl->type = PR_NETACL_TYPE_DNSMATCH; acl->pattern = pstrdup(p, aclstr); } } } } acl->aclstr = aclstr_dup; return acl; }