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