Home | History | Annotate | Line # | Download | only in inet
inet_net_pton.c revision 1.3
      1 /*
      2  * Copyright (c) 1996,1999 by Internet Software Consortium.
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
      9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
     11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
     14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     15  * SOFTWARE.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 #if defined(LIBC_SCCS) && !defined(lint)
     20 #if 0
     21 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.1 2002/08/02 02:17:21 marka Exp ";
     22 #else
     23 __RCSID("$NetBSD: inet_net_pton.c,v 1.3 2012/03/13 21:13:38 christos Exp $");
     24 #endif
     25 #endif
     26 
     27 #include "port_before.h"
     28 
     29 #include "namespace.h"
     30 #include <sys/types.h>
     31 #include <sys/socket.h>
     32 #include <netinet/in.h>
     33 #include <arpa/nameser.h>
     34 #include <arpa/inet.h>
     35 
     36 #include <isc/assertions.h>
     37 #include <stddef.h>
     38 #include <ctype.h>
     39 #include <errno.h>
     40 #include <stdio.h>
     41 #include <string.h>
     42 #include <stdlib.h>
     43 
     44 #include "port_after.h"
     45 
     46 #ifdef __weak_alias
     47 __weak_alias(inet_net_pton,_inet_net_pton)
     48 #endif
     49 
     50 #ifdef SPRINTF_CHAR
     51 # define SPRINTF(x) strlen(sprintf/**/x)
     52 #else
     53 # define SPRINTF(x) ((size_t)sprintf x)
     54 #endif
     55 
     56 
     57 /*
     58  * static int
     59  * inet_net_pton_ipv4(src, dst, size)
     60  *	convert IPv4 network number from presentation to network format.
     61  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
     62  *	"size" is in bytes and describes "dst".
     63  * return:
     64  *	number of bits, either imputed classfully or specified with /CIDR,
     65  *	or -1 if some failure occurred (check errno).  ENOENT means it was
     66  *	not an IPv4 network specification.
     67  * note:
     68  *	network byte order assumed.  this means 192.5.5.240/28 has
     69  *	0b11110000 in its fourth octet.
     70  * author:
     71  *	Paul Vixie (ISC), June 1996
     72  */
     73 static int
     74 inet_net_pton_ipv4( const char *src, u_char *dst, size_t size) {
     75 	static const char xdigits[] = "0123456789abcdef";
     76 	static const char digits[] = "0123456789";
     77 	int ch, dirty, bits;
     78 	ptrdiff_t n, tmp;
     79 	const u_char *odst = dst;
     80 
     81 	tmp = 0;
     82 	ch = *src++;
     83 	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
     84 	    && isascii((u_char)(src[1]))
     85 	    && isxdigit((u_char)(src[1]))) {
     86 		/* Hexadecimal: Eat nybble string. */
     87 		if (size == 0)
     88 			goto emsgsize;
     89 		dirty = 0;
     90 		src++;	/* skip x or X. */
     91 		while ((ch = *src++) != '\0' && isascii((u_char)ch)
     92 		    && isxdigit((u_char)ch)) {
     93 			if (isupper((u_char)ch))
     94 				ch = tolower((u_char)ch);
     95 			n = strchr(xdigits, ch) - xdigits;
     96 			INSIST(n >= 0 && n <= 15);
     97 			if (dirty == 0)
     98 				tmp = n;
     99 			else
    100 				tmp = (tmp << 4) | n;
    101 			if (++dirty == 2) {
    102 				if (size-- == 0)
    103 					goto emsgsize;
    104 				*dst++ = (u_char) tmp;
    105 				dirty = 0;
    106 			}
    107 		}
    108 		if (dirty) {  /* Odd trailing nybble? */
    109 			if (size-- == 0)
    110 				goto emsgsize;
    111 			*dst++ = (u_char) (tmp << 4);
    112 		}
    113 	} else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
    114 		/* Decimal: eat dotted digit string. */
    115 		for (;;) {
    116 			tmp = 0;
    117 			do {
    118 				n = strchr(digits, ch) - digits;
    119 				INSIST(n >= 0 && n <= 9);
    120 				tmp *= 10;
    121 				tmp += n;
    122 				if (tmp > 255)
    123 					goto enoent;
    124 			} while ((ch = *src++) != '\0' &&
    125 				 isascii((u_char)ch) && isdigit((u_char)ch));
    126 			if (size-- == 0)
    127 				goto emsgsize;
    128 			*dst++ = (u_char) tmp;
    129 			if (ch == '\0' || ch == '/')
    130 				break;
    131 			if (ch != '.')
    132 				goto enoent;
    133 			ch = *src++;
    134 			if (!isascii((u_char)ch) || !isdigit((u_char)ch))
    135 				goto enoent;
    136 		}
    137 	} else
    138 		goto enoent;
    139 
    140 	bits = -1;
    141 	if (ch == '/' && isascii((u_char)(src[0])) &&
    142 	    isdigit((u_char)(src[0])) && dst > odst) {
    143 		/* CIDR width specifier.  Nothing can follow it. */
    144 		ch = *src++;	/* Skip over the /. */
    145 		bits = 0;
    146 		do {
    147 			n = strchr(digits, ch) - digits;
    148 			INSIST(n >= 0 && n <= 9);
    149 			bits *= 10;
    150 			bits += (int)n;
    151 			if (bits > 32)
    152 				goto emsgsize;
    153 		} while ((ch = *src++) != '\0' && isascii((u_char)ch)
    154 		    && isdigit((u_char)ch));
    155 		if (ch != '\0')
    156 			goto enoent;
    157 	}
    158 
    159 	/* Firey death and destruction unless we prefetched EOS. */
    160 	if (ch != '\0')
    161 		goto enoent;
    162 
    163 	/* If nothing was written to the destination, we found no address. */
    164 	if (dst == odst)
    165 		goto enoent;
    166 	/* If no CIDR spec was given, infer width from net class. */
    167 	if (bits == -1) {
    168 		if (*odst >= 240)	/* Class E */
    169 			bits = 32;
    170 		else if (*odst >= 224)	/* Class D */
    171 			bits = 4;
    172 		else if (*odst >= 192)	/* Class C */
    173 			bits = 24;
    174 		else if (*odst >= 128)	/* Class B */
    175 			bits = 16;
    176 		else			/* Class A */
    177 			bits = 8;
    178 		/* If imputed mask is narrower than specified octets, widen. */
    179 		if (bits >= 8 && bits < ((dst - odst) * 8))
    180 			bits = (int)(dst - odst) * 8;
    181 	}
    182 	/* Extend network to cover the actual mask. */
    183 	while (bits > ((dst - odst) * 8)) {
    184 		if (size-- == 0)
    185 			goto emsgsize;
    186 		*dst++ = '\0';
    187 	}
    188 	return (bits);
    189 
    190  enoent:
    191 	errno = ENOENT;
    192 	return (-1);
    193 
    194  emsgsize:
    195 	errno = EMSGSIZE;
    196 	return (-1);
    197 }
    198 
    199 static int
    200 getbits(const char *src, int *bitsp) {
    201 	static const char digits[] = "0123456789";
    202 	int n;
    203 	int val;
    204 	char ch;
    205 
    206 	val = 0;
    207 	n = 0;
    208 	while ((ch = *src++) != '\0') {
    209 		const char *pch;
    210 
    211 		pch = strchr(digits, ch);
    212 		if (pch != NULL) {
    213 			if (n++ != 0 && val == 0)	/* no leading zeros */
    214 				return (0);
    215 			val *= 10;
    216 			val += (int)(pch - digits);
    217 			if (val > 128)			/* range */
    218 				return (0);
    219 			continue;
    220 		}
    221 		return (0);
    222 	}
    223 	if (n == 0)
    224 		return (0);
    225 	*bitsp = val;
    226 	return (1);
    227 }
    228 
    229 static int
    230 getv4(const char *src, u_char *dst, int *bitsp) {
    231 	static const char digits[] = "0123456789";
    232 	u_char *odst = dst;
    233 	int n;
    234 	u_int val;
    235 	char ch;
    236 
    237 	val = 0;
    238 	n = 0;
    239 	while ((ch = *src++) != '\0') {
    240 		const char *pch;
    241 
    242 		pch = strchr(digits, ch);
    243 		if (pch != NULL) {
    244 			if (n++ != 0 && val == 0)	/* no leading zeros */
    245 				return (0);
    246 			val *= 10;
    247 			val += (int)(pch - digits);
    248 			if (val > 255)			/* range */
    249 				return (0);
    250 			continue;
    251 		}
    252 		if (ch == '.' || ch == '/') {
    253 			if (dst - odst > 3)		/* too many octets? */
    254 				return (0);
    255 			*dst++ = val;
    256 			if (ch == '/')
    257 				return (getbits(src, bitsp));
    258 			val = 0;
    259 			n = 0;
    260 			continue;
    261 		}
    262 		return (0);
    263 	}
    264 	if (n == 0)
    265 		return (0);
    266 	if (dst - odst > 3)		/* too many octets? */
    267 		return (0);
    268 	*dst++ = val;
    269 	return (1);
    270 }
    271 
    272 static int
    273 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) {
    274 	static const char xdigits_l[] = "0123456789abcdef",
    275 			  xdigits_u[] = "0123456789ABCDEF";
    276 	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
    277 	const char *xdigits, *curtok;
    278 	int ch, saw_xdigit;
    279 	u_int val;
    280 	int digits;
    281 	int bits;
    282 	size_t bytes;
    283 	int words;
    284 	int ipv4;
    285 
    286 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
    287 	endp = tp + NS_IN6ADDRSZ;
    288 	colonp = NULL;
    289 	/* Leading :: requires some special handling. */
    290 	if (*src == ':')
    291 		if (*++src != ':')
    292 			goto enoent;
    293 	curtok = src;
    294 	saw_xdigit = 0;
    295 	val = 0;
    296 	digits = 0;
    297 	bits = -1;
    298 	ipv4 = 0;
    299 	while ((ch = *src++) != '\0') {
    300 		const char *pch;
    301 
    302 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
    303 			pch = strchr((xdigits = xdigits_u), ch);
    304 		if (pch != NULL) {
    305 			val <<= 4;
    306 			val |= (int)(pch - xdigits);
    307 			if (++digits > 4)
    308 				goto enoent;
    309 			saw_xdigit = 1;
    310 			continue;
    311 		}
    312 		if (ch == ':') {
    313 			curtok = src;
    314 			if (!saw_xdigit) {
    315 				if (colonp)
    316 					goto enoent;
    317 				colonp = tp;
    318 				continue;
    319 			} else if (*src == '\0')
    320 				goto enoent;
    321 			if (tp + NS_INT16SZ > endp)
    322 				return (0);
    323 			*tp++ = (u_char) (val >> 8) & 0xff;
    324 			*tp++ = (u_char) val & 0xff;
    325 			saw_xdigit = 0;
    326 			digits = 0;
    327 			val = 0;
    328 			continue;
    329 		}
    330 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
    331 		     getv4(curtok, tp, &bits) > 0) {
    332 			tp += NS_INADDRSZ;
    333 			saw_xdigit = 0;
    334 			ipv4 = 1;
    335 			break;	/* '\0' was seen by inet_pton4(). */
    336 		}
    337 		if (ch == '/' && getbits(src, &bits) > 0)
    338 			break;
    339 		goto enoent;
    340 	}
    341 	if (saw_xdigit) {
    342 		if (tp + NS_INT16SZ > endp)
    343 			goto enoent;
    344 		*tp++ = (u_char) (val >> 8) & 0xff;
    345 		*tp++ = (u_char) val & 0xff;
    346 	}
    347 	if (bits == -1)
    348 		bits = 128;
    349 
    350 	words = (bits + 15) / 16;
    351 	if (words < 2)
    352 		words = 2;
    353 	if (ipv4)
    354 		words = 8;
    355 	endp =  tmp + 2 * words;
    356 
    357 	if (colonp != NULL) {
    358 		/*
    359 		 * Since some memmove()'s erroneously fail to handle
    360 		 * overlapping regions, we'll do the shift by hand.
    361 		 */
    362 		const ptrdiff_t n = tp - colonp;
    363 		int i;
    364 
    365 		if (tp == endp)
    366 			goto enoent;
    367 		for (i = 1; i <= n; i++) {
    368 			endp[- i] = colonp[n - i];
    369 			colonp[n - i] = 0;
    370 		}
    371 		tp = endp;
    372 	}
    373 	if (tp != endp)
    374 		goto enoent;
    375 
    376 	bytes = (bits + 7) / 8;
    377 	if (bytes > size)
    378 		goto emsgsize;
    379 	memcpy(dst, tmp, bytes);
    380 	return (bits);
    381 
    382  enoent:
    383 	errno = ENOENT;
    384 	return (-1);
    385 
    386  emsgsize:
    387 	errno = EMSGSIZE;
    388 	return (-1);
    389 }
    390 
    391 /*
    392  * int
    393  * inet_net_pton(af, src, dst, size)
    394  *	convert network number from presentation to network format.
    395  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
    396  *	"size" is in bytes and describes "dst".
    397  * return:
    398  *	number of bits, either imputed classfully or specified with /CIDR,
    399  *	or -1 if some failure occurred (check errno).  ENOENT means it was
    400  *	not a valid network specification.
    401  * author:
    402  *	Paul Vixie (ISC), June 1996
    403  */
    404 int
    405 inet_net_pton(int af, const char *src, void *dst, size_t size) {
    406 	switch (af) {
    407 	case AF_INET:
    408 		return (inet_net_pton_ipv4(src, dst, size));
    409 	case AF_INET6:
    410 		return (inet_net_pton_ipv6(src, dst, size));
    411 	default:
    412 		errno = EAFNOSUPPORT;
    413 		return (-1);
    414 	}
    415 }
    416