Home | History | Annotate | Line # | Download | only in inet
inet_net_pton.c revision 1.2.4.1
      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.2.4.1 2012/04/17 00:05:20 yamt 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 {
     76 	static const char xdigits[] = "0123456789abcdef";
     77 	static const char digits[] = "0123456789";
     78 	int ch, dirty, bits;
     79 	ptrdiff_t n, tmp;
     80 	const u_char *odst = dst;
     81 
     82 	tmp = 0;
     83 	ch = *src++;
     84 	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
     85 	    && isascii((u_char)(src[1]))
     86 	    && isxdigit((u_char)(src[1]))) {
     87 		/* Hexadecimal: Eat nybble string. */
     88 		if (size == 0)
     89 			goto emsgsize;
     90 		dirty = 0;
     91 		src++;	/* skip x or X. */
     92 		while ((ch = *src++) != '\0' && isascii((u_char)ch)
     93 		    && isxdigit((u_char)ch)) {
     94 			if (isupper((u_char)ch))
     95 				ch = tolower((u_char)ch);
     96 			n = strchr(xdigits, ch) - xdigits;
     97 			INSIST(n >= 0 && n <= 15);
     98 			if (dirty == 0)
     99 				tmp = n;
    100 			else
    101 				tmp = (tmp << 4) | n;
    102 			if (++dirty == 2) {
    103 				if (size-- == 0)
    104 					goto emsgsize;
    105 				*dst++ = (u_char) tmp;
    106 				dirty = 0;
    107 			}
    108 		}
    109 		if (dirty) {  /* Odd trailing nybble? */
    110 			if (size-- == 0)
    111 				goto emsgsize;
    112 			*dst++ = (u_char) (tmp << 4);
    113 		}
    114 	} else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
    115 		/* Decimal: eat dotted digit string. */
    116 		for (;;) {
    117 			tmp = 0;
    118 			do {
    119 				n = strchr(digits, ch) - digits;
    120 				INSIST(n >= 0 && n <= 9);
    121 				tmp *= 10;
    122 				tmp += n;
    123 				if (tmp > 255)
    124 					goto enoent;
    125 			} while ((ch = *src++) != '\0' &&
    126 				 isascii((u_char)ch) && isdigit((u_char)ch));
    127 			if (size-- == 0)
    128 				goto emsgsize;
    129 			*dst++ = (u_char) tmp;
    130 			if (ch == '\0' || ch == '/')
    131 				break;
    132 			if (ch != '.')
    133 				goto enoent;
    134 			ch = *src++;
    135 			if (!isascii((u_char)ch) || !isdigit((u_char)ch))
    136 				goto enoent;
    137 		}
    138 	} else
    139 		goto enoent;
    140 
    141 	bits = -1;
    142 	if (ch == '/' && isascii((u_char)(src[0])) &&
    143 	    isdigit((u_char)(src[0])) && dst > odst) {
    144 		/* CIDR width specifier.  Nothing can follow it. */
    145 		ch = *src++;	/* Skip over the /. */
    146 		bits = 0;
    147 		do {
    148 			n = strchr(digits, ch) - digits;
    149 			INSIST(n >= 0 && n <= 9);
    150 			bits *= 10;
    151 			bits += (int)n;
    152 			if (bits > 32)
    153 				goto emsgsize;
    154 		} while ((ch = *src++) != '\0' && isascii((u_char)ch)
    155 		    && isdigit((u_char)ch));
    156 		if (ch != '\0')
    157 			goto enoent;
    158 	}
    159 
    160 	/* Firey death and destruction unless we prefetched EOS. */
    161 	if (ch != '\0')
    162 		goto enoent;
    163 
    164 	/* If nothing was written to the destination, we found no address. */
    165 	if (dst == odst)
    166 		goto enoent;
    167 	/* If no CIDR spec was given, infer width from net class. */
    168 	if (bits == -1) {
    169 		if (*odst >= 240)	/* Class E */
    170 			bits = 32;
    171 		else if (*odst >= 224)	/* Class D */
    172 			bits = 4;
    173 		else if (*odst >= 192)	/* Class C */
    174 			bits = 24;
    175 		else if (*odst >= 128)	/* Class B */
    176 			bits = 16;
    177 		else			/* Class A */
    178 			bits = 8;
    179 		/* If imputed mask is narrower than specified octets, widen. */
    180 		if (bits >= 8 && bits < ((dst - odst) * 8))
    181 			bits = (int)(dst - odst) * 8;
    182 	}
    183 	/* Extend network to cover the actual mask. */
    184 	while (bits > ((dst - odst) * 8)) {
    185 		if (size-- == 0)
    186 			goto emsgsize;
    187 		*dst++ = '\0';
    188 	}
    189 	return (bits);
    190 
    191  enoent:
    192 	errno = ENOENT;
    193 	return (-1);
    194 
    195  emsgsize:
    196 	errno = EMSGSIZE;
    197 	return (-1);
    198 }
    199 
    200 static int
    201 getbits(const char *src, int *bitsp)
    202 {
    203 	static const char digits[] = "0123456789";
    204 	int n;
    205 	int val;
    206 	char ch;
    207 
    208 	val = 0;
    209 	n = 0;
    210 	while ((ch = *src++) != '\0') {
    211 		const char *pch;
    212 
    213 		pch = strchr(digits, ch);
    214 		if (pch != NULL) {
    215 			if (n++ != 0 && val == 0)	/* no leading zeros */
    216 				return (0);
    217 			val *= 10;
    218 			val += (int)(pch - digits);
    219 			if (val > 128)			/* range */
    220 				return (0);
    221 			continue;
    222 		}
    223 		return (0);
    224 	}
    225 	if (n == 0)
    226 		return (0);
    227 	*bitsp = val;
    228 	return (1);
    229 }
    230 
    231 static int
    232 getv4(const char *src, u_char *dst, int *bitsp)
    233 {
    234 	static const char digits[] = "0123456789";
    235 	u_char *odst = dst;
    236 	int n;
    237 	u_int val;
    238 	char ch;
    239 
    240 	val = 0;
    241 	n = 0;
    242 	while ((ch = *src++) != '\0') {
    243 		const char *pch;
    244 
    245 		pch = strchr(digits, ch);
    246 		if (pch != NULL) {
    247 			if (n++ != 0 && val == 0)	/* no leading zeros */
    248 				return (0);
    249 			val *= 10;
    250 			val += (int)(pch - digits);
    251 			if (val > 255)			/* range */
    252 				return (0);
    253 			continue;
    254 		}
    255 		if (ch == '.' || ch == '/') {
    256 			if (dst - odst > 3)		/* too many octets? */
    257 				return (0);
    258 			*dst++ = val;
    259 			if (ch == '/')
    260 				return (getbits(src, bitsp));
    261 			val = 0;
    262 			n = 0;
    263 			continue;
    264 		}
    265 		return (0);
    266 	}
    267 	if (n == 0)
    268 		return (0);
    269 	if (dst - odst > 3)		/* too many octets? */
    270 		return (0);
    271 	*dst++ = val;
    272 	return (1);
    273 }
    274 
    275 static int
    276 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
    277 {
    278 	static const char xdigits_l[] = "0123456789abcdef",
    279 			  xdigits_u[] = "0123456789ABCDEF";
    280 	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
    281 	const char *xdigits, *curtok;
    282 	int ch, saw_xdigit;
    283 	u_int val;
    284 	int digits;
    285 	int bits;
    286 	size_t bytes;
    287 	int words;
    288 	int ipv4;
    289 
    290 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
    291 	endp = tp + NS_IN6ADDRSZ;
    292 	colonp = NULL;
    293 	/* Leading :: requires some special handling. */
    294 	if (*src == ':')
    295 		if (*++src != ':')
    296 			goto enoent;
    297 	curtok = src;
    298 	saw_xdigit = 0;
    299 	val = 0;
    300 	digits = 0;
    301 	bits = -1;
    302 	ipv4 = 0;
    303 	while ((ch = *src++) != '\0') {
    304 		const char *pch;
    305 
    306 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
    307 			pch = strchr((xdigits = xdigits_u), ch);
    308 		if (pch != NULL) {
    309 			val <<= 4;
    310 			val |= (int)(pch - xdigits);
    311 			if (++digits > 4)
    312 				goto enoent;
    313 			saw_xdigit = 1;
    314 			continue;
    315 		}
    316 		if (ch == ':') {
    317 			curtok = src;
    318 			if (!saw_xdigit) {
    319 				if (colonp)
    320 					goto enoent;
    321 				colonp = tp;
    322 				continue;
    323 			} else if (*src == '\0')
    324 				goto enoent;
    325 			if (tp + NS_INT16SZ > endp)
    326 				return (0);
    327 			*tp++ = (u_char) (val >> 8) & 0xff;
    328 			*tp++ = (u_char) val & 0xff;
    329 			saw_xdigit = 0;
    330 			digits = 0;
    331 			val = 0;
    332 			continue;
    333 		}
    334 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
    335 		     getv4(curtok, tp, &bits) > 0) {
    336 			tp += NS_INADDRSZ;
    337 			saw_xdigit = 0;
    338 			ipv4 = 1;
    339 			break;	/* '\0' was seen by inet_pton4(). */
    340 		}
    341 		if (ch == '/' && getbits(src, &bits) > 0)
    342 			break;
    343 		goto enoent;
    344 	}
    345 	if (saw_xdigit) {
    346 		if (tp + NS_INT16SZ > endp)
    347 			goto enoent;
    348 		*tp++ = (u_char) (val >> 8) & 0xff;
    349 		*tp++ = (u_char) val & 0xff;
    350 	}
    351 	if (bits == -1)
    352 		bits = 128;
    353 
    354 	words = (bits + 15) / 16;
    355 	if (words < 2)
    356 		words = 2;
    357 	if (ipv4)
    358 		words = 8;
    359 	endp =  tmp + 2 * words;
    360 
    361 	if (colonp != NULL) {
    362 		/*
    363 		 * Since some memmove()'s erroneously fail to handle
    364 		 * overlapping regions, we'll do the shift by hand.
    365 		 */
    366 		const ptrdiff_t n = tp - colonp;
    367 		int i;
    368 
    369 		if (tp == endp)
    370 			goto enoent;
    371 		for (i = 1; i <= n; i++) {
    372 			endp[- i] = colonp[n - i];
    373 			colonp[n - i] = 0;
    374 		}
    375 		tp = endp;
    376 	}
    377 	if (tp != endp)
    378 		goto enoent;
    379 
    380 	bytes = (bits + 7) / 8;
    381 	if (bytes > size)
    382 		goto emsgsize;
    383 	memcpy(dst, tmp, bytes);
    384 	return (bits);
    385 
    386  enoent:
    387 	errno = ENOENT;
    388 	return (-1);
    389 
    390  emsgsize:
    391 	errno = EMSGSIZE;
    392 	return (-1);
    393 }
    394 
    395 /*
    396  * int
    397  * inet_net_pton(af, src, dst, size)
    398  *	convert network number from presentation to network format.
    399  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
    400  *	"size" is in bytes and describes "dst".
    401  * return:
    402  *	number of bits, either imputed classfully or specified with /CIDR,
    403  *	or -1 if some failure occurred (check errno).  ENOENT means it was
    404  *	not a valid network specification.
    405  * author:
    406  *	Paul Vixie (ISC), June 1996
    407  */
    408 int
    409 inet_net_pton(int af, const char *src, void *dst, size_t size)
    410 {
    411 	switch (af) {
    412 	case AF_INET:
    413 		return (inet_net_pton_ipv4(src, dst, size));
    414 	case AF_INET6:
    415 		return (inet_net_pton_ipv6(src, dst, size));
    416 	default:
    417 		errno = EAFNOSUPPORT;
    418 		return (-1);
    419 	}
    420 }
    421