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 8000 : pg_inet_net_pton(int af, const char *src, void *dst, size_t size)
62 : {
63 8000 : switch (af)
64 : {
65 6382 : case PGSQL_AF_INET:
66 : return size == -1 ?
67 8608 : inet_net_pton_ipv4(src, dst) :
68 2226 : inet_cidr_pton_ipv4(src, dst, size);
69 1618 : case PGSQL_AF_INET6:
70 : return size == -1 ?
71 2298 : inet_net_pton_ipv6(src, dst) :
72 680 : 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 2226 : 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 2226 : tmp = 0,
103 : dirty,
104 : bits;
105 2226 : const u_char *odst = dst;
106 :
107 2226 : ch = *src++;
108 2226 : 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 : if (isupper((unsigned char) ch))
119 0 : ch = tolower((unsigned char) ch);
120 0 : n = strchr(xdigits, ch) - xdigits;
121 0 : assert(n >= 0 && n <= 15);
122 0 : if (dirty == 0)
123 0 : tmp = n;
124 : else
125 0 : tmp = (tmp << 4) | n;
126 0 : if (++dirty == 2)
127 : {
128 0 : if (size-- <= 0U)
129 0 : goto emsgsize;
130 0 : *dst++ = (u_char) tmp;
131 0 : dirty = 0;
132 : }
133 : }
134 0 : if (dirty)
135 : { /* Odd trailing nybble? */
136 0 : if (size-- <= 0U)
137 0 : goto emsgsize;
138 0 : *dst++ = (u_char) (tmp << 4);
139 : }
140 : }
141 2226 : else if (isdigit((unsigned char) ch))
142 : {
143 : /* Decimal: eat dotted digit string. */
144 : for (;;)
145 : {
146 8094 : tmp = 0;
147 : do
148 : {
149 13592 : n = strchr(digits, ch) - digits;
150 13592 : assert(n >= 0 && n <= 9);
151 13592 : tmp *= 10;
152 13592 : tmp += n;
153 13592 : if (tmp > 255)
154 12 : goto enoent;
155 26660 : } while ((ch = *src++) != '\0' &&
156 13580 : isdigit((unsigned char) ch));
157 8082 : if (size-- <= 0U)
158 0 : goto emsgsize;
159 8082 : *dst++ = (u_char) tmp;
160 8082 : if (ch == '\0' || ch == '/')
161 : break;
162 5868 : if (ch != '.')
163 0 : goto enoent;
164 5868 : ch = *src++;
165 5868 : if (!isdigit((unsigned char) ch))
166 0 : goto enoent;
167 : }
168 : }
169 : else
170 0 : goto enoent;
171 :
172 2214 : bits = -1;
173 2214 : if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
174 : {
175 : /* CIDR width specifier. Nothing can follow it. */
176 1714 : ch = *src++; /* Skip over the /. */
177 1714 : bits = 0;
178 : do
179 : {
180 3218 : n = strchr(digits, ch) - digits;
181 3218 : assert(n >= 0 && n <= 9);
182 3218 : bits *= 10;
183 3218 : bits += n;
184 3218 : } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
185 1714 : if (ch != '\0')
186 0 : goto enoent;
187 1714 : if (bits > 32)
188 0 : goto emsgsize;
189 : }
190 :
191 : /* Fiery death and destruction unless we prefetched EOS. */
192 2214 : if (ch != '\0')
193 0 : goto enoent;
194 :
195 : /* If nothing was written to the destination, we found no address. */
196 2214 : if (dst == odst)
197 0 : goto enoent;
198 : /* If no CIDR spec was given, infer width from net class. */
199 2214 : if (bits == -1)
200 : {
201 500 : if (*odst >= 240) /* Class E */
202 192 : bits = 32;
203 308 : else if (*odst >= 224) /* Class D */
204 0 : bits = 8;
205 308 : else if (*odst >= 192) /* Class C */
206 30 : bits = 24;
207 278 : else if (*odst >= 128) /* Class B */
208 0 : bits = 16;
209 : else
210 : /* Class A */
211 278 : bits = 8;
212 : /* If imputed mask is narrower than specified octets, widen. */
213 500 : if (bits < ((dst - odst) * 8))
214 248 : bits = (dst - odst) * 8;
215 :
216 : /*
217 : * If there are no additional bits specified for a class D address
218 : * adjust bits to 4.
219 : */
220 500 : if (bits == 8 && *odst == 224)
221 0 : bits = 4;
222 : }
223 : /* Extend network to cover the actual mask. */
224 2262 : while (bits > ((dst - odst) * 8))
225 : {
226 48 : if (size-- <= 0U)
227 0 : goto emsgsize;
228 48 : *dst++ = '\0';
229 : }
230 2214 : return bits;
231 :
232 12 : enoent:
233 12 : errno = ENOENT;
234 12 : return -1;
235 :
236 0 : emsgsize:
237 0 : errno = EMSGSIZE;
238 0 : return -1;
239 : }
240 :
241 : /*
242 : * int
243 : * inet_net_pton_ipv4(af, src, dst, *bits)
244 : * convert network address from presentation to network format.
245 : * accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
246 : * "dst" is assumed large enough for its "af". "bits" is set to the
247 : * /CIDR prefix length, which can have defaults (like /32 for IPv4).
248 : * return:
249 : * -1 if an error occurred (inspect errno; ENOENT means bad format).
250 : * 0 if successful conversion occurred.
251 : * note:
252 : * 192.5.5.1/28 has a nonzero host part, which means it isn't a network
253 : * as called for by inet_cidr_pton() but it can be a host address with
254 : * an included netmask.
255 : * author:
256 : * Paul Vixie (ISC), October 1998
257 : */
258 : static int
259 4156 : inet_net_pton_ipv4(const char *src, u_char *dst)
260 : {
261 : static const char digits[] = "0123456789";
262 4156 : const u_char *odst = dst;
263 : int n,
264 : ch,
265 : tmp,
266 : bits;
267 4156 : size_t size = 4;
268 :
269 : /* Get the mantissa. */
270 16228 : while (ch = *src++, isdigit((unsigned char) ch))
271 : {
272 16228 : tmp = 0;
273 : do
274 : {
275 34220 : n = strchr(digits, ch) - digits;
276 34220 : assert(n >= 0 && n <= 9);
277 34220 : tmp *= 10;
278 34220 : tmp += n;
279 34220 : if (tmp > 255)
280 12 : goto enoent;
281 34208 : } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
282 16216 : if (size-- == 0)
283 0 : goto emsgsize;
284 16216 : *dst++ = (u_char) tmp;
285 16216 : if (ch == '\0' || ch == '/')
286 : break;
287 12072 : if (ch != '.')
288 0 : goto enoent;
289 : }
290 :
291 : /* Get the prefix length if any. */
292 4144 : bits = -1;
293 4144 : if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
294 : {
295 : /* CIDR width specifier. Nothing can follow it. */
296 2276 : ch = *src++; /* Skip over the /. */
297 2276 : bits = 0;
298 : do
299 : {
300 4136 : n = strchr(digits, ch) - digits;
301 4136 : assert(n >= 0 && n <= 9);
302 4136 : bits *= 10;
303 4136 : bits += n;
304 4136 : } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
305 2276 : if (ch != '\0')
306 0 : goto enoent;
307 2276 : if (bits > 32)
308 0 : goto emsgsize;
309 : }
310 :
311 : /* Fiery death and destruction unless we prefetched EOS. */
312 4144 : if (ch != '\0')
313 0 : goto enoent;
314 :
315 : /* Prefix length can default to /32 only if all four octets spec'd. */
316 4144 : if (bits == -1)
317 : {
318 1868 : if (dst - odst == 4)
319 1868 : bits = 32;
320 : else
321 0 : goto enoent;
322 : }
323 :
324 : /* If nothing was written to the destination, we found no address. */
325 4144 : if (dst == odst)
326 0 : goto enoent;
327 :
328 : /* If prefix length overspecifies mantissa, life is bad. */
329 4144 : if ((bits / 8) > (dst - odst))
330 0 : goto enoent;
331 :
332 : /* Extend address to four octets. */
333 4504 : while (size-- > 0)
334 360 : *dst++ = 0;
335 :
336 4144 : return bits;
337 :
338 12 : enoent:
339 12 : errno = ENOENT;
340 12 : return -1;
341 :
342 0 : emsgsize:
343 0 : errno = EMSGSIZE;
344 0 : return -1;
345 : }
346 :
347 : static int
348 894 : getbits(const char *src, int *bitsp)
349 : {
350 : static const char digits[] = "0123456789";
351 : int n;
352 : int val;
353 : char ch;
354 :
355 894 : val = 0;
356 894 : n = 0;
357 3076 : while ((ch = *src++) != '\0')
358 : {
359 : const char *pch;
360 :
361 2182 : pch = strchr(digits, ch);
362 2182 : if (pch != NULL)
363 : {
364 2182 : if (n++ != 0 && val == 0) /* no leading zeros */
365 0 : return 0;
366 2182 : val *= 10;
367 2182 : val += (pch - digits);
368 2182 : if (val > 128) /* range */
369 0 : return 0;
370 2182 : continue;
371 : }
372 0 : return 0;
373 : }
374 894 : if (n == 0)
375 0 : return 0;
376 894 : *bitsp = val;
377 894 : return 1;
378 : }
379 :
380 : static int
381 40 : getv4(const char *src, u_char *dst, int *bitsp)
382 : {
383 : static const char digits[] = "0123456789";
384 40 : u_char *odst = dst;
385 : int n;
386 : u_int val;
387 : char ch;
388 :
389 40 : val = 0;
390 40 : n = 0;
391 320 : while ((ch = *src++) != '\0')
392 : {
393 : const char *pch;
394 :
395 314 : pch = strchr(digits, ch);
396 314 : if (pch != NULL)
397 : {
398 160 : if (n++ != 0 && val == 0) /* no leading zeros */
399 0 : return 0;
400 160 : val *= 10;
401 160 : val += (pch - digits);
402 160 : if (val > 255) /* range */
403 0 : return 0;
404 160 : continue;
405 : }
406 154 : if (ch == '.' || ch == '/')
407 : {
408 154 : if (dst - odst > 3) /* too many octets? */
409 0 : return 0;
410 154 : *dst++ = val;
411 154 : if (ch == '/')
412 34 : return getbits(src, bitsp);
413 120 : val = 0;
414 120 : n = 0;
415 120 : continue;
416 : }
417 0 : return 0;
418 : }
419 6 : if (n == 0)
420 0 : return 0;
421 6 : if (dst - odst > 3) /* too many octets? */
422 0 : return 0;
423 6 : *dst++ = val;
424 6 : return 1;
425 : }
426 :
427 : static int
428 938 : inet_net_pton_ipv6(const char *src, u_char *dst)
429 : {
430 938 : return inet_cidr_pton_ipv6(src, dst, 16);
431 : }
432 :
433 : #define NS_IN6ADDRSZ 16
434 : #define NS_INT16SZ 2
435 : #define NS_INADDRSZ 4
436 :
437 : static int
438 1618 : inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
439 : {
440 : static const char xdigits_l[] = "0123456789abcdef",
441 : xdigits_u[] = "0123456789ABCDEF";
442 : u_char tmp[NS_IN6ADDRSZ],
443 : *tp,
444 : *endp,
445 : *colonp;
446 : const char *xdigits,
447 : *curtok;
448 : int ch,
449 : saw_xdigit;
450 : u_int val;
451 : int digits;
452 : int bits;
453 :
454 1618 : if (size < NS_IN6ADDRSZ)
455 0 : goto emsgsize;
456 :
457 1618 : memset((tp = tmp), '\0', NS_IN6ADDRSZ);
458 1618 : endp = tp + NS_IN6ADDRSZ;
459 1618 : colonp = NULL;
460 : /* Leading :: requires some special handling. */
461 1618 : if (*src == ':')
462 70 : if (*++src != ':')
463 0 : goto enoent;
464 1618 : curtok = src;
465 1618 : saw_xdigit = 0;
466 1618 : val = 0;
467 1618 : digits = 0;
468 1618 : bits = -1;
469 36796 : while ((ch = *src++) != '\0')
470 : {
471 : const char *pch;
472 :
473 36084 : if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
474 8416 : pch = strchr((xdigits = xdigits_u), ch);
475 36084 : if (pch != NULL)
476 : {
477 27668 : val <<= 4;
478 27668 : val |= (pch - xdigits);
479 27668 : if (++digits > 4)
480 0 : goto enoent;
481 27668 : saw_xdigit = 1;
482 27668 : continue;
483 : }
484 8416 : if (ch == ':')
485 : {
486 7516 : curtok = src;
487 7516 : if (!saw_xdigit)
488 : {
489 1440 : if (colonp)
490 6 : goto enoent;
491 1434 : colonp = tp;
492 1434 : continue;
493 : }
494 6076 : else if (*src == '\0')
495 0 : goto enoent;
496 6076 : if (tp + NS_INT16SZ > endp)
497 0 : goto enoent;
498 6076 : *tp++ = (u_char) (val >> 8) & 0xff;
499 6076 : *tp++ = (u_char) val & 0xff;
500 6076 : saw_xdigit = 0;
501 6076 : digits = 0;
502 6076 : val = 0;
503 6076 : continue;
504 : }
505 940 : if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
506 40 : getv4(curtok, tp, &bits) > 0)
507 : {
508 40 : tp += NS_INADDRSZ;
509 40 : saw_xdigit = 0;
510 40 : break; /* '\0' was seen by inet_pton4(). */
511 : }
512 860 : if (ch == '/' && getbits(src, &bits) > 0)
513 860 : break;
514 0 : goto enoent;
515 : }
516 1612 : if (saw_xdigit)
517 : {
518 1450 : if (tp + NS_INT16SZ > endp)
519 0 : goto enoent;
520 1450 : *tp++ = (u_char) (val >> 8) & 0xff;
521 1450 : *tp++ = (u_char) val & 0xff;
522 : }
523 1612 : if (bits == -1)
524 718 : bits = 128;
525 :
526 1612 : endp = tmp + 16;
527 :
528 1612 : if (colonp != NULL)
529 : {
530 : /*
531 : * Since some memmove()'s erroneously fail to handle overlapping
532 : * regions, we'll do the shift by hand.
533 : */
534 1428 : const int n = tp - colonp;
535 : int i;
536 :
537 1428 : if (tp == endp)
538 0 : goto enoent;
539 10652 : for (i = 1; i <= n; i++)
540 : {
541 9224 : endp[-i] = colonp[n - i];
542 9224 : colonp[n - i] = 0;
543 : }
544 1428 : tp = endp;
545 : }
546 1612 : if (tp != endp)
547 0 : goto enoent;
548 :
549 : /*
550 : * Copy out the result.
551 : */
552 1612 : memcpy(dst, tmp, NS_IN6ADDRSZ);
553 :
554 1612 : return bits;
555 :
556 6 : enoent:
557 6 : errno = ENOENT;
558 6 : return -1;
559 :
560 0 : emsgsize:
561 0 : errno = EMSGSIZE;
562 0 : return -1;
563 : }
|