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

Generated by: LCOV version 1.13