/* 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.
*/
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);
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,
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)
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,
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)
/* 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)
{
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;
"%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;
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;
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;
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;
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;
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));
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");
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;
}
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");
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 */
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);
{
const struct PgSocket *sock = ctx;
char *user, *db, *host;
+ char host6[INET6_ADDRSTRLEN];
int port;
/* no prefix */
/* 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',
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);
}
}
}
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);
}
}
}
}
}
+/*
+ * 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;
+}
+