LCOV - code coverage report
Current view: top level - src/common - ip.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 57 75 76.0 %
Date: 2024-11-21 08:14:44 Functions: 5 5 100.0 %
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-2024, 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             : int
      53       34910 : pg_getaddrinfo_all(const char *hostname, const char *servname,
      54             :                    const struct addrinfo *hintp, struct addrinfo **result)
      55             : {
      56             :     int         rc;
      57             : 
      58             :     /* not all versions of getaddrinfo() zero *result on failure */
      59       34910 :     *result = NULL;
      60             : 
      61       34910 :     if (hintp->ai_family == AF_UNIX)
      62       27444 :         return getaddrinfo_unix(servname, hintp, result);
      63             : 
      64             :     /* NULL has special meaning to getaddrinfo(). */
      65        7466 :     rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
      66             :                      servname, hintp, result);
      67             : 
      68        7466 :     return rc;
      69             : }
      70             : 
      71             : 
      72             : /*
      73             :  *  pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
      74             :  *
      75             :  * Note: the ai_family field of the original hint structure must be passed
      76             :  * so that we can tell whether the addrinfo struct was built by the system's
      77             :  * getaddrinfo() routine or our own getaddrinfo_unix() routine.  Some versions
      78             :  * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
      79             :  * not safe to look at ai_family in the addrinfo itself.
      80             :  */
      81             : void
      82       34910 : pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
      83             : {
      84       34910 :     if (hint_ai_family == AF_UNIX)
      85             :     {
      86             :         /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
      87       54888 :         while (ai != NULL)
      88             :         {
      89       27444 :             struct addrinfo *p = ai;
      90             : 
      91       27444 :             ai = ai->ai_next;
      92       27444 :             free(p->ai_addr);
      93       27444 :             free(p);
      94             :         }
      95             :     }
      96             :     else
      97             :     {
      98             :         /* struct was built by getaddrinfo() */
      99        7466 :         if (ai != NULL)
     100        7466 :             freeaddrinfo(ai);
     101             :     }
     102       34910 : }
     103             : 
     104             : 
     105             : /*
     106             :  *  pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
     107             :  *
     108             :  * The API of this routine differs from the standard getnameinfo() definition
     109             :  * in two ways: first, the addr parameter is declared as sockaddr_storage
     110             :  * rather than struct sockaddr, and second, the node and service fields are
     111             :  * guaranteed to be filled with something even on failure return.
     112             :  */
     113             : int
     114       51656 : pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
     115             :                    char *node, int nodelen,
     116             :                    char *service, int servicelen,
     117             :                    int flags)
     118             : {
     119             :     int         rc;
     120             : 
     121       51656 :     if (addr && addr->ss_family == AF_UNIX)
     122       50864 :         rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
     123             :                               node, nodelen,
     124             :                               service, servicelen,
     125             :                               flags);
     126             :     else
     127         792 :         rc = getnameinfo((const struct sockaddr *) addr, salen,
     128             :                          node, nodelen,
     129             :                          service, servicelen,
     130             :                          flags);
     131             : 
     132       51656 :     if (rc != 0)
     133             :     {
     134           0 :         if (node)
     135           0 :             strlcpy(node, "???", nodelen);
     136           0 :         if (service)
     137           0 :             strlcpy(service, "???", servicelen);
     138             :     }
     139             : 
     140       51656 :     return rc;
     141             : }
     142             : 
     143             : 
     144             : /* -------
     145             :  *  getaddrinfo_unix - get unix socket info using IPv6-compatible API
     146             :  *
     147             :  *  Bugs: only one addrinfo is set even though hintsp is NULL or
     148             :  *        ai_socktype is 0
     149             :  *        AI_CANONNAME is not supported.
     150             :  * -------
     151             :  */
     152             : static int
     153       27444 : getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
     154             :                  struct addrinfo **result)
     155             : {
     156       27444 :     struct addrinfo hints = {0};
     157             :     struct addrinfo *aip;
     158             :     struct sockaddr_un *unp;
     159             : 
     160       27444 :     *result = NULL;
     161             : 
     162       27444 :     if (strlen(path) >= sizeof(unp->sun_path))
     163           0 :         return EAI_FAIL;
     164             : 
     165       27444 :     if (hintsp == NULL)
     166             :     {
     167           0 :         hints.ai_family = AF_UNIX;
     168           0 :         hints.ai_socktype = SOCK_STREAM;
     169             :     }
     170             :     else
     171       27444 :         memcpy(&hints, hintsp, sizeof(hints));
     172             : 
     173       27444 :     if (hints.ai_socktype == 0)
     174           0 :         hints.ai_socktype = SOCK_STREAM;
     175             : 
     176       27444 :     if (hints.ai_family != AF_UNIX)
     177             :     {
     178             :         /* shouldn't have been called */
     179           0 :         return EAI_FAIL;
     180             :     }
     181             : 
     182       27444 :     aip = calloc(1, sizeof(struct addrinfo));
     183       27444 :     if (aip == NULL)
     184           0 :         return EAI_MEMORY;
     185             : 
     186       27444 :     unp = calloc(1, sizeof(struct sockaddr_un));
     187       27444 :     if (unp == NULL)
     188             :     {
     189           0 :         free(aip);
     190           0 :         return EAI_MEMORY;
     191             :     }
     192             : 
     193       27444 :     aip->ai_family = AF_UNIX;
     194       27444 :     aip->ai_socktype = hints.ai_socktype;
     195       27444 :     aip->ai_protocol = hints.ai_protocol;
     196       27444 :     aip->ai_next = NULL;
     197       27444 :     aip->ai_canonname = NULL;
     198       27444 :     *result = aip;
     199             : 
     200       27444 :     unp->sun_family = AF_UNIX;
     201       27444 :     aip->ai_addr = (struct sockaddr *) unp;
     202       27444 :     aip->ai_addrlen = sizeof(struct sockaddr_un);
     203             : 
     204       27444 :     strcpy(unp->sun_path, path);
     205             : 
     206             :     /*
     207             :      * If the supplied path starts with @, replace that with a zero byte for
     208             :      * the internal representation.  In that mode, the entire sun_path is the
     209             :      * address, including trailing zero bytes.  But we set the address length
     210             :      * to only include the length of the original string.  That way the
     211             :      * trailing zero bytes won't show up in any network or socket lists of the
     212             :      * operating system.  This is just a convention, also followed by other
     213             :      * packages.
     214             :      */
     215       27444 :     if (path[0] == '@')
     216             :     {
     217           0 :         unp->sun_path[0] = '\0';
     218           0 :         aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
     219             :     }
     220             : 
     221       27444 :     return 0;
     222             : }
     223             : 
     224             : /*
     225             :  * Convert an address to a hostname.
     226             :  */
     227             : static int
     228       50864 : getnameinfo_unix(const struct sockaddr_un *sa, int salen,
     229             :                  char *node, int nodelen,
     230             :                  char *service, int servicelen,
     231             :                  int flags)
     232             : {
     233             :     int         ret;
     234             : 
     235             :     /* Invalid arguments. */
     236       50864 :     if (sa == NULL || sa->sun_family != AF_UNIX ||
     237       25942 :         (node == NULL && service == NULL))
     238           0 :         return EAI_FAIL;
     239             : 
     240       50864 :     if (node)
     241             :     {
     242       24922 :         ret = snprintf(node, nodelen, "%s", "[local]");
     243       24922 :         if (ret < 0 || ret >= nodelen)
     244           0 :             return EAI_MEMORY;
     245             :     }
     246             : 
     247       50864 :     if (service)
     248             :     {
     249             :         /*
     250             :          * Check whether it looks like an abstract socket, but it could also
     251             :          * just be an empty string.
     252             :          */
     253       50850 :         if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
     254           0 :             ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
     255             :         else
     256       50850 :             ret = snprintf(service, servicelen, "%s", sa->sun_path);
     257       50850 :         if (ret < 0 || ret >= servicelen)
     258           0 :             return EAI_MEMORY;
     259             :     }
     260             : 
     261       50864 :     return 0;
     262             : }

Generated by: LCOV version 1.14