/*
 * 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);
}
