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

Generated by: LCOV version 2.0-1