xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/inet_pton.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: inet_pton.c,v 1.2 2024/08/18 20:47:14 christos Exp $	*/
2897be3a4Schristos 
3897be3a4Schristos /*
4897be3a4Schristos  * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
5897be3a4Schristos  * Copyright (C) 1996-2003  Internet Software Consortium.
6897be3a4Schristos  *
7897be3a4Schristos  * Permission to use, copy, modify, and/or distribute this software for any
8897be3a4Schristos  * purpose with or without fee is hereby granted, provided that the above
9897be3a4Schristos  * copyright notice and this permission notice appear in all copies.
10897be3a4Schristos  *
11897be3a4Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12897be3a4Schristos  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13897be3a4Schristos  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14897be3a4Schristos  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15897be3a4Schristos  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16897be3a4Schristos  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17897be3a4Schristos  * PERFORMANCE OF THIS SOFTWARE.
18897be3a4Schristos  */
19897be3a4Schristos 
20897be3a4Schristos /*! \file */
21897be3a4Schristos 
22897be3a4Schristos #if defined(LIBC_SCCS) && !defined(lint)
23897be3a4Schristos static char rcsid[] =
24897be3a4Schristos 	"Id: inet_pton.c,v 1.19 2007/06/19 23:47:17 tbox Exp ";
25897be3a4Schristos #endif /* LIBC_SCCS and not lint */
26897be3a4Schristos 
27897be3a4Schristos #include <config.h>
28897be3a4Schristos 
29897be3a4Schristos #include <errno.h>
30897be3a4Schristos #include <string.h>
31897be3a4Schristos 
32897be3a4Schristos #include <isc/net.h>
33897be3a4Schristos 
34897be3a4Schristos /*% INT16 Size */
35897be3a4Schristos #define NS_INT16SZ	 2
36897be3a4Schristos /*% IPv4 Address Size */
37897be3a4Schristos #define NS_INADDRSZ	 4
38897be3a4Schristos /*% IPv6 Address Size */
39897be3a4Schristos #define NS_IN6ADDRSZ	16
40897be3a4Schristos 
41897be3a4Schristos /*
42897be3a4Schristos  * WARNING: Don't even consider trying to compile this on a system where
43897be3a4Schristos  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
44897be3a4Schristos  */
45897be3a4Schristos 
46897be3a4Schristos static int inet_pton4(const char *src, unsigned char *dst);
47897be3a4Schristos static int inet_pton6(const char *src, unsigned char *dst);
48897be3a4Schristos int isc_net_pton(int af, const char *src, void *dst);
49897be3a4Schristos 
50897be3a4Schristos /*%
51897be3a4Schristos  *	convert from presentation format (which usually means ASCII printable)
52897be3a4Schristos  *	to network format (which is usually some kind of binary format).
53897be3a4Schristos  * \return
54897be3a4Schristos  *	1 if the address was valid for the specified address family
55897be3a4Schristos  *	0 if the address wasn't valid (`dst' is untouched in this case)
56897be3a4Schristos  *	-1 if some other error occurred (`dst' is untouched in this case, too)
57897be3a4Schristos  * \author
58897be3a4Schristos  *	Paul Vixie, 1996.
59897be3a4Schristos  */
60897be3a4Schristos int
61897be3a4Schristos isc_net_pton(int af, const char *src, void *dst) {
62897be3a4Schristos 	switch (af) {
63897be3a4Schristos 	case AF_INET:
64897be3a4Schristos 		return (inet_pton4(src, dst));
65897be3a4Schristos 	case AF_INET6:
66897be3a4Schristos 		return (inet_pton6(src, dst));
67897be3a4Schristos 	default:
68897be3a4Schristos 		errno = EAFNOSUPPORT;
69897be3a4Schristos 		return (-1);
70897be3a4Schristos 	}
71897be3a4Schristos 	/* NOTREACHED */
72897be3a4Schristos }
73897be3a4Schristos 
74897be3a4Schristos /*!\fn static int inet_pton4(const char *src, unsigned char *dst)
75897be3a4Schristos  * \brief
76897be3a4Schristos  *	like inet_aton() but without all the hexadecimal and shorthand.
77897be3a4Schristos  * \return
78897be3a4Schristos  *	1 if `src' is a valid dotted quad, else 0.
79897be3a4Schristos  * \note
80897be3a4Schristos  *	does not touch `dst' unless it's returning 1.
81897be3a4Schristos  * \author
82897be3a4Schristos  *	Paul Vixie, 1996.
83897be3a4Schristos  */
84897be3a4Schristos static int
85897be3a4Schristos inet_pton4(const char *src, unsigned char *dst) {
86897be3a4Schristos 	static const char digits[] = "0123456789";
87897be3a4Schristos 	int saw_digit, octets, ch;
88897be3a4Schristos 	unsigned char tmp[NS_INADDRSZ], *tp;
89897be3a4Schristos 
90897be3a4Schristos 	saw_digit = 0;
91897be3a4Schristos 	octets = 0;
92897be3a4Schristos 	*(tp = tmp) = 0;
93897be3a4Schristos 	while ((ch = *src++) != '\0') {
94897be3a4Schristos 		const char *pch;
95897be3a4Schristos 
96897be3a4Schristos 		if ((pch = strchr(digits, ch)) != NULL) {
97897be3a4Schristos 			size_t newv = *tp * 10 + (pch - digits);
98897be3a4Schristos 
99897be3a4Schristos 			if (saw_digit && *tp == 0)
100897be3a4Schristos 				return (0);
101897be3a4Schristos 			if (newv > 255)
102897be3a4Schristos 				return (0);
103897be3a4Schristos 			*tp = (unsigned char)newv;
104897be3a4Schristos 			if (!saw_digit) {
105897be3a4Schristos 				if (++octets > 4)
106897be3a4Schristos 					return (0);
107897be3a4Schristos 				saw_digit = 1;
108897be3a4Schristos 			}
109897be3a4Schristos 		} else if (ch == '.' && saw_digit) {
110897be3a4Schristos 			if (octets == 4)
111897be3a4Schristos 				return (0);
112897be3a4Schristos 			*++tp = 0;
113897be3a4Schristos 			saw_digit = 0;
114897be3a4Schristos 		} else
115897be3a4Schristos 			return (0);
116897be3a4Schristos 	}
117897be3a4Schristos 	if (octets < 4)
118897be3a4Schristos 		return (0);
119897be3a4Schristos 	memcpy(dst, tmp, NS_INADDRSZ);
120897be3a4Schristos 	return (1);
121897be3a4Schristos }
122897be3a4Schristos 
123897be3a4Schristos /*%
124897be3a4Schristos  *	convert presentation level address to network order binary form.
125897be3a4Schristos  * \return
126897be3a4Schristos  *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
127897be3a4Schristos  * \note
128897be3a4Schristos  *	(1) does not touch `dst' unless it's returning 1.
129897be3a4Schristos  * \note
130897be3a4Schristos  *	(2) :: in a full address is silently ignored.
131897be3a4Schristos  * \author
132897be3a4Schristos  *	inspired by Mark Andrews.
133897be3a4Schristos  * \author
134897be3a4Schristos  *	Paul Vixie, 1996.
135897be3a4Schristos  */
136897be3a4Schristos static int
137897be3a4Schristos inet_pton6(const char *src, unsigned char *dst) {
138897be3a4Schristos 	static const char xdigits_l[] = "0123456789abcdef",
139897be3a4Schristos 			  xdigits_u[] = "0123456789ABCDEF";
140897be3a4Schristos 	unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
141897be3a4Schristos 	const char *xdigits, *curtok;
142897be3a4Schristos 	int ch, seen_xdigits;
143897be3a4Schristos 	unsigned int val;
144897be3a4Schristos 
145897be3a4Schristos 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
146897be3a4Schristos 	endp = tp + NS_IN6ADDRSZ;
147897be3a4Schristos 	colonp = NULL;
148897be3a4Schristos 	/* Leading :: requires some special handling. */
149897be3a4Schristos 	if (*src == ':')
150897be3a4Schristos 		if (*++src != ':')
151897be3a4Schristos 			return (0);
152897be3a4Schristos 	curtok = src;
153897be3a4Schristos 	seen_xdigits = 0;
154897be3a4Schristos 	val = 0;
155897be3a4Schristos 	while ((ch = *src++) != '\0') {
156897be3a4Schristos 		const char *pch;
157897be3a4Schristos 
158897be3a4Schristos 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
159897be3a4Schristos 			pch = strchr((xdigits = xdigits_u), ch);
160897be3a4Schristos 		if (pch != NULL) {
161897be3a4Schristos 			val <<= 4;
162897be3a4Schristos 			val |= (pch - xdigits);
163897be3a4Schristos 			if (++seen_xdigits > 4)
164897be3a4Schristos 				return (0);
165897be3a4Schristos 			continue;
166897be3a4Schristos 		}
167897be3a4Schristos 		if (ch == ':') {
168897be3a4Schristos 			curtok = src;
169897be3a4Schristos 			if (!seen_xdigits) {
170897be3a4Schristos 				if (colonp)
171897be3a4Schristos 					return (0);
172897be3a4Schristos 				colonp = tp;
173897be3a4Schristos 				continue;
174897be3a4Schristos 			}
175897be3a4Schristos 			if (NS_INT16SZ > endp - tp)
176897be3a4Schristos 				return (0);
177897be3a4Schristos 			*tp++ = (unsigned char) (val >> 8) & 0xff;
178897be3a4Schristos 			*tp++ = (unsigned char) val & 0xff;
179897be3a4Schristos 			seen_xdigits = 0;
180897be3a4Schristos 			val = 0;
181897be3a4Schristos 			continue;
182897be3a4Schristos 		}
183897be3a4Schristos 		if (ch == '.' && (NS_INADDRSZ <= endp - tp) &&
184897be3a4Schristos 		    inet_pton4(curtok, tp) > 0) {
185897be3a4Schristos 			tp += NS_INADDRSZ;
186897be3a4Schristos 			seen_xdigits = 0;
187897be3a4Schristos 			break;	/* '\0' was seen by inet_pton4(). */
188897be3a4Schristos 		}
189897be3a4Schristos 		return (0);
190897be3a4Schristos 	}
191897be3a4Schristos 	if (seen_xdigits) {
192897be3a4Schristos 		if (NS_INT16SZ > endp - tp)
193897be3a4Schristos 			return (0);
194897be3a4Schristos 		*tp++ = (unsigned char) (val >> 8) & 0xff;
195897be3a4Schristos 		*tp++ = (unsigned char) val & 0xff;
196897be3a4Schristos 	}
197897be3a4Schristos 	if (colonp != NULL) {
198897be3a4Schristos 		/*
199897be3a4Schristos 		 * Since some memmove()'s erroneously fail to handle
200897be3a4Schristos 		 * overlapping regions, we'll do the shift by hand.
201897be3a4Schristos 		 */
202897be3a4Schristos 		const size_t n = tp - colonp;
203897be3a4Schristos 		int i;
204897be3a4Schristos 
205897be3a4Schristos 		if (tp == endp)
206897be3a4Schristos 			return (0);
207897be3a4Schristos 		for (i = 1; (size_t)i <= n; i++) {
208897be3a4Schristos 			endp[- i] = colonp[n - i];
209897be3a4Schristos 			colonp[n - i] = 0;
210897be3a4Schristos 		}
211897be3a4Schristos 		tp = endp;
212897be3a4Schristos 	}
213897be3a4Schristos 	if (tp != endp)
214897be3a4Schristos 		return (0);
215897be3a4Schristos 	memcpy(dst, tmp, NS_IN6ADDRSZ);
216897be3a4Schristos 	return (1);
217897be3a4Schristos }
218