1 /*-
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1998,1999 by Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "port_before.h"
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/nameser.h>
26 #include <arpa/inet.h>
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34
35 #include "port_after.h"
36
37 #ifdef SPRINTF_CHAR
38 # define SPRINTF(x) strlen(sprintf/**/x)
39 #else
40 # define SPRINTF(x) ((size_t)sprintf x)
41 #endif
42
43 static int inet_cidr_pton_ipv4 (const char *src, u_char *dst, int *bits,
44 int ipv6);
45 static int inet_cidr_pton_ipv6 (const char *src, u_char *dst, int *bits);
46
47 static int getbits(const char *, int ipv6);
48
49 /*%
50 * int
51 * inet_cidr_pton(af, src, dst, *bits)
52 * convert network address from presentation to network format.
53 * accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
54 * "dst" is assumed large enough for its "af". "bits" is set to the
55 * /CIDR prefix length, which can have defaults (like /32 for IPv4).
56 * return:
57 * -1 if an error occurred (inspect errno; ENOENT means bad format).
58 * 0 if successful conversion occurred.
59 * note:
60 * 192.5.5.1/28 has a nonzero host part, which means it isn't a network
61 * as called for by inet_net_pton() but it can be a host address with
62 * an included netmask.
63 * author:
64 * Paul Vixie (ISC), October 1998
65 */
66 int
inet_cidr_pton(int af,const char * src,void * dst,int * bits)67 inet_cidr_pton(int af, const char *src, void *dst, int *bits) {
68 switch (af) {
69 case AF_INET:
70 return (inet_cidr_pton_ipv4(src, dst, bits, 0));
71 case AF_INET6:
72 return (inet_cidr_pton_ipv6(src, dst, bits));
73 default:
74 errno = EAFNOSUPPORT;
75 return (-1);
76 }
77 }
78
79 static const char digits[] = "0123456789";
80
81 static int
inet_cidr_pton_ipv4(const char * src,u_char * dst,int * pbits,int ipv6)82 inet_cidr_pton_ipv4(const char *src, u_char *dst, int *pbits, int ipv6) {
83 const u_char *odst = dst;
84 int n, ch, tmp, bits;
85 size_t size = 4;
86
87 /* Get the mantissa. */
88 while (ch = *src++, (isascii(ch) && isdigit(ch))) {
89 tmp = 0;
90 do {
91 n = strchr(digits, ch) - digits;
92 assert(n >= 0 && n <= 9);
93 tmp *= 10;
94 tmp += n;
95 if (tmp > 255)
96 goto enoent;
97 } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
98 if (size-- == 0U)
99 goto emsgsize;
100 *dst++ = (u_char) tmp;
101 if (ch == '\0' || ch == '/')
102 break;
103 if (ch != '.')
104 goto enoent;
105 }
106
107 /* Get the prefix length if any. */
108 bits = -1;
109 if (ch == '/' && dst > odst) {
110 bits = getbits(src, ipv6);
111 if (bits == -2)
112 goto enoent;
113 } else if (ch != '\0')
114 goto enoent;
115
116 /* Prefix length can default to /32 only if all four octets spec'd. */
117 if (bits == -1) {
118 if (dst - odst == 4)
119 bits = ipv6 ? 128 : 32;
120 else
121 goto enoent;
122 }
123
124 /* If nothing was written to the destination, we found no address. */
125 if (dst == odst)
126 goto enoent;
127
128 /* If prefix length overspecifies mantissa, life is bad. */
129 if (((bits - (ipv6 ? 96 : 0)) / 8) > (dst - odst))
130 goto enoent;
131
132 /* Extend address to four octets. */
133 while (size-- > 0U)
134 *dst++ = 0;
135
136 *pbits = bits;
137 return (0);
138
139 enoent:
140 errno = ENOENT;
141 return (-1);
142
143 emsgsize:
144 errno = EMSGSIZE;
145 return (-1);
146 }
147
148 static int
inet_cidr_pton_ipv6(const char * src,u_char * dst,int * pbits)149 inet_cidr_pton_ipv6(const char *src, u_char *dst, int *pbits) {
150 static const char xdigits_l[] = "0123456789abcdef",
151 xdigits_u[] = "0123456789ABCDEF";
152 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
153 const char *xdigits, *curtok;
154 int ch, saw_xdigit;
155 u_int val;
156 int bits;
157
158 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
159 endp = tp + NS_IN6ADDRSZ;
160 colonp = NULL;
161 /* Leading :: requires some special handling. */
162 if (*src == ':')
163 if (*++src != ':')
164 return (0);
165 curtok = src;
166 saw_xdigit = 0;
167 val = 0;
168 bits = -1;
169 while ((ch = *src++) != '\0') {
170 const char *pch;
171
172 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
173 pch = strchr((xdigits = xdigits_u), ch);
174 if (pch != NULL) {
175 val <<= 4;
176 val |= (pch - xdigits);
177 if (val > 0xffff)
178 return (0);
179 saw_xdigit = 1;
180 continue;
181 }
182 if (ch == ':') {
183 curtok = src;
184 if (!saw_xdigit) {
185 if (colonp)
186 return (0);
187 colonp = tp;
188 continue;
189 } else if (*src == '\0') {
190 return (0);
191 }
192 if (tp + NS_INT16SZ > endp)
193 return (0);
194 *tp++ = (u_char) (val >> 8) & 0xff;
195 *tp++ = (u_char) val & 0xff;
196 saw_xdigit = 0;
197 val = 0;
198 continue;
199 }
200 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
201 inet_cidr_pton_ipv4(curtok, tp, &bits, 1) == 0) {
202 tp += NS_INADDRSZ;
203 saw_xdigit = 0;
204 break; /*%< '\\0' was seen by inet_pton4(). */
205 }
206 if (ch == '/') {
207 bits = getbits(src, 1);
208 if (bits == -2)
209 goto enoent;
210 break;
211 }
212 goto enoent;
213 }
214 if (saw_xdigit) {
215 if (tp + NS_INT16SZ > endp)
216 goto emsgsize;
217 *tp++ = (u_char) (val >> 8) & 0xff;
218 *tp++ = (u_char) val & 0xff;
219 }
220 if (colonp != NULL) {
221 /*
222 * Since some memmove()'s erroneously fail to handle
223 * overlapping regions, we'll do the shift by hand.
224 */
225 const int n = tp - colonp;
226 int i;
227
228 if (tp == endp)
229 goto enoent;
230 for (i = 1; i <= n; i++) {
231 endp[- i] = colonp[n - i];
232 colonp[n - i] = 0;
233 }
234 tp = endp;
235 }
236
237 memcpy(dst, tmp, NS_IN6ADDRSZ);
238
239 *pbits = bits;
240 return (0);
241
242 enoent:
243 errno = ENOENT;
244 return (-1);
245
246 emsgsize:
247 errno = EMSGSIZE;
248 return (-1);
249 }
250
251 static int
getbits(const char * src,int ipv6)252 getbits(const char *src, int ipv6) {
253 int bits = 0;
254 char *cp, ch;
255
256 if (*src == '\0') /*%< syntax */
257 return (-2);
258 do {
259 ch = *src++;
260 cp = strchr(digits, ch);
261 if (cp == NULL) /*%< syntax */
262 return (-2);
263 bits *= 10;
264 bits += cp - digits;
265 if (bits == 0 && *src != '\0') /*%< no leading zeros */
266 return (-2);
267 if (bits > (ipv6 ? 128 : 32)) /*%< range error */
268 return (-2);
269 } while (*src != '\0');
270
271 return (bits);
272 }
273
274 /*! \file */
275