]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/inet_net_pton.c
pgindent run.
[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.16 2003/08/04 00:43:25 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 {
344         static const char digits[] = "0123456789";
345         int                     n;
346         int                     val;
347         char            ch;
348
349         val = 0;
350         n = 0;
351         while ((ch = *src++) != '\0')
352         {
353                 const char *pch;
354
355                 pch = strchr(digits, ch);
356                 if (pch != NULL)
357                 {
358                         if (n++ != 0 && val == 0)       /* no leading zeros */
359                                 return (0);
360                         val *= 10;
361                         val += (pch - digits);
362                         if (val > 128)          /* range */
363                                 return (0);
364                         continue;
365                 }
366                 return (0);
367         }
368         if (n == 0)
369                 return (0);
370         *bitsp = val;
371         return (1);
372 }
373
374 static int
375 getv4(const char *src, u_char *dst, int *bitsp)
376 {
377         static const char digits[] = "0123456789";
378         u_char     *odst = dst;
379         int                     n;
380         u_int           val;
381         char            ch;
382
383         val = 0;
384         n = 0;
385         while ((ch = *src++) != '\0')
386         {
387                 const char *pch;
388
389                 pch = strchr(digits, ch);
390                 if (pch != NULL)
391                 {
392                         if (n++ != 0 && val == 0)       /* no leading zeros */
393                                 return (0);
394                         val *= 10;
395                         val += (pch - digits);
396                         if (val > 255)          /* range */
397                                 return (0);
398                         continue;
399                 }
400                 if (ch == '.' || ch == '/')
401                 {
402                         if (dst - odst > 3) /* too many octets? */
403                                 return (0);
404                         *dst++ = val;
405                         if (ch == '/')
406                                 return (getbits(src, bitsp));
407                         val = 0;
408                         n = 0;
409                         continue;
410                 }
411                 return (0);
412         }
413         if (n == 0)
414                 return (0);
415         if (dst - odst > 3)                     /* too many octets? */
416                 return (0);
417         *dst++ = val;
418         return (1);
419 }
420
421 static int
422 inet_net_pton_ipv6(const char *src, u_char *dst)
423 {
424         return inet_cidr_pton_ipv6(src, dst, 16);
425 }
426
427 #define NS_IN6ADDRSZ 16
428 #define NS_INT16SZ 2
429 #define NS_INADDRSZ 4
430
431 static int
432 inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
433 {
434         static const char xdigits_l[] = "0123456789abcdef",
435                                 xdigits_u[] = "0123456789ABCDEF";
436         u_char          tmp[NS_IN6ADDRSZ],
437                            *tp,
438                            *endp,
439                            *colonp;
440         const char *xdigits,
441                            *curtok;
442         int                     ch,
443                                 saw_xdigit;
444         u_int           val;
445         int                     digits;
446         int                     bits;
447
448         if (size < NS_IN6ADDRSZ)
449                 goto emsgsize;
450
451         memset((tp = tmp), '\0', NS_IN6ADDRSZ);
452         endp = tp + NS_IN6ADDRSZ;
453         colonp = NULL;
454         /* Leading :: requires some special handling. */
455         if (*src == ':')
456                 if (*++src != ':')
457                         goto enoent;
458         curtok = src;
459         saw_xdigit = 0;
460         val = 0;
461         digits = 0;
462         bits = -1;
463         while ((ch = *src++) != '\0')
464         {
465                 const char *pch;
466
467                 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
468                         pch = strchr((xdigits = xdigits_u), ch);
469                 if (pch != NULL)
470                 {
471                         val <<= 4;
472                         val |= (pch - xdigits);
473                         if (++digits > 4)
474                                 goto enoent;
475                         saw_xdigit = 1;
476                         continue;
477                 }
478                 if (ch == ':')
479                 {
480                         curtok = src;
481                         if (!saw_xdigit)
482                         {
483                                 if (colonp)
484                                         goto enoent;
485                                 colonp = tp;
486                                 continue;
487                         }
488                         else if (*src == '\0')
489                                 goto enoent;
490                         if (tp + NS_INT16SZ > endp)
491                                 return (0);
492                         *tp++ = (u_char) (val >> 8) & 0xff;
493                         *tp++ = (u_char) val & 0xff;
494                         saw_xdigit = 0;
495                         digits = 0;
496                         val = 0;
497                         continue;
498                 }
499                 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
500                         getv4(curtok, tp, &bits) > 0)
501                 {
502                         tp += NS_INADDRSZ;
503                         saw_xdigit = 0;
504                         break;                          /* '\0' was seen by inet_pton4(). */
505                 }
506                 if (ch == '/' && getbits(src, &bits) > 0)
507                         break;
508                 goto enoent;
509         }
510         if (saw_xdigit)
511         {
512                 if (tp + NS_INT16SZ > endp)
513                         goto enoent;
514                 *tp++ = (u_char) (val >> 8) & 0xff;
515                 *tp++ = (u_char) val & 0xff;
516         }
517         if (bits == -1)
518                 bits = 128;
519
520         endp = tmp + 16;
521
522         if (colonp != NULL)
523         {
524                 /*
525                  * Since some memmove()'s erroneously fail to handle overlapping
526                  * regions, we'll do the shift by hand.
527                  */
528                 const int       n = tp - colonp;
529                 int                     i;
530
531                 if (tp == endp)
532                         goto enoent;
533                 for (i = 1; i <= n; i++)
534                 {
535                         endp[-i] = colonp[n - i];
536                         colonp[n - i] = 0;
537                 }
538                 tp = endp;
539         }
540         if (tp != endp)
541                 goto enoent;
542
543         /*
544          * Copy out the result.
545          */
546         memcpy(dst, tmp, NS_IN6ADDRSZ);
547
548         return (bits);
549
550 enoent:
551         errno = ENOENT;
552         return (-1);
553
554 emsgsize:
555         errno = EMSGSIZE;
556         return (-1);
557 }