summaryrefslogtreecommitdiffhomepage
path: root/src/hostspec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hostspec.c')
-rw-r--r--src/hostspec.c166
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;
+}