From: Marko Kreen Date: Wed, 13 Oct 2010 15:38:14 +0000 (+0300) Subject: dns reorg: use struct addrinfo as main storage unit X-Git-Tag: pgbouncer_1_4_rc3~21 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5c32d3878d42cf44ac04561ddfe2ef556d30de50;p=pgbouncer dns reorg: use struct addrinfo as main storage unit --- diff --git a/configure.ac b/configure.ac index a4a07f0..df7f4f4 100644 --- a/configure.ac +++ b/configure.ac @@ -45,7 +45,7 @@ AC_CHECK_TOOL([STRIP], [strip]) dnl Checks for header files. AC_USUAL_HEADER_CHECK -AC_CHECK_HEADERS([crypt.h regex.h]) +AC_CHECK_HEADERS([crypt.h]) AC_CHECK_HEADERS([sys/resource.h sys/wait.h]) dnl Checks for typedefs, structures, and compiler characteristics. @@ -72,9 +72,6 @@ AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(hstrerror, resolv) AC_CHECK_FUNCS(crypt lstat) -AC_SEARCH_LIBS(getaddrinfo_a, anl) -AC_CHECK_FUNCS(getaddrinfo_a) - dnl Find libevent AC_USUAL_LIBEVENT(1) diff --git a/include/dnslookup.h b/include/dnslookup.h index 270afa2..3581794 100644 --- a/include/dnslookup.h +++ b/include/dnslookup.h @@ -16,11 +16,27 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#if 1 + +/* pick dns implementation */ +#ifdef EV_ET +#define USE_LIBEVENT2 +#else +#ifdef HAVE_GETADDRINFO_A +#define USE_GETADDRINFO_A +#else +#define USE_LIBEVENT1 +#endif +#endif + +#else +#define USE_LIBEVENT2 +#endif + struct DNSContext; -typedef void (*adns_callback_f)(void *arg, int af, const void *addr); +typedef void (*adns_callback_f)(void *arg, const struct sockaddr *ai, int salen); struct DNSContext *adns_create_context(void); void adns_reload(struct DNSContext *ctx); diff --git a/lib b/lib index cff57c8..ab07cc3 160000 --- a/lib +++ b/lib @@ -1 +1 @@ -Subproject commit cff57c8f076ee0919f0537a7bd2e32781ceb721a +Subproject commit ab07cc3188da8ba1a449fde831741a00bb329bef diff --git a/src/dnslookup.c b/src/dnslookup.c index 178ccbd..1bca94f 100644 --- a/src/dnslookup.c +++ b/src/dnslookup.c @@ -24,29 +24,21 @@ * libevent2 - does not return TTL, uses hosts file. */ -#ifdef HAVE_GETADDRINFO_A - +#ifdef USE_GETADDRINFO_A /* getaddrinfo_a */ #include #include -#define NEED_GAI_RESULT - -#else -#ifdef EV_ET +#endif -/* libevent 2 */ +#ifdef USE_LIBEVENT2 #include -#define LIBEVENT2 #define addrinfo evutil_addrinfo #define freeaddrinfo evutil_freeaddrinfo -#define NEED_GAI_RESULT - -#else +#endif +#ifdef USE_LIBEVENT1 /* libevent 1 */ #include - -#endif #endif @@ -67,10 +59,9 @@ struct DNSRequest { bool done; - int res_af; - int res_count; - int res_pos; - void *res_list; + struct addrinfo *result; + struct addrinfo *current; + usec_t res_ttl; }; @@ -81,13 +72,10 @@ struct DNSContext { static void deliver_info(struct DNSRequest *req); -#ifdef NEED_GAI_RESULT -struct addrinfo; static void got_result_gai(int result, struct addrinfo *res, void *arg); -#endif -#ifdef HAVE_GETADDRINFO_A +#ifdef USE_GETADDRINFO_A /* * ADNS with glibc's getaddrinfo_a() @@ -174,7 +162,6 @@ failed: free(grq); failed2: req->done = true; - req->res_af = 0; deliver_info(req); } @@ -188,9 +175,9 @@ static void impl_release(struct DNSContext *ctx) } } -#else /* !HAVE_GETADDRINFO_A */ +#endif /* USE_GETADDRINFO_A */ -#ifdef LIBEVENT2 +#ifdef USE_LIBEVENT2 /* * ADNS with libevent2 @@ -220,47 +207,79 @@ static void impl_release(struct DNSContext *ctx) evdns_base_free(dns, 0); } -#else +#endif + +#ifdef USE_LIBEVENT1 /* * ADNS with libevent 1.x */ -static void got_result_evdns(int result, char type, int count, int ttl, void *addresses, void *arg) +static struct addrinfo *mk_addrinfo(void *ip) { - struct DNSRequest *req = arg; - int adrlen = 4; + struct addrinfo *ai; + struct sockaddr_in *sa; + ai = calloc(1, sizeof(*ai)); + if (!ai) + return NULL; + sa = calloc(1, sizeof(*sa)); + if (!sa) { + free(ai); + return NULL; + } + memcpy(&sa->sin_addr, ip, 4); + sa->sin_family = AF_INET; + ai->ai_addr = (struct sockaddr *)sa; + ai->ai_addrlen = sizeof(*sa); + return ai; +} - log_noise("dns: got_result_evdns: type=%d cnt=%d ttl=%d", type, count, ttl); +#define freeaddrinfo(x) local_freeaddrinfo(x) - req->done = true; +static void freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *cur; + while (ai) { + cur = ai; + ai = ai->ai_next; + free(cur->ai_addr); + free(cur); + } +} - if (result != DNS_ERR_NONE || count < 1) { - /* lookup failed */ - goto failed; - } else if (type == DNS_IPv4_A) { - struct in_addr *a = addresses; - log_noise("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; +static struct addrinfo *convert_ipv4_result(uint8_t *adrs, int count) +{ + struct addrinfo *ai, *last = NULL; + int i; + + for (i = count - 1; i >= 0; i--) { + ai = mk_addrinfo(adrs + i * 4); + if (!ai) + goto failed; + ai->ai_next = last; + last = ai; } - 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; + return last; failed: - req->res_af = 0; - req->res_list = NULL; - deliver_info(req); + freeaddrinfo(last); + return NULL; +} + +static void got_result_evdns(int result, char type, int count, int ttl, void *addresses, void *arg) +{ + struct DNSRequest *req = arg; + struct addrinfo *ai; + + log_noise("dns: got_result_evdns: type=%d cnt=%d ttl=%d", type, count, ttl); + if (result == DNS_IPv4_A) { + ai = convert_ipv4_result(addresses, count); + if (ai) { + got_result_gai(0, ai, req); + return; + } + } + /* lookup failed */ + got_result_gai(1, NULL, req); } static bool impl_init(struct DNSContext *ctx) @@ -276,9 +295,7 @@ static void impl_launch_query(struct DNSRequest *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); + got_result_gai(1, NULL, req); } } @@ -287,7 +304,6 @@ static void impl_release(struct DNSContext *ctx) evdns_shutdown(0); } -#endif #endif /* @@ -298,13 +314,9 @@ static void deliver_info(struct DNSRequest *req) { struct UserCallback *ucb; struct List *el; - const uint8_t *res = req->res_list; - int adrlen = 0; + const struct addrinfo *ai = req->current; + char sabuf[128]; - 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); @@ -313,15 +325,16 @@ loop: 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); + log_noise("dns: deliver_info(%s) addr=%s", req->name, + sa2str(ai->ai_addr, sabuf, sizeof(sabuf))); + ucb->cb_func(ucb->cb_arg, ai->ai_addr, ai->ai_addrlen); free(ucb); - /* round-robin between results */ - if (req->res_count > 1) { - req->res_pos++; - if (req->res_pos >= req->res_count) - req->res_pos = 0; + /* scroll req list */ + if (ai) { + req->current = ai->ai_next; + if (!req->current) + req->current = req->result; } goto loop; @@ -334,6 +347,14 @@ static int req_cmp(long arg, struct AANode *node) return strcmp(s1, req->name); } +static void req_reset(struct DNSRequest *req) +{ + req->done = false; + if (req->result) + freeaddrinfo(req->result); + req->result = req->current = NULL; +} + static void req_free(struct AANode *node, void *arg) { struct UserCallback *ucb; @@ -344,7 +365,7 @@ static void req_free(struct AANode *node, void *arg) ucb = container_of(el, struct UserCallback, node); free(ucb); } - free(req->res_list); + req_reset(req); free(req->name); free(req); } @@ -413,11 +434,7 @@ void adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_f if (req->done) { if (req->res_ttl < get_cached_time()) { log_noise("dns: ttl over: %s", req->name); - req->done = false; - free(req->res_list); - req->res_list = NULL; - req->res_af = 0; - + req_reset(req); impl_launch_query(req); } else deliver_info(req); @@ -425,81 +442,27 @@ void adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_f return; nomem: log_warning("dns(%s): req failed, no mem", name); - cb_func(cb_arg, 0, NULL); + cb_func(cb_arg, NULL, 0); } -#ifdef NEED_GAI_RESULT - /* struct addrinfo -> deliver_info() */ static void got_result_gai(int result, struct addrinfo *res, void *arg) { struct DNSRequest *req = arg; - struct addrinfo *ai; - int count = 0; - int af = 0; - int adrlen; - uint8_t *dst; - if (result != 0) { + req_reset(req); + + if (result == 0) { + req->result = res; + req->current = res; + } else { /* 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); - freeaddrinfo(res); - goto failed; - } - - log_noise("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_noise("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->done = true; - req->res_af = 0; - req->res_list = NULL; deliver_info(req); } -#endif - diff --git a/src/objects.c b/src/objects.c index e6adb0b..9f23c27 100644 --- a/src/objects.c +++ b/src/objects.c @@ -811,7 +811,7 @@ void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) * Connection creation utilities */ -static void connect_server(struct PgSocket *server, struct sockaddr *sa, int salen) +static void connect_server(struct PgSocket *server, const struct sockaddr *sa, int salen) { bool res; @@ -836,27 +836,23 @@ static void connect_server(struct PgSocket *server, struct sockaddr *sa, int sal log_noise("failed to launch new connection"); } -static void dns_callback(void *arg, int af, const void *addr) +static void dns_callback(void *arg, const struct sockaddr *sa, int salen) { struct PgSocket *server = arg; struct PgDatabase *db = server->pool->db; struct sockaddr_in sa_in; - struct sockaddr *sa; - int salen; - if (af == AF_INET) { + if (!sa) { + disconnect_server(server, true, "server dns lookup failed"); + return; + } else if (sa->sa_family == 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; + memcpy(&sa_in, sa, sizeof(sa_in)); 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;