]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/inet_net_pton.c
Add ipv6 address parsing support to 'inet' and 'cidr' data types.
[postgresql] / src / backend / utils / adt / inet_net_pton.c
1 /*
2  * Copyright (c) 1996 by Internet Software Consortium.
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  */
17
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static const char rcsid[] = "$Id: inet_net_pton.c,v 1.15 2003/06/24 22:21:22 momjian Exp $";
20 #endif
21
22 #include "postgres.h"
23
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #include "utils/inet.h"
33
34 #include "utils/builtins.h"
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  * static int
43  * inet_net_pton(af, src, dst, size)
44  *      convert network number from presentation to network format.
45  *      accepts hex octets, hex strings, decimal octets, and /CIDR.
46  *      "size" is in bytes and describes "dst".
47  * return:
48  *      number of bits, either imputed classfully or specified with /CIDR,
49  *      or -1 if some failure occurred (check errno).  ENOENT means it was
50  *      not a valid network specification.
51  * author:
52  *      Paul Vixie (ISC), June 1996
53  *
54  * Changes:
55  *      I added the inet_cidr_pton function (also from Paul) and changed
56  *      the names to reflect their current use.
57  *
58  */
59 int
60 inet_net_pton(int af, const char *src, void *dst, size_t size)
61 {
62         switch (af)
63         {
64                 case PGSQL_AF_INET:
65                         return size == -1 ?
66                                 inet_net_pton_ipv4(src, dst) :
67                                 inet_cidr_pton_ipv4(src, dst, size);
68                 case PGSQL_AF_INET6:
69                         return size == -1 ?
70                                 inet_net_pton_ipv6(src, dst) :
71                                 inet_cidr_pton_ipv6(src, dst, size);
72                 default:
73                         errno = EAFNOSUPPORT;
74                         return (-1);
75         }
76 }
77
78 /*
79  * static int
80  * inet_cidr_pton_ipv4(src, dst, size)
81  *      convert IPv4 network number from presentation to network format.
82  *      accepts hex octets, hex strings, decimal octets, and /CIDR.
83  *      "size" is in bytes and describes "dst".
84  * return:
85  *      number of bits, either imputed classfully or specified with /CIDR,
86  *      or -1 if some failure occurred (check errno).  ENOENT means it was
87  *      not an IPv4 network specification.
88  * note:
89  *      network byte order assumed.  this means 192.5.5.240/28 has
90  *      0x11110000 in its fourth octet.
91  * author:
92  *      Paul Vixie (ISC), June 1996
93  */
94 static int
95 inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
96 {
97         static const char
98                                 xdigits[] = "0123456789abcdef",
99                                 digits[] = "0123456789";
100         int                     n,
101                                 ch,
102                                 tmp,
103                                 dirty,
104                                 bits;
105         const u_char *odst = dst;
106
107         ch = *src++;
108         if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
109                 && isxdigit((unsigned char) src[1]))
110         {
111                 /* Hexadecimal: Eat nybble string. */
112                 if (size <= 0)
113                         goto emsgsize;
114                 dirty = 0;
115                 tmp = 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-- <= 0)
130                                         goto emsgsize;
131                                 *dst++ = (u_char) tmp;
132                                 dirty = 0;
133                         }
134                 }
135                 if (dirty)
136                 {                                               /* Odd trailing nybble? */
137                         if (size-- <= 0)
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-- <= 0)
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         /* Firey 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 = 4;
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 >= 8 && bits < ((dst - odst) * 8))
215                         bits = (dst - odst) * 8;
216         }
217         /* Extend network to cover the actual mask. */
218         while (bits > ((dst - odst) * 8))
219         {
220                 if (size-- <= 0)
221                         goto emsgsize;
222                 *dst++ = '\0';
223         }
224         return (bits);
225
226 enoent:
227         errno = ENOENT;
228         return (-1);
229
230 emsgsize:
231         errno = EMSGSIZE;
232         return (-1);
233 }
234
235 /*
236  * int
237  * inet_net_pton(af, src, dst, *bits)
238  *      convert network address from presentation to network format.
239  *      accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
240  *      "dst" is assumed large enough for its "af".  "bits" is set to the
241  *      /CIDR prefix length, which can have defaults (like /32 for IPv4).
242  * return:
243  *      -1 if an error occurred (inspect errno; ENOENT means bad format).
244  *      0 if successful conversion occurred.
245  * note:
246  *      192.5.5.1/28 has a nonzero host part, which means it isn't a network
247  *      as called for by inet_cidr_pton() but it can be a host address with
248  *      an included netmask.
249  * author:
250  *      Paul Vixie (ISC), October 1998
251  */
252 static int
253 inet_net_pton_ipv4(const char *src, u_char *dst)
254 {
255         static const char digits[] = "0123456789";
256         const u_char *odst = dst;
257         int                     n,
258                                 ch,
259                                 tmp,
260                                 bits;
261         size_t          size = 4;
262
263         /* Get the mantissa. */
264         while (ch = *src++, isdigit((unsigned char) ch))
265         {
266                 tmp = 0;
267                 do
268                 {
269                         n = strchr(digits, ch) - digits;
270                         assert(n >= 0 && n <= 9);
271                         tmp *= 10;
272                         tmp += n;
273                         if (tmp > 255)
274                                 goto enoent;
275                 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
276                 if (size-- == 0)
277                         goto emsgsize;
278                 *dst++ = (u_char) tmp;
279                 if (ch == '\0' || ch == '/')
280                         break;
281                 if (ch != '.')
282                         goto enoent;
283         }
284
285         /* Get the prefix length if any. */
286         bits = -1;
287         if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
288         {
289                 /* CIDR width specifier.  Nothing can follow it. */
290                 ch = *src++;                    /* Skip over the /. */
291                 bits = 0;
292                 do
293                 {
294                         n = strchr(digits, ch) - digits;
295                         assert(n >= 0 && n <= 9);
296                         bits *= 10;
297                         bits += n;
298                 } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
299                 if (ch != '\0')
300                         goto enoent;
301                 if (bits > 32)
302                         goto emsgsize;
303         }
304
305         /* Firey death and destruction unless we prefetched EOS. */
306         if (ch != '\0')
307                 goto enoent;
308
309         /* Prefix length can default to /32 only if all four octets spec'd. */
310         if (bits == -1)
311         {
312                 if (dst - odst == 4)
313                         bits = 32;
314                 else
315                         goto enoent;
316         }
317
318         /* If nothing was written to the destination, we found no address. */
319         if (dst == odst)
320                 goto enoent;
321
322         /* If prefix length overspecifies mantissa, life is bad. */
323         if ((bits / 8) > (dst - odst))
324                 goto enoent;
325
326         /* Extend address to four octets. */
327         while (size-- > 0)
328                 *dst++ = 0;
329
330         return bits;
331
332 enoent:
333         errno = ENOENT;
334         return (-1);
335
336 emsgsize:
337         errno = EMSGSIZE;
338         return (-1);
339 }
340
341 static int
342 getbits(const char *src, int *bitsp) {
343         static const char digits[] = "0123456789";
344         int n;
345         int val;
346         char ch;
347
348         val = 0;
349         n = 0;
350         while ((ch = *src++) != '\0') {
351                 const char *pch;
352
353                 pch = strchr(digits, ch);
354                 if (pch != NULL) {
355                         if (n++ != 0 && val == 0)       /* no leading zeros */
356                                 return (0);
357                         val *= 10;
358                         val += (pch - digits);
359                         if (val > 128)                  /* range */
360                                 return (0);
361                         continue;
362                 }
363                 return (0);
364         }
365         if (n == 0)
366                 return (0);
367         *bitsp = val;
368         return (1);
369 }
370
371 static int
372 getv4(const char *src, u_char *dst, int *bitsp) {
373         static const char digits[] = "0123456789";
374         u_char *odst = dst;
375         int n;
376         u_int val;
377         char ch;
378
379         val = 0;
380         n = 0;
381         while ((ch = *src++) != '\0') {
382                 const char *pch;
383
384                 pch = strchr(digits, ch);
385                 if (pch != NULL) {
386                         if (n++ != 0 && val == 0)       /* no leading zeros */
387                                 return (0);
388                         val *= 10;
389                         val += (pch - digits);
390                         if (val > 255)                  /* range */
391                                 return (0);
392                         continue;
393                 }
394                 if (ch == '.' || ch == '/') {
395                         if (dst - odst > 3)             /* too many octets? */
396                                 return (0);
397                         *dst++ = val;
398                         if (ch == '/')
399                                 return (getbits(src, bitsp));
400                         val = 0;
401                         n = 0;
402                         continue;
403                 }
404                 return (0);
405         }
406         if (n == 0)
407                 return (0);
408         if (dst - odst > 3)             /* too many octets? */
409                 return (0);
410         *dst++ = val;
411         return (1);
412 }
413
414 static int
415 inet_net_pton_ipv6(const char *src, u_char *dst)
416 {
417         return inet_cidr_pton_ipv6(src, dst, 16);
418 }
419
420 #define NS_IN6ADDRSZ 16
421 #define NS_INT16SZ 2
422 #define NS_INADDRSZ 4
423
424 static int
425 inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) {
426         static const char xdigits_l[] = "0123456789abcdef",
427                           xdigits_u[] = "0123456789ABCDEF";
428         u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
429         const char *xdigits, *curtok;
430         int ch, saw_xdigit;
431         u_int val;
432         int digits;
433         int bits;
434
435         if (size < NS_IN6ADDRSZ)
436                 goto emsgsize;
437
438         memset((tp = tmp), '\0', NS_IN6ADDRSZ);
439         endp = tp + NS_IN6ADDRSZ;
440         colonp = NULL;
441         /* Leading :: requires some special handling. */
442         if (*src == ':')
443                 if (*++src != ':')
444                         goto enoent;
445         curtok = src;
446         saw_xdigit = 0;
447         val = 0;
448         digits = 0;
449         bits = -1;
450         while ((ch = *src++) != '\0') {
451                 const char *pch;
452
453                 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
454                         pch = strchr((xdigits = xdigits_u), ch);
455                 if (pch != NULL) {
456                         val <<= 4;
457                         val |= (pch - xdigits);
458                         if (++digits > 4)
459                                 goto enoent;
460                         saw_xdigit = 1;
461                         continue;
462                 }
463                 if (ch == ':') {
464                         curtok = src;
465                         if (!saw_xdigit) {
466                                 if (colonp)
467                                         goto enoent;
468                                 colonp = tp;
469                                 continue;
470                         } else if (*src == '\0')
471                                 goto enoent;
472                         if (tp + NS_INT16SZ > endp)
473                                 return (0);
474                         *tp++ = (u_char) (val >> 8) & 0xff;
475                         *tp++ = (u_char) val & 0xff;
476                         saw_xdigit = 0;
477                         digits = 0;
478                         val = 0;
479                         continue;
480                 }
481                 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
482                      getv4(curtok, tp, &bits) > 0) {
483                         tp += NS_INADDRSZ;
484                         saw_xdigit = 0;
485                         break;  /* '\0' was seen by inet_pton4(). */
486                 }
487                 if (ch == '/' && getbits(src, &bits) > 0)
488                         break;
489                 goto enoent;
490         }
491         if (saw_xdigit) {
492                 if (tp + NS_INT16SZ > endp)
493                         goto enoent;
494                 *tp++ = (u_char) (val >> 8) & 0xff;
495                 *tp++ = (u_char) val & 0xff;
496         }
497         if (bits == -1)
498                 bits = 128;
499
500         endp =  tmp + 16;
501
502         if (colonp != NULL) {
503                 /*
504                  * Since some memmove()'s erroneously fail to handle
505                  * overlapping regions, we'll do the shift by hand.
506                  */
507                 const int n = tp - colonp;
508                 int i;
509
510                 if (tp == endp)
511                         goto enoent;
512                 for (i = 1; i <= n; i++) {
513                         endp[- i] = colonp[n - i];
514                         colonp[n - i] = 0;
515                 }
516                 tp = endp;
517         }
518         if (tp != endp)
519                 goto enoent;
520
521         /*
522          * Copy out the result.
523          */
524         memcpy(dst, tmp, NS_IN6ADDRSZ);
525
526         return (bits);
527
528  enoent:
529         errno = ENOENT;
530         return (-1);
531
532  emsgsize:
533         errno = EMSGSIZE;
534         return (-1);
535 }