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

Generated by: LCOV version 1.14