+ const char *p, *pd; /* pointers to current character in scan */
+ const char *pnum; /* pointer to current number to decode */
+ const char *pscope; /* pointer to IPv6 scope ID */
+ uint8_t a[18]; /* octet array to store address bytes */
+ int i; /* index of next octet to decode */
+ int dci; /* index of octet to insert double-colon zeroes */
+ int dcount, xdcount; /* count of digits in current number */
+ int needmore; /* set when we know we need more input (e.g. after colon, period) */
+ int dots; /* # of dots */
+ int hex; /* contains hex values */
+ unsigned long val; /* decoded value */
+ int s; /* index used for sliding array to insert elided zeroes */
+
+#define HEXVALUE 0
+#define DECIMALVALUE 1
+#define GET(TYPE) \
+ do { \
+ if ((dcount <= 0) || (dcount > (((TYPE) == DECIMALVALUE) ? 3 : 4))) \
+ return (0); \
+ if (((TYPE) == DECIMALVALUE) && xdcount) \
+ return (0); \
+ val = strtoul(pnum, NULL, ((TYPE) == DECIMALVALUE) ? 10 : 16); \
+ if (((TYPE) == DECIMALVALUE) && (val >= 256)) \
+ return (0); \
+ /* check if there is room left in the array */ \
+ if (i > (int)(sizeof(a) - (((TYPE) == HEXVALUE) ? 2 : 1) - ((dci != -1) ? 2 : 0))) \
+ return (0); \
+ if ((TYPE) == HEXVALUE) \
+ a[i++] = ((val >> 8) & 0xff); \
+ a[i++] = (val & 0xff); \
+ } while (0)
+
+ hex = 0;
+ dots = 0;
+ dci = -1;
+ i = dcount = xdcount = 0;
+ pnum = p = uaddr;
+ pscope = NULL;
+ needmore = 1;
+ if ((*p == ':') && (*++p != ':')) /* if it starts with colon, gotta be a double */
+ return (0);
+
+ while (*p) {
+ if (IS_XDIGIT(*p)) {
+ dcount++;
+ if (!IS_DIGIT(*p))
+ xdcount++;
+ needmore = 0;
+ p++;
+ } else if (*p == '.') {
+ /* rest is decimal IPv4 dotted quad and/or port */
+ if (!dots) {
+ /* this is the first, so count them */
+ for (pd = p; *pd; pd++) {
+ if (*pd == '.') {
+ if (++dots > 5)
+ return (0);
+ } else if (hex && (*pd == '%')) {
+ break;
+ } else if ((*pd < '0') || (*pd > '9')) {
+ return (0);
+ }
+ }
+ if ((dots != 2) && (dots != 3) && (dots != 5))
+ return (0);
+ if (hex && (dots == 2)) { /* hex+port */
+ if (!dcount && needmore)
+ return (0);
+ if (dcount) /* last hex may be elided zero */
+ GET(HEXVALUE);
+ } else {
+ GET(DECIMALVALUE);
+ }
+ } else {
+ GET(DECIMALVALUE);
+ }
+ dcount = xdcount = 0;
+ needmore = 1;
+ pnum = ++p;
+ } else if (*p == ':') {
+ hex = 1;
+ if (dots)
+ return (0);
+ if (!dcount) { /* missing number, probably double colon */
+ if (dci >= 0) /* can only have one double colon */
+ return (0);
+ dci = i;
+ needmore = 0;
+ } else {
+ GET(HEXVALUE);
+ dcount = xdcount = 0;
+ needmore = 1;
+ }
+ pnum = ++p;
+ } else if (*p == '%') { /* scope ID delimiter */
+ if (!hex)
+ return (0);
+ p++;
+ pscope = p;
+ break;
+ } else { /* unexpected character */
+ return (0);
+ }
+ }
+ if (needmore && !dcount)
+ return (0);
+ if (dcount) /* decode trailing number */
+ GET(dots ? DECIMALVALUE : HEXVALUE);
+ if (dci >= 0) { /* got a double-colon at i, need to insert a range of zeroes */
+ /* if we got a port, slide to end of array */
+ /* otherwise, slide to end of address (non-port) values */
+ int end = ((dots == 2) || (dots == 5)) ? sizeof(a) : (sizeof(a) - 2);
+ if (i % 2) /* length of zero range must be multiple of 2 */
+ return (0);
+ if (i >= end) /* no room? */
+ return (0);
+ /* slide (i-dci) numbers up from index dci */
+ for (s=0; s < (i - dci); s++)
+ a[end-1-s] = a[i-1-s];
+ /* zero (end-i) numbers at index dci */
+ for (s=0; s < (end - i); s++)
+ a[dci+s] = 0;
+ i = end;
+ }
+
+ /* copy out resulting socket address */
+ if (hex) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)addr;
+ if ((((dots == 0) || (dots == 3)) && (i != (sizeof(a)-2))))
+ return (0);
+ if ((((dots == 2) || (dots == 5)) && (i != sizeof(a))))
+ return (0);
+ bzero(sin6, sizeof(struct sockaddr_in6));
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ bcopy(a, &sin6->sin6_addr.s6_addr, sizeof(struct in6_addr));
+ if ((dots == 5) || (dots == 2))
+ sin6->sin6_port = htons((a[16] << 8) | a[17]);
+ if (pscope) {
+ for (p=pscope; IS_DIGIT(*p); p++)
+ ;
+ if (*p && !IS_DIGIT(*p)) { /* name */
+ ifnet_t interface = NULL;
+ if (ifnet_find_by_name(pscope, &interface) == 0)
+ sin6->sin6_scope_id = ifnet_index(interface);
+ if (interface)
+ ifnet_release(interface);
+ } else { /* decimal number */
+ sin6->sin6_scope_id = strtoul(pscope, NULL, 10);
+ }
+ /* XXX should we also embed scope id for linklocal? */
+ }
+ } else {
+ struct sockaddr_in *sin = (struct sockaddr_in*)addr;
+ if ((dots != 3) && (dots != 5))
+ return (0);
+ if ((dots == 3) && (i != 4))
+ return (0);
+ if ((dots == 5) && (i != 6))
+ return (0);
+ bzero(sin, sizeof(struct sockaddr_in));
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ bcopy(a, &sin->sin_addr.s_addr, sizeof(struct in_addr));
+ if (dots == 5)
+ sin->sin_port = htons((a[4] << 8) | a[5]);