459050b11ca2d48f26e358a5c7b2db587ffbd2ca galt Sat Jun 29 13:13:45 2019 -0700 Fixed minor bug found while testing new CIDR-enabled subnet filter for programs that listen for connections and filter clients. diff --git src/lib/internet.c src/lib/internet.c index 81cbc51..2b97abf 100644 --- src/lib/internet.c +++ src/lib/internet.c @@ -1,264 +1,263 @@ /* internet - some stuff to make it easier to use * internet sockets and the like. */ /* Copyright (C) 2011 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "internet.h" boolean internetIsDottedQuad(char *s) /* Returns TRUE if it looks like s is a dotted quad. */ { int i; if (!isdigit(s[0])) return FALSE; for (i=0; i<3; ++i) { s = strchr(s, '.'); if (s == NULL) return FALSE; s += 1; if (!isdigit(s[0])) return FALSE; } return TRUE; } bits32 internetHostIp(char *hostName) /* Get IP v4 address (in host byte order) for hostName. * Warn and return 0 if there's a problem. */ { bits32 ret; if (internetIsDottedQuad(hostName)) { internetDottedQuadToIp(hostName, &ret); } else { /* getaddrinfo is thread-safe and widely supported */ struct addrinfo hints, *res; struct in_addr addr; int err; zeroBytes(&hints, sizeof(hints)); hints.ai_family = AF_INET; if ((err = getaddrinfo(hostName, NULL, &hints, &res)) != 0) { warn("getaddrinfo() error on hostName=%s: %s\n", hostName, gai_strerror(err)); return 0; } addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr; ret = ntohl((uint32_t)addr.s_addr); freeaddrinfo(res); } return ret; } boolean internetFillInAddress(char *hostName, int port, struct sockaddr_in *address) /* Fill in address. Return FALSE if can't. */ { ZeroVar(address); address->sin_family = AF_INET; address->sin_port = htons(port); if (hostName == NULL) address->sin_addr.s_addr = INADDR_ANY; else { if ((address->sin_addr.s_addr = htonl(internetHostIp(hostName))) == 0) return FALSE; } return TRUE; } boolean internetIpToDottedQuad(bits32 ip, char dottedQuad[17]) /* Convert IP4 address in host byte order to dotted quad * notation. Warn and return FALSE if there's a * problem. */ { #ifndef __CYGWIN32__ struct in_addr ia; zeroBytes(dottedQuad, 17); ZeroVar(&ia); ia.s_addr = htonl(ip); if (inet_ntop(AF_INET, &ia, dottedQuad, 16) == NULL) { warn("conversion problem on 0x%x in internetIpToDottedQuad: %s", ip, strerror(errno)); return FALSE; } return TRUE; #else warn("Sorry, internetIpToDottedQuad not supported in Windows."); return FALSE; #endif } boolean internetDottedQuadToIp(char *dottedQuad, bits32 *retIp) /* Convert dotted quad format address to IP4 address in * host byte order. Warn and return FALSE if there's a * problem. */ { #ifndef __CYGWIN32__ struct in_addr ia; if (inet_pton(AF_INET, dottedQuad, &ia) < 0) { warn("internetDottedQuadToIp problem on %s: %s", dottedQuad, strerror(errno)); return FALSE; } *retIp = ntohl(ia.s_addr); return TRUE; #else warn("Sorry, internetDottedQuadToIp not supported in Windows."); return FALSE; #endif } void internetParseDottedQuad(char *dottedQuad, unsigned char quad[4]) /* Parse dotted quads into quad */ { char *s = dottedQuad; int i; if (!internetIsDottedQuad(s)) errAbort("%s is not a dotted quad", s); for (i=0; i<4; ++i) { quad[i] = atoi(s); s = strchr(s, '.') + 1; } } void internetUnpackIp(bits32 packed, unsigned char unpacked[4]) /* Convert from 32 bit to 4-byte format with most significant * byte first. */ { int i; for (i=3; i>=0; --i) { unpacked[i] = (packed&0xff); packed >>= 8; } } bits32 internetPackIp(unsigned char unpacked[4]) /* Convert from 4-byte format with most significant * byte first to native 32-bit format. */ { int i; bits32 packed = 0; for (i=0; i<=3; ++i) { packed <<= 8; packed |= unpacked[i]; } return packed; } void internetCidrRange(struct cidr *cidr, bits32 *pStartIp, bits32 *pEndIp) /* get range of CIDR formatted subnet as start and end IPs. */ { bits32 packedIp = cidr->ip; int bits = cidr->subnetLength; int r = 32 - bits; bits32 start = packedIp & (((unsigned int) 0xFFFFFFFF) << r); bits32 end; // shr or shl 32 of a 32-bit value // does nothing at all rather than turning it to zeroes. if (bits == 32) end = packedIp; else end = packedIp | (((unsigned int) 0xFFFFFFFF) >> bits); *pStartIp = start; *pEndIp = end; } boolean internetIpInSubnetCidr(unsigned char unpackedIp[4], struct cidr *cidr) /* Return true if unpacked IP address is in subnet cidr. */ { bits32 subnetIp = cidr->ip; -//printf("packed32 bits=%u %08x\n", subnetIp, subnetIp); // DEBUG REMOVE int r = 32 - cidr->subnetLength; -bits32 caremask = subnetIp & (((unsigned int) 0xFFFFFFFF) << r); +bits32 caremask = ((unsigned int) 0xFFFFFFFF) << r; bits32 packedIp = internetPackIp(unpackedIp); if ((subnetIp & caremask) == (packedIp & caremask)) return TRUE; return FALSE; } static void notGoodSubnetCidr(char *sns) /* Complain about subnet format. */ { errAbort("'%s' is not a properly formatted subnet. Subnets must consist of\n" "one to four dot-separated numbers between 0 and 255 \n" "optionally followed by an slash and subnet bit length integer between 1 and 32.\n" "A trailing dot on the subnet IP address is not allowed.", sns); } struct cidr *internetParseSubnetCidr(char *cidr) /* parse input CIDR format IP for range or subnet */ { if (!cidr) return NULL; char *s = cloneString(cidr); char *c = strchr(s, '/'); char *ip = s; int bits = -1; if (c) // has slash { *c++ = 0; bits = atoi(c); } if (ip[strlen(ip)-1] == '.') // trailing dot not allowed in subnet ip notGoodSubnetCidr(cidr); char *snsCopy = cloneString(ip); char expIp[17]; char *words[5]; int wordCount, i; expIp[0] = 0; wordCount = chopByChar(snsCopy, '.', words, ArraySize(words)); if (wordCount > 4 || wordCount < 1) notGoodSubnetCidr(cidr); for (i=0; i<4; ++i) { int x = 0; // does not matter what these bits are, it is just filler. char *s = "0"; if (i<wordCount) { s = words[i]; if (!isdigit(s[0])) notGoodSubnetCidr(cidr); x = atoi(s); if (x > 255) notGoodSubnetCidr(cidr); } safecat(expIp, sizeof expIp, s); if (i < 3) safecat(expIp, sizeof expIp, "."); } if (bits == -1) bits = 8 * wordCount; if ((bits > 32) || (bits < 1)) notGoodSubnetCidr(cidr); ip = expIp; unsigned char quadIp[4]; internetParseDottedQuad(ip, quadIp); bits32 packedIp = 0; packedIp = internetPackIp(quadIp); struct cidr *result; AllocVar(result); result->ip = packedIp; result->subnetLength = bits; return result; }