]> granicus.if.org Git - pgbouncer/commitdiff
Another try at async DNS - getaddrinfo_a()
authorMarko Kreen <markokr@gmail.com>
Tue, 28 Sep 2010 18:05:20 +0000 (11:05 -0700)
committerMarko Kreen <markokr@gmail.com>
Tue, 28 Sep 2010 18:05:20 +0000 (11:05 -0700)
The libevent's evdns seems not ready for serious use:

- proper random req id is available only in 2.0.x
- it does not use random source port for different reqs
- it does not check if req id conflicts with another one

configure.ac
src/dnslookup.c

index e8ff7cbcee9275075183abf6bb607ff2352607d5..a4a07f03a3c72074a90bd47257e9bd4cbdf2490a 100644 (file)
@@ -72,6 +72,9 @@ 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 854175a29fd2a21c64fdc120d23555a52c66feb3..3ab9bb55ce07e75a546549ffe801d9be9f45940f 100644 (file)
 #include "bouncer.h"
 
 /*
+ * getaddrinfo_a - glibc only
  * libevent1 - returns TTL, ignores hosts file.
  * libevent2 - does not return TTL, uses hosts file.
  */
 
-/* do we have libevent2? */
+#undef HAVE_GETADDRINFO_A
+
+#ifdef HAVE_GETADDRINFO_A
+
+/* getaddrinfo_a */
+#include <netdb.h>
+#include <signal.h>
+#define NEED_GAI_RESULT
+
+#else
 #ifdef EV_ET
-#define LIBEVENT2
-#endif
 
-#ifdef LIBEVENT2
+/* libevent 2 */
 #include <event2/dns.h>
+#define LIBEVENT2
+#define addrinfo evutil_addrinfo
+#define freeaddrinfo evutil_freeaddrinfo
+#define NEED_GAI_RESULT
+
 #else
+
+/* libevent 1 */
 #include <evdns.h>
+
+#endif
 #endif
 
 
@@ -66,82 +83,121 @@ 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 LIBEVENT2
+
+#ifdef HAVE_GETADDRINFO_A
 
 /*
- * ADNS with libevent2 <event2/dns.h>
+ * ADNS with glibc's getaddrinfo_a()
  */
 
-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;
+struct GaiRequest {
+       struct List node;
+       struct DNSRequest *req;
+       struct gaicb gairq;
+};
 
-       if (result != DNS_ERR_NONE) {
-               /* lookup failed */
-               log_warning("lookup failed: %s: result=%d", req->name, result);
-               goto failed;
-       }
+struct GaiContext {
+       struct DNSContext *ctx;
+       struct List gairq_list;
+       struct event ev;
+       struct sigevent sev;
+};
 
-       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)
+static void dns_signal(int f, short ev, void *arg)
+{
+       struct GaiContext *gctx = arg;
+       struct List *el, *tmp;
+       struct GaiRequest *rq;
+       int e;
+       list_for_each_safe(el, &gctx->gairq_list, tmp) {
+               rq = container_of(el, struct GaiRequest, node);
+               e = gai_error(&rq->gairq);
+               if (e == EAI_INPROGRESS)
                        continue;
-               count++;
-       }
 
-       /* did not found usable entry */
-       if (!af) {
-               log_warning("dns(%s): no usable address", req->name);
-               evutil_freeaddrinfo(res);
-               goto failed;
+               /* got one */
+               list_del(&rq->node);
+               rq->req->done = true;
+               got_result_gai(e, rq->gairq.ar_result, rq->req);
+               free(rq);
        }
+}
 
-       log_noise("dns(%s): got_result_gai: count=%d, adrlen=%d", req->name, count, adrlen);
+static bool impl_init(struct DNSContext *ctx)
+{
+       struct GaiContext *gctx = calloc(1, sizeof(*gctx));
+       if (!gctx)
+               return false;
+       list_init(&gctx->gairq_list);
+       gctx->ctx = ctx;
 
-       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;
+       gctx->sev.sigev_notify = SIGEV_SIGNAL;
+       gctx->sev.sigev_signo = SIGALRM;
 
-       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;
+       signal_set(&gctx->ev, SIGALRM, dns_signal, gctx);
+       if (signal_add(&gctx->ev, NULL) < 0) {
+               free(gctx);
+               return false;
        }
+       ctx->edns = gctx;
+       return true;
+}
 
-       deliver_info(req);
+static void impl_launch_query(struct DNSRequest *req)
+{
+       struct GaiContext *gctx = req->ctx->edns;
+       struct GaiRequest *grq = calloc(1, sizeof(*grq));
+       int res;
+       struct gaicb *cb;
+
+       grq = calloc(1, sizeof(*grq));
+       if (!grq)
+               goto failed2;
+
+       list_init(&grq->node);
+       grq->req = req;
+       grq->gairq.ar_name = req->name;
+       list_append(&gctx->gairq_list, &grq->node);
+
+       cb = &grq->gairq;
+       res = getaddrinfo_a(GAI_NOWAIT, &cb, 1, &gctx->sev);
+       if (res != 0)
+               goto failed;
        return;
+
 failed:
+       log_warning("dns: getaddrinfo_a(%s)=%d", req->name, res);
+       list_del(&grq->node);
+       free(grq);
+failed2:
+       req->done = true;
        req->res_af = 0;
-       req->res_list = NULL;
        deliver_info(req);
 }
 
+static void impl_release(struct DNSContext *ctx)
+{
+       struct GaiContext *gctx = ctx->edns;
+       if (gctx) {
+               signal_del(&gctx->ev);
+               free(gctx);
+               ctx->edns = NULL;
+       }
+}
+
+#else /* !HAVE_GETADDRINFO_A */
+
+#ifdef LIBEVENT2
+
+/*
+ * ADNS with libevent2 <event2/dns.h>
+ */
+
 static bool impl_init(struct DNSContext *ctx)
 {
        ctx->edns = evdns_base_new(NULL, 1);
@@ -233,6 +289,7 @@ static void impl_release(struct DNSContext *ctx)
        evdns_shutdown(0);
 }
 
+#endif
 #endif
 
 /*
@@ -300,6 +357,8 @@ static void req_free(struct AANode *node, void *arg)
 struct DNSContext *adns_create_context(void)
 {
        struct DNSContext *ctx = calloc(1, sizeof(*ctx));
+       if (!ctx)
+               return NULL;
 
        aatree_init(&ctx->req_tree, req_cmp, req_free);
        if (!impl_init(ctx)) {
@@ -370,4 +429,78 @@ nomem:
        cb_func(cb_arg, 0, NULL);
 }
 
+#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) {
+               /* 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