| /* |
| * cidr_from_str() - Generate a CIDR structure from a string in addr/len |
| * form. |
| */ |
| #include "config.h" |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdio.h> /* I'm always stuffing debug printf's into here */ |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| |
| #include "../include/cidr.h" |
| |
| CIDR *cidr_from_str(const char *addr) |
| { |
| size_t alen; |
| CIDR *toret, *ctmp; |
| const char *pfx, *buf; |
| char *buf2; /* strtoul() can't use a (const char *) */ |
| int i, j; |
| int pflen; |
| unsigned long octet; |
| int nocts, eocts; |
| short foundpf, foundmask, nsect; |
| alen = strlen(addr); |
| |
| /* There has to be *SOMETHING* to work with */ |
| if (alen == 0) { |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* And we know it can only contain a given set of chars */ |
| buf = addr + strspn(addr, "0123456789abcdefABCDEFxX.:/in-rpt"); |
| if (*buf != '\0') { |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| toret = cidr_alloc(); |
| |
| /* First check if we're a PTR-style string */ |
| /* |
| * XXX This could be folded with *pfx; they aren't used in code paths |
| * that overlap. I'm keeping them separate just to keep my sanity |
| * though. |
| */ |
| buf = NULL; |
| /* Handle the deprecated RFC1886 form of v6 PTR */ |
| if ((alen >= 8) && strcasecmp(addr + alen - 8, ".ip6.int") == 0) { |
| toret->proto = CIDR_IPV6; |
| buf = addr + alen - 8; |
| } |
| |
| if (buf != NULL |
| || ((alen >= 5) && strcasecmp(addr + alen - 5, ".arpa") == 0)) { |
| /* |
| * Do all this processing here, instead of trying to intermix it |
| * with the rest of the formats. This might lead to some code |
| * duplication, but it'll be easier to read. |
| */ |
| if (buf == NULL) { /* If not set by .ip6.int above */ |
| /* First, see what protocol it is */ |
| if ((alen >= 9) |
| && strncasecmp(addr + alen - 9, ".ip6", 3) == 0) { |
| toret->proto = CIDR_IPV6; |
| buf = addr + alen - 9; |
| } else if ((alen >= 13) |
| && strncasecmp(addr + alen - 13, ".in-addr", |
| 7) == 0) { |
| toret->proto = CIDR_IPV4; |
| buf = addr + alen - 13; |
| } else { |
| /* Unknown */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| } |
| /* |
| * buf now points to the period after the last (first) bit of |
| * address numbering in the PTR name. |
| */ |
| |
| /* |
| * Now convert based on that protocol. Note that we're going to |
| * be slightly asymmetrical to the way cidr_to_str() works, in |
| * how we handle the netmask. cidr_to_str() ignores it, and |
| * treats the PTR-style output solely as host addresses. We'll |
| * use the netmask bits to specify how much of the address is |
| * given in the PTR we get. That is, if we get |
| * "3.2.1.in-addr.arpa", we'll set a /24 netmask on the returned |
| * result. This way, the calling program can tell the difference |
| * between "3.2.1..." and "0.3.2.1..." if it really cares to. |
| */ |
| buf--; /* Step before the period */ |
| if (toret->proto == CIDR_IPV4) { |
| for (i = 11; i <= 14; /* */ ) { |
| /* If we're before the beginning, we're done */ |
| if (buf < addr) |
| break; |
| |
| /* Step backward until we at the start of an octet */ |
| while (isdigit(*buf) && buf >= addr) |
| buf--; |
| |
| /* |
| * Save that number (++i here to show that this octet is |
| * now set. |
| */ |
| octet = strtoul(buf + 1, NULL, 10); |
| if (octet > (unsigned long)0xff) { |
| /* Bad octet! No biscuit! */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| toret->addr[++i] = octet; |
| |
| /* |
| * Back up a step to get before the '.', and process the |
| * next [previous] octet. If we were at the beginning of |
| * the string already, the test at the top of the loop |
| * will drop us out. |
| */ |
| buf--; |
| } |
| |
| /* Too much? */ |
| if (buf >= addr) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * Now, what about the mask? We set the netmask bits to |
| * describe how much information we've actually gotten, if we |
| * didn't get all 4 octets. Because of the way .in-addr.arpa |
| * works, the mask can only fall on an octet boundary, so we |
| * don't need too many fancy tricks. 'i' is still set from |
| * the above loop to whatever the last octet we filled in is, |
| * so we don't even have to special case anything. |
| */ |
| for (j = 0; j <= i; j++) |
| toret->mask[j] = 0xff; |
| |
| /* Done processing */ |
| } else if (toret->proto == CIDR_IPV6) { |
| /* |
| * This processing happens somewhat similarly to IPV4 above, |
| * the format is simplier, and we need to be a little |
| * sneakier about the mask, since it can fall on a half-octet |
| * boundary with .ip6.arpa format. |
| */ |
| for (i = 0; i <= 15; i++) { |
| /* If we're before the beginning, we're done */ |
| if (buf < addr) |
| break; |
| |
| /* We better point at a number */ |
| if (!isxdigit(*buf)) { |
| /* Bad input */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Save the current number */ |
| octet = strtoul(buf, NULL, 16); |
| if (octet > (unsigned long)0xff) { |
| /* Bad octet! No biscuit! */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| toret->addr[i] = octet << 4; |
| toret->mask[i] = 0xf0; |
| |
| /* If we're at the beginning of the string, we're thru */ |
| if (buf == addr) { |
| /* Shift back to skip error condition at end of loop */ |
| buf--; |
| break; |
| } |
| |
| /* If we're not, stepping back should give us a period */ |
| if (*--buf != '.') { |
| /* Bad input */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Stepping back again should give us a number */ |
| if (!isxdigit(*--buf)) { |
| /* Bad input */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Save that one */ |
| octet = strtoul(buf, NULL, 16); |
| if (octet > (unsigned long)0xff) { |
| /* Bad octet! No biscuit! */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| toret->addr[i] |= octet & 0x0f; |
| toret->mask[i] |= 0x0f; |
| |
| /* |
| * Step back and loop back around. If that last step |
| * back moves us to before the beginning of the string, |
| * the condition at the top of the loop will drop us out. |
| */ |
| while (*--buf == '.' && buf >= addr) |
| /* nothing */ ; |
| } |
| |
| /* Too much? */ |
| if (buf >= addr) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Mask is set in the loop for v6 */ |
| } else { |
| /* Shouldn't happen */ |
| cidr_free(toret); |
| errno = ENOENT; /* Bad choice of errno */ |
| return (NULL); |
| } |
| |
| /* Return the value we built up, and we're done! */ |
| return (toret); |
| |
| /* NOTREACHED */ |
| } |
| buf = NULL; /* Done */ |
| |
| /* |
| * It's not a PTR form, so find the '/' prefix marker if we can. We |
| * support both prefix length and netmasks after the /, so flag if we |
| * find a mask. |
| */ |
| foundpf = foundmask = 0; |
| for (i = alen - 1; i >= 0; i--) { |
| /* Handle both possible forms of netmasks */ |
| if (addr[i] == '.' || addr[i] == ':') |
| foundmask = 1; |
| |
| /* Are we at the beginning of the prefix? */ |
| if (addr[i] == '/') { |
| foundpf = 1; |
| break; |
| } |
| } |
| |
| if (foundpf == 0) { |
| /* We didn't actually find a prefix, so reset the foundmask */ |
| foundmask = 0; |
| |
| /* |
| * pfx is only used if foundpf==1, but set it to NULL here to |
| * quiet gcc down. |
| */ |
| pfx = NULL; |
| } else { |
| /* Remember where the prefix is */ |
| pfx = addr + i; |
| |
| if (foundmask == 0) { |
| /* |
| * If we didn't find a netmask, it may be that it's one of |
| * the v4 forms without dots. Technically, it COULD be |
| * expressed as a single (32-bit) number that happens to be |
| * between 0 and 32 inclusive, so there's no way to be |
| * ABSOLUTELY sure when we have a prefix length and not a |
| * netmask. But, that would be a non-contiguous netmask, |
| * which we don't attempt to support, so we can probably |
| * safely ignore that case. So try a few things... |
| */ |
| /* If it's a hex or octal number, assume it's a mask */ |
| if (pfx[1] == '0' && tolower(pfx[2]) == 'x') |
| foundmask = 1; /* Hex */ |
| else if (pfx[1] == '0') |
| foundmask = 1; /* Oct */ |
| else if (isdigit(pfx[1])) { |
| /* |
| * If we get here, it looks like a decimal number, and we |
| * know there aren't any periods or colons in it, so if |
| * it's valid, it can ONLY be a single 32-bit decimal |
| * spanning the whole 4-byte v4 address range. If that's |
| * true, it's GOTTA be a valid number, it's GOTTA reach |
| * to the end of the strong, and it's GOTTA be at least |
| * 2**31 and less than 2**32. |
| */ |
| octet = strtoul(pfx + 1, &buf2, 10); |
| if (*buf2 == '\0' |
| && octet >= (unsigned long)(1 << 31) |
| && octet <= (unsigned long)0xffffffff) |
| foundmask = 1; /* Valid! */ |
| |
| octet = 0; |
| buf2 = NULL; /* Done */ |
| } |
| } |
| } |
| i = 0; /* Done */ |
| |
| /* |
| * Now, let's figure out what kind of address this is. A v6 address |
| * will contain a : within the first 5 characters ('0000:'), a v4 |
| * address will have a . within the first 4 ('123.'), UNLESS it's |
| * just a single number (in hex, octal, or decimal). Anything else |
| * isn't an address we know anything about, so fail. |
| */ |
| if ((buf = strchr(addr, ':')) != NULL && (buf - addr) <= 5) |
| toret->proto = CIDR_IPV6; |
| else if ((buf = strchr(addr, '.')) != NULL && (buf - addr) <= 4) |
| toret->proto = CIDR_IPV4; |
| else { |
| /* |
| * Special v4 forms |
| */ |
| if (*addr == '0' && tolower(*(addr + 1)) == 'x') { |
| /* Hex? */ |
| buf = |
| (addr + 2) + strspn(addr + 2, |
| "0123456789abcdefABCDEF"); |
| if (*buf == '\0' || *buf == '/') |
| toret->proto = CIDR_IPV4; /* Yep */ |
| } else if (*addr == '0') { |
| /* Oct? */ |
| /* (note: this also catches the [decimal] address '0' */ |
| buf = (addr + 1) + strspn(addr + 1, "01234567"); |
| if (*buf == '\0' || *buf == '/') |
| toret->proto = CIDR_IPV4; /* Yep */ |
| } else { |
| /* Dec? */ |
| buf = (addr) + strspn(addr, "0123456789"); |
| if (*buf == '\0' || *buf == '/') |
| toret->proto = CIDR_IPV4; /* Yep */ |
| } |
| |
| /* Did we catch anything? */ |
| if (toret->proto == 0) { |
| /* Unknown */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| } |
| buf = NULL; /* Done */ |
| |
| /* |
| * So now we know what sort of address it is, we can go ahead and |
| * have a parser for either. |
| */ |
| if (toret->proto == CIDR_IPV4) { |
| /* |
| * Parse a v4 address. Now, we're being a little tricksy here, |
| * and parsing it from the end instead of from the front. |
| */ |
| |
| /* |
| * First, find out how many bits we have. We need to have 4 or |
| * less... |
| */ |
| buf = strchr(addr, '.'); |
| /* Through here, nsect counts dots */ |
| for (nsect = 0; buf != NULL && (pfx != NULL ? buf < pfx : 1); |
| buf = strchr(buf, '.')) { |
| nsect++; /* One more section */ |
| buf++; /* Move past . */ |
| if (nsect > 3) { |
| /* Bad! We can't have more than 4 sections... */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| } |
| buf = NULL; /* Done */ |
| nsect++; /* sects = dots+1 */ |
| |
| /* |
| * First, initialize this so we can skip building the bits if we |
| * don't have to. |
| */ |
| pflen = -1; |
| |
| /* |
| * Initialize the first 12 octets of the address/mask to look |
| * like a v6-mapped address. This is the correct info for those |
| * octets to have if/when we decide to use this v4 address as a |
| * v6 one. |
| */ |
| for (i = 0; i <= 9; i++) |
| toret->addr[i] = 0; |
| for (i = 10; i <= 11; i++) |
| toret->addr[i] = 0xff; |
| for (i = 0; i <= 11; i++) |
| toret->mask[i] = 0xff; |
| |
| /* |
| * Handle the prefix/netmask. If it's not set at all, slam it to |
| * the maximum, and put us at the end of the string to start out. |
| * Ditto if the '/' is the end of the string. |
| */ |
| if (foundpf == 0) { |
| pflen = 32; |
| i = alen - 1; |
| } else if (foundpf == 1 && *(pfx + 1) == '\0') { |
| pflen = 32; |
| i = pfx - addr - 1; |
| } |
| |
| /* |
| * Or, if we found it, and it's a NETMASK, we need to parse it |
| * just like an address. So, cheat a little and call ourself |
| * recursively, and then just count the bits in our returned |
| * address for the pflen. |
| */ |
| if (foundpf == 1 && foundmask == 1 && pflen == -1) { |
| ctmp = cidr_from_str(pfx + 1); |
| if (ctmp == NULL) { |
| /* This shouldn't happen */ |
| cidr_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| /* Stick it in the mask */ |
| for (i = 0; i <= 11; i++) |
| ctmp->mask[i] = 0; |
| for (i = 12; i <= 15; i++) |
| ctmp->mask[i] = ctmp->addr[i]; |
| |
| /* Get our prefix length */ |
| pflen = cidr_get_pflen(ctmp); |
| cidr_free(ctmp); |
| if (pflen == -1) { |
| /* Failed; probably non-contiguous */ |
| cidr_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| |
| /* And set us to before the '/' like below */ |
| i = pfx - addr - 1; |
| } |
| |
| /* |
| * Finally, if we did find it and it's a normal prefix length, |
| * just pull it it, parse it out, and set ourselves to the first |
| * character before the / for the address reading |
| */ |
| if (foundpf == 1 && foundmask == 0 && pflen == -1) { |
| pflen = (int)strtol(pfx + 1, NULL, 10); |
| i = pfx - addr - 1; |
| } |
| |
| /* |
| * If pflen is set, we need to turn it into a mask for the bits. |
| * XXX pflen actually should ALWAYS be set, so we might not need |
| * to make this conditional at all... |
| */ |
| if (pflen > 0) { |
| /* 0 < pflen <= 32 */ |
| if (pflen < 0 || pflen > 32) { |
| /* Always bad */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * Now pflen is in the 0...32 range and thus good. Set it in |
| * the structure. Note that memset zero'd the whole thing to |
| * start. We ignore mask[<12] with v4 addresses normally, |
| * but they're already set to all-1 anyway, since if we ever |
| * DO care about them, that's the most appropriate thing for |
| * them to be. |
| * |
| * This is a horribly grody set of macros. I'm only using |
| * them here to test them out before using them in the v6 |
| * section, where I'll need them more due to the sheer number |
| * of clauses I'll have to get written. Here's the straight |
| * code I had written that the macro should be writing for me |
| * now: |
| * |
| * if(pflen>24) |
| * for(j=24 ; j<pflen ; j++) |
| * toret->mask[15] |= 1<<(31-j); |
| * if(pflen>16) |
| * for(j=16 ; j<pflen ; j++) |
| * toret->mask[14] |= 1<<(23-j); |
| * if(pflen>8) |
| * for(j=8 ; j<pflen ; j++) |
| * toret->mask[13] |= 1<<(15-j); |
| * if(pflen>0) |
| * for(j=0 ; j<pflen ; j++) |
| * toret->mask[12] |= 1<<(7-j); |
| */ |
| #define UMIN(x,y) ((x)<(y)?(x):(y)) |
| #define MASKNUM(x) (24-((15-x)*8)) |
| #define WRMASKSET(x) \ |
| if(pflen>MASKNUM(x)) \ |
| for(j=MASKNUM(x) ; j<UMIN(pflen,MASKNUM(x)+8) ; j++) \ |
| toret->mask[x] |= 1<<(MASKNUM(x)+7-j); |
| |
| WRMASKSET(15); |
| WRMASKSET(14); |
| WRMASKSET(13); |
| WRMASKSET(12); |
| |
| #undef WRMASKET |
| #undef MASKNUM |
| #undef UMIN |
| } |
| |
| /* Normal v4 prefix */ |
| /* |
| * Now we have 4 octets to grab. If any of 'em fail, or are |
| * outside the 0...255 range, bomb. |
| */ |
| nocts = 0; |
| |
| /* Here, i should be before the /, but we may have multiple */ |
| while (i > 0 && addr[i] == '/') |
| i--; |
| |
| for ( /* i */ ; i >= 0; i--) { |
| /* |
| * As long as it's still a number or an 'x' (as in '0x'), |
| * keep backing up. Could be hex, so don't just use |
| * isdigit(). |
| */ |
| if ((isxdigit(addr[i]) || tolower(addr[i]) == 'x') |
| && i > 0) |
| continue; |
| |
| /* |
| * It's no longer a number. So, grab the number we just |
| * moved before. |
| */ |
| /* Cheat for "beginning-of-string" rather than "NaN" */ |
| if (i == 0) |
| i--; |
| /* Theoretically, this can be in hex/oct/dec... */ |
| if (addr[i + 1] == '0' && tolower(addr[i + 2]) == 'x') |
| octet = strtoul(addr + i + 1, &buf2, 16); |
| else if (addr[i + 1] == '0') |
| octet = strtoul(addr + i + 1, &buf2, 8); |
| else |
| octet = strtoul(addr + i + 1, &buf2, 10); |
| |
| /* If buf isn't pointing at one of [./'\0'], it's screwed */ |
| if (!(*buf2 == '.' || *buf2 == '/' || *buf2 == '\0')) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| buf2 = NULL; /* Done */ |
| |
| /* |
| * Now, because of the way compressed IPv4 addresses work, |
| * this number CAN be greater than 255, IF it's the last bit |
| * in the address (the first bit we parse), in which case it |
| * must be no bigger than needed to fill the unaccounted-for |
| * 'slots' in the address. |
| * |
| * See |
| * <http://www.opengroup.org/onlinepubs/007908799/xns/inet_addr.html> |
| * for details. |
| */ |
| if ((nocts != 0 && octet > 255) |
| || (nocts == 0 |
| && octet > (0xffffffff >> (8 * (nsect - 1))))) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Save the lower 8 bits into this octet */ |
| toret->addr[15 - nocts++] = octet & 0xff; |
| |
| /* |
| * If this is the 'last' piece of the address (the first we |
| * process), and there are fewer than 4 pieces total, we need |
| * to extend it out into additional fields. See above |
| * reference. |
| */ |
| if (nocts == 1) { |
| if (nsect <= 3) |
| toret->addr[15 - nocts++] = |
| (octet >> 8) & 0xff; |
| if (nsect <= 2) |
| toret->addr[15 - nocts++] = |
| (octet >> 16) & 0xff; |
| if (nsect == 1) |
| toret->addr[15 - nocts++] = |
| (octet >> 24) & 0xff; |
| } |
| |
| /* |
| * If we've got 4 of 'em, we're actually done. We got the |
| * prefix above, so just return direct from here. |
| */ |
| if (nocts == 4) |
| return (toret); |
| } |
| |
| /* |
| * If we get here, it failed to get all 4. That shouldn't |
| * happen, since we catch proper abbreviated forms above. |
| */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } else if (toret->proto == CIDR_IPV6) { |
| /* |
| * Parse a v6 address. Like the v4, we start from the end and |
| * parse backward. However, to handle compressed form, if we hit |
| * a ::, we drop off and start parsing from the beginning, |
| * because at the end we'll then have a hole that is what the :: |
| * is supposed to contain, which is already automagically 0 from |
| * the memset() we did earlier. Neat! |
| * |
| * Initialize the prefix length |
| */ |
| pflen = -1; |
| |
| /* If no prefix was found, assume the max */ |
| if (foundpf == 0) { |
| pflen = 128; |
| /* Stretch back to the end of the string */ |
| i = alen - 1; |
| } else if (foundpf == 1 && *(pfx + 1) == '\0') { |
| pflen = 128; |
| i = pfx - addr - 1; |
| } |
| |
| /* |
| * If we got a netmask, rather than a prefix length, parse it and |
| * count the bits, like we did for v4. |
| */ |
| if (foundpf == 1 && foundmask == 1 && pflen == -1) { |
| ctmp = cidr_from_str(pfx + 1); |
| if (ctmp == NULL) { |
| /* This shouldn't happen */ |
| cidr_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| /* Stick it in the mask */ |
| for (i = 0; i <= 15; i++) |
| ctmp->mask[i] = ctmp->addr[i]; |
| |
| /* Get the prefix length */ |
| pflen = cidr_get_pflen(ctmp); |
| cidr_free(ctmp); |
| if (pflen == -1) { |
| /* Failed; probably non-contiguous */ |
| cidr_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| |
| /* And set us to before the '/' like below */ |
| i = pfx - addr - 1; |
| } |
| |
| /* Finally, the normal prefix case */ |
| if (foundpf == 1 && foundmask == 0 && pflen == -1) { |
| pflen = (int)strtol(pfx + 1, NULL, 10); |
| i = pfx - addr - 1; |
| } |
| |
| /* |
| * Now, if we have a pflen, turn it into a mask. |
| * XXX pflen actually should ALWAYS be set, so we might not need |
| * to make this conditional at all... |
| */ |
| if (pflen > 0) { |
| /* Better be 0...128 */ |
| if (pflen < 0 || pflen > 128) { |
| /* Always bad */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * Now save the pflen. See comments on the similar code up in |
| * the v4 section about the macros. |
| */ |
| #define UMIN(x,y) ((x)<(y)?(x):(y)) |
| #define MASKNUM(x) (120-((15-x)*8)) |
| #define WRMASKSET(x) \ |
| if(pflen>MASKNUM(x)) \ |
| for(j=MASKNUM(x) ; j<UMIN(pflen,MASKNUM(x)+8) ; j++) \ |
| toret->mask[x] |= 1<<(MASKNUM(x)+7-j); |
| |
| WRMASKSET(15); |
| WRMASKSET(14); |
| WRMASKSET(13); |
| WRMASKSET(12); |
| WRMASKSET(11); |
| WRMASKSET(10); |
| WRMASKSET(9); |
| WRMASKSET(8); |
| WRMASKSET(7); |
| WRMASKSET(6); |
| WRMASKSET(5); |
| WRMASKSET(4); |
| WRMASKSET(3); |
| WRMASKSET(2); |
| WRMASKSET(1); |
| WRMASKSET(0); |
| |
| #undef WRMASKET |
| #undef MASKNUM |
| #undef UMIN |
| } |
| |
| /* |
| * Now we have 16 octets to grab. If any of 'em fail, or are |
| * outside the 0...0xff range, bomb. However, we MAY have a |
| * v4-ish form, whether it's a formal v4 mapped/compat address, |
| * or just a v4 address written in a v6 block. So, look for |
| * .-separated octets, but there better be exactly 4 of them |
| * before we hit a :. |
| */ |
| nocts = 0; |
| |
| /* Bump before / (or multiple /'s */ |
| while (i > 0 && addr[i] == '/') |
| i--; |
| |
| for ( /* i */ ; i >= 0; i--) { |
| /* |
| * First, check the . cases, and handle them all in one |
| * place. These can only happen at the beginning, when we |
| * have no octets yet, and if it happens at all, we need to |
| * have 4 of them. |
| */ |
| if (nocts == 0 && addr[i] == '.') { |
| i++; /* Shift back to after the '.' */ |
| |
| for ( /* i */ ; i > 0 && nocts < 4; i--) { |
| /* This shouldn't happen except at the end */ |
| if (addr[i] == ':' && nocts < 3) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* If it's not a . or :, move back 1 */ |
| if (addr[i] != '.' && addr[i] != ':') |
| continue; |
| |
| /* Should be a [decimal] octet right after here */ |
| octet = strtoul(addr + i + 1, NULL, 10); |
| /* Be sure */ |
| if (octet > 255) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Save it */ |
| toret->addr[15 - nocts] = octet & 0xff; |
| nocts++; |
| |
| /* And find the next octet */ |
| } |
| |
| /* |
| * At this point, 4 dotted-decimal octets should be |
| * consumed. i has gone back one step past the : before |
| * the decimal, so addr[i+1] should be the ':' that |
| * preceeds them. Verify. |
| */ |
| if (nocts != 4 || addr[i + 1] != ':') { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| } |
| |
| /* |
| * Now we've either gotten 4 octets filled in from |
| * dotted-decimal stuff, or we've filled in nothing and have |
| * no dotted decimal. |
| */ |
| |
| /* As long as it's not our separator, keep moving */ |
| if (addr[i] != ':' && i > 0) |
| continue; |
| |
| /* If it's a :, and our NEXT char is a : too, flee */ |
| if (addr[i] == ':' && addr[i + 1] == ':') { |
| /* |
| * If i is 0, we're already at the beginning of the |
| * string, so we can just return; we've already filled in |
| * everything but the leading 0's, which are already |
| * zero-filled from the memory |
| */ |
| if (i == 0) |
| return (toret); |
| |
| /* Else, i!=0, and we break out */ |
| break; |
| } |
| |
| /* If it's not a number either... well, bad data */ |
| if (!isxdigit(addr[i]) && addr[i] != ':' && i > 0) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * It's no longer a number. So, grab the number we just |
| * moved before. |
| */ |
| /* Cheat for "beginning-of-string" rather than "NaN" */ |
| if (i == 0) |
| i--; |
| octet = strtoul(addr + i + 1, &buf2, 16); |
| if (*buf2 != ':' && *buf2 != '/' && *buf2 != '\0') { |
| /* Got something unexpected */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| buf2 = NULL; |
| |
| /* Remember, this is TWO octets */ |
| if (octet > 0xffff) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Save it */ |
| toret->addr[15 - nocts] = octet & 0xff; |
| nocts++; |
| toret->addr[15 - nocts] = (octet >> 8) & 0xff; |
| nocts++; |
| |
| /* If we've got all of 'em, just return from here. */ |
| if (nocts == 16) |
| return (toret); |
| } |
| |
| /* |
| * Now, if i is >=0 and we've got two :'s, jump around to the |
| * front of the string and start parsing inward. |
| */ |
| if (i >= 0 && addr[i] == ':' && addr[i + 1] == ':') { |
| /* Remember how many octets we put on the end */ |
| eocts = nocts; |
| |
| /* Remember how far we were into the string */ |
| j = i; |
| |
| /* Going this way, we do things a little differently */ |
| i = 0; |
| while (i < j) { |
| /* |
| * The first char better be a number. If it's not, bail |
| * (a leading '::' was already handled in the loop above |
| * by just returning). |
| */ |
| if (i == 0 && !isxdigit(addr[i])) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * We should be pointing at the beginning of a digit |
| * string now. Translate it into an octet. |
| */ |
| octet = strtoul(addr + i, &buf2, 16); |
| if (*buf2 != ':' && *buf2 != '/' |
| && *buf2 != '\0') { |
| /* Got something unexpected */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| buf2 = NULL; |
| |
| /* Sanity (again, 2 octets) */ |
| if (octet > 0xffff) { |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* Save it */ |
| toret->addr[nocts - eocts] = |
| (octet >> 8) & 0xff; |
| nocts++; |
| toret->addr[nocts - eocts] = octet & 0xff; |
| nocts++; |
| |
| /* |
| * Discussion: If we're in this code block, it's because |
| * we hit a ::-compression while parsing from the end |
| * backward. So, if we hit 15 octets here, it's an |
| * error, because with the at-least-2 that were minimized, |
| * that makes 17 total, which is too many. So, error |
| * out. |
| */ |
| if (nocts == 15) { |
| cidr_free(toret); |
| return (NULL); |
| } |
| |
| /* Now skip around to the end of this number */ |
| while (isxdigit(addr[i]) && i < j) |
| i++; |
| |
| /* |
| * If i==j, we're back where we started. So we've filled |
| * in all the leading stuff, and the struct is ready to |
| * return. |
| */ |
| if (i == j) |
| return (toret); |
| |
| /* |
| * Else, there's more to come. We better be pointing at |
| * a ':', else die. |
| */ |
| if (addr[i] != ':') { |
| cidr_free(toret); |
| return (NULL); |
| } |
| |
| /* Skip past : */ |
| i++; |
| |
| /* If we're at j now, we had a ':::', which is invalid */ |
| if (i == j) { |
| cidr_free(toret); |
| return (NULL); |
| } |
| |
| /* Head back around */ |
| } |
| } |
| |
| /* If we get here, it failed somewhere odd */ |
| cidr_free(toret); |
| errno = EINVAL; |
| return (NULL); |
| } else { |
| /* Shouldn't happen */ |
| cidr_free(toret); |
| errno = ENOENT; /* Bad choice of errno */ |
| return (NULL); |
| } |
| |
| /* NOTREACHED */ |
| errno = ENOENT; |
| return (NULL); |
| } |