LCOV - code coverage report
Current view: top level - src/backend/utils/adt - inet_net_pton.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 202 274 73.7 %
Date: 2025-07-04 00:16:42 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"       /* 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        6712 : pg_inet_net_pton(int af, const char *src, void *dst, size_t size)
      62             : {
      63        6712 :     switch (af)
      64             :     {
      65        5446 :         case PGSQL_AF_INET:
      66             :             return size == -1 ?
      67        7392 :                 inet_net_pton_ipv4(src, dst) :
      68        1946 :                 inet_cidr_pton_ipv4(src, dst, size);
      69        1266 :         case PGSQL_AF_INET6:
      70             :             return size == -1 ?
      71        1746 :                 inet_net_pton_ipv6(src, dst) :
      72         480 :                 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        1946 : 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        1946 :                 tmp = 0,
     103             :                 dirty,
     104             :                 bits;
     105        1946 :     const u_char *odst = dst;
     106             : 
     107        1946 :     ch = *src++;
     108        1946 :     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        1946 :     else if (isdigit((unsigned char) ch))
     141             :     {
     142             :         /* Decimal: eat dotted digit string. */
     143             :         for (;;)
     144             :         {
     145        6974 :             tmp = 0;
     146             :             do
     147             :             {
     148       11960 :                 n = strchr(digits, ch) - digits;
     149       11960 :                 assert(n >= 0 && n <= 9);
     150       11960 :                 tmp *= 10;
     151       11960 :                 tmp += n;
     152       11960 :                 if (tmp > 255)
     153          12 :                     goto enoent;
     154       11948 :             } while ((ch = *src++) != '\0' &&
     155       11448 :                      isdigit((unsigned char) ch));
     156        6962 :             if (size-- <= 0U)
     157           0 :                 goto emsgsize;
     158        6962 :             *dst++ = (u_char) tmp;
     159        6962 :             if (ch == '\0' || ch == '/')
     160             :                 break;
     161        5028 :             if (ch != '.')
     162           0 :                 goto enoent;
     163        5028 :             ch = *src++;
     164        5028 :             if (!isdigit((unsigned char) ch))
     165           0 :                 goto enoent;
     166             :         }
     167             :     }
     168             :     else
     169           0 :         goto enoent;
     170             : 
     171        1934 :     bits = -1;
     172        1934 :     if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
     173             :     {
     174             :         /* CIDR width specifier.  Nothing can follow it. */
     175        1434 :         ch = *src++;            /* Skip over the /. */
     176        1434 :         bits = 0;
     177             :         do
     178             :         {
     179        2658 :             n = strchr(digits, ch) - digits;
     180        2658 :             assert(n >= 0 && n <= 9);
     181        2658 :             bits *= 10;
     182        2658 :             bits += n;
     183        2658 :         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
     184        1434 :         if (ch != '\0')
     185           0 :             goto enoent;
     186        1434 :         if (bits > 32)
     187           0 :             goto emsgsize;
     188             :     }
     189             : 
     190             :     /* Fiery death and destruction unless we prefetched EOS. */
     191        1934 :     if (ch != '\0')
     192           0 :         goto enoent;
     193             : 
     194             :     /* If nothing was written to the destination, we found no address. */
     195        1934 :     if (dst == odst)
     196           0 :         goto enoent;
     197             :     /* If no CIDR spec was given, infer width from net class. */
     198        1934 :     if (bits == -1)
     199             :     {
     200         500 :         if (*odst >= 240)        /* Class E */
     201         192 :             bits = 32;
     202         308 :         else if (*odst >= 224)   /* Class D */
     203           0 :             bits = 8;
     204         308 :         else if (*odst >= 192)   /* Class C */
     205          30 :             bits = 24;
     206         278 :         else if (*odst >= 128)   /* Class B */
     207           0 :             bits = 16;
     208             :         else
     209             :             /* Class A */
     210         278 :             bits = 8;
     211             :         /* If imputed mask is narrower than specified octets, widen. */
     212         500 :         if (bits < ((dst - odst) * 8))
     213         248 :             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         500 :         if (bits == 8 && *odst == 224)
     220           0 :             bits = 4;
     221             :     }
     222             :     /* Extend network to cover the actual mask. */
     223        1982 :     while (bits > ((dst - odst) * 8))
     224             :     {
     225          48 :         if (size-- <= 0U)
     226           0 :             goto emsgsize;
     227          48 :         *dst++ = '\0';
     228             :     }
     229        1934 :     return bits;
     230             : 
     231          12 : enoent:
     232          12 :     errno = ENOENT;
     233          12 :     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        3500 : inet_net_pton_ipv4(const char *src, u_char *dst)
     259             : {
     260             :     static const char digits[] = "0123456789";
     261        3500 :     const u_char *odst = dst;
     262             :     int         n,
     263             :                 ch,
     264             :                 tmp,
     265             :                 bits;
     266        3500 :     size_t      size = 4;
     267             : 
     268             :     /* Get the mantissa. */
     269       13604 :     while (ch = *src++, isdigit((unsigned char) ch))
     270             :     {
     271       13604 :         tmp = 0;
     272             :         do
     273             :         {
     274       29340 :             n = strchr(digits, ch) - digits;
     275       29340 :             assert(n >= 0 && n <= 9);
     276       29340 :             tmp *= 10;
     277       29340 :             tmp += n;
     278       29340 :             if (tmp > 255)
     279          12 :                 goto enoent;
     280       29328 :         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
     281       13592 :         if (size-- == 0)
     282           0 :             goto emsgsize;
     283       13592 :         *dst++ = (u_char) tmp;
     284       13592 :         if (ch == '\0' || ch == '/')
     285             :             break;
     286       10104 :         if (ch != '.')
     287           0 :             goto enoent;
     288             :     }
     289             : 
     290             :     /* Get the prefix length if any. */
     291        3488 :     bits = -1;
     292        3488 :     if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
     293             :     {
     294             :         /* CIDR width specifier.  Nothing can follow it. */
     295        1652 :         ch = *src++;            /* Skip over the /. */
     296        1652 :         bits = 0;
     297             :         do
     298             :         {
     299        2888 :             n = strchr(digits, ch) - digits;
     300        2888 :             assert(n >= 0 && n <= 9);
     301        2888 :             bits *= 10;
     302        2888 :             bits += n;
     303        2888 :         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
     304        1652 :         if (ch != '\0')
     305           0 :             goto enoent;
     306        1652 :         if (bits > 32)
     307           0 :             goto emsgsize;
     308             :     }
     309             : 
     310             :     /* Fiery death and destruction unless we prefetched EOS. */
     311        3488 :     if (ch != '\0')
     312           0 :         goto enoent;
     313             : 
     314             :     /* Prefix length can default to /32 only if all four octets spec'd. */
     315        3488 :     if (bits == -1)
     316             :     {
     317        1836 :         if (dst - odst == 4)
     318        1836 :             bits = 32;
     319             :         else
     320           0 :             goto enoent;
     321             :     }
     322             : 
     323             :     /* If nothing was written to the destination, we found no address. */
     324        3488 :     if (dst == odst)
     325           0 :         goto enoent;
     326             : 
     327             :     /* If prefix length overspecifies mantissa, life is bad. */
     328        3488 :     if ((bits / 8) > (dst - odst))
     329           0 :         goto enoent;
     330             : 
     331             :     /* Extend address to four octets. */
     332        3848 :     while (size-- > 0)
     333         360 :         *dst++ = 0;
     334             : 
     335        3488 :     return bits;
     336             : 
     337          12 : enoent:
     338          12 :     errno = ENOENT;
     339          12 :     return -1;
     340             : 
     341           0 : emsgsize:
     342           0 :     errno = EMSGSIZE;
     343           0 :     return -1;
     344             : }
     345             : 
     346             : static int
     347         694 : getbits(const char *src, int *bitsp)
     348             : {
     349             :     static const char digits[] = "0123456789";
     350             :     int         n;
     351             :     int         val;
     352             :     char        ch;
     353             : 
     354         694 :     val = 0;
     355         694 :     n = 0;
     356        2276 :     while ((ch = *src++) != '\0')
     357             :     {
     358             :         const char *pch;
     359             : 
     360        1582 :         pch = strchr(digits, ch);
     361        1582 :         if (pch != NULL)
     362             :         {
     363        1582 :             if (n++ != 0 && val == 0)   /* no leading zeros */
     364           0 :                 return 0;
     365        1582 :             val *= 10;
     366        1582 :             val += (pch - digits);
     367        1582 :             if (val > 128)       /* range */
     368           0 :                 return 0;
     369        1582 :             continue;
     370             :         }
     371           0 :         return 0;
     372             :     }
     373         694 :     if (n == 0)
     374           0 :         return 0;
     375         694 :     *bitsp = val;
     376         694 :     return 1;
     377             : }
     378             : 
     379             : static int
     380          40 : getv4(const char *src, u_char *dst, int *bitsp)
     381             : {
     382             :     static const char digits[] = "0123456789";
     383          40 :     u_char     *odst = dst;
     384             :     int         n;
     385             :     u_int       val;
     386             :     char        ch;
     387             : 
     388          40 :     val = 0;
     389          40 :     n = 0;
     390         320 :     while ((ch = *src++) != '\0')
     391             :     {
     392             :         const char *pch;
     393             : 
     394         314 :         pch = strchr(digits, ch);
     395         314 :         if (pch != NULL)
     396             :         {
     397         160 :             if (n++ != 0 && val == 0)   /* no leading zeros */
     398           0 :                 return 0;
     399         160 :             val *= 10;
     400         160 :             val += (pch - digits);
     401         160 :             if (val > 255)       /* range */
     402           0 :                 return 0;
     403         160 :             continue;
     404             :         }
     405         154 :         if (ch == '.' || ch == '/')
     406             :         {
     407         154 :             if (dst - odst > 3) /* too many octets? */
     408           0 :                 return 0;
     409         154 :             *dst++ = val;
     410         154 :             if (ch == '/')
     411          34 :                 return getbits(src, bitsp);
     412         120 :             val = 0;
     413         120 :             n = 0;
     414         120 :             continue;
     415             :         }
     416           0 :         return 0;
     417             :     }
     418           6 :     if (n == 0)
     419           0 :         return 0;
     420           6 :     if (dst - odst > 3)          /* too many octets? */
     421           0 :         return 0;
     422           6 :     *dst++ = val;
     423           6 :     return 1;
     424             : }
     425             : 
     426             : static int
     427         786 : inet_net_pton_ipv6(const char *src, u_char *dst)
     428             : {
     429         786 :     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        1266 : 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        1266 :     if (size < NS_IN6ADDRSZ)
     454           0 :         goto emsgsize;
     455             : 
     456        1266 :     memset((tp = tmp), '\0', NS_IN6ADDRSZ);
     457        1266 :     endp = tp + NS_IN6ADDRSZ;
     458        1266 :     colonp = NULL;
     459             :     /* Leading :: requires some special handling. */
     460        1266 :     if (*src == ':')
     461          70 :         if (*++src != ':')
     462           0 :             goto enoent;
     463        1266 :     curtok = src;
     464        1266 :     saw_xdigit = 0;
     465        1266 :     val = 0;
     466        1266 :     digits = 0;
     467        1266 :     bits = -1;
     468       27996 :     while ((ch = *src++) != '\0')
     469             :     {
     470             :         const char *pch;
     471             : 
     472       27436 :         if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
     473        6456 :             pch = strchr((xdigits = xdigits_u), ch);
     474       27436 :         if (pch != NULL)
     475             :         {
     476       20980 :             val <<= 4;
     477       20980 :             val |= (pch - xdigits);
     478       20980 :             if (++digits > 4)
     479           0 :                 goto enoent;
     480       20980 :             saw_xdigit = 1;
     481       20980 :             continue;
     482             :         }
     483        6456 :         if (ch == ':')
     484             :         {
     485        5756 :             curtok = src;
     486        5756 :             if (!saw_xdigit)
     487             :             {
     488        1088 :                 if (colonp)
     489           6 :                     goto enoent;
     490        1082 :                 colonp = tp;
     491        1082 :                 continue;
     492             :             }
     493        4668 :             else if (*src == '\0')
     494           0 :                 goto enoent;
     495        4668 :             if (tp + NS_INT16SZ > endp)
     496           0 :                 goto enoent;
     497        4668 :             *tp++ = (u_char) (val >> 8) & 0xff;
     498        4668 :             *tp++ = (u_char) val & 0xff;
     499        4668 :             saw_xdigit = 0;
     500        4668 :             digits = 0;
     501        4668 :             val = 0;
     502        4668 :             continue;
     503             :         }
     504         740 :         if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
     505          40 :             getv4(curtok, tp, &bits) > 0)
     506             :         {
     507          40 :             tp += NS_INADDRSZ;
     508          40 :             saw_xdigit = 0;
     509          40 :             break;              /* '\0' was seen by inet_pton4(). */
     510             :         }
     511         660 :         if (ch == '/' && getbits(src, &bits) > 0)
     512         660 :             break;
     513           0 :         goto enoent;
     514             :     }
     515        1260 :     if (saw_xdigit)
     516             :     {
     517        1098 :         if (tp + NS_INT16SZ > endp)
     518           0 :             goto enoent;
     519        1098 :         *tp++ = (u_char) (val >> 8) & 0xff;
     520        1098 :         *tp++ = (u_char) val & 0xff;
     521             :     }
     522        1260 :     if (bits == -1)
     523         566 :         bits = 128;
     524             : 
     525        1260 :     endp = tmp + 16;
     526             : 
     527        1260 :     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        1076 :         const int   n = tp - colonp;
     534             :         int         i;
     535             : 
     536        1076 :         if (tp == endp)
     537           0 :             goto enoent;
     538        7484 :         for (i = 1; i <= n; i++)
     539             :         {
     540        6408 :             endp[-i] = colonp[n - i];
     541        6408 :             colonp[n - i] = 0;
     542             :         }
     543        1076 :         tp = endp;
     544             :     }
     545        1260 :     if (tp != endp)
     546           0 :         goto enoent;
     547             : 
     548             :     /*
     549             :      * Copy out the result.
     550             :      */
     551        1260 :     memcpy(dst, tmp, NS_IN6ADDRSZ);
     552             : 
     553        1260 :     return bits;
     554             : 
     555           6 : enoent:
     556           6 :     errno = ENOENT;
     557           6 :     return -1;
     558             : 
     559           0 : emsgsize:
     560           0 :     errno = EMSGSIZE;
     561           0 :     return -1;
     562             : }

Generated by: LCOV version 1.16