]> granicus.if.org Git - pgbouncer/commitdiff
dns reorg: use struct addrinfo as main storage unit
authorMarko Kreen <markokr@gmail.com>
Wed, 13 Oct 2010 15:38:14 +0000 (18:38 +0300)
committerMarko Kreen <markokr@gmail.com>
Wed, 13 Oct 2010 15:38:14 +0000 (18:38 +0300)
configure.ac
include/dnslookup.h
lib
src/dnslookup.c
src/objects.c

index a4a07f03a3c72074a90bd47257e9bd4cbdf2490a..df7f4f4b8695e9a0d5cffdfe6cdd14db057889bb 100644 (file)
@@ -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)
 
index 270afa222209ce8bd6be703f123c3c9d5c1f13e4..3581794e1052586921f439dff65961f531f88cef 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <usual/base.h>
+#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 cff57c8f076ee0919f0537a7bd2e32781ceb721a..ab07cc3188da8ba1a449fde831741a00bb329bef 160000 (submodule)
--- a/lib
+++ b/lib
@@ -1 +1 @@
-Subproject commit cff57c8f076ee0919f0537a7bd2e32781ceb721a
+Subproject commit ab07cc3188da8ba1a449fde831741a00bb329bef
index 178ccbd7655876a077d7e50e8bd75224144ea470..1bca94f73422cc20d574017c778d560ebc56f96b 100644 (file)
  * libevent2 - does not return TTL, uses hosts file.
  */
 
-#ifdef HAVE_GETADDRINFO_A
-
+#ifdef USE_GETADDRINFO_A
 /* getaddrinfo_a */
 #include <netdb.h>
 #include <signal.h>
-#define NEED_GAI_RESULT
-
-#else
-#ifdef EV_ET
+#endif
 
-/* libevent 2 */
+#ifdef USE_LIBEVENT2
 #include <event2/dns.h>
-#define LIBEVENT2
 #define addrinfo evutil_addrinfo
 #define freeaddrinfo evutil_freeaddrinfo
-#define NEED_GAI_RESULT
-
-#else
+#endif
 
+#ifdef USE_LIBEVENT1
 /* libevent 1 */
 #include <evdns.h>
-
-#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 <event2/dns.h>
@@ -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 <evdns.h>
  */
 
-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
-
index e6adb0be83153c2cadf21993574737eb1122b226..9f23c27198ad0972139e8bd2ab2e789ede8d488a 100644 (file)
@@ -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;