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-02-21 16:15:14 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        5076 : pg_inet_net_pton(int af, const char *src, void *dst, size_t size)
      62             : {
      63        5076 :     switch (af)
      64             :     {
      65        4124 :         case PGSQL_AF_INET:
      66             :             return size == -1 ?
      67        5408 :                 inet_net_pton_ipv4(src, dst) :
      68        1284 :                 inet_cidr_pton_ipv4(src, dst, size);
      69         952 :         case PGSQL_AF_INET6:
      70             :             return size == -1 ?
      71        1276 :                 inet_net_pton_ipv6(src, dst) :
      72         324 :                 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        1284 : 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        1284 :                 tmp = 0,
     103             :                 dirty,
     104             :                 bits;
     105        1284 :     const u_char *odst = dst;
     106             : 
     107        1284 :     ch = *src++;
     108        1284 :     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        1284 :     else if (isdigit((unsigned char) ch))
     142             :     {
     143             :         /* Decimal: eat dotted digit string. */
     144             :         for (;;)
     145             :         {
     146        4326 :             tmp = 0;
     147             :             do
     148             :             {
     149        8084 :                 n = strchr(digits, ch) - digits;
     150        8084 :                 assert(n >= 0 && n <= 9);
     151        8084 :                 tmp *= 10;
     152        8084 :                 tmp += n;
     153        8084 :                 if (tmp > 255)
     154          12 :                     goto enoent;
     155       15644 :             } while ((ch = *src++) != '\0' &&
     156        8072 :                      isdigit((unsigned char) ch));
     157        4314 :             if (size-- <= 0U)
     158           0 :                 goto emsgsize;
     159        4314 :             *dst++ = (u_char) tmp;
     160        4314 :             if (ch == '\0' || ch == '/')
     161             :                 break;
     162        3042 :             if (ch != '.')
     163           0 :                 goto enoent;
     164        3042 :             ch = *src++;
     165        3042 :             if (!isdigit((unsigned char) ch))
     166           0 :                 goto enoent;
     167             :         }
     168             :     }
     169             :     else
     170           0 :         goto enoent;
     171             : 
     172        1272 :     bits = -1;
     173        1272 :     if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
     174             :     {
     175             :         /* CIDR width specifier.  Nothing can follow it. */
     176         772 :         ch = *src++;            /* Skip over the /. */
     177         772 :         bits = 0;
     178             :         do
     179             :         {
     180        1346 :             n = strchr(digits, ch) - digits;
     181        1346 :             assert(n >= 0 && n <= 9);
     182        1346 :             bits *= 10;
     183        1346 :             bits += n;
     184        1346 :         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
     185         772 :         if (ch != '\0')
     186           0 :             goto enoent;
     187         772 :         if (bits > 32)
     188           0 :             goto emsgsize;
     189             :     }
     190             : 
     191             :     /* Fiery death and destruction unless we prefetched EOS. */
     192        1272 :     if (ch != '\0')
     193           0 :         goto enoent;
     194             : 
     195             :     /* If nothing was written to the destination, we found no address. */
     196        1272 :     if (dst == odst)
     197           0 :         goto enoent;
     198             :     /* If no CIDR spec was given, infer width from net class. */
     199        1272 :     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        1320 :     while (bits > ((dst - odst) * 8))
     225             :     {
     226          48 :         if (size-- <= 0U)
     227           0 :             goto emsgsize;
     228          48 :         *dst++ = '\0';
     229             :     }
     230        1272 :     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        2840 : inet_net_pton_ipv4(const char *src, u_char *dst)
     260             : {
     261             :     static const char digits[] = "0123456789";
     262        2840 :     const u_char *odst = dst;
     263             :     int         n,
     264             :                 ch,
     265             :                 tmp,
     266             :                 bits;
     267        2840 :     size_t      size = 4;
     268             : 
     269             :     /* Get the mantissa. */
     270       10964 :     while (ch = *src++, isdigit((unsigned char) ch))
     271             :     {
     272       10964 :         tmp = 0;
     273             :         do
     274             :         {
     275       24452 :             n = strchr(digits, ch) - digits;
     276       24452 :             assert(n >= 0 && n <= 9);
     277       24452 :             tmp *= 10;
     278       24452 :             tmp += n;
     279       24452 :             if (tmp > 255)
     280          12 :                 goto enoent;
     281       24440 :         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
     282       10952 :         if (size-- == 0)
     283           0 :             goto emsgsize;
     284       10952 :         *dst++ = (u_char) tmp;
     285       10952 :         if (ch == '\0' || ch == '/')
     286             :             break;
     287        8124 :         if (ch != '.')
     288           0 :             goto enoent;
     289             :     }
     290             : 
     291             :     /* Get the prefix length if any. */
     292        2828 :     bits = -1;
     293        2828 :     if (ch == '/' && isdigit((unsigned char) src[0]) && dst > odst)
     294             :     {
     295             :         /* CIDR width specifier.  Nothing can follow it. */
     296        1028 :         ch = *src++;            /* Skip over the /. */
     297        1028 :         bits = 0;
     298             :         do
     299             :         {
     300        1650 :             n = strchr(digits, ch) - digits;
     301        1650 :             assert(n >= 0 && n <= 9);
     302        1650 :             bits *= 10;
     303        1650 :             bits += n;
     304        1650 :         } while ((ch = *src++) != '\0' && isdigit((unsigned char) ch));
     305        1028 :         if (ch != '\0')
     306           0 :             goto enoent;
     307        1028 :         if (bits > 32)
     308           0 :             goto emsgsize;
     309             :     }
     310             : 
     311             :     /* Fiery death and destruction unless we prefetched EOS. */
     312        2828 :     if (ch != '\0')
     313           0 :         goto enoent;
     314             : 
     315             :     /* Prefix length can default to /32 only if all four octets spec'd. */
     316        2828 :     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        2828 :     if (dst == odst)
     326           0 :         goto enoent;
     327             : 
     328             :     /* If prefix length overspecifies mantissa, life is bad. */
     329        2828 :     if ((bits / 8) > (dst - odst))
     330           0 :         goto enoent;
     331             : 
     332             :     /* Extend address to four octets. */
     333        3188 :     while (size-- > 0)
     334         360 :         *dst++ = 0;
     335             : 
     336        2828 :     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         532 : getbits(const char *src, int *bitsp)
     349             : {
     350             :     static const char digits[] = "0123456789";
     351             :     int         n;
     352             :     int         val;
     353             :     char        ch;
     354             : 
     355         532 :     val = 0;
     356         532 :     n = 0;
     357        1636 :     while ((ch = *src++) != '\0')
     358             :     {
     359             :         const char *pch;
     360             : 
     361        1104 :         pch = strchr(digits, ch);
     362        1104 :         if (pch != NULL)
     363             :         {
     364        1104 :             if (n++ != 0 && val == 0)   /* no leading zeros */
     365           0 :                 return 0;
     366        1104 :             val *= 10;
     367        1104 :             val += (pch - digits);
     368        1104 :             if (val > 128)       /* range */
     369           0 :                 return 0;
     370        1104 :             continue;
     371             :         }
     372           0 :         return 0;
     373             :     }
     374         532 :     if (n == 0)
     375           0 :         return 0;
     376         532 :     *bitsp = val;
     377         532 :     return 1;
     378             : }
     379             : 
     380             : static int
     381          36 : getv4(const char *src, u_char *dst, int *bitsp)
     382             : {
     383             :     static const char digits[] = "0123456789";
     384          36 :     u_char     *odst = dst;
     385             :     int         n;
     386             :     u_int       val;
     387             :     char        ch;
     388             : 
     389          36 :     val = 0;
     390          36 :     n = 0;
     391         288 :     while ((ch = *src++) != '\0')
     392             :     {
     393             :         const char *pch;
     394             : 
     395         282 :         pch = strchr(digits, ch);
     396         282 :         if (pch != NULL)
     397             :         {
     398         144 :             if (n++ != 0 && val == 0)   /* no leading zeros */
     399           0 :                 return 0;
     400         144 :             val *= 10;
     401         144 :             val += (pch - digits);
     402         144 :             if (val > 255)       /* range */
     403           0 :                 return 0;
     404         144 :             continue;
     405             :         }
     406         138 :         if (ch == '.' || ch == '/')
     407             :         {
     408         138 :             if (dst - odst > 3) /* too many octets? */
     409           0 :                 return 0;
     410         138 :             *dst++ = val;
     411         138 :             if (ch == '/')
     412          30 :                 return getbits(src, bitsp);
     413         108 :             val = 0;
     414         108 :             n = 0;
     415         108 :             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         628 : inet_net_pton_ipv6(const char *src, u_char *dst)
     429             : {
     430         628 :     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         952 : 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         952 :     if (size < NS_IN6ADDRSZ)
     455           0 :         goto emsgsize;
     456             : 
     457         952 :     memset((tp = tmp), '\0', NS_IN6ADDRSZ);
     458         952 :     endp = tp + NS_IN6ADDRSZ;
     459         952 :     colonp = NULL;
     460             :     /* Leading :: requires some special handling. */
     461         952 :     if (*src == ':')
     462          66 :         if (*++src != ':')
     463           0 :             goto enoent;
     464         952 :     curtok = src;
     465         952 :     saw_xdigit = 0;
     466         952 :     val = 0;
     467         952 :     digits = 0;
     468         952 :     bits = -1;
     469       20376 :     while ((ch = *src++) != '\0')
     470             :     {
     471             :         const char *pch;
     472             : 
     473       19968 :         if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
     474        4760 :             pch = strchr((xdigits = xdigits_u), ch);
     475       19968 :         if (pch != NULL)
     476             :         {
     477       15208 :             val <<= 4;
     478       15208 :             val |= (pch - xdigits);
     479       15208 :             if (++digits > 4)
     480           0 :                 goto enoent;
     481       15208 :             saw_xdigit = 1;
     482       15208 :             continue;
     483             :         }
     484        4760 :         if (ch == ':')
     485             :         {
     486        4222 :             curtok = src;
     487        4222 :             if (!saw_xdigit)
     488             :             {
     489         774 :                 if (colonp)
     490           6 :                     goto enoent;
     491         768 :                 colonp = tp;
     492         768 :                 continue;
     493             :             }
     494        3448 :             else if (*src == '\0')
     495           0 :                 goto enoent;
     496        3448 :             if (tp + NS_INT16SZ > endp)
     497           0 :                 goto enoent;
     498        3448 :             *tp++ = (u_char) (val >> 8) & 0xff;
     499        3448 :             *tp++ = (u_char) val & 0xff;
     500        3448 :             saw_xdigit = 0;
     501        3448 :             digits = 0;
     502        3448 :             val = 0;
     503        3448 :             continue;
     504             :         }
     505         574 :         if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
     506          36 :             getv4(curtok, tp, &bits) > 0)
     507             :         {
     508          36 :             tp += NS_INADDRSZ;
     509          36 :             saw_xdigit = 0;
     510          36 :             break;              /* '\0' was seen by inet_pton4(). */
     511             :         }
     512         502 :         if (ch == '/' && getbits(src, &bits) > 0)
     513         502 :             break;
     514           0 :         goto enoent;
     515             :     }
     516         946 :     if (saw_xdigit)
     517             :     {
     518         790 :         if (tp + NS_INT16SZ > endp)
     519           0 :             goto enoent;
     520         790 :         *tp++ = (u_char) (val >> 8) & 0xff;
     521         790 :         *tp++ = (u_char) val & 0xff;
     522             :     }
     523         946 :     if (bits == -1)
     524         414 :         bits = 128;
     525             : 
     526         946 :     endp = tmp + 16;
     527             : 
     528         946 :     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         762 :         const int   n = tp - colonp;
     535             :         int         i;
     536             : 
     537         762 :         if (tp == endp)
     538           0 :             goto enoent;
     539        4734 :         for (i = 1; i <= n; i++)
     540             :         {
     541        3972 :             endp[-i] = colonp[n - i];
     542        3972 :             colonp[n - i] = 0;
     543             :         }
     544         762 :         tp = endp;
     545             :     }
     546         946 :     if (tp != endp)
     547           0 :         goto enoent;
     548             : 
     549             :     /*
     550             :      * Copy out the result.
     551             :      */
     552         946 :     memcpy(dst, tmp, NS_IN6ADDRSZ);
     553             : 
     554         946 :     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