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