/** * Test that an ip address is within a range specified by a CIDR in the same address family (v4 or v6). * * @return AM_SUCCESS on match else NOT_FOUND */ static am_status_t get_in_masked_range_status(const char * addr, const char * range) { struct in_addr addr4; struct in6_addr addr6; int bits; if (read_full_ip(addr, &addr4)) { struct in_addr cidr; if (read_ip(range, &cidr, &bits)) { return cidr_match(&addr4, &cidr, bits) ? AM_SUCCESS : AM_NOT_FOUND; } } else if (read_full_ip6(addr, &addr6)) { struct in6_addr cidr; if (read_ip6(range, &cidr, &bits)) { return cidr6_match(&addr6, &cidr, bits) ? AM_SUCCESS : AM_NOT_FOUND; } } return AM_NOT_FOUND; }
/* same as the above but only for a single incoming ip address */ int match_ips (char *remote, char **ips, int index) { int i, ret = 0; int result = 0; struct addrinfo hint; struct addrinfo *remres = 0; struct addrinfo *testres = 0; struct sockaddr_in *remaddr = NULL; struct sockaddr_in *testaddr = NULL; struct sockaddr_in6 *remaddr6 = NULL; struct sockaddr_in6 *testaddr6 = NULL; char *ip; char *mask; char *tmpip; int bits = 32; // the default mask if they don't include a mask bool ipv6 = false; memset(&hint, '\0', sizeof hint); hint.ai_family = AF_UNSPEC; /* get the info for the remote ip address.*/ ret = getaddrinfo(remote, NULL, &hint, &remres); if (ret != 0){ // we shouldn't see a bad address here but we should check anyway log_error("getaddrinfo: %s (likely an invalid remote ip address)\n", gai_strerror(ret)); free(remres); return 0; } // cast the address information to the appropriate struct if (remres->ai_family == AF_INET) remaddr = (struct sockaddr_in*)remres->ai_addr; else { remaddr6 = (struct sockaddr_in6*)remres->ai_addr; ipv6 = true; } for (i = 0; i < index; i++) { /* use strndupa because strdup and free was causing odd errors in valgrind * i think this is because of what was happening with the strtok */ tmpip = strndupa(ips[i], strlen(ips[i])); // we need to examine the incoming address to see if it has a slash // indicating that it is being masked. ip = strtok(tmpip, "/"); mask = strtok(NULL, "\0"); // convert whatever we have into an int if (mask != NULL) { bits = atoi(mask); } // if the int is outside of the range then set it to the narrowest possible // as you can see we aren't supporting a /0 mask. TODO: fix it so we can. if ((bits == 0 || bits > 32) && !ipv6) { bits = 32; } else if ((bits == 0 || bits > 128) && ipv6) { bits = 128; } // go through the above for each ip address we are testing against ret = getaddrinfo(ip, NULL, &hint, &testres); if (ret != 0) { log_error("getaddrinfo: %s (likely an invalid user defined ip address)\n", gai_strerror(ret)); continue; } if (testres->ai_family == AF_INET) testaddr = (struct sockaddr_in*)testres->ai_addr; else testaddr6 = (struct sockaddr_in6*)testres->ai_addr; if (remres->ai_family == testres->ai_family) { if (remres->ai_family == AF_INET) { if (cidr4_match(remaddr->sin_addr.s_addr, testaddr->sin_addr.s_addr, bits)) { result = 1; freeaddrinfo(testres); goto Cleanup; } } else { /* this hasn't been tested as of yet */ if (cidr6_match(remaddr6, testaddr6, bits)) { result = 1; freeaddrinfo(testres); goto Cleanup; } } } freeaddrinfo(testres); } Cleanup: freeaddrinfo(remres); return result; }