[PATCH] Support IPv6 for pool connections to PostgreSQL servers.

Michael Stapelberg stapelberg at debian.org
Tue Aug 12 17:43:02 JST 2014


There are still other places in the code where IPv6 is not supported,
e.g. where pgpool creates its listening socket.
---
 src/protocol/pool_connection_pool.c | 147 ++++++++++++++++++++----------------
 1 file changed, 83 insertions(+), 64 deletions(-)

diff --git a/src/protocol/pool_connection_pool.c b/src/protocol/pool_connection_pool.c
index 347b081..ef6bf11 100644
--- a/src/protocol/pool_connection_pool.c
+++ b/src/protocol/pool_connection_pool.c
@@ -509,57 +509,14 @@ int connect_unix_domain_socket_by_port(int port, char *socket_dir, bool retry)
 	return fd;
 }
 
-/*
- * Connect to PostgreSQL server by using INET domain socket.
- * If retry is true, retry to call connect() upon receiving EINTR error.
- */
-int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
+bool connect_with_timeout(int fd, struct addrinfo *walk, char *host, int port, bool retry)
 {
-	int fd;
-	int len;
-	int on = 1;
-	struct sockaddr_in addr;
-	struct hostent *hp;
-	struct timeval timeout;
 	struct timeval *tm;
+	struct timeval timeout;
 	fd_set rset, wset;
+	int sts;
 	int error;
 	socklen_t socklen;
-	int sts;
-
-	fd = socket(AF_INET, SOCK_STREAM, 0);
-	if (fd < 0)
-	{
-		pool_error("connect_inet_domain_socket_by_port: socket() failed: %s", strerror(errno));
-		return -1;
-	}
-
-	/* set nodelay */
-	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
-				   (char *) &on,
-				   sizeof(on)) < 0)
-	{
-		pool_error("connect_inet_domain_socket_by_port: setsockopt() failed: %s", strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	memset((char *) &addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-
-	addr.sin_port = htons(port);
-	len = sizeof(struct sockaddr_in);
-
-	hp = gethostbyname(host);
-	if ((hp == NULL) || (hp->h_addrtype != AF_INET))
-	{
-		pool_error("connect_inet_domain_socket: gethostbyname() failed: %s host: %s", hstrerror(h_errno), host);
-		close(fd);
-		return -1;
-	}
-	memmove((char *) &(addr.sin_addr),
-			(char *) hp->h_addr,
-			hp->h_length);
 
 	pool_set_nonblock(fd);
 
@@ -568,18 +525,16 @@ int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
 		if (exit_request)		/* exit request already sent */
 		{
 			pool_log("connect_inet_domain_socket_by_port: exit request has been sent");
-			close(fd);
-			return -1;
+			return false;
 		}
 
 		if (health_check_timer_expired && getpid() == mypid)		/* has health check timer expired */
 		{
 			pool_log("connect_inet_domain_socket_by_port: health check timer expired");
-			close(fd);
-			return -1;
+			return false;
 		}
 
-		if (connect(fd, (struct sockaddr *)&addr, len) < 0)
+		if (connect(fd, walk->ai_addr, walk->ai_addrlen) < 0)
 		{
 			if (errno == EISCONN)
 			{
@@ -598,8 +553,7 @@ int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
 			{
 				pool_error("connect_inet_domain_socket(%s:%d): connect() failed: %s",
 						   host, port, strerror(errno));
-				close(fd);
-				return -1;
+				return false;
 			}
 
 			if (pool_config->connect_timeout == 0)
@@ -636,8 +590,7 @@ int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
 				else
 				{
 					pool_error("connect_inet_domain_socket(%s:%d): select() timed out", host, port);
-					close(fd);
-					return -1;
+					return false;
 				}
 			}
 			else if (sts > 0)
@@ -659,8 +612,7 @@ int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
 						/* Solaris returns error in this case */
 						pool_error("connect_inet_domain_socket(%s:%d): getsockopt() failed: %s",
 								   host, port, strerror(errno));
-						close(fd);
-						return -1;
+						return false;
 					}
 
 					/* Non Solaris case */
@@ -668,16 +620,14 @@ int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
 					{
 						pool_error("connect_inet_domain_socket(%s:%d): getsockopt() detected error: %s",
 								   host, port, strerror(error));
-						close(fd);
-						return -1;
+						return false;
 					}
 				}
 				else
 				{
 					pool_error("connect_inet_domain_socket(%s:%d): both read data and write data was not set",
 							   host, port);
-					close(fd);
-					return -1;
+					return false;
 				}
 			}
 			else		/* select returns error */
@@ -690,15 +640,84 @@ int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
 				}
 				pool_log("connect_inet_domain_socket(%s:%d): select() interrupted",
 						 host, port);
-				close(fd);
-				return -1;
+				return false;
 			}
 		}
 		break;
 	}
 
 	pool_unset_nonblock(fd);
-	return fd;
+	return true;
+}
+
+
+/*
+ * Connect to PostgreSQL server by using INET domain socket.
+ * If retry is true, retry to call connect() upon receiving EINTR error.
+ */
+int connect_inet_domain_socket_by_port(char *host, int port, bool retry)
+{
+	int fd = -1;
+	int on = 1;
+	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)
+	{
+		pool_error("connect_inet_domain_socket_by_port: asprintf() failed: %s", strerror(errno));
+		return -1;
+	}
+
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+
+	if ((ret = getaddrinfo(host, portstr, &hints, &res)) != 0)
+	{
+		pool_error("connect_inet_domain_socket_by_port: getaddrinfo() failed: %s", gai_strerror(ret));
+		free(portstr);
+		return -1;
+	}
+
+	free(portstr);
+
+	for (walk = res; walk != NULL; walk = walk->ai_next)
+	{
+		fd = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol);
+		if (fd < 0)
+		{
+			pool_error("connect_inet_domain_socket_by_port: socket() failed: %s", strerror(errno));
+			continue;
+		}
+
+		/* set nodelay */
+		if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
+					   (char *) &on,
+					   sizeof(on)) < 0)
+		{
+			pool_error("connect_inet_domain_socket_by_port: setsockopt() failed: %s", strerror(errno));
+			close(fd);
+			freeaddrinfo(res);
+			return -1;
+		}
+
+		if (!connect_with_timeout(fd, walk, host, port, retry))
+		{
+			close(fd);
+			fd = -1;
+			continue;
+		}
+
+		freeaddrinfo(res);
+		return fd;
+	}
+
+	freeaddrinfo(res);
+	return -1;
 }
 
 /*
-- 
2.1.0.rc1


--=-=-=--


More information about the pgpool-general mailing list