Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
      3  * Copyright (c) 1996,1999 by Internet Software Consortium.
      4  *
      5  * Permission to use, copy, modify, and distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 #ifndef lint
     20 __RCSID("$NetBSD: strtoaddr.c,v 1.5 2024/09/02 16:15:33 christos Exp $");
     21 #endif
     22 
     23 #include <config.h>
     24 
     25 #include "netdissect-stdinc.h"
     26 #include <stddef.h>
     27 #include <string.h>
     28 
     29 #include "netdissect-ctype.h"
     30 
     31 #include "strtoaddr.h"
     32 
     33 #ifndef NS_INADDRSZ
     34 #define NS_INADDRSZ	4	/* IPv4 T_A */
     35 #endif
     36 
     37 #ifndef NS_IN6ADDRSZ
     38 #define NS_IN6ADDRSZ	16	/* IPv6 T_AAAA */
     39 #endif
     40 
     41 #ifndef NS_INT16SZ
     42 #define NS_INT16SZ	2	/* #/bytes of data in a uint16_t */
     43 #endif
     44 
     45 /*%
     46  * WARNING: Don't even consider trying to compile this on a system where
     47  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
     48  */
     49 
     50 /* int
     51  * strtoaddr(src, dst)
     52  *	convert presentation level IPv4 address to network order binary form.
     53  * return:
     54  *	1 if `src' is a valid input, else 0.
     55  * notice:
     56  *	does not touch `dst' unless it's returning 1.
     57  * author:
     58  *	Paul Vixie, 1996.
     59  */
     60 int
     61 strtoaddr(const char *src, void *dst)
     62 {
     63 	uint32_t val;
     64 	u_int digit;
     65 	ptrdiff_t n;
     66 	unsigned char c;
     67 	u_int parts[4];
     68 	u_int *pp = parts;
     69 
     70 	c = *src;
     71 	for (;;) {
     72 		/*
     73 		 * Collect number up to ``.''.
     74 		 * Values are specified as for C:
     75 		 * 0x=hex, 0=octal, isdigit=decimal.
     76 		 */
     77 		if (!ND_ASCII_ISDIGIT(c))
     78 			return (0);
     79 		val = 0;
     80 		if (c == '0') {
     81 			c = *++src;
     82 			if (c == 'x' || c == 'X')
     83 				return (0);
     84 			else if (ND_ASCII_ISDIGIT(c) && c != '9')
     85 				return (0);
     86 		}
     87 		for (;;) {
     88 			if (ND_ASCII_ISDIGIT(c)) {
     89 				digit = c - '0';
     90 				val = (val * 10) + digit;
     91 				c = *++src;
     92 			} else
     93 				break;
     94 		}
     95 		if (c == '.') {
     96 			/*
     97 			 * Internet format:
     98 			 *	a.b.c.d
     99 			 *	a.b.c	(with c treated as 16 bits)
    100 			 *	a.b	(with b treated as 24 bits)
    101 			 *	a	(with a treated as 32 bits)
    102 			 */
    103 			if (pp >= parts + 3)
    104 				return (0);
    105 			*pp++ = val;
    106 			c = *++src;
    107 		} else
    108 			break;
    109 	}
    110 	/*
    111 	 * Check for trailing characters.
    112 	 */
    113 	if (c != '\0' && c != ' ' && c != '\t')
    114 		return (0);
    115 	/*
    116 	 * Find the number of parts specified.
    117 	 * It must be 4; we only support dotted quads, we don't
    118 	 * support shorthand.
    119 	 */
    120 	n = pp - parts + 1;
    121 	if (n != 4)
    122 		return (0);
    123 	/*
    124 	 * parts[0-2] were set to the first 3 parts of the address;
    125 	 * val was set to the 4th part.
    126 	 *
    127 	 * Check if any part is bigger than 255.
    128 	 */
    129 	if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
    130 		return (0);
    131 	/*
    132 	 * Add the other three parts to val.
    133 	 */
    134 	val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
    135 	if (dst) {
    136 		val = htonl(val);
    137 		memcpy(dst, &val, NS_INADDRSZ);
    138 	}
    139 	return (1);
    140 }
    141 
    142 /* int
    143  * strtoaddr6(src, dst)
    144  *	convert presentation level IPv6 address to network order binary form.
    145  * return:
    146  *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
    147  * notice:
    148  *	(1) does not touch `dst' unless it's returning 1.
    149  *	(2) :: in a full address is silently ignored.
    150  * credit:
    151  *	inspired by Mark Andrews.
    152  * author:
    153  *	Paul Vixie, 1996.
    154  */
    155 int
    156 strtoaddr6(const char *src, void *dst)
    157 {
    158 	static const char xdigits_l[] = "0123456789abcdef",
    159 			  xdigits_u[] = "0123456789ABCDEF";
    160 	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
    161 	const char *xdigits, *curtok;
    162 	int ch, seen_xdigits;
    163 	u_int val;
    164 
    165 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
    166 	endp = tp + NS_IN6ADDRSZ;
    167 	colonp = NULL;
    168 	/* Leading :: requires some special handling. */
    169 	if (*src == ':')
    170 		if (*++src != ':')
    171 			return (0);
    172 	curtok = src;
    173 	seen_xdigits = 0;
    174 	val = 0;
    175 	while ((ch = *src++) != '\0') {
    176 		const char *pch;
    177 
    178 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
    179 			pch = strchr((xdigits = xdigits_u), ch);
    180 		if (pch != NULL) {
    181 			val <<= 4;
    182 			val |= (int)(pch - xdigits);
    183 			if (++seen_xdigits > 4)
    184 				return (0);
    185 			continue;
    186 		}
    187 		if (ch == ':') {
    188 			curtok = src;
    189 			if (!seen_xdigits) {
    190 				if (colonp)
    191 					return (0);
    192 				colonp = tp;
    193 				continue;
    194 			} else if (*src == '\0')
    195 				return (0);
    196 			if (tp + NS_INT16SZ > endp)
    197 				return (0);
    198 			*tp++ = (u_char) (val >> 8) & 0xff;
    199 			*tp++ = (u_char) val & 0xff;
    200 			seen_xdigits = 0;
    201 			val = 0;
    202 			continue;
    203 		}
    204 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
    205 		    strtoaddr(curtok, tp) > 0) {
    206 			tp += NS_INADDRSZ;
    207 			seen_xdigits = 0;
    208 			break;	/*%< '\\0' was seen by strtoaddr(). */
    209 		}
    210 		return (0);
    211 	}
    212 	if (seen_xdigits) {
    213 		if (tp + NS_INT16SZ > endp)
    214 			return (0);
    215 		*tp++ = (u_char) (val >> 8) & 0xff;
    216 		*tp++ = (u_char) val & 0xff;
    217 	}
    218 	if (colonp != NULL) {
    219 		/*
    220 		 * Since some memmove()'s erroneously fail to handle
    221 		 * overlapping regions, we'll do the shift by hand.
    222 		 */
    223 		const ptrdiff_t n = tp - colonp;
    224 		int i;
    225 
    226 		if (tp == endp)
    227 			return (0);
    228 		for (i = 1; i <= n; i++) {
    229 			endp[- i] = colonp[n - i];
    230 			colonp[n - i] = 0;
    231 		}
    232 		tp = endp;
    233 	}
    234 	if (tp != endp)
    235 		return (0);
    236 	memcpy(dst, tmp, NS_IN6ADDRSZ);
    237 	return (1);
    238 }
    239