From: Marko Kreen Date: Fri, 10 Sep 2010 08:34:38 +0000 (+0300) Subject: Async DNS lookup support via libevent DNS module. X-Git-Tag: pgbouncer_1_4_rc3~45 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c735970ae1aff392ab07f39d93248035d4dec700;p=pgbouncer Async DNS lookup support via libevent DNS module. With libevent 1.x we use evdns_base_resolve_ipv4() API, which does not use /etc/hosts, but returns TTL. With libevent 2.x we use evdns_getaddrinfo() API, which uses /etc/hosts but does not return TTL. As we need to have our own TTL handling anyway, the TTL is ignored on libevent 1.x too, to be consistent. New config variable 'dns_max_ttl', which sets time how long result can be reused. --- diff --git a/Makefile b/Makefile index aac55ee..675fbf6 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ # sources SRCS = client.c loader.c objects.c pooler.c proto.c sbuf.c server.c util.c \ admin.c stats.c takeover.c janitor.c pktbuf.c system.c main.c \ - varcache.c + varcache.c dnslookup.c HDRS = client.h loader.h objects.h pooler.h proto.h sbuf.h server.h util.h \ admin.h stats.h takeover.h janitor.h pktbuf.h system.h bouncer.h \ - varcache.h iobuf.h + varcache.h iobuf.h dnslookup.h # data & dirs to include in tgz DOCS = doc/overview.txt doc/usage.txt doc/config.txt doc/todo.txt diff --git a/doc/config.txt b/doc/config.txt index afd70f5..638c532 100644 --- a/doc/config.txt +++ b/doc/config.txt @@ -312,6 +312,14 @@ aspect of that is that their statistics are also forgotten. [seconds] Default: 3600 +==== dns_max_ttl ==== + +How long the DNS lookups can be cached. If a DNS lookup returns +several answers, pgbouncer will robin-between them in the meantime. +Actual DNS TTL is ignored. [seconds] + +Default: 15 + === Dangerous timeouts === Setting following timeouts cause unexpected errors. diff --git a/include/bouncer.h b/include/bouncer.h index e989dca..ea00a55 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -85,6 +85,7 @@ extern int cf_sbuf_len; #include "sbuf.h" #include "pktbuf.h" #include "varcache.h" +#include "dnslookup.h" #include "admin.h" #include "loader.h" @@ -239,8 +240,8 @@ struct PgDatabase { PgUser *forced_user; /* if not NULL, the user/psw is forced */ - PgAddr addr; /* address prepared for connect() */ - char unix_socket_dir[UNIX_PATH_MAX]; /* custom unix socket dir */ + const char *host; /* host or unix socket name */ + int port; int pool_size; /* max server connections in one pool */ int res_pool_size; /* additional server connections in case of trouble */ @@ -341,6 +342,7 @@ extern usec_t cf_client_idle_timeout; extern usec_t cf_client_login_timeout; extern int cf_server_round_robin; extern int cf_disable_pqexec; +extern usec_t cf_dns_max_ttl; extern int cf_auth_type; extern char *cf_auth_file; @@ -373,6 +375,8 @@ extern ConfElem bouncer_params[]; extern usec_t g_suspend_start; +extern struct DNSContext *adns; + static inline PgSocket * _MUSTCHECK pop_socket(struct StatList *slist) { diff --git a/include/dnslookup.h b/include/dnslookup.h new file mode 100644 index 0000000..270afa2 --- /dev/null +++ b/include/dnslookup.h @@ -0,0 +1,30 @@ +/* + * PgBouncer - Lightweight connection pooler for PostgreSQL. + * + * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +struct DNSContext; + +typedef void (*adns_callback_f)(void *arg, int af, const void *addr); + +struct DNSContext *adns_create_context(void); +void adns_reload(struct DNSContext *ctx); +void adns_free_context(struct DNSContext *ctx); + +void adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *arg); + diff --git a/include/sbuf.h b/include/sbuf.h index ca2c472..e614476 100644 --- a/include/sbuf.h +++ b/include/sbuf.h @@ -61,7 +61,6 @@ typedef void (*sbuf_libevent_cb)(int, short, void *); struct SBuf { struct event ev; /* libevent handle */ - bool is_unix; /* is it unix socket */ uint8_t wait_type; /* track wait state */ uint8_t pkt_action; /* method for handling current pkt */ @@ -80,7 +79,7 @@ struct SBuf { void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn); bool sbuf_accept(SBuf *sbuf, int read_sock, bool is_unix) _MUSTCHECK; -bool sbuf_connect(SBuf *sbuf, const PgAddr *addr, const char *unix_dir, int timeout_sec) _MUSTCHECK; +bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec) _MUSTCHECK; bool sbuf_pause(SBuf *sbuf) _MUSTCHECK; void sbuf_continue(SBuf *sbuf); diff --git a/src/admin.c b/src/admin.c index 91f4682..fd90ed9 100644 --- a/src/admin.c +++ b/src/admin.c @@ -422,7 +422,6 @@ static bool admin_show_databases(PgSocket *admin, const char *arg) { PgDatabase *db; struct List *item; - char *host; const char *f_user; PktBuf *buf; @@ -438,14 +437,9 @@ static bool admin_show_databases(PgSocket *admin, const char *arg) statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); - if (!db->addr.is_unix) { - host = inet_ntoa(db->addr.ip_addr); - } else - host = NULL; - f_user = db->forced_user ? db->forced_user->name : NULL; pktbuf_write_DataRow(buf, "ssissii", - db->name, host, db->addr.port, + db->name, db->host, db->port, db->dbname, f_user, db->pool_size, db->res_pool_size); @@ -1164,8 +1158,7 @@ void admin_setup(void) if (!db) fatal("no memory for admin database"); - db->addr.port = cf_listen_port; - db->addr.is_unix = 1; + db->port = cf_listen_port; db->pool_size = 2; db->admin = 1; if (!force_user(db, "pgbouncer", "")) diff --git a/src/dnslookup.c b/src/dnslookup.c new file mode 100644 index 0000000..4fb7511 --- /dev/null +++ b/src/dnslookup.c @@ -0,0 +1,376 @@ +/* + * PgBouncer - Lightweight connection pooler for PostgreSQL. + * + * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bouncer.h" + +/* + * libevent1 - returns TTL, ignores hosts file. + * libevent2 - does not return TTL, uses hosts file. + */ + +/* do we have libevent2? */ +#ifdef EV_ET +#define LIBEVENT2 +#endif + +#ifdef LIBEVENT2 +#include +#else +#include +#endif + + +struct UserCallback { + struct List node; + adns_callback_f cb_func; + void *cb_arg; +}; + +struct DNSRequest { + struct AANode node; + struct DNSContext *ctx; + + struct List ucb_list; + + const char *name; + int namelen; + + bool done; + + int res_af; + int res_count; + int res_pos; + void *res_list; + usec_t res_ttl; +}; + +struct DNSContext { + struct AATree req_tree; + void *edns; +}; + +static void deliver_info(struct DNSRequest *req); + +#undef log_debug +#define log_debug log_warning + + +#ifdef LIBEVENT2 + +/* + * ADNS with libevent2 + */ + +static void got_result_gai(int result, struct evutil_addrinfo *res, void *arg) +{ + struct DNSRequest *req = arg; + struct evutil_addrinfo *ai; + int count = 0; + int af = 0; + int adrlen; + uint8_t *dst; + + if (result != DNS_ERR_NONE) { + /* lookup failed */ + log_warning("lookup failed: %s: result=%d", req->name, result); + goto failed; + } + + for (ai = res; ai; ai = ai->ai_next) { + /* pick single family for this address */ + if (!af) { + if (ai->ai_family == AF_INET) { + af = ai->ai_family; + req->res_af = af; + adrlen = 4; + } else { + continue; + } + } + if (ai->ai_family != af) + continue; + count++; + } + + /* did not found usable entry */ + if (!af) { + log_warning("dns(%s): no usable address", req->name); + evutil_freeaddrinfo(res); + goto failed; + } + + log_debug("dns(%s): got_result_gai: count=%d, adrlen=%d", req->name, count, adrlen); + + req->res_pos = 0; + req->done = true; + req->res_count = count; + req->res_list = malloc(adrlen * count); + if (!req->res_list) { + log_warning("req->res_list alloc failed"); + goto failed; + } + req->res_ttl = get_cached_time() + cf_dns_max_ttl; + + dst = req->res_list; + for (ai = res; ai; ai = ai->ai_next) { + struct sockaddr_in *in; + if (ai->ai_family != af) + continue; + in = (void*)ai->ai_addr; + log_debug("dns(%s) result: %s", req->name, inet_ntoa(in->sin_addr)); + memcpy(dst, &in->sin_addr, adrlen); + dst += adrlen; + } + + deliver_info(req); + return; +failed: + req->res_af = 0; + req->res_list = NULL; + deliver_info(req); +} + +static bool impl_init(struct DNSContext *ctx) +{ + ctx->edns = evdns_base_new(NULL, 1); + if (!ctx->edns) { + log_warning("evdns_base_new failed"); + return false; + } + return true; +} + +static void impl_launch_query(struct DNSRequest *req) +{ + struct evdns_getaddrinfo_request *gai_req; + + gai_req = evdns_getaddrinfo(req->ctx->edns, req->name, NULL, NULL, got_result_gai, req); + log_noise("dns: evdns_getaddrinfo(%s)=%p", req->name, gai_req); +} + +static void impl_release(struct DNSContext *ctx) +{ + struct evdns_base *dns = ctx->edns; + evdns_base_free(dns, 0); +} + +#else + +/* + * ADNS with libevent 1.x + */ + +static void got_result_evdns(int result, char type, int count, int ttl, void *addresses, void *arg) +{ + struct DNSRequest *req = arg; + int adrlen = 4; + + log_noise("dns: got_result_evdns: type=%d cnt=%d ttl=%d", type, count, ttl); + + req->done = true; + + if (result != DNS_ERR_NONE || count < 1) { + /* lookup failed */ + goto failed; + } else if (type == DNS_IPv4_A) { + struct in_addr *a = addresses; + log_debug("dns(%s): got_result_evdns: %s", req->name, inet_ntoa(*a)); + req->res_af = AF_INET; + adrlen = 4; + } else { + log_warning("dns(%s): got_result_evdns unknown result: %d", req->name, type); + /* unknown result */ + goto failed; + } + req->res_pos = 0; + req->res_count = count; + req->res_list = malloc(adrlen * count); + if (!req->res_list) + goto failed; + memcpy(req->res_list, addresses, adrlen * count); + req->res_ttl = get_cached_time() + cf_dns_max_ttl; + deliver_info(req); + return; +failed: + req->res_af = 0; + req->res_list = NULL; + deliver_info(req); +} + +static bool impl_init(struct DNSContext *ctx) +{ + return evdns_init() == 0; +} + +static void impl_launch_query(struct DNSRequest *req) +{ + int err; + + err = evdns_resolve_ipv4(req->name, 0, got_result_evdns, req); + log_noise("dns(%s): evdns_resolve_ipv4 = %d", req->name, err); + if (err != 0 && !req->done) { + /* if callback was not yet called, do it now */ + req->done = true; + req->res_af = 0; + deliver_info(req); + } +} + +static void impl_release(struct DNSContext *ctx) +{ + evdns_shutdown(0); +} + +#endif + +/* + * Generic framework + */ + +static void deliver_info(struct DNSRequest *req) +{ + struct UserCallback *ucb; + struct List *el; + const uint8_t *res = req->res_list; + int adrlen = 0; + + if (req->res_af == AF_INET) + adrlen = 4; + else if (req->res_af == AF_INET6) + adrlen = 16; +loop: + /* get next req */ + el = list_pop(&req->ucb_list); + if (!el) + return; + ucb = container_of(el, struct UserCallback, node); + + /* launch callback */ + log_noise("dns: deliver_info(%s) type=%d pos=%d", req->name, req->res_af, req->res_pos); + ucb->cb_func(ucb->cb_arg, req->res_af, res + req->res_pos * adrlen); + + /* round-robin between results */ + if (req->res_count > 1) { + req->res_pos++; + if (req->res_pos >= req->res_count) + req->res_pos = 0; + } + + /* drop request */ + list_del(&ucb->node); + free(ucb); + + goto loop; +} + +static int req_cmp(long arg, struct AANode *node) +{ + const char *s1 = (char *)arg; + struct DNSRequest *req = container_of(node, struct DNSRequest, node); + return strcmp(s1, req->name); +} + +static void req_free(struct AANode *node, void *arg) +{ + struct UserCallback *ucb; + struct DNSRequest *req; + struct List *el; + req = container_of(node, struct DNSRequest, node); + while ((el = list_pop(&req->ucb_list)) != NULL) { + ucb = container_of(el, struct UserCallback, node); + free(ucb); + } + free(req->res_list); + free(req->name); + free(req); +} + +struct DNSContext *adns_create_context(void) +{ + struct DNSContext *ctx = calloc(1, sizeof(*ctx)); + + aatree_init(&ctx->req_tree, req_cmp, req_free); + if (!impl_init(ctx)) { + adns_free_context(ctx); + return NULL; + } + return ctx; +} + +void adns_free_context(struct DNSContext *ctx) +{ + if (ctx) { + impl_release(ctx); + aatree_destroy(&ctx->req_tree); + free(ctx); + } +} + +void adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *cb_arg) +{ + int namelen = strlen(name); + struct DNSRequest *req; + struct UserCallback *ucb; + struct AANode *node; + + /* setup actual lookup */ + node = aatree_search(&ctx->req_tree, (long)name); + if (node) { + req = container_of(node, struct DNSRequest, node); + } else { + log_debug("dns: new req: %s", name); + req = calloc(1, sizeof(*req)); + if (!req) + goto nomem; + req->ctx = ctx; + req->name = name; + req->namelen = namelen; + list_init(&req->ucb_list); + aatree_insert(&ctx->req_tree, (long)req->name, &req->node); + impl_launch_query(req); + } + + /* remember user callback */ + ucb = calloc(1, sizeof(*ucb)); + if (!ucb) + goto nomem; + list_init(&ucb->node); + ucb->cb_func = cb_func; + ucb->cb_arg = cb_arg; + list_append(&req->ucb_list, &ucb->node); + + /* if already have final result, report it */ + if (req->done) { + if (req->res_ttl < get_cached_time()) { + log_debug("dns: ttl over: %s", req->name); + req->done = false; + free(req->res_list); + req->res_list = NULL; + req->res_af = 0; + + impl_launch_query(req); + } else + deliver_info(req); + } + return; +nomem: + log_warning("dns(%s): req failed, no mem", name); + cb_func(cb_arg, 0, NULL); +} + + diff --git a/src/loader.c b/src/loader.c index 0f8ce67..d4d2f99 100644 --- a/src/loader.c +++ b/src/loader.c @@ -187,10 +187,8 @@ void parse_database(char *name, char *connstr) char *datestyle = NULL; char *timezone = NULL; char *connect_query = NULL; - char *unix_dir = ""; char *appname = NULL; - in_addr_t v_addr = INADDR_NONE; int v_port; if (strcmp(name, "*") == 0) { @@ -238,44 +236,6 @@ void parse_database(char *name, char *connstr) } } - /* host= */ - if (!host) { - /* default unix socket dir */ - if (!*cf_unix_socket_dir) { - log_error("skipping database %s because" - " unix socket not configured", name); - return; - } - } else if (host[0] == '/') { - /* custom unix socket dir */ - unix_dir = host; - host = NULL; - } else if (host[0] >= '0' && host[0] <= '9') { - /* ip-address */ - v_addr = inet_addr(host); - if (v_addr == INADDR_NONE) { - log_error("skipping database %s because" - " of bad host: %s", name, host); - return; - } - } else { - /* resolve host by name */ - struct hostent *h = gethostbyname(host); - if (h == NULL || h->h_addr_list[0] == NULL) { - log_error("%s: resolving host=%s failed: %s", - name, host, hstrerror(h_errno)); - return; - } - if (h->h_addrtype != AF_INET || h->h_length != 4) { - log_error("%s: host=%s has unknown addr type", - name, host); - return; - } - - /* result should be already in correct endianess */ - memcpy(&v_addr, h->h_addr_list[0], 4); - } - /* port= */ v_port = atoi(port); if (v_port == 0) { @@ -290,6 +250,23 @@ void parse_database(char *name, char *connstr) return; } + /* host= */ + if (host) { + host = strdup(host); + if (!host) { + log_error("failed to allocate host="); + return; + } + } + if (!host) { + /* default unix socket dir */ + if (!*cf_unix_socket_dir) { + log_error("skipping database %s because" + " unix socket not configured", name); + return; + } + } + /* tag the db as alive */ db->db_dead = 0; /* assuming not an autodb */ @@ -301,13 +278,11 @@ void parse_database(char *name, char *connstr) bool changed = false; if (strcmp(db->dbname, dbname) != 0) changed = true; - else if (host && db->addr.is_unix) + else if (!!host != !!db->host) changed = true; - else if (!host && !db->addr.is_unix) + else if (host && strcmp(host, db->host) != 0) changed = true; - else if (host && v_addr != db->addr.ip_addr.s_addr) - changed = true; - else if (v_port != db->addr.port) + else if (v_port != db->port) changed = true; else if (username && !db->forced_user) changed = true; @@ -315,8 +290,6 @@ void parse_database(char *name, char *connstr) changed = true; else if (!username && db->forced_user) changed = true; - else if (strcmp(db->unix_socket_dir, unix_dir) != 0) - changed = true; else if ((db->connect_query && !connect_query) || (!db->connect_query && connect_query) || (connect_query && strcmp(connect_query, db->connect_query) != 0)) @@ -329,13 +302,11 @@ void parse_database(char *name, char *connstr) /* if pool_size < 0 it will be set later */ db->pool_size = pool_size; db->res_pool_size = res_pool_size; - db->addr.port = v_port; - db->addr.ip_addr.s_addr = v_addr; - db->addr.is_unix = host ? 0 : 1; - safe_strcpy(db->unix_socket_dir, unix_dir, sizeof(db->unix_socket_dir)); - if (host) - log_debug("%s: host=%s/%s", name, host, inet_ntoa(db->addr.ip_addr)); + if (db->host) + free(db->host); + db->host = host; + db->port = v_port; /* assign connect_query */ set_connect_query(db, connect_query); diff --git a/src/main.c b/src/main.c index 4e10a09..59b2ee4 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,9 @@ static void usage(int err, char *exe) exit(err); } +/* async dns handler */ +struct DNSContext *adns; + /* * configuration storage */ @@ -103,6 +106,7 @@ char *cf_server_check_query = "select 1"; usec_t cf_server_check_delay = 30 * USEC; int cf_server_round_robin = 0; int cf_disable_pqexec = 0; +usec_t cf_dns_max_ttl = 15 * USEC; char *cf_ignore_startup_params = ""; @@ -180,6 +184,7 @@ ConfElem bouncer_params[] = { {"suspend_timeout", true, CF_TIME, &cf_suspend_timeout}, {"ignore_startup_parameters", true, CF_STR, &cf_ignore_startup_params}, {"disable_pqexec", false, CF_INT, &cf_disable_pqexec}, +{"dns_max_ttl", true, CF_TIME, &cf_dns_max_ttl}, {"pkt_buf", false, CF_INT, &cf_sbuf_len}, {"sbuf_loopcnt", true, CF_INT, &cf_sbuf_loopcnt}, @@ -649,6 +654,15 @@ static void takeover_part1(void) event_base_free(evtmp); } +static void dns_setup(void) +{ + if (adns) + return; + adns = adns_create_context(); + if (!adns) + fatal_perror("dns setup failed"); +} + /* boot everything */ int main(int argc, char *argv[]) { @@ -745,6 +759,7 @@ int main(int argc, char *argv[]) srandom(time(NULL) ^ getpid()); if (!event_init()) fatal("event_init() failed"); + dns_setup(); signal_setup(); janitor_setup(); stats_setup(); diff --git a/src/objects.c b/src/objects.c index 743ca57..de005ad 100644 --- a/src/objects.c +++ b/src/objects.c @@ -807,13 +807,104 @@ void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) log_noise("sbuf_close failed, retry later"); } +/* + * Connection creation utilities + */ + +static void connect_server(struct PgSocket *server, struct sockaddr *sa, int salen) +{ + bool res; + + /* 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 = in->sin_port; + server->remote_addr.ip_addr = in->sin_addr; + } + + /* start connecting */ + res = sbuf_connect(&server->sbuf, sa, salen, + cf_server_connect_timeout / USEC); + if (!res) + log_noise("failed to launch new connection"); +} + +static void dns_callback(void *arg, int af, const void *addr) +{ + struct PgSocket *server = arg; + struct PgDatabase *db = server->pool->db; + struct sockaddr_in sa_in; + struct sockaddr *sa; + int salen; + + if (af == AF_INET) { + char buf[64]; + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = af; + sa_in.sin_addr.s_addr = *(in_addr_t *)addr; + sa_in.sin_port = htons(db->port); + sa = (struct sockaddr *)&sa_in; + salen = sizeof(sa_in); + slog_debug(server, "dns_callback: inet4: %s", + sa2str(sa, buf, sizeof(buf))); + } else if (!af) { + disconnect_server(server, true, "server dns lookup failed"); + return; + } else { + disconnect_server(server, true, "unknown dns type"); + return; + } + + connect_server(server, sa, salen); +} + +static void dns_connect(struct PgSocket *server) +{ + struct sockaddr_un sa_un; + struct sockaddr_in sa_in; + struct sockaddr *sa; + int salen; + struct PgDatabase *db = server->pool->db; + const char *host = db->host; + const char *unix_dir; + int sa_len; + + if (!host || host[0] == '/') { + slog_noise(server, "unix socket: %s", sa_un.sun_path); + memset(&sa_un, 0, sizeof(sa_un)); + sa_un.sun_family = AF_UNIX; + unix_dir = host ? host : cf_unix_socket_dir; + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), + "%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') { + slog_noise(server, "inet socket: %s", db->host); + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_addr.s_addr = inet_addr(db->host); + sa_in.sin_port = htons(db->port); + sa = (struct sockaddr *)&sa_in; + sa_len = sizeof(sa_in); + } else { + slog_noise(server, "dns socket: %s", db->host); + /* launch dns lookup */ + adns_resolve(adns, db->host, dns_callback, server); + return; + } + + connect_server(server, sa, salen); +} + /* the pool needs new connection, if possible */ void launch_new_connection(PgPool *pool) { PgSocket *server; int total; - const char *unix_dir = cf_unix_socket_dir; - bool res; /* allow only small number of connection attempts at a time */ if (!statlist_empty(&pool->new_server_list)) { @@ -860,7 +951,6 @@ allow_new: /* initialize it */ server->pool = pool; server->auth_user = server->pool->user; - server->remote_addr = server->pool->db->addr; server->connect_time = get_cached_time(); pool->last_connect_time = get_cached_time(); change_server_state(server, SV_LOGIN); @@ -868,15 +958,7 @@ allow_new: if (cf_log_connections) slog_info(server, "new connection to server"); - /* override socket location if requested */ - if (server->pool->db->unix_socket_dir[0]) - unix_dir = server->pool->db->unix_socket_dir; - - /* start connecting */ - res = sbuf_connect(&server->sbuf, &server->remote_addr, unix_dir, - cf_server_connect_timeout / USEC); - if (!res) - log_noise("failed to launch new connection"); + dns_connect(server); } /* new client connection attempt */ diff --git a/src/sbuf.c b/src/sbuf.c index 370ceab..6f95023 100644 --- a/src/sbuf.c +++ b/src/sbuf.c @@ -88,7 +88,6 @@ bool sbuf_accept(SBuf *sbuf, int sock, bool is_unix) tune_socket(sock, is_unix); sbuf->sock = sock; - sbuf->is_unix = is_unix; if (!cf_reboot) { res = sbuf_wait_for_data(sbuf); @@ -107,55 +106,32 @@ bool sbuf_accept(SBuf *sbuf, int sock, bool is_unix) } /* need to connect() to get a socket */ -bool sbuf_connect(SBuf *sbuf, const PgAddr *addr, const char *unix_dir, int timeout_sec) +bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec) { - int res, sock, domain; - struct sockaddr_in sa_in; - struct sockaddr_un sa_un; - struct sockaddr *sa; - socklen_t len; + int res, sock; struct timeval timeout; + bool is_unix = sa->sa_family == AF_UNIX; Assert(iobuf_empty(sbuf->io) && sbuf->sock == 0); AssertSanity(sbuf); - /* prepare sockaddr */ - if (addr->is_unix) { - sa = (void*)&sa_un; - len = sizeof(sa_un); - memset(sa, 0, len); - sa_un.sun_family = AF_UNIX; - snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), - "%s/.s.PGSQL.%d", unix_dir, addr->port); - domain = AF_UNIX; - } else { - sa = (void*)&sa_in; - len = sizeof(sa_in); - memset(sa, 0, len); - sa_in.sin_family = AF_INET; - sa_in.sin_addr = addr->ip_addr; - sa_in.sin_port = htons(addr->port); - domain = AF_INET; - } - /* * common stuff */ - sock = socket(domain, SOCK_STREAM, 0); + sock = socket(sa->sa_family, SOCK_STREAM, 0); if (sock < 0) /* probably fd limit */ goto failed; - tune_socket(sock, addr->is_unix); + tune_socket(sock, is_unix); - sbuf->is_unix = addr->is_unix; sbuf->sock = sock; timeout.tv_sec = timeout_sec; timeout.tv_usec = 0; /* launch connection */ - res = safe_connect(sock, sa, len); + res = safe_connect(sock, sa, sa_len); if (res == 0) { /* unix socket gives connection immidiately */ sbuf_connect_cb(sock, EV_WRITE, sbuf);