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 * src/backend/utils/adt/inet_net_pton.c
18 */
19
20 #if defined(LIBC_SCCS) && !defined(lint)
21 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
22 #endif
23
24 #include "postgres.h"
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <assert.h>
31 #include <ctype.h>
32
33 #include "utils/builtins.h" /* pgrminclude ignore */ /* needed on some
34 * platforms */
35 #include "utils/inet.h"
36
37
38 static int inet_net_pton_ipv4(const char *src, u_char *dst);
39 static int inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
40 static int inet_net_pton_ipv6(const char *src, u_char *dst);
41 static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
42
43
44 /*
45 * int
46 * inet_net_pton(af, src, dst, size)
47 * convert network number from presentation to network format.
48 * accepts hex octets, hex strings, decimal octets, and /CIDR.
49 * "size" is in bytes and describes "dst".
50 * return:
51 * number of bits, either imputed classfully or specified with /CIDR,
52 * or -1 if some failure occurred (check errno). ENOENT means it was
53 * not a valid network specification.
54 * author:
55 * Paul Vixie (ISC), June 1996
56 *
57 * Changes:
58 * I added the inet_cidr_pton function (also from Paul) and changed
59 * the names to reflect their current use.
60 *
61 */
62 int
inet_net_pton(int af,const char * src,void * dst,size_t size)63 inet_net_pton(int af, const char *src, void *dst, size_t size)
64 {
65 switch (af)
66 {
67 case PGSQL_AF_INET:
68 return size == -1 ?
69 inet_net_pton_ipv4(src, dst) :
70 inet_cidr_pton_ipv4(src, dst, size);
71 case PGSQL_AF_INET6:
72 return size == -1 ?
73 inet_net_pton_ipv6(src, dst) :
74 inet_cidr_pton_ipv6(src, dst, size);
75 default:
76 errno = EAFNOSUPPORT;
77 return (-1);
78 }
79 }
80
81 /*
82 * static int
83 * inet_cidr_pton_ipv4(src, dst, size)
84 * convert IPv4 network number from presentation to network format.
85 * accepts hex octets, hex strings, decimal octets, and /CIDR.
86 * "size" is in bytes and describes "dst".
87 * return:
88 * number of bits, either imputed classfully or specified with /CIDR,
89 * or -1 if some failure occurred (check errno). ENOENT means it was
90 * not an IPv4 network specification.
91 * note:
92 * network byte order assumed. this means 192.5.5.240/28 has
93 * 0b11110000 in its fourth octet.
94 * author:
95 * Paul Vixie (ISC), June 1996
96 */
97 static int
inet_cidr_pton_ipv4(const char * src,u_char * dst,size_t size)98 inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
99 {
100 static const char xdigits[] = "0123456789abcdef";
101 static const char digits[] = "0123456789";
102 int n,
103 ch,
104 tmp = 0,
105 dirty,
106 bits;
107 const u_char *odst = dst;
108
109 ch = *src++;
110 if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
111 && isxdigit((unsigned char) src[1]))
112 {
113 /* Hexadecimal: Eat nybble string. */
114 if (size <= 0U)
115 goto emsgsize;
116 dirty = 0;
117 src++; /* skip x or X. */
118 while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
119 {
120 if (isupper((unsigned char) ch))
121 ch = tolower((unsigned char) ch);
122 n = strchr(xdigits, ch) - xdigits;
123 assert(n >= 0 && n <= 15);
124 if (dirty == 0)
125 tmp = n;
126 else
127 tmp = (tmp << 4) | n;
128 if (++dirty == 2)
129 {
130 if (size-- <= 0U)
131 goto emsgsize;
132 *dst++ = (u_char) tmp;
133 dirty = 0;
134 }
135 }
136 if (dirty)
137 { /* Odd trailing nybble? */
138 if (size-- <= 0U)
139 goto emsgsize;
140 *dst++ = (u_char) (tmp << 4);
141 }
142 }
143 else if (isdigit((unsigned char) ch))
144 {
145 /* Decimal: eat dotted digit string. */
146 for (;;)
147 {
148 tmp = 0;
149 do
150 {
151 n = strchr(digits, ch) - digits;
152 assert(n >= 0 && n <= 9);
153 tmp *= 10;
154 tmp += n;
155 if (tmp > 255)
156 goto enoent;
157 } while ((ch = *src++) != '\0' &&
158 isdigit((unsigned char) ch));
159 if (size-- <= 0U)
160 goto emsgsize;
161 *dst++ = (u_char) tmp;
162 if (ch == '\0' || ch == '/')
163 break;
164 if (ch != '.')
165 goto enoent;
166 ch = *src++;
167 if (!isdigit((unsigned char) ch))
168 goto enoent;
169 }
170 }
171 else
172 goto enoent;
173
174 bits = -1;
175 if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
176 {
177 /* CIDR width specifier. Nothing can follow it. */
178 ch = *src++; /* Skip over the /. */
179 bits = 0;
180 do
181 {
182 n = strchr(digits, ch) - digits;
183 assert(n >= 0 && n <= 9);
184 bits *= 10;
185 bits += n;
186 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
187 if (ch != '\0')
188 goto enoent;
189 if (bits > 32)
190 goto emsgsize;
191 }
192
193 /* Firey death and destruction unless we prefetched EOS. */
194 if (ch != '\0')
195 goto enoent;
196
197 /* If nothing was written to the destination, we found no address. */
198 if (dst == odst)
199 goto enoent;
200 /* If no CIDR spec was given, infer width from net class. */
201 if (bits == -1)
202 {
203 if (*odst >= 240) /* Class E */
204 bits = 32;
205 else if (*odst >= 224) /* Class D */
206 bits = 8;
207 else if (*odst >= 192) /* Class C */
208 bits = 24;
209 else if (*odst >= 128) /* Class B */
210 bits = 16;
211 else
212 /* Class A */
213 bits = 8;
214 /* If imputed mask is narrower than specified octets, widen. */
215 if (bits < ((dst - odst) * 8))
216 bits = (dst - odst) * 8;
217
218 /*
219 * If there are no additional bits specified for a class D address
220 * adjust bits to 4.
221 */
222 if (bits == 8 && *odst == 224)
223 bits = 4;
224 }
225 /* Extend network to cover the actual mask. */
226 while (bits > ((dst - odst) * 8))
227 {
228 if (size-- <= 0U)
229 goto emsgsize;
230 *dst++ = '\0';
231 }
232 return (bits);
233
234 enoent:
235 errno = ENOENT;
236 return (-1);
237
238 emsgsize:
239 errno = EMSGSIZE;
240 return (-1);
241 }
242
243 /*
244 * int
245 * inet_net_pton(af, src, dst, *bits)
246 * convert network address from presentation to network format.
247 * accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
248 * "dst" is assumed large enough for its "af". "bits" is set to the
249 * /CIDR prefix length, which can have defaults (like /32 for IPv4).
250 * return:
251 * -1 if an error occurred (inspect errno; ENOENT means bad format).
252 * 0 if successful conversion occurred.
253 * note:
254 * 192.5.5.1/28 has a nonzero host part, which means it isn't a network
255 * as called for by inet_cidr_pton() but it can be a host address with
256 * an included netmask.
257 * author:
258 * Paul Vixie (ISC), October 1998
259 */
260 static int
inet_net_pton_ipv4(const char * src,u_char * dst)261 inet_net_pton_ipv4(const char *src, u_char *dst)
262 {
263 static const char digits[] = "0123456789";
264 const u_char *odst = dst;
265 int n,
266 ch,
267 tmp,
268 bits;
269 size_t size = 4;
270
271 /* Get the mantissa. */
272 while (ch = *src++, isdigit((unsigned char) ch))
273 {
274 tmp = 0;
275 do
276 {
277 n = strchr(digits, ch) - digits;
278 assert(n >= 0 && n <= 9);
279 tmp *= 10;
280 tmp += n;
281 if (tmp > 255)
282 goto enoent;
283 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
284 if (size-- == 0)
285 goto emsgsize;
286 *dst++ = (u_char) tmp;
287 if (ch == '\0' || ch == '/')
288 break;
289 if (ch != '.')
290 goto enoent;
291 }
292
293 /* Get the prefix length if any. */
294 bits = -1;
295 if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
296 {
297 /* CIDR width specifier. Nothing can follow it. */
298 ch = *src++; /* Skip over the /. */
299 bits = 0;
300 do
301 {
302 n = strchr(digits, ch) - digits;
303 assert(n >= 0 && n <= 9);
304 bits *= 10;
305 bits += n;
306 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
307 if (ch != '\0')
308 goto enoent;
309 if (bits > 32)
310 goto emsgsize;
311 }
312
313 /* Firey death and destruction unless we prefetched EOS. */
314 if (ch != '\0')
315 goto enoent;
316
317 /* Prefix length can default to /32 only if all four octets spec'd. */
318 if (bits == -1)
319 {
320 if (dst - odst == 4)
321 bits = 32;
322 else
323 goto enoent;
324 }
325
326 /* If nothing was written to the destination, we found no address. */
327 if (dst == odst)
328 goto enoent;
329
330 /* If prefix length overspecifies mantissa, life is bad. */
331 if ((bits / 8) > (dst - odst))
332 goto enoent;
333
334 /* Extend address to four octets. */
335 while (size-- > 0)
336 *dst++ = 0;
337
338 return bits;
339
340 enoent:
341 errno = ENOENT;
342 return (-1);
343
344 emsgsize:
345 errno = EMSGSIZE;
346 return (-1);
347 }
348
349 static int
getbits(const char * src,int * bitsp)350 getbits(const char *src, int *bitsp)
351 {
352 static const char digits[] = "0123456789";
353 int n;
354 int val;
355 char ch;
356
357 val = 0;
358 n = 0;
359 while ((ch = *src++) != '\0')
360 {
361 const char *pch;
362
363 pch = strchr(digits, ch);
364 if (pch != NULL)
365 {
366 if (n++ != 0 && val == 0) /* no leading zeros */
367 return (0);
368 val *= 10;
369 val += (pch - digits);
370 if (val > 128) /* range */
371 return (0);
372 continue;
373 }
374 return (0);
375 }
376 if (n == 0)
377 return (0);
378 *bitsp = val;
379 return (1);
380 }
381
382 static int
getv4(const char * src,u_char * dst,int * bitsp)383 getv4(const char *src, u_char *dst, int *bitsp)
384 {
385 static const char digits[] = "0123456789";
386 u_char *odst = dst;
387 int n;
388 u_int val;
389 char ch;
390
391 val = 0;
392 n = 0;
393 while ((ch = *src++) != '\0')
394 {
395 const char *pch;
396
397 pch = strchr(digits, ch);
398 if (pch != NULL)
399 {
400 if (n++ != 0 && val == 0) /* no leading zeros */
401 return (0);
402 val *= 10;
403 val += (pch - digits);
404 if (val > 255) /* range */
405 return (0);
406 continue;
407 }
408 if (ch == '.' || ch == '/')
409 {
410 if (dst - odst > 3) /* too many octets? */
411 return (0);
412 *dst++ = val;
413 if (ch == '/')
414 return (getbits(src, bitsp));
415 val = 0;
416 n = 0;
417 continue;
418 }
419 return (0);
420 }
421 if (n == 0)
422 return (0);
423 if (dst - odst > 3) /* too many octets? */
424 return (0);
425 *dst++ = val;
426 return (1);
427 }
428
429 static int
inet_net_pton_ipv6(const char * src,u_char * dst)430 inet_net_pton_ipv6(const char *src, u_char *dst)
431 {
432 return inet_cidr_pton_ipv6(src, dst, 16);
433 }
434
435 #define NS_IN6ADDRSZ 16
436 #define NS_INT16SZ 2
437 #define NS_INADDRSZ 4
438
439 static int
inet_cidr_pton_ipv6(const char * src,u_char * dst,size_t size)440 inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
441 {
442 static const char xdigits_l[] = "0123456789abcdef",
443 xdigits_u[] = "0123456789ABCDEF";
444 u_char tmp[NS_IN6ADDRSZ],
445 *tp,
446 *endp,
447 *colonp;
448 const char *xdigits,
449 *curtok;
450 int ch,
451 saw_xdigit;
452 u_int val;
453 int digits;
454 int bits;
455
456 if (size < NS_IN6ADDRSZ)
457 goto emsgsize;
458
459 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
460 endp = tp + NS_IN6ADDRSZ;
461 colonp = NULL;
462 /* Leading :: requires some special handling. */
463 if (*src == ':')
464 if (*++src != ':')
465 goto enoent;
466 curtok = src;
467 saw_xdigit = 0;
468 val = 0;
469 digits = 0;
470 bits = -1;
471 while ((ch = *src++) != '\0')
472 {
473 const char *pch;
474
475 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
476 pch = strchr((xdigits = xdigits_u), ch);
477 if (pch != NULL)
478 {
479 val <<= 4;
480 val |= (pch - xdigits);
481 if (++digits > 4)
482 goto enoent;
483 saw_xdigit = 1;
484 continue;
485 }
486 if (ch == ':')
487 {
488 curtok = src;
489 if (!saw_xdigit)
490 {
491 if (colonp)
492 goto enoent;
493 colonp = tp;
494 continue;
495 }
496 else if (*src == '\0')
497 goto enoent;
498 if (tp + NS_INT16SZ > endp)
499 goto enoent;
500 *tp++ = (u_char) (val >> 8) & 0xff;
501 *tp++ = (u_char) val & 0xff;
502 saw_xdigit = 0;
503 digits = 0;
504 val = 0;
505 continue;
506 }
507 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
508 getv4(curtok, tp, &bits) > 0)
509 {
510 tp += NS_INADDRSZ;
511 saw_xdigit = 0;
512 break; /* '\0' was seen by inet_pton4(). */
513 }
514 if (ch == '/' && getbits(src, &bits) > 0)
515 break;
516 goto enoent;
517 }
518 if (saw_xdigit)
519 {
520 if (tp + NS_INT16SZ > endp)
521 goto enoent;
522 *tp++ = (u_char) (val >> 8) & 0xff;
523 *tp++ = (u_char) val & 0xff;
524 }
525 if (bits == -1)
526 bits = 128;
527
528 endp = tmp + 16;
529
530 if (colonp != NULL)
531 {
532 /*
533 * Since some memmove()'s erroneously fail to handle overlapping
534 * regions, we'll do the shift by hand.
535 */
536 const int n = tp - colonp;
537 int i;
538
539 if (tp == endp)
540 goto enoent;
541 for (i = 1; i <= n; i++)
542 {
543 endp[-i] = colonp[n - i];
544 colonp[n - i] = 0;
545 }
546 tp = endp;
547 }
548 if (tp != endp)
549 goto enoent;
550
551 /*
552 * Copy out the result.
553 */
554 memcpy(dst, tmp, NS_IN6ADDRSZ);
555
556 return (bits);
557
558 enoent:
559 errno = ENOENT;
560 return (-1);
561
562 emsgsize:
563 errno = EMSGSIZE;
564 return (-1);
565 }
566