1 /* $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $ */
2
3 /*
4 * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org>
5 * Copyright (c) 1996,1999 by Internet Software Consortium.
6 *
7 * SPDX-License-Identifier: ISC
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 * SOFTWARE.
21 */
22
23 #include "port_before.h"
24
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29
30 #include <assert.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36
37 #include "port_after.h"
38
39 static int inet_net_pton_ipv4(const char *, u_char *, size_t);
40 static int inet_net_pton_ipv6(const char *, u_char *, size_t);
41
42 /*
43 * static int
44 * inet_net_pton(af, src, dst, size)
45 * convert network number from presentation to network format.
46 * accepts hex octets, hex strings, decimal octets, and /CIDR.
47 * "size" is in bytes and describes "dst".
48 * return:
49 * number of bits, either imputed classfully or specified with /CIDR,
50 * or -1 if some failure occurred (check errno). ENOENT means it was
51 * not a valid network specification.
52 * author:
53 * Paul Vixie (ISC), June 1996
54 */
55 int
inet_net_pton(int af,const char * src,void * dst,size_t size)56 inet_net_pton(int af, const char *src, void *dst, size_t size)
57 {
58 switch (af) {
59 case AF_INET:
60 return (inet_net_pton_ipv4(src, dst, size));
61 case AF_INET6:
62 return (inet_net_pton_ipv6(src, dst, size));
63 default:
64 errno = EAFNOSUPPORT;
65 return (-1);
66 }
67 }
68
69 /*
70 * static int
71 * inet_net_pton_ipv4(src, dst, size)
72 * convert IPv4 network number from presentation to network format.
73 * accepts hex octets, hex strings, decimal octets, and /CIDR.
74 * "size" is in bytes and describes "dst".
75 * return:
76 * number of bits, either imputed classfully or specified with /CIDR,
77 * or -1 if some failure occurred (check errno). ENOENT means it was
78 * not an IPv4 network specification.
79 * note:
80 * network byte order assumed. this means 192.5.5.240/28 has
81 * 0b11110000 in its fourth octet.
82 * author:
83 * Paul Vixie (ISC), June 1996
84 */
85 static int
inet_net_pton_ipv4(const char * src,u_char * dst,size_t size)86 inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
87 {
88 static const char
89 xdigits[] = "0123456789abcdef",
90 digits[] = "0123456789";
91 int n, ch, tmp, dirty, bits;
92 const u_char *odst = dst;
93
94 ch = (unsigned char)*src++;
95 if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
96 && isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) {
97 /* Hexadecimal: Eat nybble string. */
98 if (size == 0)
99 goto emsgsize;
100 tmp = 0, dirty = 0;
101 src++; /* skip x or X. */
102 while ((ch = (unsigned char)*src++) != '\0' &&
103 isascii(ch) && isxdigit(ch)) {
104 if (isupper(ch))
105 ch = tolower(ch);
106 n = strchr(xdigits, ch) - xdigits;
107 assert(n >= 0 && n <= 15);
108 if (dirty == 0)
109 tmp = n;
110 else
111 tmp = (tmp << 4) | n;
112 if (++dirty == 2) {
113 if (size-- == 0)
114 goto emsgsize;
115 *dst++ = (u_char) tmp;
116 dirty = 0;
117 }
118 }
119 if (dirty) { /* Odd trailing nybble? */
120 if (size-- == 0)
121 goto emsgsize;
122 *dst++ = (u_char) (tmp << 4);
123 }
124 } else if (isascii(ch) && isdigit(ch)) {
125 /* Decimal: eat dotted digit string. */
126 for (;;) {
127 tmp = 0;
128 do {
129 n = strchr(digits, ch) - digits;
130 assert(n >= 0 && n <= 9);
131 tmp *= 10;
132 tmp += n;
133 if (tmp > 255)
134 goto enoent;
135 } while ((ch = (unsigned char)*src++) != '\0' &&
136 isascii(ch) && isdigit(ch));
137 if (size-- == 0)
138 goto emsgsize;
139 *dst++ = (u_char) tmp;
140 if (ch == '\0' || ch == '/')
141 break;
142 if (ch != '.')
143 goto enoent;
144 ch = (unsigned char)*src++;
145 if (!isascii(ch) || !isdigit(ch))
146 goto enoent;
147 }
148 } else
149 goto enoent;
150
151 bits = -1;
152 if (ch == '/' && isascii((unsigned char)src[0]) &&
153 isdigit((unsigned char)src[0]) && dst > odst) {
154 /* CIDR width specifier. Nothing can follow it. */
155 ch = (unsigned char)*src++; /* Skip over the /. */
156 bits = 0;
157 do {
158 n = strchr(digits, ch) - digits;
159 assert(n >= 0 && n <= 9);
160 bits *= 10;
161 bits += n;
162 if (bits > 32)
163 goto emsgsize;
164 } while ((ch = (unsigned char)*src++) != '\0' &&
165 isascii(ch) && isdigit(ch));
166 if (ch != '\0')
167 goto enoent;
168 }
169
170 /* Fiery death and destruction unless we prefetched EOS. */
171 if (ch != '\0')
172 goto enoent;
173
174 /* If nothing was written to the destination, we found no address. */
175 if (dst == odst)
176 goto enoent;
177 /* If no CIDR spec was given, infer width from net class. */
178 if (bits == -1) {
179 if (*odst >= 240) /* Class E */
180 bits = 32;
181 else if (*odst >= 224) /* Class D */
182 bits = 4;
183 else if (*odst >= 192) /* Class C */
184 bits = 24;
185 else if (*odst >= 128) /* Class B */
186 bits = 16;
187 else /* Class A */
188 bits = 8;
189 /* If imputed mask is narrower than specified octets, widen. */
190 if (bits < ((dst - odst) * 8))
191 bits = (dst - odst) * 8;
192 }
193 /* Extend network to cover the actual mask. */
194 while (bits > ((dst - odst) * 8)) {
195 if (size-- == 0)
196 goto emsgsize;
197 *dst++ = '\0';
198 }
199 return (bits);
200
201 enoent:
202 errno = ENOENT;
203 return (-1);
204
205 emsgsize:
206 errno = EMSGSIZE;
207 return (-1);
208 }
209
210
211 static int
inet_net_pton_ipv6(const char * src,u_char * dst,size_t size)212 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
213 {
214 struct in6_addr in6;
215 int ret;
216 int bits;
217 size_t bytes;
218 char buf[INET6_ADDRSTRLEN + sizeof("/128")];
219 char *sep;
220 const char *errstr;
221
222 if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
223 errno = EMSGSIZE;
224 return (-1);
225 }
226
227 sep = strchr(buf, '/');
228 if (sep != NULL)
229 *sep++ = '\0';
230
231 ret = inet_pton(AF_INET6, buf, &in6);
232 if (ret != 1)
233 return (-1);
234
235 if (sep == NULL)
236 bits = 128;
237 else {
238 bits = strtonum(sep, 0, 128, &errstr);
239 if (errstr) {
240 errno = EINVAL;
241 return (-1);
242 }
243 }
244
245 bytes = (bits + 7) / 8;
246 if (bytes > size) {
247 errno = EMSGSIZE;
248 return (-1);
249 }
250 memcpy(dst, &in6.s6_addr, bytes);
251 return (bits);
252 }
253
254 /*
255 * Weak aliases for applications that use certain private entry points,
256 * and fail to include <arpa/inet.h>.
257 */
258 #undef inet_net_pton
259 __weak_reference(__inet_net_pton, inet_net_pton);
260