/* $NetBSD: inet_pton.c,v 1.16 2000/02/07 18:51:02 itojun Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char rcsid[] = "Id: inet_pton.c,v 8.7 1996/08/05 08:31:35 vixie Exp "; #else __RCSID("$NetBSD: inet_pton.c,v 1.16 2000/02/07 18:51:02 itojun Exp $"); #endif #endif /* LIBC_SCCS and not lint */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef __weak_alias __weak_alias(inet_aton,_inet_aton) __weak_alias(inet_pton,_inet_pton) #endif /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4 __P((const char *src, u_char *dst, int pton)); static int inet_pton6 __P((const char *src, u_char *dst)); /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(af, src, dst) int af; const char *src; void *dst; { _DIAGASSERT(src != NULL); _DIAGASSERT(dst != NULL); switch (af) { case AF_INET: return (inet_pton4(src, dst, 1)); case AF_INET6: return (inet_pton6(src, dst)); default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst, pton) * when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand. * when last arg is 1: inet_pton(). decimal dotted-quad only. * return: * 1 if `src' is a valid input, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(src, dst, pton) const char *src; u_char *dst; int pton; { u_int32_t val; u_int digit; int base, n; unsigned char c; u_int parts[4]; register u_int *pp = parts; _DIAGASSERT(src != NULL); _DIAGASSERT(dst != NULL); c = *src; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, isdigit=decimal. */ if (!isdigit(c)) return (0); val = 0; base = 10; if (c == '0') { c = *++src; if (c == 'x' || c == 'X') base = 16, c = *++src; else if (isdigit(c) && c != '9') base = 8; } /* inet_pton() takes decimal only */ if (pton && base != 10) return (0); for (;;) { if (isdigit(c)) { digit = c - '0'; if (digit >= base) break; val = (val * base) + digit; c = *++src; } else if (base == 16 && isxdigit(c)) { digit = c + 10 - (islower(c) ? 'a' : 'A'); if (digit >= 16) break; val = (val << 4) | digit; c = *++src; } else break; } if (c == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16 bits) * a.b (with b treated as 24 bits) * a (with a treated as 32 bits) */ if (pp >= parts + 3) return (0); *pp++ = val; c = *++src; } else break; } /* * Check for trailing characters. */ if (c != '\0' && !isspace(c)) return (0); /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; /* inet_pton() takes dotted-quad only. it does not take shorthand. */ if (pton && n != 4) return (0); switch (n) { case 0: return (0); /* initial nondigit */ case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (parts[0] > 0xff || val > 0xffffff) return (0); val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if ((parts[0] | parts[1]) > 0xff || val > 0xffff) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if ((parts[0] | parts[1] | parts[2] | val) > 0xff) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (dst) { val = htonl(val); memcpy(dst, &val, INADDRSZ); } return (1); } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * (1) does not touch `dst' unless it's returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(src, dst) const char *src; u_char *dst; { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit; u_int val; _DIAGASSERT(src != NULL); _DIAGASSERT(dst != NULL); memset((tp = tmp), '\0', IN6ADDRSZ); endp = tp + IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; saw_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return (0); saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return (0); colonp = tp; continue; } else if (*src == '\0') return (0); if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp, 1) > 0) { tp += INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return (0); } if (saw_xdigit) { if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return (0); for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, IN6ADDRSZ); return (1); } /* * Ascii internet address interpretation routine. * The value returned is in network order. */ u_long inet_addr(cp) register const char *cp; { struct in_addr val; _DIAGASSERT(cp != NULL); if (inet_pton4(cp, (u_char *)(void *)&val.s_addr, 0)) return (val.s_addr); return (INADDR_NONE); } /* * Check whether "cp" is a valid ascii representation * of an Internet address and convert to a binary address. * Returns 1 if the address is valid, 0 if not. * This replaces inet_addr, the return value from which * cannot distinguish between failure and a local broadcast address. */ int inet_aton(cp, addr) register const char *cp; struct in_addr *addr; { _DIAGASSERT(cp != NULL); _DIAGASSERT(addr != NULL); return inet_pton4(cp, (u_char *)(void *)&addr->s_addr, 0); }