LCOV - code coverage report
Current view: top level - src/backend/utils/adt - inet_net_pton.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 202 275 73.5 %
Date: 2024-04-18 05:10:52 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14