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