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