chiark / gitweb /
Add adns support in background resolver.
[mLib] / bres-adns.c
diff --git a/bres-adns.c b/bres-adns.c
new file mode 100644 (file)
index 0000000..54e6708
--- /dev/null
@@ -0,0 +1,339 @@
+/* -*-c-*-
+ *
+ * $Id: bres-adns.c,v 1.1 2003/12/13 20:37:59 mdw Exp $
+ *
+ * Background reverse name resolution (ADNS version)
+ *
+ * (c) 2003 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ *
+ * HOWEVER, since GNU adns is covered by the full GNU General Public
+ * License, this file (only) is also covered by the full GNU GPL, and
+ * you may NOT take advantage of the more liberal conditions of the
+ * LGPL when modifying or redistributing this file.  This doesn't mean
+ * that a program which uses the interface provided by this file is
+ * covered by the GPL, since @bres.c@ provides the same interface and
+ * is LGPL.  However, it does mean that if your program depends, for
+ * some reason (e.g., to meet particular performance criteria), on
+ * this adns-based implementation of mLib's background resolver then it
+ * must be licensed under the full GPL.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: bres-adns.c,v $
+ * Revision 1.1  2003/12/13 20:37:59  mdw
+ * Add adns support in background resolver.
+ *
+ */
+
+#ifndef HAVE_ADNS
+#  error "You need the ADNS library to compile this file."
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <adns.h>
+
+#include "alloc.h"
+#include "bres.h"
+#include "macros.h"
+#include "report.h"
+#include "sel.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static adns_state ads;
+static sel_state *sel;
+static sel_hook selhook;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @bres_abort@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a queued job.
+ */
+
+void bres_abort(bres_client *rc)
+{
+  if (rc->q == adns_r_addr) xfree(rc->u.name);
+  if (rc->a) free(rc->a);
+  adns_cancel(rc->aq);
+}
+
+/* --- @bres_byaddr@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *             @struct in_addr addr@ = address to resolve
+ *             @void (*func)(struct hostent *h, void *p)@ = handler function
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds an address lookup job to the queue.  The job will be
+ *             processed when there's a spare resolver process to deal with
+ *             it.
+ */
+
+void bres_byaddr(bres_client *rc, struct in_addr addr,
+                void (*func)(struct hostent */*h*/, void */*p*/),
+                void *p)
+{
+  int e;
+  struct sockaddr_in sin;
+
+  if (!ads) goto fail;
+  sin.sin_family = AF_INET;
+  sin.sin_addr = addr;
+  sin.sin_port = 0;
+  if ((e = adns_submit_reverse(ads, (struct sockaddr *)&sin, adns_r_ptr,
+                              0, rc, &rc->aq)) != 0)
+    goto fail;
+  rc->a = 0;
+  rc->q = adns_r_ptr;
+  rc->u.addr = addr;
+  rc->func = func;
+  rc->p = p;
+  return;
+
+fail:
+  func(0, p);
+}
+
+/* --- @bres_byname@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *             @const char *name@ = name to resolve
+ *             @void (*func)(struct hostent *h, void *p)@ = handler function
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds a name lookup job to the queue.  The job will be
+ *             processed when there's a spare resolver process to deal with
+ *             it.
+ */
+
+void bres_byname(bres_client *rc, const char *name,
+                void (*func)(struct hostent */*h*/, void */*p*/),
+                void *p)
+{
+  int e;
+
+  if (!ads) goto fail;
+  if ((e = adns_submit(ads, name, adns_r_addr,
+                      adns_qf_search, rc, &rc->aq)) != 0)
+    goto fail;
+  rc->a = 0;
+  rc->q = adns_r_addr;
+  rc->u.name = xstrdup(name);
+  rc->func = func;
+  rc->p = p;
+  return;
+
+fail:
+  func(0, p);
+}
+
+/* --- @bres_exec@ --- *
+ *
+ * Arguments:  @const char *file@ = file containing server code or null
+ *
+ * Returns:    ---
+ *
+ * Use:                Makes `bres' use a standalone server rather than copies of
+ *             the current process.  This can reduce memory consumption for
+ *             large processes, at the expense of startup time (which
+ *             shouldn't be too bad anyway, because of the resolver design).
+ *             If the filename is null, a default set up at install time is
+ *             used.  It's probably a good idea to leave it alone.
+ */
+
+void bres_exec(const char *file)
+{
+  /* Nothin' doin' */
+}
+
+/* --- @report@ --- *
+ *
+ * Arguments:  @bres_client *c@ = client descriptor block
+ *             @adns_answer *a@ = A-record answer from resolver library
+ *             @adns_rr_addr *av@ = vector of address records
+ *             @int an@ = number of address records (must be positive)
+ *             @char **nv@ = vector of name strings
+ *             @int nn@ = number of name strings (must be positive)
+ *
+ * Returns:    ---
+ *
+ * Use:                Formats the given answer into a @struct hostent@ and reports
+ *             it to the waiting client application.
+ */
+
+static void report(bres_client *rc, adns_answer *a,
+                  adns_rr_addr *av, int an,
+                  char **nv, int nn)
+{
+  struct hostent h;
+  char *n[16];
+  char *aa[16];
+  int i, j;
+
+  j = 0;
+  if (a->cname) n[j++] = a->cname;
+  else { n[j++] = *nv; nv++; nn--; }
+  for (i = 0; i < nn && j < N(n) - 1; i++)
+    if (strcmp(n[0], nv[i]) != 0) n[j++] = nv[i];
+  n[j++] = 0;
+  for (i = j = 0; i < an && j < N(av) - 1; i++) {
+    if (av[i].addr.sa.sa_family == AF_INET)
+      aa[j++] = (char *)&av[i].addr.inet.sin_addr;
+  }
+  aa[j++] = 0;
+  h.h_name = n[0];
+  h.h_aliases = n + 1;
+  h.h_addrtype = AF_INET;
+  h.h_length = sizeof(struct in_addr);
+  h.h_addr_list = aa;
+  rc->func(&h, rc->p);  
+}
+
+/* --- @beforehook@, @afterhook@ --- *
+ *
+ * Arguments:  @sel_state *s@ = select state
+ *             @sel_args *sa@ = argument block
+ *             @void *p@ = uninteresting pointer
+ *
+ * Returns:    ---
+ *
+ * Use:                Processes the selector's arguments before @select@ is
+ *             called, to allow ADNS to do its thing.
+ */
+
+static void beforehook(sel_state *s, sel_args *sa, void *p)
+{
+  adns_beforeselect(ads, &sa->maxfd,
+                   &sa->fd[SEL_READ], &sa->fd[SEL_WRITE], &sa->fd[SEL_EXC],
+                   &sa->tvp, &sa->tv, &sa->now);
+}
+
+static void afterhook(sel_state *s, sel_args *sa, void *p)
+{
+  void *c;
+  bres_client *rc;
+  adns_query q;
+  adns_answer *a;
+  int e;
+  int i;
+
+  adns_afterselect(ads, sa->maxfd,
+                  &sa->fd[SEL_READ], &sa->fd[SEL_WRITE], &sa->fd[SEL_EXC],
+                  &sa->now);
+  while (q = 0, (e = adns_check(ads, &q, &a, &c)) == 0) {
+    rc = c;
+    if (a->status != 0)
+      goto fail;
+    else switch (rc->q) {
+      case adns_r_addr:
+       assert(a->type == adns_r_addr);
+       report(rc, a, a->rrs.addr, a->nrrs, &rc->u.name, 1);
+       free(rc->u.name);
+       free(a);
+       break;
+      case adns_r_ptr:
+       if (a->type == adns_r_ptr) {
+         rc->a = a;
+         if ((e = adns_submit(ads, a->rrs.str[0], adns_r_addr,
+                              0, rc, &q)) != 0)
+           goto fail;
+         rc->aq = q;
+       } else {
+         assert(a->type == adns_r_addr);
+         for (i = 0; i < a->nrrs; i++) {
+           if (a->rrs.addr[i].addr.sa.sa_family == AF_INET &&
+               a->rrs.addr[i].addr.inet.sin_addr.s_addr ==
+                 rc->u.addr.s_addr)
+             goto match;
+         }
+         goto fail;
+       match:
+         report(rc, a, &a->rrs.addr[i], 1, rc->a->rrs.str, rc->a->nrrs);
+         free(rc->a);
+         free(a);
+       }
+       break;
+      default:
+       abort();
+    }
+    continue;
+
+  fail:
+    rc->func(0, rc->p);
+    if (rc->q == adns_r_addr) xfree(rc->u.name);
+    if (rc->a) free(rc->a);
+    free(a);
+  }
+}
+
+/* --- @bres_init@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to select multiplexor
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the background resolver for use.
+ */
+
+void bres_init(sel_state *s)
+{
+  int e;
+
+  if ((e = adns_init(&ads, 0, 0)) != 0) {
+    moan("adns_init failed: resolver won't work");
+    return;
+  }
+  sel_addhook(s, &selhook, beforehook, afterhook, 0);
+  sel = s;
+}
+
+/*----- That's all, folks -------------------------------------------------*/