LCOV - code coverage report
Current view: top level - src/common - ip.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 76.0 % 75 57
Test Date: 2026-03-04 04:14:49 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * ip.c
       4              :  *    IPv6-aware network access.
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/common/ip.c
      12              :  *
      13              :  * This file and the IPV6 implementation were initially provided by
      14              :  * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
      15              :  * http://www.lbsd.net.
      16              :  *
      17              :  *-------------------------------------------------------------------------
      18              :  */
      19              : 
      20              : #ifndef FRONTEND
      21              : #include "postgres.h"
      22              : #else
      23              : #include "postgres_fe.h"
      24              : #endif
      25              : 
      26              : #include <unistd.h>
      27              : #include <sys/stat.h>
      28              : #include <sys/socket.h>
      29              : #include <netdb.h>
      30              : #include <netinet/in.h>
      31              : #include <netinet/tcp.h>
      32              : #include <arpa/inet.h>
      33              : #include <sys/file.h>
      34              : 
      35              : #include "common/ip.h"
      36              : 
      37              : 
      38              : 
      39              : static int  getaddrinfo_unix(const char *path,
      40              :                              const struct addrinfo *hintsp,
      41              :                              struct addrinfo **result);
      42              : 
      43              : static int  getnameinfo_unix(const struct sockaddr_un *sa, int salen,
      44              :                              char *node, int nodelen,
      45              :                              char *service, int servicelen,
      46              :                              int flags);
      47              : 
      48              : 
      49              : /*
      50              :  *  pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
      51              :  *
      52              :  * The API of this routine differs from the standard getaddrinfo() definition
      53              :  * in that it requires a valid hintp, a null pointer is not allowed.
      54              :  */
      55              : int
      56        19889 : pg_getaddrinfo_all(const char *hostname, const char *servname,
      57              :                    const struct addrinfo *hintp, struct addrinfo **result)
      58              : {
      59              :     int         rc;
      60              : 
      61              :     /* not all versions of getaddrinfo() zero *result on failure */
      62        19889 :     *result = NULL;
      63              : 
      64        19889 :     if (hintp->ai_family == AF_UNIX)
      65        15397 :         return getaddrinfo_unix(servname, hintp, result);
      66              : 
      67              :     /* NULL has special meaning to getaddrinfo(). */
      68         4492 :     rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
      69              :                      servname, hintp, result);
      70              : 
      71         4492 :     return rc;
      72              : }
      73              : 
      74              : 
      75              : /*
      76              :  *  pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
      77              :  *
      78              :  * Note: the ai_family field of the original hint structure must be passed
      79              :  * so that we can tell whether the addrinfo struct was built by the system's
      80              :  * getaddrinfo() routine or our own getaddrinfo_unix() routine.  Some versions
      81              :  * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
      82              :  * not safe to look at ai_family in the addrinfo itself.
      83              :  */
      84              : void
      85        19889 : pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
      86              : {
      87        19889 :     if (hint_ai_family == AF_UNIX)
      88              :     {
      89              :         /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
      90        30794 :         while (ai != NULL)
      91              :         {
      92        15397 :             struct addrinfo *p = ai;
      93              : 
      94        15397 :             ai = ai->ai_next;
      95        15397 :             free(p->ai_addr);
      96        15397 :             free(p);
      97              :         }
      98              :     }
      99              :     else
     100              :     {
     101              :         /* struct was built by getaddrinfo() */
     102         4492 :         if (ai != NULL)
     103         4492 :             freeaddrinfo(ai);
     104              :     }
     105        19889 : }
     106              : 
     107              : 
     108              : /*
     109              :  *  pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
     110              :  *
     111              :  * The API of this routine differs from the standard getnameinfo() definition
     112              :  * in two ways: first, the addr parameter is declared as sockaddr_storage
     113              :  * rather than struct sockaddr, and second, the node and service fields are
     114              :  * guaranteed to be filled with something even on failure return.
     115              :  */
     116              : int
     117        28769 : pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
     118              :                    char *node, int nodelen,
     119              :                    char *service, int servicelen,
     120              :                    int flags)
     121              : {
     122              :     int         rc;
     123              : 
     124        28769 :     if (addr && addr->ss_family == AF_UNIX)
     125        28366 :         rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
     126              :                               node, nodelen,
     127              :                               service, servicelen,
     128              :                               flags);
     129              :     else
     130          403 :         rc = getnameinfo((const struct sockaddr *) addr, salen,
     131              :                          node, nodelen,
     132              :                          service, servicelen,
     133              :                          flags);
     134              : 
     135        28769 :     if (rc != 0)
     136              :     {
     137            0 :         if (node)
     138            0 :             strlcpy(node, "???", nodelen);
     139            0 :         if (service)
     140            0 :             strlcpy(service, "???", servicelen);
     141              :     }
     142              : 
     143        28769 :     return rc;
     144              : }
     145              : 
     146              : 
     147              : /* -------
     148              :  *  getaddrinfo_unix - get unix socket info using IPv6-compatible API
     149              :  *
     150              :  *  Bugs: only one addrinfo is set even though hintsp is NULL or
     151              :  *        ai_socktype is 0
     152              :  *        AI_CANONNAME is not supported.
     153              :  * -------
     154              :  */
     155              : static int
     156        15397 : getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
     157              :                  struct addrinfo **result)
     158              : {
     159        15397 :     struct addrinfo hints = {0};
     160              :     struct addrinfo *aip;
     161              :     struct sockaddr_un *unp;
     162              : 
     163        15397 :     *result = NULL;
     164              : 
     165        15397 :     if (strlen(path) >= sizeof(unp->sun_path))
     166            0 :         return EAI_FAIL;
     167              : 
     168        15397 :     if (hintsp == NULL)
     169              :     {
     170            0 :         hints.ai_family = AF_UNIX;
     171            0 :         hints.ai_socktype = SOCK_STREAM;
     172              :     }
     173              :     else
     174        15397 :         memcpy(&hints, hintsp, sizeof(hints));
     175              : 
     176        15397 :     if (hints.ai_socktype == 0)
     177            0 :         hints.ai_socktype = SOCK_STREAM;
     178              : 
     179        15397 :     if (hints.ai_family != AF_UNIX)
     180              :     {
     181              :         /* shouldn't have been called */
     182            0 :         return EAI_FAIL;
     183              :     }
     184              : 
     185        15397 :     aip = calloc(1, sizeof(struct addrinfo));
     186        15397 :     if (aip == NULL)
     187            0 :         return EAI_MEMORY;
     188              : 
     189        15397 :     unp = calloc(1, sizeof(struct sockaddr_un));
     190        15397 :     if (unp == NULL)
     191              :     {
     192            0 :         free(aip);
     193            0 :         return EAI_MEMORY;
     194              :     }
     195              : 
     196        15397 :     aip->ai_family = AF_UNIX;
     197        15397 :     aip->ai_socktype = hints.ai_socktype;
     198        15397 :     aip->ai_protocol = hints.ai_protocol;
     199        15397 :     aip->ai_next = NULL;
     200        15397 :     aip->ai_canonname = NULL;
     201        15397 :     *result = aip;
     202              : 
     203        15397 :     unp->sun_family = AF_UNIX;
     204        15397 :     aip->ai_addr = (struct sockaddr *) unp;
     205        15397 :     aip->ai_addrlen = sizeof(struct sockaddr_un);
     206              : 
     207        15397 :     strcpy(unp->sun_path, path);
     208              : 
     209              :     /*
     210              :      * If the supplied path starts with @, replace that with a zero byte for
     211              :      * the internal representation.  In that mode, the entire sun_path is the
     212              :      * address, including trailing zero bytes.  But we set the address length
     213              :      * to only include the length of the original string.  That way the
     214              :      * trailing zero bytes won't show up in any network or socket lists of the
     215              :      * operating system.  This is just a convention, also followed by other
     216              :      * packages.
     217              :      */
     218        15397 :     if (path[0] == '@')
     219              :     {
     220            0 :         unp->sun_path[0] = '\0';
     221            0 :         aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
     222              :     }
     223              : 
     224        15397 :     return 0;
     225              : }
     226              : 
     227              : /*
     228              :  * Convert an address to a hostname.
     229              :  */
     230              : static int
     231        28366 : getnameinfo_unix(const struct sockaddr_un *sa, int salen,
     232              :                  char *node, int nodelen,
     233              :                  char *service, int servicelen,
     234              :                  int flags)
     235              : {
     236              :     int         ret;
     237              : 
     238              :     /* Invalid arguments. */
     239        28366 :     if (sa == NULL || sa->sun_family != AF_UNIX ||
     240        14484 :         (node == NULL && service == NULL))
     241            0 :         return EAI_FAIL;
     242              : 
     243        28366 :     if (node)
     244              :     {
     245        13882 :         ret = snprintf(node, nodelen, "%s", "[local]");
     246        13882 :         if (ret < 0 || ret >= nodelen)
     247            0 :             return EAI_MEMORY;
     248              :     }
     249              : 
     250        28366 :     if (service)
     251              :     {
     252              :         /*
     253              :          * Check whether it looks like an abstract socket, but it could also
     254              :          * just be an empty string.
     255              :          */
     256        28359 :         if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
     257            0 :             ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
     258              :         else
     259        28359 :             ret = snprintf(service, servicelen, "%s", sa->sun_path);
     260        28359 :         if (ret < 0 || ret >= servicelen)
     261            0 :             return EAI_MEMORY;
     262              :     }
     263              : 
     264        28366 :     return 0;
     265              : }
        

Generated by: LCOV version 2.0-1