diff options
Diffstat (limited to 'src/hostspec.c')
-rw-r--r-- | src/hostspec.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/src/hostspec.c b/src/hostspec.c new file mode 100644 index 0000000..adbad53 --- /dev/null +++ b/src/hostspec.c @@ -0,0 +1,166 @@ +#include "common.h" +#include "hostspec.h" +#include "heap.h" +#include "network.h" + +/* + * Fills in the netmask array given a numeric value. + * + * Returns: + * 0 on success + * -1 on failure (invalid mask value) + * + */ +static int +fill_netmask_array (char *bitmask_string, int v6, + unsigned char array[], size_t len) +{ + unsigned int i; + unsigned long int mask; + char *endptr; + + errno = 0; /* to distinguish success/failure after call */ + mask = strtoul (bitmask_string, &endptr, 10); + + /* check for various conversion errors */ + if ((errno == ERANGE && mask == ULONG_MAX) + || (errno != 0 && mask == 0) || (endptr == bitmask_string)) + return -1; + + if (v6 == 0) { + /* The mask comparison is done as an IPv6 address, so + * convert to a longer mask in the case of IPv4 + * addresses. */ + mask += 12 * 8; + } + + /* check valid range for a bit mask */ + if (mask > (8 * len)) + return -1; + + /* we have a valid range to fill in the array */ + for (i = 0; i != len; ++i) { + if (mask >= 8) { + array[i] = 0xff; + mask -= 8; + } else if (mask > 0) { + array[i] = (unsigned char) (0xff << (8 - mask)); + mask = 0; + } else { + array[i] = 0; + } + } + + return 0; +} + + +/* parse a location string containing either an ipv4/ipv4 + hostmask tuple + or a dnsname into a struct hostspec. + returns 0 on success, non-0 on error (might be memory allocation, bogus + ip address or mask). +*/ +int hostspec_parse(char *location, struct hostspec *h) { + char *mask, ip_dst[IPV6_LEN]; + + h->type = HST_NONE; + if(!location) return 0; + + memset(h, 0, sizeof(*h)); + if ((mask = strrchr(location, '/'))) + *(mask++) = 0; + + /* + * Check for a valid IP address (the simplest case) first. + */ + if (full_inet_pton (location, ip_dst) > 0) { + h->type = HST_NUMERIC; + memcpy (h->address.ip.network, ip_dst, IPV6_LEN); + if(!mask) memset (h->address.ip.mask, 0xff, IPV6_LEN); + else { + char dst[sizeof(struct in6_addr)]; + int v6, i; + /* Check if the IP address before the netmask is + * an IPv6 address */ + if (inet_pton(AF_INET6, location, dst) > 0) + v6 = 1; + else + v6 = 0; + + if (fill_netmask_array + (mask, v6, &(h->address.ip.mask[0]), IPV6_LEN) + < 0) + goto err; + + for (i = 0; i < IPV6_LEN; i++) + h->address.ip.network[i] = ip_dst[i] & + h->address.ip.mask[i]; + } + } else { + /* either bogus IP or hostname */ + /* bogus ipv6 ? */ + if (mask || strchr (location, ':')) + goto err; + + /* In all likelihood a string */ + h->type = HST_STRING; + h->address.string = safestrdup (location); + if (!h->address.string) + goto err; + } + /* restore mask */ + if(mask) *(--mask) = '/'; + return 0; +err:; + if(mask) *(--mask) = '/'; + return -1; +} + +static int string_match(const char *ip, const char *addrspec) +{ + size_t test_length, match_length; + if(!strcasecmp(ip, addrspec)) return 1; + if(addrspec[0] != '.') return 0; + test_length = strlen (ip); + match_length = strlen (addrspec); + if (test_length < match_length) return 0; + return (strcasecmp + (ip + (test_length - match_length), + addrspec) == 0); +} + +static int numeric_match(const uint8_t addr[], const struct hostspec *h) +{ + uint8_t x, y; + int i; + + for (i = 0; i != IPV6_LEN; ++i) { + x = addr[i] & h->address.ip.mask[i]; + y = h->address.ip.network[i]; + + /* If x and y don't match, the IP addresses don't match */ + if (x != y) + return 0; + } + + return 1; +} + +/* check whether ip matches hostspec. + return 1 on match, 0 on non-match */ +int hostspec_match(const char *ip, const struct hostspec *h) { + int is_numeric_addr; + uint8_t numeric_addr[IPV6_LEN]; + if (ip[0] == '\0') return 0; + is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0); + switch (h->type) { + case HST_STRING: + if(is_numeric_addr) return 0; + return string_match (ip, h->address.string); + case HST_NUMERIC: + return numeric_match (numeric_addr, h); + case HST_NONE: + return 0; + } + return 0; +} |