[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