]> granicus.if.org Git - pgbouncer/commitdiff
IPv6 support
authorMarko Kreen <markokr@gmail.com>
Wed, 16 Feb 2011 06:57:16 +0000 (08:57 +0200)
committerMarko Kreen <markokr@gmail.com>
Wed, 16 Feb 2011 14:08:53 +0000 (16:08 +0200)
Original patch by Hannu Krosing

include/bouncer.h
src/admin.c
src/objects.c
src/pooler.c
src/server.c
src/takeover.c
src/util.c

index a17aff510b1c19cf6ff19fd41e66c3ff1d9d804a..01f037ada515ace6d0331499967cb9a345ad13af 100644 (file)
@@ -131,15 +131,31 @@ extern int cf_sbuf_len;
 /* buffer size for startup noise */
 #define STARTUP_BUF    1024
 
+
 /*
  * Remote/local address
  */
+
+/* buffer for pgaddr string conversions */
+#define PGADDR_BUF  INET6_ADDRSTRLEN
+
 struct PgAddr {
-       struct in_addr ip_addr;
+       unsigned char  af;
        unsigned short port;
-       bool is_unix;
+       union {
+               struct in_addr addr4;
+               struct in6_addr addr6;
+       };
 };
 
+static inline bool pga_is_unix(const PgAddr *a) { return a->af == AF_UNIX; }
+static inline int pga_port(const PgAddr *a) { return a->port; }
+
+void pga_set(PgAddr *a, int fam, int port);
+void pga_copy(PgAddr *a, const struct sockaddr *sa);
+const char *pga_ntop(const PgAddr *a, char *dst, int dstlen);
+bool pga_pton(PgAddr *a, const char *s, int port);
+
 /*
  * Stats, kept per-pool.
  */
index 94c1ee97176d856bcf7951772471abd169bb989c..6fdcbefcab82f488861b2649cfd766d62c911f8a 100644 (file)
@@ -266,7 +266,7 @@ static bool send_one_fd(PgSocket *admin,
        msg.msg_iovlen = 1;
 
        /* attach a fd */
-       if (admin->remote_addr.is_unix && admin->own_user) {
+       if (pga_is_unix(&admin->remote_addr) && admin->own_user) {
                msg.msg_control = cntbuf;
                msg.msg_controllen = sizeof(cntbuf);
 
@@ -303,17 +303,19 @@ static bool show_one_fd(PgSocket *admin, PgSocket *sk)
        const struct PStr *std_strings = v->var_list[VStdStr];
        const struct PStr *datestyle = v->var_list[VDateStyle];
        const struct PStr *timezone = v->var_list[VTimeZone];
+       char addrbuf[PGADDR_BUF];
 
        mbuf_init_fixed_reader(&tmp, sk->cancel_key, 8);
        if (!mbuf_get_uint64be(&tmp, &ckey))
                return false;
 
+
        return send_one_fd(admin, sbuf_socket(&sk->sbuf),
                           is_server_socket(sk) ? "server" : "client",
                           sk->auth_user ? sk->auth_user->name : NULL,
                           sk->pool ? sk->pool->db->name : NULL,
-                          addr->is_unix ? "unix" : inet_ntoa(addr->ip_addr),
-                          addr->port,
+                          pga_ntop(addr, addrbuf, sizeof(addrbuf)),
+                          pga_port(addr),
                           ckey,
                           sk->link ? sbuf_socket(&sk->link->sbuf) : 0,
                           client_encoding ? client_encoding->str : NULL,
@@ -510,12 +512,7 @@ static void socket_header(PktBuf *buf, bool debug)
 
 static void adr2txt(const PgAddr *adr, char *dst, unsigned dstlen)
 {
-       if (adr->is_unix) {
-               safe_strcpy(dst, "unix", dstlen);
-       } else {
-               char *tmp = inet_ntoa(adr->ip_addr);
-               safe_strcpy(dst, tmp, dstlen);
-       }
+       pga_ntop(adr, dst, dstlen);
 }
 
 static void socket_row(PktBuf *buf, PgSocket *sk, const char *state, bool debug)
@@ -543,8 +540,8 @@ static void socket_row(PktBuf *buf, PgSocket *sk, const char *state, bool debug)
                             is_server_socket(sk) ? "S" :"C",
                             sk->auth_user ? sk->auth_user->name : "(nouser)",
                             sk->pool ? sk->pool->db->name : "(nodb)",
-                            state, r_addr, sk->remote_addr.port,
-                            l_addr, sk->local_addr.port,
+                            state, r_addr, pga_port(&sk->remote_addr),
+                            l_addr, pga_port(&sk->local_addr),
                             sk->connect_time,
                             sk->request_time,
                             ptrbuf, linkbuf,
@@ -1114,7 +1111,7 @@ bool admin_pre_login(PgSocket *client)
        client->own_user = 0;
 
        /* tag same uid as special */
-       if (client->remote_addr.is_unix) {
+       if (pga_is_unix(&client->remote_addr)) {
                res = getpeereid(sbuf_socket(&client->sbuf), &peer_uid, &peer_gid);
                if (res >= 0 && peer_uid == getuid()
                        && strcmp("pgbouncer", username) == 0)
index 475176ebf631effad624fd5e8e47290b7d901869..ddf1c57026a718cefabe36b5c6eae75d6d77ed86 100644 (file)
@@ -824,12 +824,9 @@ static void connect_server(struct PgSocket *server, const struct sockaddr *sa, i
        /* fill remote_addr */
        memset(&server->remote_addr, 0, sizeof(server->remote_addr));
        if (sa->sa_family == AF_UNIX) {
-               server->remote_addr.port = server->pool->db->port;
-               server->remote_addr.is_unix = true;
-       } else if (sa->sa_family == AF_INET) {
-               struct sockaddr_in *in = (struct sockaddr_in *)sa;
-               server->remote_addr.port = ntohs(in->sin_port);
-               server->remote_addr.ip_addr = in->sin_addr;
+               pga_set(&server->remote_addr, AF_UNIX, server->pool->db->port);
+       } else {
+               pga_copy(&server->remote_addr, sa);
        }
 
        if (cf_log_connections)
@@ -873,6 +870,7 @@ static void dns_connect(struct PgSocket *server)
 {
        struct sockaddr_un sa_un;
        struct sockaddr_in sa_in;
+       struct sockaddr_in6 sa_in6;
        struct sockaddr *sa;
        struct PgDatabase *db = server->pool->db;
        const char *host = db->host;
@@ -893,7 +891,15 @@ static void dns_connect(struct PgSocket *server)
                         "%s/.s.PGSQL.%d", unix_dir, db->port);
                sa = (struct sockaddr *)&sa_un;
                sa_len = sizeof(sa_un);
-       } else if (host[0] >= '0' && host[0] <= '9') {
+       } else if (strchr(host, ':')) {  // assume IPv6 address on any : in addr
+               slog_noise(server, "inet6 socket: %s", db->host);
+               memset(&sa_in6, 0, sizeof(sa_in6));
+               sa_in6.sin6_family = AF_INET6;
+               inet_pton(AF_INET6, db->host, (void *) sa_in6.sin6_addr.s6_addr);
+               sa_in6.sin6_port = htons(db->port);
+               sa = (struct sockaddr *)&sa_in6;
+               sa_len = sizeof(sa_in6);
+       } else if (host[0] >= '0' && host[0] <= '9') { // else try IPv4
                slog_noise(server, "inet socket: %s", db->host);
                memset(&sa_in, 0, sizeof(sa_in));
                sa_in.sin_family = AF_INET;
@@ -1125,7 +1131,7 @@ bool use_client_socket(int fd, PgAddr *addr,
        PgSocket *client;
        PktBuf tmp;
 
-       client = accept_client(fd, NULL, addr->is_unix);
+       client = accept_client(fd, NULL, pga_is_unix(addr));
        if (client == NULL)
                return false;
        client->suspended = 1;
@@ -1184,7 +1190,7 @@ bool use_server_socket(int fd, PgAddr *addr,
        if (!server)
                return false;
 
-       res = sbuf_accept(&server->sbuf, fd, addr->is_unix);
+       res = sbuf_accept(&server->sbuf, fd, pga_is_unix(addr));
        if (!res)
                return false;
 
@@ -1194,8 +1200,8 @@ bool use_server_socket(int fd, PgAddr *addr,
        server->connect_time = server->request_time = get_cached_time();
        server->query_start = 0;
 
-       fill_remote_addr(server, fd, addr->is_unix);
-       fill_local_addr(server, fd, addr->is_unix);
+       fill_remote_addr(server, fd, pga_is_unix(addr));
+       fill_local_addr(server, fd, pga_is_unix(addr));
 
        if (linkfd) {
                server->ready = 0;
index 6455ceb323dbaf46b39b009f1118ad34e894b539..0acefc447812caf3e736420b05545d2fc655757e 100644 (file)
 
 static int fd_net = 0;
 static int fd_unix = 0;
+#ifdef HAVE_IPV6
+static int fd_net_v6 = 0;
+#endif
+
 
 static struct event ev_net;
 static struct event ev_unix;
@@ -159,27 +163,49 @@ void pooler_tune_accept(bool on)
 static int create_net_socket(const char *listen_addr, int listen_port)
 {
        int sock;
-       struct sockaddr_in sa;
+       struct sockaddr_in sa4;
+       struct sockaddr_in6 sa6;
+       struct sockaddr * sa;
+       int sa_size = 0;
        int res;
        int val;
 
-       /* create socket */
-       sock = socket(AF_INET, SOCK_STREAM, 0);
-       if (sock < 0)
-               fatal_perror("socket");
 
-       /* parse address */
-       memset(&sa, 0, sizeof(sa));
-       sa.sin_family = AF_INET;
-       sa.sin_port = htons(cf_listen_port);
+       /* parse address as IPv4 */
+       memset(&sa4, 0, sizeof(sa));
+       sa = (struct sockaddr *)&sa4;
+       sa_size = sizeof(sa4);
+       sa4.sin_family = AF_INET;
+       sa4.sin_port = htons(cf_listen_port);
        if (strcmp(listen_addr, "*") == 0) {
-               sa.sin_addr.s_addr = htonl(INADDR_ANY);
+               sa4.sin_addr.s_addr = htonl(INADDR_ANY);
        } else {
-               sa.sin_addr.s_addr = inet_addr(listen_addr);
-               if (sa.sin_addr.s_addr == INADDR_NONE)
+               sa4.sin_addr.s_addr = inet_addr(listen_addr);
+       }
+       if (sa4.sin_addr.s_addr == INADDR_NONE) {
+               /* IPv4 addr not foundm re-parse address as IPv6 */
+               log_info("trying to parse %s as IPv6 address", listen_addr);
+               sa = (struct sockaddr *)&sa6;
+               sa_size = sizeof(sa6);
+               memset(&sa6, 0, sizeof(sa6));
+               sa6.sin6_family = AF_INET6;
+               sa6.sin6_port = htons(cf_listen_port);
+
+               if (inet_pton(AF_INET6, listen_addr, (void *) sa6.sin6_addr.s6_addr) <= 0)
                        fatal("cannot parse addr: '%s'", listen_addr);
        }
 
+       /* create socket of right type */ //NB! turn if() into x?y:z
+       if (sa6.sin6_family == AF_INET6){
+               sock = socket(AF_INET6, SOCK_STREAM, 0);
+               log_info("created AF_INET6 socket");
+       } else {
+               sock = socket(AF_INET, SOCK_STREAM, 0);
+               log_info("created AF_INET socket");
+       }
+       if (sock < 0)
+               fatal_perror("socket");
+
        /* relaxed binding */
        val = 1;
        res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
@@ -187,7 +213,7 @@ static int create_net_socket(const char *listen_addr, int listen_port)
                fatal_perror("setsockopt");
 
        /* bind to address */
-       res = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
+       res = bind(sock, sa, sa_size);
        if (res < 0)
                fatal_perror("bind");
 
@@ -215,19 +241,20 @@ static void err_wait_func(int sock, short flags, void *arg)
 
 static const char *addrpair(const PgAddr *src, const PgAddr *dst)
 {
-       static char ip1buf[64], ip2buf[64], buf[256];
+       static char ip1buf[INET6_ADDRSTRLEN], ip2buf[INET6_ADDRSTRLEN],
+                   buf[INET6_ADDRSTRLEN+INET6_ADDRSTRLEN+128];
        const char *ip1, *ip2;
-       if (src->is_unix)
+       if (pga_is_unix(src))
                return "unix->unix";
 
-       ip1 = inet_ntop(AF_INET, &src->ip_addr, ip1buf, sizeof(ip1buf));
+       ip1 = pga_ntop(src, ip1buf, sizeof(ip1buf));
        if (!ip1)
                ip1 = strerror(errno);
-       ip2 = inet_ntop(AF_INET, &dst->ip_addr, ip2buf, sizeof(ip2buf));
+       ip2 = pga_ntop(src, ip2buf, sizeof(ip2buf));
        if (!ip2)
                ip2 = strerror(errno);
        snprintf(buf, sizeof(buf), "%s:%d -> %s:%d",
-                ip1, src->port, ip2, dst->port);
+                ip1, pga_port(src), ip2, pga_port(dst));
        return buf;
 }
 
index 01c6106616f92ec46e578646a31756815e4632da..6870de01f23b15bd2c8db3ecd2498698105c6d27 100644 (file)
@@ -308,7 +308,7 @@ static bool handle_connect(PgSocket *server)
        bool res = false;
        PgPool *pool = server->pool;
 
-       fill_local_addr(server, sbuf_socket(&server->sbuf), server->remote_addr.is_unix);
+       fill_local_addr(server, sbuf_socket(&server->sbuf), pga_is_unix(&server->remote_addr));
 
        if (!statlist_empty(&pool->cancel_req_list)) {
                slog_debug(server, "use it for pending cancel req");
index 8dbf06584d605b6b7e17ef91d47d23bf328d5e12..ad1a76b5f251d12a7079e5b88052b5221c456c5a 100644 (file)
@@ -111,12 +111,10 @@ static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg)
                  client_enc ? client_enc : "NULL");
 
        /* fill address */
-       addr.is_unix = strcmp(saddr, "unix") == 0 ? true : false;
-       if (addr.is_unix) {
-               addr.port = cf_listen_port;
+       if (strcmp(saddr, "unix") == 0) {
+               pga_set(&addr, AF_UNIX, cf_listen_port);
        } else {
-               addr.ip_addr.s_addr = inet_addr(saddr);
-               addr.port = port;
+               pga_pton(&addr, saddr, port);
        }
 
        /* decide what to do with it */
@@ -127,7 +125,7 @@ static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg)
                res = use_server_socket(fd, &addr, db, user, ckey, oldfd, linkfd,
                                  client_enc, std_string, datestyle, timezone);
        else if (strcmp(task, "pooler") == 0)
-               res = use_pooler_socket(fd, addr.is_unix);
+               res = use_pooler_socket(fd, pga_is_unix(&addr));
        else
                fatal("unknown task: %s", task);
 
index 3f8a6244cd933e9b99aa6081362b9dd5c5184cda..f3a539096c807cd37664b1cbcf7f66c936ae536f 100644 (file)
@@ -29,6 +29,7 @@ int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstl
 {
        const struct PgSocket *sock = ctx;
        char *user, *db, *host;
+       char host6[INET6_ADDRSTRLEN];
        int port;
 
        /* no prefix */
@@ -38,12 +39,12 @@ int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstl
        /* format prefix */
        db = sock->pool ? sock->pool->db->name : "(nodb)";
        user = sock->auth_user ? sock->auth_user->name : "(nouser)";
-       if (sock->remote_addr.is_unix) {
+       if (pga_is_unix(&sock->remote_addr)) {
                host = "unix";
        } else {
-               host = inet_ntoa(sock->remote_addr.ip_addr);
+               pga_ntop(&sock->remote_addr, host6, INET6_ADDRSTRLEN);
        }
-       port = sock->remote_addr.port;
+       port = pga_port(&sock->remote_addr);
 
        return snprintf(dst, dstlen, "%c-%p: %s/%s@%s:%d ",
                        is_server_socket(sock) ? 'S' : 'C',
@@ -223,23 +224,16 @@ loop:
 void fill_remote_addr(PgSocket *sk, int fd, bool is_unix)
 {
        PgAddr *dst = &sk->remote_addr;
-       struct sockaddr_in adr;
-       socklen_t len = sizeof(adr);
+       socklen_t len = sizeof(PgAddr);
        int err;
 
-       dst->ip_addr.s_addr = INADDR_ANY;
-       dst->port = 0;
-       dst->is_unix = is_unix;
        if (is_unix) {
-               dst->port = cf_listen_port;
+               pga_set(dst, AF_UNIX, cf_listen_port);
        } else {
-               err = getpeername(fd, (struct sockaddr *)&adr, &len);
+               err = getpeername(fd, (struct sockaddr *)dst, &len);
                if (err < 0) {
                        log_error("fill_remote_addr: getpeername(%d) = %s",
                                  fd, strerror(errno));
-               } else {
-                       dst->ip_addr = adr.sin_addr;
-                       dst->port = ntohs(adr.sin_port);
                }
        }
 }
@@ -247,23 +241,16 @@ void fill_remote_addr(PgSocket *sk, int fd, bool is_unix)
 void fill_local_addr(PgSocket *sk, int fd, bool is_unix)
 {
        PgAddr *dst = &sk->local_addr;
-       struct sockaddr_in adr;
-       socklen_t len = sizeof(adr);
+       socklen_t len = sizeof(PgAddr);
        int err;
 
-       dst->ip_addr.s_addr = INADDR_ANY;
-       dst->port = 0;
-       dst->is_unix = is_unix;
        if (is_unix) {
-               dst->port = cf_listen_port;
+               pga_set(dst, AF_UNIX, cf_listen_port);
        } else {
-               err = getsockname(fd, (struct sockaddr *)&adr, &len);
+               err = getsockname(fd, (struct sockaddr *)dst, &len);
                if (err < 0) {
                        log_error("fill_local_addr: getsockname(%d) = %s",
                                  fd, strerror(errno));
-               } else {
-                       dst->ip_addr = adr.sin_addr;
-                       dst->port = ntohs(adr.sin_port);
                }
        }
 }
@@ -311,3 +298,71 @@ void rescue_timers(void)
        }
 }
 
+/*
+ * PgAddr operations
+ */
+
+/* set family and port */
+void pga_set(PgAddr *a, int af, int port)
+{
+       a->af = af;
+       a->port = port;
+}
+
+/* copy sockaddr_in/in6 to PgAddr */
+void pga_copy(PgAddr *a, const struct sockaddr *sa)
+{
+       const struct sockaddr_in *sa4;
+       const struct sockaddr_in6 *sa6;
+
+       a->af = sa->sa_family;
+       switch (a->af) {
+       case AF_INET:
+               sa4 = (struct sockaddr_in *)sa;
+               a->port = ntohs(sa4->sin_port);
+               a->addr4 = sa4->sin_addr;
+               break;
+       case AF_INET6:
+               sa6 = (struct sockaddr_in6 *)sa;
+               a->port = ntohs(sa6->sin6_port);
+               a->addr6 = sa6->sin6_addr;
+               break;
+       }
+}
+
+/* convert pgaddr to string */
+const char *pga_ntop(const PgAddr *a, char *dst, int dstlen)
+{
+       switch (a->af) {
+       case AF_UNIX:
+               strlcpy(dst, "unix", dstlen);
+               return dst;
+       case AF_INET:
+               return inet_ntop(a->af, &a->addr4, dst, dstlen);
+       case AF_INET6:
+               return inet_ntop(a->af, &a->addr6, dst, dstlen);
+       default:
+               return NULL;
+       }
+}
+
+/* parse address from string */
+bool pga_pton(PgAddr *a, const char *s, int port)
+{
+       int res;
+       if (strcmp(s, "unix") == 0) {
+               pga_set(a, AF_UNIX, port);
+               return true;
+       }
+       if (strchr(s, ':')) {
+               pga_set(a, AF_INET6, port);
+               res = inet_pton(AF_INET6, s, &a->addr6);
+       } else {
+               pga_set(a, AF_INET, port);
+               res = inet_pton(AF_INET, s, &a->addr4);
+       }
+       if (res == 0)
+               errno = EINVAL;
+       return res > 0;
+}
+