| /* |
| * cidr_to_str() - Generate a textual representation of the given CIDR |
| * subnet. |
| */ |
| #include "config.h" |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "cidr.h" |
| #include "abstract_mem.h" |
| |
| char *cidr_to_str(const CIDR * block, int flags) |
| { |
| int i; |
| int zst, zcur, zlen, zmax; |
| short pflen; |
| short lzer; /* Last zero */ |
| char *toret; |
| char tmpbuf[128]; /* We shouldn't need more than ~5 anywhere */ |
| CIDR *nmtmp; |
| char *nmstr; |
| int nmflags; |
| uint8_t moct; |
| uint16_t v6sect; |
| |
| /* Just in case */ |
| if (block->proto == CIDR_NOPROTO) { |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * Sanity: If we have both ONLYADDR and ONLYPFLEN, we really don't |
| * have anything to *DO*... |
| */ |
| if ((flags & CIDR_ONLYADDR) && (flags & CIDR_ONLYPFLEN)) { |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| /* |
| * Now, in any case, there's a maximum length for any address, which |
| * is the completely expanded form of a v6-{mapped,compat} address |
| * with a netmask instead of a prefix. That's 8 pieces of 4 |
| * characters each (32), separated by :'s (+7=39), plus the slash |
| * (+1=40), plus another separated-8*4 (+39=79), plus the trailing |
| * null (+1=80). We'll just allocate 128 for kicks. |
| * |
| * I'm not, at this time anyway, going to try and allocate only and |
| * exactly as much as we need for any given address. Whether |
| * consumers of the library can count on this behavior... well, I |
| * haven't decided yet. Lemme alone. |
| */ |
| toret = gsh_calloc(1, 128); |
| |
| /* |
| * If it's a v4 address, we mask off everything but the last 4 |
| * octets, and just proceed from there. |
| */ |
| if ((block->proto == CIDR_IPV4 && !(flags & CIDR_FORCEV6)) |
| || (flags & CIDR_FORCEV4)) { |
| /* First off, creating the in-addr.arpa form is special */ |
| if (flags & CIDR_REVERSE) { |
| /* |
| * Build the d.c.b.a.in-addr.arpa form. Note that we ignore |
| * flags like CIDR_VERBOSE and the like here, since they lead |
| * to non-valid reverse paths (or at least, paths that no DNS |
| * implementation will look for). So it pretty much always |
| * looks exactly the same. Also, we don't mess with dealing |
| * with netmaks or anything here; we just assume it's a |
| * host address, and treat it as such. |
| */ |
| |
| sprintf(toret, "%d.%d.%d.%d.in-addr.arpa", |
| block->addr[15], block->addr[14], |
| block->addr[13], block->addr[12]); |
| return (toret); |
| } |
| |
| /* Are we bothering to show the address? */ |
| if (!(flags & CIDR_ONLYPFLEN)) { |
| /* If we're USEV6'ing, add whatever prefixes we need */ |
| if (flags & CIDR_USEV6) { |
| if (flags & CIDR_NOCOMPACT) { |
| if (flags & CIDR_VERBOSE) |
| strcat(toret, |
| "0000:0000:0000:0000:0000:"); |
| else |
| strcat(toret, "0:0:0:0:0:"); |
| } else |
| strcat(toret, "::"); |
| |
| if (flags & CIDR_USEV4COMPAT) { |
| if (flags & CIDR_NOCOMPACT) { |
| if (flags & CIDR_VERBOSE) |
| strcat(toret, "0000:"); |
| else |
| strcat(toret, "0:"); |
| } |
| } else |
| strcat(toret, "ffff:"); |
| } |
| |
| /* USEV6 */ |
| /* Now, slap on the v4 address */ |
| for (i = 12; i <= 15; i++) { |
| sprintf(tmpbuf, "%u", (block->addr)[i]); |
| strcat(toret, tmpbuf); |
| if (i < 15) |
| strcat(toret, "."); |
| } |
| } |
| |
| /* ! ONLYPFLEN */ |
| /* Are we bothering to show the pf/mask? */ |
| if (!(flags & CIDR_ONLYADDR)) { |
| /* |
| * And the prefix/netmask. Don't show the '/' if we're only |
| * showing the pflen/mask. |
| */ |
| if (!(flags & CIDR_ONLYPFLEN)) |
| strcat(toret, "/"); |
| |
| /* Which are we showing? */ |
| if (flags & CIDR_NETMASK) { |
| /* |
| * In this case, we can just print out like the address |
| * above. |
| */ |
| for (i = 12; i <= 15; i++) { |
| moct = (block->mask)[i]; |
| if (flags & CIDR_WILDCARD) |
| moct = ~(moct); |
| sprintf(tmpbuf, "%u", moct); |
| strcat(toret, tmpbuf); |
| if (i < 15) |
| strcat(toret, "."); |
| } |
| } else { |
| /* |
| * For this, iterate over each octet, |
| * then each bit within the octet. |
| */ |
| pflen = cidr_get_pflen(block); |
| if (pflen == -1) { |
| gsh_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| /* Special handling for forced modes */ |
| if (block->proto == CIDR_IPV6 |
| && (flags & CIDR_FORCEV4)) |
| pflen -= 96; |
| |
| sprintf(tmpbuf, "%u", |
| (flags & CIDR_USEV6) ? pflen + |
| 96 : pflen); |
| |
| strcat(toret, tmpbuf); |
| } |
| } |
| |
| /* ! ONLYADDR */ |
| /* That's it for a v4 address, in any of our forms */ |
| } else if ((block->proto == CIDR_IPV6 && !(flags & CIDR_FORCEV4)) |
| || (flags & CIDR_FORCEV6)) { |
| /* First off, creating the .ip6.arpa form is special */ |
| if (flags & CIDR_REVERSE) { |
| /* |
| * Build the ...ip6.arpa form. See notes in the CIDR_REVERSE |
| * section of PROTO_IPV4 above for various notes. |
| */ |
| sprintf(toret, |
| "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." |
| "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." |
| "%x.%x.%x.%x.%x.ip6.arpa", |
| block->addr[15] & 0x0f, block->addr[15] >> 4, |
| block->addr[14] & 0x0f, block->addr[14] >> 4, |
| block->addr[13] & 0x0f, block->addr[13] >> 4, |
| block->addr[12] & 0x0f, block->addr[12] >> 4, |
| block->addr[11] & 0x0f, block->addr[11] >> 4, |
| block->addr[10] & 0x0f, block->addr[10] >> 4, |
| block->addr[9] & 0x0f, block->addr[9] >> 4, |
| block->addr[8] & 0x0f, block->addr[8] >> 4, |
| block->addr[7] & 0x0f, block->addr[7] >> 4, |
| block->addr[6] & 0x0f, block->addr[6] >> 4, |
| block->addr[5] & 0x0f, block->addr[5] >> 4, |
| block->addr[4] & 0x0f, block->addr[4] >> 4, |
| block->addr[3] & 0x0f, block->addr[3] >> 4, |
| block->addr[2] & 0x0f, block->addr[2] >> 4, |
| block->addr[1] & 0x0f, block->addr[1] >> 4, |
| block->addr[0] & 0x0f, block->addr[0] >> 4); |
| return (toret); |
| } |
| /* Are we showing the address part? */ |
| if (!(flags & CIDR_ONLYPFLEN)) { |
| /* It's a simple, boring, normal v6 address */ |
| |
| /* First, find the longest string of 0's, if there is one */ |
| zst = zcur = -1; |
| zlen = zmax = 0; |
| for (i = 0; i <= 15; i += 2) { |
| if (block->addr[i] == 0 |
| && block->addr[i + 1] == 0) { |
| /* This section is zero */ |
| if (zcur != -1) { |
| /* We're already in a block of 0's */ |
| zlen++; |
| } else { |
| /* Starting a new block */ |
| zcur = i; |
| zlen = 1; |
| } |
| } else { |
| /* This section is non-zero */ |
| if (zcur != -1) { |
| /* |
| * We were in 0's. See if we set a new record, |
| * and if we did, note it and move on. |
| */ |
| if (zlen > zmax) { |
| zst = zcur; |
| zmax = zlen; |
| } |
| |
| /* We're out of 0's, so reset start */ |
| zcur = -1; |
| } |
| } |
| } |
| |
| /* |
| * If zcur is !=-1, we were in 0's when the loop ended. Redo |
| * the "if we have a record, update" logic. |
| */ |
| if (zcur != -1 && zlen > zmax) { |
| zst = zcur; |
| zmax = zlen; |
| } |
| |
| /* |
| * Now, what makes it HARD is the options we have. To make |
| * some things simpler, we'll take two octets at a time for |
| * our run through. |
| */ |
| lzer = 0; |
| for (i = 0; i <= 15; i += 2) { |
| /* |
| * Start with a cheat; if this begins our already-found |
| * longest block of 0's, and we're not NOCOMPACT'ing, |
| * stick in a ::, increment past them, and keep on |
| * playing. |
| */ |
| if (i == zst && !(flags & CIDR_NOCOMPACT)) { |
| strcat(toret, "::"); |
| i += (zmax * 2) - 2; |
| lzer = 1; |
| continue; |
| } |
| |
| /* |
| * First, if we're not the first set, we may need a : |
| * before us. If we're not compacting, we always want |
| * it. If we ARE compacting, we want it unless the |
| * previous octet was a 0 that we're minimizing. |
| */ |
| if (i != 0 |
| && ((flags & CIDR_NOCOMPACT) || lzer == 0)) |
| strcat(toret, ":"); |
| lzer = 0; /* Reset */ |
| |
| /* |
| * From here on, we no longer have to worry about |
| * CIDR_NOCOMPACT. |
| */ |
| |
| /* Combine the pair of octets into one number */ |
| v6sect = 0; |
| v6sect |= (block->addr)[i] << 8; |
| v6sect |= (block->addr)[i + 1]; |
| |
| /* |
| * If we're being VERBOSE, use leading 0's. Otherwise, |
| * only use as many digits as we need. |
| */ |
| if (flags & CIDR_VERBOSE) |
| sprintf(tmpbuf, "%.4x", v6sect); |
| else |
| sprintf(tmpbuf, "%x", v6sect); |
| strcat(toret, tmpbuf); |
| |
| /* And loop back around to the next 2-octet set */ |
| } /* for(each 16-bit set) */ |
| } |
| |
| /* ! ONLYPFLEN */ |
| /* Prefix/netmask */ |
| if (!(flags & CIDR_ONLYADDR)) { |
| /* Only show the / if we're not showing just the prefix */ |
| if (!(flags & CIDR_ONLYPFLEN)) |
| strcat(toret, "/"); |
| |
| if (flags & CIDR_NETMASK) { |
| /* |
| * We already wrote how to build the whole v6 form, so |
| * just call ourselves recurively for this. |
| */ |
| nmtmp = cidr_alloc(); |
| |
| nmtmp->proto = block->proto; |
| for (i = 0; i <= 15; i++) |
| if (flags & CIDR_WILDCARD) |
| nmtmp->addr[i] = |
| ~(block->mask[i]); |
| else |
| nmtmp->addr[i] = block->mask[i]; |
| |
| /* |
| * Strip flags: |
| * - CIDR_NETMASK would make us recurse forever. |
| * - CIDR_ONLYPFLEN would not show the address bit, which |
| * is the part we want here. |
| * Add flag CIDR_ONLYADDR because that's the bit we care |
| * about. |
| */ |
| nmflags = flags; |
| nmflags &= ~(CIDR_NETMASK) & ~(CIDR_ONLYPFLEN); |
| nmflags |= CIDR_ONLYADDR; |
| nmstr = cidr_to_str(nmtmp, nmflags); |
| cidr_free(nmtmp); |
| if (nmstr == NULL) { |
| gsh_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| |
| /* No need to strip the prefix, it doesn't have it */ |
| |
| /* Just add it on */ |
| strcat(toret, nmstr); |
| gsh_free(nmstr); |
| } else { |
| /* Just figure the and show prefix length */ |
| pflen = cidr_get_pflen(block); |
| if (pflen == -1) { |
| gsh_free(toret); |
| return (NULL); /* Preserve errno */ |
| } |
| /* Special handling for forced modes */ |
| if (block->proto == CIDR_IPV4 |
| && (flags & CIDR_FORCEV6)) |
| pflen += 96; |
| |
| sprintf(tmpbuf, "%u", pflen); |
| strcat(toret, tmpbuf); |
| } |
| } /* ! ONLYADDR */ |
| } else { |
| /* Well, *I* dunno what the fuck it is */ |
| gsh_free(toret); |
| errno = ENOENT; /* Bad choice of errno */ |
| return (NULL); |
| } |
| |
| /* Give back the string */ |
| return (toret); |
| } |