Allow pcp clients to connect to IPv6 addresses.
authorTatsuo Ishii <ishii@postgresql.org>
Thu, 15 May 2025 09:03:50 +0000 (18:03 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Thu, 15 May 2025 11:01:05 +0000 (20:01 +0900)
We have already allowed pcp server to connect to IPv6 addresses, but
pcp clients were not allowed to connect to them until today. This
commit allows pcp clients to connect to IPv6 addresses.

Discussion: [pgpool-general: 9481] Does pgpool 4.6.0 support pure ipv6 configuration?
https://www.pgpool.net/pipermail/pgpool-general/2025-May/009484.html
Backpatch-through: v4.6

src/libs/pcp/pcp.c

index 03717d1712221631510283315c4ac52e91c9ecb5..7e30f137501d4752e09eb8f97a0a938bffdfe80b 100644 (file)
@@ -8,7 +8,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2024     PgPool Global Development Group
+ * Copyright (c) 2003-2025     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -98,19 +98,17 @@ static char *pwdfMatchesString(char *buf, char *token);
 PCPConnInfo *
 pcp_connect(char *hostname, int port, char *username, char *password, FILE *Pfdebug)
 {
-       struct sockaddr_in addr;
        struct sockaddr_un unix_addr;
-       struct hostent *hp;
        char       *password_from_file = NULL;
        char            os_user[256];
        PCPConnInfo *pcpConn = palloc0(sizeof(PCPConnInfo));
        int                     fd;
        int                     on = 1;
-       int                     len;
 
        pcpConn->connState = PCP_CONNECTION_NOT_CONNECTED;
        pcpConn->Pfdebug = Pfdebug;
 
+       /* Unix domain socket? */
        if (hostname == NULL || *hostname == '\0' || *hostname == '/')
        {
                char       *path;
@@ -154,49 +152,79 @@ pcp_connect(char *hostname, int port, char *username, char *password, FILE *Pfde
        }
        else
        {
-               fd = socket(AF_INET, SOCK_STREAM, 0);
-               if (fd < 0)
+               /* Inet domain socket case */
+               char       *portstr;
+               int                     ret;
+               struct addrinfo *res;
+               struct addrinfo *walk;
+               struct addrinfo hints;
+
+               /*
+                * getaddrinfo() requires a string because it also accepts service names,
+                * such as "http".
+                */
+               if (asprintf(&portstr, "%d", port) == -1)
                {
                        pcp_internal_error(pcpConn,
-                                                          "ERROR: failed to create INET domain socket with error \"%s\"", strerror(errno));
+                                                          "ERROR: asprintf() failed");
                        pcpConn->connState = PCP_CONNECTION_BAD;
                        return pcpConn;
                }
 
-               if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
-                                          (char *) &on, sizeof(on)) < 0)
-               {
-                       close(fd);
+               memset(&hints, 0, sizeof(struct addrinfo));
+               hints.ai_family = PF_UNSPEC;
+               hints.ai_socktype = SOCK_STREAM;
 
+               if ((ret = getaddrinfo(hostname, portstr, &hints, &res)) != 0)
+               {
                        pcp_internal_error(pcpConn,
-                                                          "ERROR: set socket option failed with error \"%s\"", strerror(errno));
+                                                          "ERROR: getaddrinfo failed \"%s\"",
+                                                          gai_strerror(ret));
                        pcpConn->connState = PCP_CONNECTION_BAD;
+                       free(portstr);
                        return pcpConn;
                }
 
-               memset((char *) &addr, 0, sizeof(addr));
-               addr.sin_family = AF_INET;
-               hp = gethostbyname(hostname);
-               if ((hp == NULL) || (hp->h_addrtype != AF_INET))
+               free(portstr);
+
+               fd = -1;
+               for (walk = res; walk != NULL; walk = walk->ai_next)
                {
-                       close(fd);
-                       pcp_internal_error(pcpConn,
-                                                          "ERROR: could not retrieve hostname. gethostbyname failed with error \"%s\"", strerror(errno));
-                       pcpConn->connState = PCP_CONNECTION_BAD;
-                       return pcpConn;
+                       fd = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol);
+                       if (fd < 0)
+                       {
+                               pcp_internal_error(pcpConn,
+                                                                  "ERROR: failed to create INET domain socket with error \"%s\"",
+                                                                  strerror(errno));
+                               return pcpConn;
+                       }
+
+                       if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
+                                                  (char *) &on, sizeof(on)) < 0)
+                       {
+                               close(fd);
+                               pcp_internal_error(pcpConn,
+                                                                  "ERROR: set socket option failed with error \"%s\"", strerror(errno));
+                               pcpConn->connState = PCP_CONNECTION_BAD;
+                               return pcpConn;
+                       }
 
+                       if (connect(fd, walk->ai_addr, walk->ai_addrlen) < 0)
+                       {
+                               close(fd);
+                               pcp_internal_error(pcpConn,
+                                                                  "ERROR: connection to host \"%s\" failed with error \"%s\"", hostname, strerror(errno));
+                               pcpConn->connState = PCP_CONNECTION_BAD;
+                               return pcpConn;
+                       }
+                       break;  /* successfully connected */
                }
-               memmove((char *) &(addr.sin_addr),
-                               (char *) hp->h_addr,
-                               hp->h_length);
-               addr.sin_port = htons(port);
 
-               len = sizeof(struct sockaddr_in);
-               if (connect(fd, (struct sockaddr *) &addr, len) < 0)
+               /* no address available */
+               if (fd == -1)
                {
-                       close(fd);
                        pcp_internal_error(pcpConn,
-                                                          "ERROR: connection to host \"%s\" failed with error \"%s\"", hostname, strerror(errno));
+                                                          "ERROR: connection to host \"%s\" failed", hostname);
                        pcpConn->connState = PCP_CONNECTION_BAD;
                        return pcpConn;
                }
@@ -211,6 +239,7 @@ pcp_connect(char *hostname, int port, char *username, char *password, FILE *Pfde
                pcpConn->connState = PCP_CONNECTION_BAD;
                return pcpConn;
        }
+
        pcpConn->connState = PCP_CONNECTION_CONNECTED;
 
        /*