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