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 | * 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 |
62 | inet_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 | */ |
96 | static int |
97 | 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, |
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 | |
233 | enoent: |
234 | errno = ENOENT; |
235 | return -1; |
236 | |
237 | emsgsize: |
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 | */ |
259 | static int |
260 | inet_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 | |
339 | enoent: |
340 | errno = ENOENT; |
341 | return -1; |
342 | |
343 | emsgsize: |
344 | errno = EMSGSIZE; |
345 | return -1; |
346 | } |
347 | |
348 | static int |
349 | getbits(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 | |
381 | static int |
382 | getv4(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 | |
428 | static int |
429 | inet_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 | |
438 | static int |
439 | 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 | |
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 | |
557 | enoent: |
558 | errno = ENOENT; |
559 | return -1; |
560 | |
561 | emsgsize: |
562 | errno = EMSGSIZE; |
563 | return -1; |
564 | } |
565 | |