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)
21static 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
37static int inet_net_pton_ipv4(const char *src, u_char *dst);
38static int inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
39static int inet_net_pton_ipv6(const char *src, u_char *dst);
40static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
41
42
43/*
44 * int
45 * 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 */
61int
62inet_net_pton(int af, const char *src, void *dst, size_t size)
63{
64 switch (af)
65 {
66 case PGSQL_AF_INET:
67 return size == -1 ?
68 inet_net_pton_ipv4(src, dst) :
69 inet_cidr_pton_ipv4(src, dst, size);
70 case PGSQL_AF_INET6:
71 return size == -1 ?
72 inet_net_pton_ipv6(src, dst) :
73 inet_cidr_pton_ipv6(src, dst, size);
74 default:
75 errno = EAFNOSUPPORT;
76 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 */
96static int
97inet_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,
103 tmp = 0,
104 dirty,
105 bits;
106 const u_char *odst = dst;
107
108 ch = *src++;
109 if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
110 && isxdigit((unsigned char) src[1]))
111 {
112 /* Hexadecimal: Eat nybble string. */
113 if (size <= 0U)
114 goto emsgsize;
115 dirty = 0;
116 src++; /* skip x or X. */
117 while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
118 {
119 if (isupper((unsigned char) ch))
120 ch = tolower((unsigned char) ch);
121 n = strchr(xdigits, ch) - xdigits;
122 assert(n >= 0 && n <= 15);
123 if (dirty == 0)
124 tmp = n;
125 else
126 tmp = (tmp << 4) | n;
127 if (++dirty == 2)
128 {
129 if (size-- <= 0U)
130 goto emsgsize;
131 *dst++ = (u_char) tmp;
132 dirty = 0;
133 }
134 }
135 if (dirty)
136 { /* Odd trailing nybble? */
137 if (size-- <= 0U)
138 goto emsgsize;
139 *dst++ = (u_char) (tmp << 4);
140 }
141 }
142 else if (isdigit((unsigned char) ch))
143 {
144 /* Decimal: eat dotted digit string. */
145 for (;;)
146 {
147 tmp = 0;
148 do
149 {
150 n = strchr(digits, ch) - digits;
151 assert(n >= 0 && n <= 9);
152 tmp *= 10;
153 tmp += n;
154 if (tmp > 255)
155 goto enoent;
156 } while ((ch = *src++) != '\0' &&
157 isdigit((unsigned char) ch));
158 if (size-- <= 0U)
159 goto emsgsize;
160 *dst++ = (u_char) tmp;
161 if (ch == '\0' || ch == '/')
162 break;
163 if (ch != '.')
164 goto enoent;
165 ch = *src++;
166 if (!isdigit((unsigned char) ch))
167 goto enoent;
168 }
169 }
170 else
171 goto enoent;
172
173 bits = -1;
174 if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
175 {
176 /* CIDR width specifier. Nothing can follow it. */
177 ch = *src++; /* Skip over the /. */
178 bits = 0;
179 do
180 {
181 n = strchr(digits, ch) - digits;
182 assert(n >= 0 && n <= 9);
183 bits *= 10;
184 bits += n;
185 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
186 if (ch != '\0')
187 goto enoent;
188 if (bits > 32)
189 goto emsgsize;
190 }
191
192 /* Fiery death and destruction unless we prefetched EOS. */
193 if (ch != '\0')
194 goto enoent;
195
196 /* If nothing was written to the destination, we found no address. */
197 if (dst == odst)
198 goto enoent;
199 /* If no CIDR spec was given, infer width from net class. */
200 if (bits == -1)
201 {
202 if (*odst >= 240) /* Class E */
203 bits = 32;
204 else if (*odst >= 224) /* Class D */
205 bits = 8;
206 else if (*odst >= 192) /* Class C */
207 bits = 24;
208 else if (*odst >= 128) /* Class B */
209 bits = 16;
210 else
211 /* Class A */
212 bits = 8;
213 /* If imputed mask is narrower than specified octets, widen. */
214 if (bits < ((dst - odst) * 8))
215 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 */
221 if (bits == 8 && *odst == 224)
222 bits = 4;
223 }
224 /* Extend network to cover the actual mask. */
225 while (bits > ((dst - odst) * 8))
226 {
227 if (size-- <= 0U)
228 goto emsgsize;
229 *dst++ = '\0';
230 }
231 return bits;
232
233enoent:
234 errno = ENOENT;
235 return -1;
236
237emsgsize:
238 errno = EMSGSIZE;
239 return -1;
240}
241
242/*
243 * int
244 * inet_net_pton(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 */
259static int
260inet_net_pton_ipv4(const char *src, u_char *dst)
261{
262 static const char digits[] = "0123456789";
263 const u_char *odst = dst;
264 int n,
265 ch,
266 tmp,
267 bits;
268 size_t size = 4;
269
270 /* Get the mantissa. */
271 while (ch = *src++, isdigit((unsigned char) ch))
272 {
273 tmp = 0;
274 do
275 {
276 n = strchr(digits, ch) - digits;
277 assert(n >= 0 && n <= 9);
278 tmp *= 10;
279 tmp += n;
280 if (tmp > 255)
281 goto enoent;
282 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
283 if (size-- == 0)
284 goto emsgsize;
285 *dst++ = (u_char) tmp;
286 if (ch == '\0' || ch == '/')
287 break;
288 if (ch != '.')
289 goto enoent;
290 }
291
292 /* Get the prefix length if any. */
293 bits = -1;
294 if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
295 {
296 /* CIDR width specifier. Nothing can follow it. */
297 ch = *src++; /* Skip over the /. */
298 bits = 0;
299 do
300 {
301 n = strchr(digits, ch) - digits;
302 assert(n >= 0 && n <= 9);
303 bits *= 10;
304 bits += n;
305 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
306 if (ch != '\0')
307 goto enoent;
308 if (bits > 32)
309 goto emsgsize;
310 }
311
312 /* Fiery death and destruction unless we prefetched EOS. */
313 if (ch != '\0')
314 goto enoent;
315
316 /* Prefix length can default to /32 only if all four octets spec'd. */
317 if (bits == -1)
318 {
319 if (dst - odst == 4)
320 bits = 32;
321 else
322 goto enoent;
323 }
324
325 /* If nothing was written to the destination, we found no address. */
326 if (dst == odst)
327 goto enoent;
328
329 /* If prefix length overspecifies mantissa, life is bad. */
330 if ((bits / 8) > (dst - odst))
331 goto enoent;
332
333 /* Extend address to four octets. */
334 while (size-- > 0)
335 *dst++ = 0;
336
337 return bits;
338
339enoent:
340 errno = ENOENT;
341 return -1;
342
343emsgsize:
344 errno = EMSGSIZE;
345 return -1;
346}
347
348static int
349getbits(const char *src, int *bitsp)
350{
351 static const char digits[] = "0123456789";
352 int n;
353 int val;
354 char ch;
355
356 val = 0;
357 n = 0;
358 while ((ch = *src++) != '\0')
359 {
360 const char *pch;
361
362 pch = strchr(digits, ch);
363 if (pch != NULL)
364 {
365 if (n++ != 0 && val == 0) /* no leading zeros */
366 return 0;
367 val *= 10;
368 val += (pch - digits);
369 if (val > 128) /* range */
370 return 0;
371 continue;
372 }
373 return 0;
374 }
375 if (n == 0)
376 return 0;
377 *bitsp = val;
378 return 1;
379}
380
381static int
382getv4(const char *src, u_char *dst, int *bitsp)
383{
384 static const char digits[] = "0123456789";
385 u_char *odst = dst;
386 int n;
387 u_int val;
388 char ch;
389
390 val = 0;
391 n = 0;
392 while ((ch = *src++) != '\0')
393 {
394 const char *pch;
395
396 pch = strchr(digits, ch);
397 if (pch != NULL)
398 {
399 if (n++ != 0 && val == 0) /* no leading zeros */
400 return 0;
401 val *= 10;
402 val += (pch - digits);
403 if (val > 255) /* range */
404 return 0;
405 continue;
406 }
407 if (ch == '.' || ch == '/')
408 {
409 if (dst - odst > 3) /* too many octets? */
410 return 0;
411 *dst++ = val;
412 if (ch == '/')
413 return getbits(src, bitsp);
414 val = 0;
415 n = 0;
416 continue;
417 }
418 return 0;
419 }
420 if (n == 0)
421 return 0;
422 if (dst - odst > 3) /* too many octets? */
423 return 0;
424 *dst++ = val;
425 return 1;
426}
427
428static int
429inet_net_pton_ipv6(const char *src, u_char *dst)
430{
431 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
438static int
439inet_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
455 if (size < NS_IN6ADDRSZ)
456 goto emsgsize;
457
458 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
459 endp = tp + NS_IN6ADDRSZ;
460 colonp = NULL;
461 /* Leading :: requires some special handling. */
462 if (*src == ':')
463 if (*++src != ':')
464 goto enoent;
465 curtok = src;
466 saw_xdigit = 0;
467 val = 0;
468 digits = 0;
469 bits = -1;
470 while ((ch = *src++) != '\0')
471 {
472 const char *pch;
473
474 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
475 pch = strchr((xdigits = xdigits_u), ch);
476 if (pch != NULL)
477 {
478 val <<= 4;
479 val |= (pch - xdigits);
480 if (++digits > 4)
481 goto enoent;
482 saw_xdigit = 1;
483 continue;
484 }
485 if (ch == ':')
486 {
487 curtok = src;
488 if (!saw_xdigit)
489 {
490 if (colonp)
491 goto enoent;
492 colonp = tp;
493 continue;
494 }
495 else if (*src == '\0')
496 goto enoent;
497 if (tp + NS_INT16SZ > endp)
498 goto enoent;
499 *tp++ = (u_char) (val >> 8) & 0xff;
500 *tp++ = (u_char) val & 0xff;
501 saw_xdigit = 0;
502 digits = 0;
503 val = 0;
504 continue;
505 }
506 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
507 getv4(curtok, tp, &bits) > 0)
508 {
509 tp += NS_INADDRSZ;
510 saw_xdigit = 0;
511 break; /* '\0' was seen by inet_pton4(). */
512 }
513 if (ch == '/' && getbits(src, &bits) > 0)
514 break;
515 goto enoent;
516 }
517 if (saw_xdigit)
518 {
519 if (tp + NS_INT16SZ > endp)
520 goto enoent;
521 *tp++ = (u_char) (val >> 8) & 0xff;
522 *tp++ = (u_char) val & 0xff;
523 }
524 if (bits == -1)
525 bits = 128;
526
527 endp = tmp + 16;
528
529 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 const int n = tp - colonp;
536 int i;
537
538 if (tp == endp)
539 goto enoent;
540 for (i = 1; i <= n; i++)
541 {
542 endp[-i] = colonp[n - i];
543 colonp[n - i] = 0;
544 }
545 tp = endp;
546 }
547 if (tp != endp)
548 goto enoent;
549
550 /*
551 * Copy out the result.
552 */
553 memcpy(dst, tmp, NS_IN6ADDRSZ);
554
555 return bits;
556
557enoent:
558 errno = ENOENT;
559 return -1;
560
561emsgsize:
562 errno = EMSGSIZE;
563 return -1;
564}
565