Header And Logo

PostgreSQL
| The world's most advanced open source database.

inet_net_pton.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (c) 1996,1999 by Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00010  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00011  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00012  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00013  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00014  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00015  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00016  *
00017  *    src/backend/utils/adt/inet_net_pton.c
00018  */
00019 
00020 #if defined(LIBC_SCCS) && !defined(lint)
00021 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
00022 #endif
00023 
00024 #include "postgres.h"
00025 
00026 #include <sys/types.h>
00027 #include <sys/socket.h>
00028 #include <netinet/in.h>
00029 #include <arpa/inet.h>
00030 #include <assert.h>
00031 #include <ctype.h>
00032 
00033 #include "utils/builtins.h" /* pgrminclude ignore */    /* needed on some
00034                                                          * platforms */
00035 #include "utils/inet.h"
00036 
00037 
00038 static int  inet_net_pton_ipv4(const char *src, u_char *dst);
00039 static int  inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
00040 static int  inet_net_pton_ipv6(const char *src, u_char *dst);
00041 static int  inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
00042 
00043 
00044 /*
00045  * int
00046  * inet_net_pton(af, src, dst, size)
00047  *  convert network number from presentation to network format.
00048  *  accepts hex octets, hex strings, decimal octets, and /CIDR.
00049  *  "size" is in bytes and describes "dst".
00050  * return:
00051  *  number of bits, either imputed classfully or specified with /CIDR,
00052  *  or -1 if some failure occurred (check errno).  ENOENT means it was
00053  *  not a valid network specification.
00054  * author:
00055  *  Paul Vixie (ISC), June 1996
00056  *
00057  * Changes:
00058  *  I added the inet_cidr_pton function (also from Paul) and changed
00059  *  the names to reflect their current use.
00060  *
00061  */
00062 int
00063 inet_net_pton(int af, const char *src, void *dst, size_t size)
00064 {
00065     switch (af)
00066     {
00067         case PGSQL_AF_INET:
00068             return size == -1 ?
00069                 inet_net_pton_ipv4(src, dst) :
00070                 inet_cidr_pton_ipv4(src, dst, size);
00071         case PGSQL_AF_INET6:
00072             return size == -1 ?
00073                 inet_net_pton_ipv6(src, dst) :
00074                 inet_cidr_pton_ipv6(src, dst, size);
00075         default:
00076             errno = EAFNOSUPPORT;
00077             return (-1);
00078     }
00079 }
00080 
00081 /*
00082  * static int
00083  * inet_cidr_pton_ipv4(src, dst, size)
00084  *  convert IPv4 network number from presentation to network format.
00085  *  accepts hex octets, hex strings, decimal octets, and /CIDR.
00086  *  "size" is in bytes and describes "dst".
00087  * return:
00088  *  number of bits, either imputed classfully or specified with /CIDR,
00089  *  or -1 if some failure occurred (check errno).  ENOENT means it was
00090  *  not an IPv4 network specification.
00091  * note:
00092  *  network byte order assumed.  this means 192.5.5.240/28 has
00093  *  0b11110000 in its fourth octet.
00094  * author:
00095  *  Paul Vixie (ISC), June 1996
00096  */
00097 static int
00098 inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size)
00099 {
00100     static const char xdigits[] = "0123456789abcdef";
00101     static const char digits[] = "0123456789";
00102     int         n,
00103                 ch,
00104                 tmp = 0,
00105                 dirty,
00106                 bits;
00107     const u_char *odst = dst;
00108 
00109     ch = *src++;
00110     if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
00111         && isxdigit((unsigned char) src[1]))
00112     {
00113         /* Hexadecimal: Eat nybble string. */
00114         if (size <= 0U)
00115             goto emsgsize;
00116         dirty = 0;
00117         src++;                  /* skip x or X. */
00118         while ((ch = *src++) != '\0' && isxdigit((unsigned char) ch))
00119         {
00120             if (isupper((unsigned char) ch))
00121                 ch = tolower((unsigned char) ch);
00122             n = strchr(xdigits, ch) - xdigits;
00123             assert(n >= 0 && n <= 15);
00124             if (dirty == 0)
00125                 tmp = n;
00126             else
00127                 tmp = (tmp << 4) | n;
00128             if (++dirty == 2)
00129             {
00130                 if (size-- <= 0U)
00131                     goto emsgsize;
00132                 *dst++ = (u_char) tmp;
00133                 dirty = 0;
00134             }
00135         }
00136         if (dirty)
00137         {                       /* Odd trailing nybble? */
00138             if (size-- <= 0U)
00139                 goto emsgsize;
00140             *dst++ = (u_char) (tmp << 4);
00141         }
00142     }
00143     else if (isdigit((unsigned char) ch))
00144     {
00145         /* Decimal: eat dotted digit string. */
00146         for (;;)
00147         {
00148             tmp = 0;
00149             do
00150             {
00151                 n = strchr(digits, ch) - digits;
00152                 assert(n >= 0 && n <= 9);
00153                 tmp *= 10;
00154                 tmp += n;
00155                 if (tmp > 255)
00156                     goto enoent;
00157             } while ((ch = *src++) != '\0' &&
00158                      isdigit((unsigned char) ch));
00159             if (size-- <= 0U)
00160                 goto emsgsize;
00161             *dst++ = (u_char) tmp;
00162             if (ch == '\0' || ch == '/')
00163                 break;
00164             if (ch != '.')
00165                 goto enoent;
00166             ch = *src++;
00167             if (!isdigit((unsigned char) ch))
00168                 goto enoent;
00169         }
00170     }
00171     else
00172         goto enoent;
00173 
00174     bits = -1;
00175     if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
00176     {
00177         /* CIDR width specifier.  Nothing can follow it. */
00178         ch = *src++;            /* Skip over the /. */
00179         bits = 0;
00180         do
00181         {
00182             n = strchr(digits, ch) - digits;
00183             assert(n >= 0 && n <= 9);
00184             bits *= 10;
00185             bits += n;
00186         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
00187         if (ch != '\0')
00188             goto enoent;
00189         if (bits > 32)
00190             goto emsgsize;
00191     }
00192 
00193     /* Firey death and destruction unless we prefetched EOS. */
00194     if (ch != '\0')
00195         goto enoent;
00196 
00197     /* If nothing was written to the destination, we found no address. */
00198     if (dst == odst)
00199         goto enoent;
00200     /* If no CIDR spec was given, infer width from net class. */
00201     if (bits == -1)
00202     {
00203         if (*odst >= 240)       /* Class E */
00204             bits = 32;
00205         else if (*odst >= 224)  /* Class D */
00206             bits = 8;
00207         else if (*odst >= 192)  /* Class C */
00208             bits = 24;
00209         else if (*odst >= 128)  /* Class B */
00210             bits = 16;
00211         else
00212             /* Class A */
00213             bits = 8;
00214         /* If imputed mask is narrower than specified octets, widen. */
00215         if (bits < ((dst - odst) * 8))
00216             bits = (dst - odst) * 8;
00217 
00218         /*
00219          * If there are no additional bits specified for a class D address
00220          * adjust bits to 4.
00221          */
00222         if (bits == 8 && *odst == 224)
00223             bits = 4;
00224     }
00225     /* Extend network to cover the actual mask. */
00226     while (bits > ((dst - odst) * 8))
00227     {
00228         if (size-- <= 0U)
00229             goto emsgsize;
00230         *dst++ = '\0';
00231     }
00232     return (bits);
00233 
00234 enoent:
00235     errno = ENOENT;
00236     return (-1);
00237 
00238 emsgsize:
00239     errno = EMSGSIZE;
00240     return (-1);
00241 }
00242 
00243 /*
00244  * int
00245  * inet_net_pton(af, src, dst, *bits)
00246  *  convert network address from presentation to network format.
00247  *  accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
00248  *  "dst" is assumed large enough for its "af".  "bits" is set to the
00249  *  /CIDR prefix length, which can have defaults (like /32 for IPv4).
00250  * return:
00251  *  -1 if an error occurred (inspect errno; ENOENT means bad format).
00252  *  0 if successful conversion occurred.
00253  * note:
00254  *  192.5.5.1/28 has a nonzero host part, which means it isn't a network
00255  *  as called for by inet_cidr_pton() but it can be a host address with
00256  *  an included netmask.
00257  * author:
00258  *  Paul Vixie (ISC), October 1998
00259  */
00260 static int
00261 inet_net_pton_ipv4(const char *src, u_char *dst)
00262 {
00263     static const char digits[] = "0123456789";
00264     const u_char *odst = dst;
00265     int         n,
00266                 ch,
00267                 tmp,
00268                 bits;
00269     size_t      size = 4;
00270 
00271     /* Get the mantissa. */
00272     while (ch = *src++, isdigit((unsigned char) ch))
00273     {
00274         tmp = 0;
00275         do
00276         {
00277             n = strchr(digits, ch) - digits;
00278             assert(n >= 0 && n <= 9);
00279             tmp *= 10;
00280             tmp += n;
00281             if (tmp > 255)
00282                 goto enoent;
00283         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
00284         if (size-- == 0)
00285             goto emsgsize;
00286         *dst++ = (u_char) tmp;
00287         if (ch == '\0' || ch == '/')
00288             break;
00289         if (ch != '.')
00290             goto enoent;
00291     }
00292 
00293     /* Get the prefix length if any. */
00294     bits = -1;
00295     if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
00296     {
00297         /* CIDR width specifier.  Nothing can follow it. */
00298         ch = *src++;            /* Skip over the /. */
00299         bits = 0;
00300         do
00301         {
00302             n = strchr(digits, ch) - digits;
00303             assert(n >= 0 && n <= 9);
00304             bits *= 10;
00305             bits += n;
00306         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
00307         if (ch != '\0')
00308             goto enoent;
00309         if (bits > 32)
00310             goto emsgsize;
00311     }
00312 
00313     /* Firey death and destruction unless we prefetched EOS. */
00314     if (ch != '\0')
00315         goto enoent;
00316 
00317     /* Prefix length can default to /32 only if all four octets spec'd. */
00318     if (bits == -1)
00319     {
00320         if (dst - odst == 4)
00321             bits = 32;
00322         else
00323             goto enoent;
00324     }
00325 
00326     /* If nothing was written to the destination, we found no address. */
00327     if (dst == odst)
00328         goto enoent;
00329 
00330     /* If prefix length overspecifies mantissa, life is bad. */
00331     if ((bits / 8) > (dst - odst))
00332         goto enoent;
00333 
00334     /* Extend address to four octets. */
00335     while (size-- > 0)
00336         *dst++ = 0;
00337 
00338     return bits;
00339 
00340 enoent:
00341     errno = ENOENT;
00342     return (-1);
00343 
00344 emsgsize:
00345     errno = EMSGSIZE;
00346     return (-1);
00347 }
00348 
00349 static int
00350 getbits(const char *src, int *bitsp)
00351 {
00352     static const char digits[] = "0123456789";
00353     int         n;
00354     int         val;
00355     char        ch;
00356 
00357     val = 0;
00358     n = 0;
00359     while ((ch = *src++) != '\0')
00360     {
00361         const char *pch;
00362 
00363         pch = strchr(digits, ch);
00364         if (pch != NULL)
00365         {
00366             if (n++ != 0 && val == 0)   /* no leading zeros */
00367                 return (0);
00368             val *= 10;
00369             val += (pch - digits);
00370             if (val > 128)      /* range */
00371                 return (0);
00372             continue;
00373         }
00374         return (0);
00375     }
00376     if (n == 0)
00377         return (0);
00378     *bitsp = val;
00379     return (1);
00380 }
00381 
00382 static int
00383 getv4(const char *src, u_char *dst, int *bitsp)
00384 {
00385     static const char digits[] = "0123456789";
00386     u_char     *odst = dst;
00387     int         n;
00388     u_int       val;
00389     char        ch;
00390 
00391     val = 0;
00392     n = 0;
00393     while ((ch = *src++) != '\0')
00394     {
00395         const char *pch;
00396 
00397         pch = strchr(digits, ch);
00398         if (pch != NULL)
00399         {
00400             if (n++ != 0 && val == 0)   /* no leading zeros */
00401                 return (0);
00402             val *= 10;
00403             val += (pch - digits);
00404             if (val > 255)      /* range */
00405                 return (0);
00406             continue;
00407         }
00408         if (ch == '.' || ch == '/')
00409         {
00410             if (dst - odst > 3) /* too many octets? */
00411                 return (0);
00412             *dst++ = val;
00413             if (ch == '/')
00414                 return (getbits(src, bitsp));
00415             val = 0;
00416             n = 0;
00417             continue;
00418         }
00419         return (0);
00420     }
00421     if (n == 0)
00422         return (0);
00423     if (dst - odst > 3)         /* too many octets? */
00424         return (0);
00425     *dst++ = val;
00426     return (1);
00427 }
00428 
00429 static int
00430 inet_net_pton_ipv6(const char *src, u_char *dst)
00431 {
00432     return inet_cidr_pton_ipv6(src, dst, 16);
00433 }
00434 
00435 #define NS_IN6ADDRSZ 16
00436 #define NS_INT16SZ 2
00437 #define NS_INADDRSZ 4
00438 
00439 static int
00440 inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
00441 {
00442     static const char xdigits_l[] = "0123456789abcdef",
00443                 xdigits_u[] = "0123456789ABCDEF";
00444     u_char      tmp[NS_IN6ADDRSZ],
00445                *tp,
00446                *endp,
00447                *colonp;
00448     const char *xdigits,
00449                *curtok;
00450     int         ch,
00451                 saw_xdigit;
00452     u_int       val;
00453     int         digits;
00454     int         bits;
00455 
00456     if (size < NS_IN6ADDRSZ)
00457         goto emsgsize;
00458 
00459     memset((tp = tmp), '\0', NS_IN6ADDRSZ);
00460     endp = tp + NS_IN6ADDRSZ;
00461     colonp = NULL;
00462     /* Leading :: requires some special handling. */
00463     if (*src == ':')
00464         if (*++src != ':')
00465             goto enoent;
00466     curtok = src;
00467     saw_xdigit = 0;
00468     val = 0;
00469     digits = 0;
00470     bits = -1;
00471     while ((ch = *src++) != '\0')
00472     {
00473         const char *pch;
00474 
00475         if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
00476             pch = strchr((xdigits = xdigits_u), ch);
00477         if (pch != NULL)
00478         {
00479             val <<= 4;
00480             val |= (pch - xdigits);
00481             if (++digits > 4)
00482                 goto enoent;
00483             saw_xdigit = 1;
00484             continue;
00485         }
00486         if (ch == ':')
00487         {
00488             curtok = src;
00489             if (!saw_xdigit)
00490             {
00491                 if (colonp)
00492                     goto enoent;
00493                 colonp = tp;
00494                 continue;
00495             }
00496             else if (*src == '\0')
00497                 goto enoent;
00498             if (tp + NS_INT16SZ > endp)
00499                 return (0);
00500             *tp++ = (u_char) (val >> 8) & 0xff;
00501             *tp++ = (u_char) val & 0xff;
00502             saw_xdigit = 0;
00503             digits = 0;
00504             val = 0;
00505             continue;
00506         }
00507         if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
00508             getv4(curtok, tp, &bits) > 0)
00509         {
00510             tp += NS_INADDRSZ;
00511             saw_xdigit = 0;
00512             break;              /* '\0' was seen by inet_pton4(). */
00513         }
00514         if (ch == '/' && getbits(src, &bits) > 0)
00515             break;
00516         goto enoent;
00517     }
00518     if (saw_xdigit)
00519     {
00520         if (tp + NS_INT16SZ > endp)
00521             goto enoent;
00522         *tp++ = (u_char) (val >> 8) & 0xff;
00523         *tp++ = (u_char) val & 0xff;
00524     }
00525     if (bits == -1)
00526         bits = 128;
00527 
00528     endp = tmp + 16;
00529 
00530     if (colonp != NULL)
00531     {
00532         /*
00533          * Since some memmove()'s erroneously fail to handle overlapping
00534          * regions, we'll do the shift by hand.
00535          */
00536         const int   n = tp - colonp;
00537         int         i;
00538 
00539         if (tp == endp)
00540             goto enoent;
00541         for (i = 1; i <= n; i++)
00542         {
00543             endp[-i] = colonp[n - i];
00544             colonp[n - i] = 0;
00545         }
00546         tp = endp;
00547     }
00548     if (tp != endp)
00549         goto enoent;
00550 
00551     /*
00552      * Copy out the result.
00553      */
00554     memcpy(dst, tmp, NS_IN6ADDRSZ);
00555 
00556     return (bits);
00557 
00558 enoent:
00559     errno = ENOENT;
00560     return (-1);
00561 
00562 emsgsize:
00563     errno = EMSGSIZE;
00564     return (-1);
00565 }