Logo Search packages:      
Sourcecode: ldapdns version File versions  Download package

engine.c

#include "error.h"
#include "config.h"
#include "version.h"
#include "ldapdns.h"
#include "env.h"
#include "ip.h"
#include "dns.h"

#include "supervise.h"
#include "profile.h"

#include <stdio.h>
#include <sys/stat.h>

#ifndef LDAP_PORT
#define LDAP_PORT 389
#endif

static void inline add_peer_ns(dns_ctx *c, int inc);

static int default_refresh    = 10800;
static int default_retry      = 7200;
static int default_expire     = 604800;
static int default_minimum    = 86400;

static list_t other_threads = 0;
pthread_mutex_t handler_lock;
pthread_mutex_t log_lock;
pthread_mutex_t host_lock;
list_t host_lp;
config_t ldapdns;
ldap_ctx *ldap_thread;
dns_ctx *handler;

static int engine_message = 0;
static pthread_cond_t engine_pause_cond;
static pthread_mutex_t engine_message_mutex;
static pthread_t eph;

static void handle_messages(void)
{
      pthread_mutex_lock(&engine_message_mutex);
      switch (engine_message) {
      case 0: /* do nothing */
            break;
      case 1: /* paused */
            while (engine_message) {
                  pthread_cond_wait(&engine_pause_cond,
                              &engine_message_mutex);
                  if (engine_message == 2)
                        break;
            }
            if (engine_message == 0)
                  break;
      case 2: /* please exit */
            pthread_mutex_unlock(&engine_message_mutex);
            pthread_exit(0);
            exit(0);
            break;
      };
      pthread_mutex_unlock(&engine_message_mutex);
}
static void kill_threads(void)
{
      ldap_ctx *o;
      pthread_t *x;
      int i;

#ifdef HAVE_pthread_kill_other_threads_np
      pthread_kill_other_threads_np();
      return;
#endif

      for (i = 0; i < ldapdns.ldap_threads; i++) {
            o = &ldap_thread[i];
            if (o->id != pthread_self())
                  pthread_cancel(o->id);
      }
      while ((x = (pthread_t *)list_pop(&other_threads)) != 0) {
            pthread_cancel(*x);
            mem_free(x);
      }
}
static void handle_signal(int signo)
{
      if (pthread_self() != eph) {
            /* we are NOT the main thread */
            switch (signo) {
            case SIGTERM:
            case SIGINT:
                  pthread_exit(0);
            };
            return;

      }

      switch (signo) {
      case SIGSTOP:
            if (engine_message == 0) {
                  pthread_mutex_lock(&engine_message_mutex);
                  engine_message = 1;
                  pthread_mutex_unlock(&engine_message_mutex);
                  pthread_mutex_lock(&log_lock);
                  log(log_info, "pausing");
                  pthread_mutex_unlock(&log_lock);
            }
            break;
      case SIGCONT:
            if (engine_message == 1) {
                  pthread_mutex_lock(&engine_message_mutex);
                  engine_message = 0;
                  pthread_cond_broadcast(&engine_pause_cond);
                  pthread_mutex_unlock(&engine_message_mutex);
                  pthread_mutex_lock(&log_lock);
                  log(log_info, "resuming");
                  pthread_mutex_unlock(&log_lock);
            }
            break;
      case SIGTERM:
      case SIGINT:
            pthread_mutex_lock(&log_lock);
            log(log_info, "shutting down");
            pthread_mutex_unlock(&log_lock);
            pthread_mutex_lock(&engine_message_mutex);
            if (engine_message == 1) {
                  pthread_cond_broadcast(&engine_pause_cond);
            }
            engine_message = 2;
            pthread_mutex_unlock(&engine_message_mutex);
            kill_threads();
            /* ask the parent to die nicely */
            if (getppid() != 1)
                  kill(getppid(), SIGTERM);
            pthread_exit(0);
            exit(0);
            break;
      case SIGHUP:
            /* unused signals (for now) */
            break;
      };
}
static void inline cleanup_lists(dns_ctx *c, int ex)
{
      char *x;
#define cleanup_l(LL) if (c->LL) while ((x = list_pop(&c->LL))) mem_free(x)
      if (ex & 1) cleanup_l(subreq_tries);
      if (ex & 1) cleanup_l(subreq_done);
      if (ex & 2) {
            cleanup_l(NS);
            mem_free(c->search_base);
            c->search_base = 0;
            c->adlen = -1;
      }
      cleanup_l(DNSRecord);
      cleanup_l(A);
      cleanup_l(CNAME);
      cleanup_l(ADM);
      cleanup_l(MX);
      cleanup_l(SRV);
      cleanup_l(TXT);
      cleanup_l(PTR);
      cleanup_l(Generic);
#undef cleanup_l
}

static int start_ldap_connection(ldap_ctx *o, char *hostnamestr)
{
      int r;
      char *x;
      int port;
#ifdef LDAP_OPT_PROTOCOL_VERSION
      int version;
#endif

      x = strchr(hostnamestr, ':');
      if (!x || (*(x+1) && *(x+1) == '/')) {
            port = LDAP_PORT;
      } else {
            port = atoi(x+1);
            *x = 0;
      }

      o->message_sent = 0;
      o->message_wait = 0; // XXX
      if (ldap_is_ldap_url(hostnamestr)) {
            ldap_initialize(&o->ldap_con, hostnamestr);
      } else {
            o->ldap_con = ldap_init(hostnamestr, port);
            if (x) *x = ':'; /* egad */
      }

      if (!o->ldap_con)
            return -1;

#ifdef LDAP_OPT_PROTOCOL_VERSION
      o->protocol_version = version = 3;
      if (ldap_set_option(o->ldap_con, LDAP_OPT_PROTOCOL_VERSION, &version)
                  != LDAP_SUCCESS) {

            o->protocol_version = version = 2;
            if (ldap_set_option(o->ldap_con, LDAP_OPT_PROTOCOL_VERSION,
                              &version) != LDAP_SUCCESS) {
                  ldap_unbind(o->ldap_con);
                  o->ldap_con = 0;
            }

            return -1;
      }
#else
      o->protocol_version = 2;
#endif

#ifdef ACCELERATE_CACHE
      if (ldapdns.accelerate_cache)
            ldap_enable_cache(o->ldap_con, ldapdns.accelerate_cache, 0);
#endif

      if (ldapdns.auth_mode == AUTH_MODE_SASL) {
            /* to be tested; prefer the DSA */
            r = ldap_bind_s(o->ldap_con,
                        ldapdns.ldap_name,
                        ldapdns.ldap_cred,
                        LDAP_AUTH_KRBV42);
            if (r != LDAP_SUCCESS) {
                  /* then try kerberos against LDAP */
                  r = ldap_bind_s(o->ldap_con,
                              ldapdns.ldap_name,
                              ldapdns.ldap_cred,
                              LDAP_AUTH_KRBV41);
            }
      } else if (ldapdns.auth_mode == AUTH_MODE_SIMPLE) {
            r = ldap_simple_bind_s(o->ldap_con,
                        ldapdns.ldap_name,
                        ldapdns.ldap_cred);
      } else if (ldapdns.auth_mode == AUTH_MODE_ANONYMOUS) {
            r = ldap_simple_bind_s(o->ldap_con, "", "");
      }

      if (r == LDAP_SUCCESS) {
            return 1;
      }

      /* no need to keep synchronous here */
      ldap_unbind(o->ldap_con);
      o->ldap_con = 0;
      return -1;
      
}
static void complete_phase(dns_ctx *c, int flag);
static void restart_ldap_connection(ldap_ctx *o)
{
      list_t lp;
      dns_ctx *x;

      pthread_mutex_lock(&handler_lock);
      x = handler;
      pthread_mutex_unlock(&handler_lock);
      for (; x; x = x->next) {
            if (x->c == o) {
                  pthread_mutex_lock(&log_lock);
                  warning("handler %d being closed (ldap went away)", x->n);
                  pthread_mutex_unlock(&log_lock);

                  complete_phase(x, '?');
                  x->c = 0; /* give it up */
                  x->phase = PHASE_IDLE;
            }
      }

      /* hopefully this will only be called by LDAP_SERVERDOWN */
      if (o->ldap_con)
            ldap_unbind(o->ldap_con);
      o->ldap_con = 0;

      pthread_mutex_lock(&log_lock);
      warning("handler %d was hung up on, restarting", o->n);
      pthread_mutex_unlock(&log_lock);

      pthread_mutex_lock(&host_lock);
top_restart_l:
      for (lp = host_lp; lp; lp = lp->next) {
            if (start_ldap_connection(o, lp->str) == 1)
                  goto done_restart_l;
      }
      for (lp = ldapdns.hosts; lp; lp = lp->next) {
            if (start_ldap_connection(o, lp->str) == 1)
                  goto done_restart_l;
      }
      goto top_restart_l;
done_restart_l:
      host_lp = lp;
      pthread_mutex_unlock(&host_lock);
}
static void complete_phase(dns_ctx *c, int flag)
{
      str_t out;
      char *q;
      char id[2];

      /* it is possible for these to be uninitialized here */
      if (c->response && caddr(c->response) && clen(c->response))
            tp_write(c);

      /* dynamic */
      if (c->axfr_base) mem_free(c->axfr_base);

      c->message_id = -1;
      c->phase = PHASE_IDLE;

      if (c->c) {
            pthread_mutex_lock(&c->c->load_lock);
            c->c->load--;
            pthread_mutex_unlock(&c->c->load_lock);
      }


      if (ldapdns.always_hangup || (flag != '+' && flag != '-')) {
            /* okay, something "weird" happened
             * we want to signal a hangup on this hangle
             */
            tp_close(c);
      }

      if (use_syslog) {
            /* encouragement: don't use syslog :) */
            return;
      }

      pthread_mutex_lock(&log_lock);
      if (c->request_name_alloc) {
            dns_to_name(out, c->request_name_alloc, 0);
            q = str(out);
      } else {
            q = "(unknown)";
      }
      if (c->response && c->response->buf)  {
            id[0] = c->response->buf[0];
            id[1] = c->response->buf[1];
      } else {
            id[0] = c->request_buf[0];
            id[1] = c->request_buf[1];
      }
      status("%02x%02x%02x%02x:%02x%02x:%02x%02x %c %02x%02x %s",
                  (unsigned char)c->ip[0],
                  (unsigned char)c->ip[1],
                  (unsigned char)c->ip[2],
                  (unsigned char)c->ip[3],
                  (unsigned char)(c->port & 0xFF00) >> 8,
                  (unsigned char)(c->port & 0x00FF),
                  (unsigned char)id[0],
                  (unsigned char)id[1],

                  flag,

                  (unsigned char)c->request_record[0],
                  (unsigned char)c->request_record[1],
                  q);

      if (c->request_name_alloc) mem_free(q);
      pthread_mutex_unlock(&log_lock);
}
static void do_zonesearch(dns_ctx *c, char *q);
static void try_subrequest(dns_ctx *c, char *trydomain)
{
      /* put it back into phase 1 with the subrequest flag set... */
      c->subreq++;
      c->subreq_in = c->subreq_in_alloc = trydomain;
      do_zonesearch(c, trydomain);
}
static void finish_subrequest(dns_ctx *c)
{
      char *q;
      list_t lp;

      /* nuke old A list */
      while ((q = list_pop(&c->A))) {
            mem_free(q);
      }
      /* nuke old PTR list */
      while ((q = list_pop(&c->PTR))) {
            mem_free(q);
      }
      /* nuke old Generic list */
      while ((q = list_pop(&c->Generic))) {
            mem_free(q);
      }

      c->subreq--;
      if (c->subreq_in_alloc) {
            mem_free(c->subreq_in_alloc);
            c->subreq_in = 0;
            c->subreq_in_alloc = 0;
      }

      if (c->subreq_tries) {
            for (;;) {
                  q = list_pop(&c->subreq_tries);
                  /* out of requests */
                  if (!q) {
                        complete_phase(c, '+');
                        return;
                  }

                  /* make sure we haven't done this guy yet */
                  for (lp = c->subreq_done; lp; lp = lp->next) {
                        if (str_equali(lp->str, q)) {
                              /* skip this round */
                              mem_free(q);
                              q = 0;
                              break;
                        }
                  }
                  /* got one? */
                  if (q) {
                        list_push(&c->subreq_done, str_dup(q));
                        try_subrequest(c, q);
                        return;
                  }
            }
      } else
            complete_phase(c, '+');
}
static void do_axfrsearch(dns_ctx *c, char *q)
{
      const char *attrs[6] = {
                        "mail",
                        "nSRecord",
                        "sOARecord",
                        "modifyTimestamp"
      };
      str_t sa, sb;
      int r;

retry_axfr_search_top_l:
      if (ldapdns.dn_mode == DN_MODE_RFC1279
                  || ldapdns.dn_mode == DN_MODE_MSDNS) {
            attrs[4] = (const char *)"dNSRecord";
            attrs[5] = (const char *)0;
      } else if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {

            response_refuse(c);
            complete_phase(c, '-');
            return;
      } else {
            attrs[4] = (const char *)0;
      }

      /* q is valid: safe */
      dns_to_name(sa, q, 0);
      name_to_ldap(sb, str(sa)); /* calls str_init sb */
      if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
            str_cat(sb, ", ");
            str_cat(sb, ldapdns.ldap_suffix);
      }

      pthread_mutex_lock(&c->c->lock);
      r = ldap_search(c->c->ldap_con,
                  str(sb),          /* base */
                  LDAP_SCOPE_BASE,  /* scope */
                  "(objectClass=*)",      /* ldap filter */
                  (char **)attrs,         /* attrs */
                  0);               /* attrsonly */
      mem_free(str(sa));
      mem_free(str(sb));

      if (r == -1) {
            /* uh oh: possibly an out of memory error */
            restart_ldap_connection(c->c);
            pthread_mutex_unlock(&c->c->lock);
            goto retry_axfr_search_top_l;
      }

      //printf("sent query for %d (was %d)\n", r, c->message_id);
      c->message_id = r;
      c->phase = PHASE_AXFRFIRST;

      c->c->message_sent++;
      //warning("axfrfirst %d / %d", c->c->message_sent, c->c->message_wait);
      if (c->c->message_wait)
            pthread_cond_broadcast(&c->c->active);

      pthread_mutex_unlock(&c->c->lock);
}
static void do_simple_search(dns_ctx *c, char *q)
{
      const char *attrs[12];
      str_t sb, sr;
      register int i, j;
      int r;

      str_init(sr);
      str_copy(sr, "(");
      str_cat(sr, c->request_attr);
      str_addch(sr, '=');
      for (i = 0; i < *q; i++) {
            j = q[i+1];
            if (!isdigit(((unsigned int)j))
                        && !isalpha(((unsigned int)j)) && j != '-') {
                  /* not even going to try... */
                  response_refuse(c);
                  complete_phase(c, '-');
                  mem_free(str(sr));
                  return;
            }
            str_addch(sr, j);
      }
      str_addch(sr, ')');

retry_simple_search_top_l:
      attrs[0] = (const char *)"aRecord";
      attrs[1] = (const char *)"mXRecord";
      attrs[2] = (const char *)"cNAMERecord";
      attrs[3] = (const char *)"seeAlso";
      attrs[4] = (const char *)"description";
      attrs[5] = (const char *)"photo";
      attrs[6] = (const char *)"mail";
      attrs[7] = (const char *)"nSRecord";
      attrs[8] = (const char *)"sOARecord";
      attrs[9] = (const char *)"modifyTimestamp";

      if (ldapdns.dn_mode == DN_MODE_RFC1279
                  || ldapdns.dn_mode == DN_MODE_MSDNS) {
            attrs[10] = (const char *)"dNSRecord";
            attrs[11] = (const char *)0;
      } else {
            attrs[10] = (const char *)0;
      }

      /* q is valid: safe */
      str_init(sb);
      if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
            str_cat(sb, ldapdns.ldap_suffix);
      }

      pthread_mutex_lock(&c->c->lock);
      r = ldap_search(c->c->ldap_con,
                  str(sb),          /* base */
                  LDAP_SCOPE_ONELEVEL,    /* scope */
                  str(sr),          /* ldap filter */
                  (char **)attrs,         /* attrs */
                  0);               /* attrsonly */
      mem_free(str(sr));
      mem_free(str(sb));

      if (r == -1) {
            /* uh oh: possibly an out of memory error */
            restart_ldap_connection(c->c);
            pthread_mutex_unlock(&c->c->lock);
            goto retry_simple_search_top_l;
      }

      //printf("sent query for %d (was %d)\n", r, c->message_id);
      c->message_id = r;
      c->phase = PHASE_SIMPLESEARCH;

      c->c->message_sent++;
      //warning("simplesearch %d / %d", c->c->message_sent, c->c->message_wait);
      if (c->c->message_wait)
            pthread_cond_broadcast(&c->c->active);

      pthread_mutex_unlock(&c->c->lock);
}
static void do_attrsearch(dns_ctx *c, char *q, int wild)
{
      const char *attrs[10];
      str_t sa, sb;
      list_t lp;
      int r;

retry_attr_search_top_l:
      if (!q || !*q) {
            if (c->subreq) {
                  finish_subrequest(c);
            } else {
                  response_nxdomain(c);
                  complete_phase(c, '-');
            }
            return;
      }

      attrs[0] = (const char *)"aRecord";
      attrs[1] = (const char *)"mXRecord";
      attrs[2] = (const char *)"cNAMERecord";
      attrs[3] = (const char *)"seeAlso";
      attrs[4] = (const char *)"description";
      attrs[5] = (const char *)"photo";
      attrs[6] = (const char *)"mail";
      attrs[7] = (const char *)"modifyTimestamp";

      if (ldapdns.dn_mode == DN_MODE_RFC1279
                  || ldapdns.dn_mode == DN_MODE_MSDNS) {
            attrs[8] = (const char *)"dNSRecord";
            attrs[9] = (const char *)0;
      } else {
            attrs[8] = (const char *)0;
      }

      dns_to_name(sa, q, 0);
      if (wild) {
            /* wildcard search */
            str_init(sb);
            str_copy(sb, "*.");
            str_cat(sb, str(sa));
            str_copy(sa, str(sb));
            mem_free(str(sb));
      }

      if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
            //printf("sa=%d, ad=%d\n", str_len(sa), c->adlen);
            if (str_len(sa) <= c->adlen) {
                  str_init(sb);
                  str_copy(sb, c->search_base);
            } else {
                  str(sa)[str_len(sa) - c->adlen] = 0;
                  name_to_ldap(sb, str(sa)); /* calls str_init sb */
                  str_cat(sb, ", ");
                  str_cat(sb, c->search_base);
            }
      } else {
            /* q is valid: safe */
            name_to_ldap(sb, str(sa)); /* calls str_init sb */
            if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
                  str_cat(sb, ", ");
                  str_cat(sb, ldapdns.ldap_suffix);
            }
      }

      // printf("X2 searching (%p) [%s]\n", c, str(sb));
      pthread_mutex_lock(&c->c->lock);
      r = ldap_search(c->c->ldap_con,
                  str(sb),          /* base */
                  LDAP_SCOPE_BASE,  /* scope */
                  "(objectClass=*)",      /* ldap filter */
                  (char **)attrs,         /* attrs */
                  0);               /* attrsonly */
      mem_free(str(sa));
      mem_free(str(sb));

      if (r == -1) {
            /* uh oh: possibly an out of memory error */
            restart_ldap_connection(c->c);
            pthread_mutex_unlock(&c->c->lock);
            goto retry_attr_search_top_l;
      }

      //printf("attr: sent query for %d (was %d)\n", r, c->message_id);
      c->message_id = r;
      c->phase = PHASE_ATTRSEARCH;

      c->c->message_sent++;
      //warning("attrsearch %d / %d", c->c->message_sent, c->c->message_wait);
      if (c->c->message_wait)
            pthread_cond_broadcast(&c->c->active);

      pthread_mutex_unlock(&c->c->lock);
}
static void do_zonesearch(dns_ctx *c, char *q)
{
      const char *attrs[5];
      str_t sa, sb;
      char *base;
      char *filter;
      int r;

      if (c->search_base) {
            mem_free(c->search_base);
            c->search_base = 0;
      }
      c->adlen = -1;

retry_zone_search_top_l:
      if (!q || !*q) {
            if (c->subreq) {
                  finish_subrequest(c);
            } else {
                  response_nxdomain(c);
                  complete_phase(c, '-');
            }
            return;
      }

      attrs[0] = (const char *)"nSRecord";
      attrs[1] = (const char *)"sOARecord";
      attrs[2] = (const char *)"modifyTimestamp";
      if (ldapdns.dn_mode == DN_MODE_RFC1279
                  || ldapdns.dn_mode == DN_MODE_MSDNS) {
            attrs[3] = (const char *)"dNSRecord";
            attrs[4] = (const char *)0;
      } else if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
            attrs[3] = (const char *)"associatedDomain";
            attrs[4] = (const char *)0;
      } else {
            attrs[3] = (const char *)0;
      }

      /* q is valid: safe */
      dns_to_name(sa, q, 0);
      if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
            str_init(sb);
            str_copy(sb, "(|(associatedDomain=");
            str_cat(sb, str(sa));
            for (r = 0; str(sa)[r]; r++) {
                  if (str(sa)[r] == '.') {
                        while (str(sa)[r] == '.') r++;
                        str_cat(sb, ")(associatedDomain=");
                        str_cat(sb, str(sa)+r);
                  }
            }
            str_cat(sb, "))");
            filter = str(sb);

            if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
                  base = ldapdns.ldap_suffix;
            } else {
                  base = "";
            }
      } else {
            name_to_ldap(sb, str(sa)); /* calls str_init */
            if (! str(sb) || ! str(sb)[0]) {
                  mem_free(str(sa));
                  mem_free(str(sb));
      
                  /* hrm... we're empty... */
                  if (c->subreq) {
                        finish_subrequest(c);
                  } else {
                        response_refuse(c);
                        complete_phase(c, '-');
                  }
                  return;
            }


            if (ldapdns.dn_mode == DN_MODE_MSDNS) {
                  /* msdns is like dc=@, dc=domain for this phase */
                  str_copy(sa, "dc=@, ");
                  str_cat(sa, str(sb));
                  str_copy(sb, str(sa));
            }
            if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
                  str_cat(sb, ", ");
                  str_cat(sb, ldapdns.ldap_suffix);
            }
            base = str(sb);
            filter = "(objectClass=*)";
      }

      //printf("searching (%p) [%s]\n", c, str(sb));
      pthread_mutex_lock(&c->c->lock);
      r = ldap_search(c->c->ldap_con,
                  base,             /* base */
ldapdns.dn_mode == DN_MODE_LDAPDNS ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE,
                  filter,                 /* ldap filter */
                  (char **)attrs,         /* attrs */
                  0);               /* attrsonly */
      mem_free(str(sa));
      mem_free(str(sb));

      if (r == -1) {
            /* uh oh: possibly an out of memory error */
            restart_ldap_connection(c->c);
            pthread_mutex_unlock(&c->c->lock);
            goto retry_zone_search_top_l;
      }

      c->message_id = r;
      c->phase = PHASE_ZONESEARCH;

      c->c->message_sent++;
      //warning("zonesearch %d / %d", c->c->message_sent, c->c->message_wait);
      if (c->c->message_wait)
            pthread_cond_broadcast(&c->c->active);

      pthread_mutex_unlock(&c->c->lock);
}

static void engine_dns_answer_notify(dns_ctx *c, char header[12])
{
      char *q, qtype[2], qclass[2];
      str_t d;
      int r, pid, status;

      /* query */
      q = 0;
      if (!dns_packet_getname(c, &q)) goto NOQ;
      if (!dns_packet_copy(c, qtype, 2)) goto NOQ;
      if (!dns_packet_copy(c, qclass, 2)) goto NOQ;
      if (!q) goto NOQ; /* offensive */

      /* setup notify response */
      if (!response_notify(c, q, qtype, qclass)) goto NOQ;
      response_id(c, header);

      if (qclass[0] == DNS_C_IN[0] && qclass[1] == DNS_C_IN[1]) {
            c->response->buf[2] |= 4;
      } else if (qclass[0] != DNS_C_ANY[0] || qclass[1] != DNS_C_ANY[1]) {
            goto WEIRDCLASS;
      }
      if (qtype[0] != DNS_T_SOA[0] && qtype[1] != DNS_T_SOA[1]) {
            /* okie... */
            goto NOTIMP;
      }

      /* handle axfr processing on domain (q) */
      dns_to_name(d, q, 0);

      /* double fork */
      pid = fork();
      if (pid == 0) {
            pid = fork();
            if (pid == 0) {
                  /* run notify tool */
                  char ip[64];
                  /* Clib */
                  /* IPv4 */
                  sprintf(ip, "%d.%d.%d.%d", c->ip[0],
                              c->ip[1], c->ip[2], c->ip[3]);
                  env_put("REMOTE_ADDR", ip);
                  execlp(ldapdns.notify, ldapdns.notify, str(d), 0);
                  _exit(127);
            }
            _exit(pid == -1 ? 127 : 0);
      } else if (pid == -1) {
            mem_free(str(d));
            goto SERVFAIL;
      }
      /* wait for first child (almost immediately) */
      while ((r = wait(&status)) != pid && r != -1);
      if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
            mem_free(str(d));
            goto SERVFAIL;
      }

      /* respond success */
      mem_free(str(d));
      complete_phase(c, '+');
      return;
SERVFAIL:
      response_rcode(c, DNS_R_SERVFAIL);
      complete_phase(c, 'S');
      return;
NOTIMP:
      response_rcode(c, DNS_R_NOTIMP);
      complete_phase(c, 'I');
      return;
WEIRDCLASS:
      response_rcode(c, DNS_R_FORMERR);
      complete_phase(c, 'C');
      return;
NOQ:
      complete_phase(c, '/');
      return;
}
static int translate_netbios(dns_ctx *c, char *q)
{
      register int i, j;

      /* convert in place if it makes sense */
      if (q[0] != 32) {
            warning("i'm configured to do NETBIOS but this packet isn't right");
            return 0;
      }

      /* convert to ascii :) */
      for (i = j = 1; i < 33; i++, j++, j++) {
            q[i] = ((q[j] - 0x41) << 4) | ((q[j+1] - 0x41));
      }
      q[0] = 16; q[17] = 0;

      /* spaces are nulls */
      for (i = 1; i < 17; i++) {
            if (q[i] == 0x20 || q[i] == 0) {
                  q[i+1] = 0;
                  q[0] = i;
                  break;
            }
      }
      /* slide over the rest of the query */
      for (j = 33; q[j]; i++, j++) {
            q[i] = q[j];
      }
      return 1;
}
static void engine_dns_answer_query(dns_ctx *c, char header[12])
{
      char *q, *x, *y, qtype[2], qclass[2];

      /* query */
      q = 0;
      if (!dns_packet_getname(c, &q)) goto NOQ;
      if (!dns_packet_copy(c, qtype, 2)) goto NOQ;
      if (!dns_packet_copy(c, qclass, 2)) goto NOQ;
      if (!q) goto NOQ; /* offensive */

      if (c->request_name_alloc) {
            mem_free(c->request_name_alloc);
            c->request_name_alloc = 0;
      }

      /* if this is a netbios request, do funny things */
      if (ldapdns.netbios
                  /* these numbers are magic to NBT */
                  && qtype[0] == 0
                        && (qtype[1] == 0x20 || qtype[1] == 0x21)
                  && qclass[0] == 0 && qclass[1] == 0x01) {
            if (!translate_netbios(c, q))
                  goto WEIRDCLASS;

            /* mark this lookup as netbios */
            c->protnum = PROT_NETBIOS;
      } else
            c->protnum = PROT_DNS;

      /* set default SOA */
      c->refresh = default_refresh;
      c->retry = default_retry;
      c->expire = default_expire;
      c->minimum = default_minimum;
      c->ttl = default_minimum;

      c->subreq = 0;
      c->subreq_valid = 0;
      c->request_name_zone = 0;
      c->subreq_in = c->subreq_in_alloc = 0;
      c->wantdie = 0;
      c->search_base = 0;
      c->adlen = -1;

      cleanup_lists(c, 3);
      while ((x = list_pop(&c->ns))) {
            mem_free(x);
      }
      c->ns = 0;

      if (!response_query(c, q, qtype, qclass)) goto NOQ;
      response_id(c, header);
      if (qclass[0] == DNS_C_IN[0] && qclass[1] == DNS_C_IN[1]) {
            c->response->buf[2] |= 4;
      } else if (qclass[0] != DNS_C_ANY[0] || qclass[1] != DNS_C_ANY[1]) {
            goto WEIRDCLASS;
      }

      c->response->buf[3] &= ~128;

      /* QR flag: set response */
      if (!(header[2] & 1)) c->response->buf[2] &= ~1;

      /* lowercase domainname */
      dns_domain_lower(q);

      /* fill this up */
      c->request_name_alloc = c->request_name = q;
      c->request_record[0] = qtype[0];
      c->request_record[1] = qtype[1];

      /* cache? */
#if 0
      if (ldapdns.roots && header[2] & 1) {
            /* recursion desired */
            /* XXX: cache hooks start here */
            return;
      }
#endif
      /* try to retarget this */
      if (*q) {
            y = q + (*q)+1;
            x = ht_fetch(&ldapdns.search, y, __str_clen(y));
            if (x) {
                  /* simple rewritten search */
                  c->request_attr = x;
                  do_simple_search(c, q);
                  return;
            }
      }

      if ((qtype[0] == DNS_T_AXFR[0] && qtype[1] == DNS_T_AXFR[1])
      || (qtype[0] == DNS_T_IXFR[0] && qtype[1] == DNS_T_IXFR[1])) {
            if (!c->axfr_base) {
                  /* no AXFR allowed on this session */
                  warning("no AXFR base");
                  goto NOTIMP;
            }
            if (c->axfr_base[0] == '\0') {
                  /* anywhere: single dot */
                  do_axfrsearch(c, q);
                  return;
            }

            /* q MUST be a valid dns domain */
            for (x = q; *x; x += ((*x) + 1)) {
                  if (str_equali(x, c->axfr_base)) {
                        /* okay! this is nice */
                        do_axfrsearch(c, q);
                        return;
                  }
            }
            goto NOTIMP;
      }

      /* zone search */
      do_zonesearch(c, q);
      return;
NOTIMP:
      response_rcode(c, DNS_R_NOTIMP);
      complete_phase(c, 'I');
      return;
WEIRDCLASS:
      response_rcode(c, DNS_R_FORMERR);
      complete_phase(c, 'C');
      return;
NOQ:
      complete_phase(c, '/');
      return;
}
static int do_update_section(dns_ctx *c, char *d, char header[12], int section, list_t *p)
{
      char *x, xtype[2], xclass[2], xttl[4];
      unsigned short n, m;
      bin_t r;
      int i, j;

      /* read off sections */
      /* Clib */
      memcpy(&n, header + section, 2); n = ntohs(n);
      for (i = 0; i < n; i++) {
            x = 0;
            if (!(j=dns_packet_getname(c, &x))) return 0;
            if (!dns_packet_copy(c, xtype, 2)) return 0;
            if (!dns_packet_copy(c, xclass, 2)) return 0;
            if (!dns_packet_copy(c, xttl, 4)) return 0;
            if (!dns_packet_copy(c, (char *)&m, sizeof(m))) return 0;
            m = ntohs(m); /* m = length of packet; Clib */

            bin_init(r);
            bin_copy(r, xclass, 2);
            bin_cat(r, xtype, 2);
            bin_cat(r, xttl, 2);
            bin_cat(r, x, __str_clen(x));
            bin_cat(r, d, __str_clen(d)+1);/* also get \0 */
            mem_free(x);
            bin_cat(r, (char *)&m, sizeof(m)); /* length of rrdata */
            if (m > 0) {
                  j = clen(r);
                  bin_needplus(r, m);
                  if (!dns_packet_copy(c, caddr(r)+j, m)) return 0;
            }

            list_push(p, caddr(r));
      }
      return 1;
}
static int inline handle_generic_compare(dns_ctx *c, const unsigned char *p,
            const unsigned char *pdata, unsigned int plen) {
      unsigned short glen;
      int nl;

      glen = *(unsigned short *)(p+2);
      p += 4;
      while (glen > 0) {
            if (*p == 0xFF) {
                  glen--; p++;
                  if (glen <= 0) {
                        return 0;
                  }
                  if (*p == 0x00) {
                        /* expecting a \0377 */
                        if (*pdata != 0xFF) {
                              return 0;
                        }
                        pdata++; plen--;
                        glen--; p++;
                  } else {
                        /* dns-encoded name */
                        for (;;) {
                              if (*pdata != *p)
                                    return 0;
                              if (*p == 0) {
                                    glen--; p++;
                                    pdata++; plen--;
                                    break;
                              }
                              if (glen <= 0)
                                    return 0;
                              nl = ((*p)+1);
                              pdata += nl; plen -= nl;
                              p += nl; glen -= nl;
                        }
                  }
            } else if (*p == *pdata) {
                  glen--; p++;
                  pdata++; plen--;
            } else {
                  return 0;
            }
      }
      return 1;
}
static int rrset_test(dns_ctx *c, char *pdata)
{
      char *xtype = pdata;
      char *domain, *q;
      unsigned short n;
      list_t *r;
      int offset, i;

      pdata++;pdata++; /* TTL */
      pdata++;pdata++; /* domain */
      domain = pdata;
      while (*pdata) pdata++; pdata++;
      memcpy(&n, pdata, sizeof(n)); /* length of rrdata */
      pdata++; pdata++;

      /* Generic test first */
      if (c->Generic) {
            /* Generic first; should handle compression ! */
            while ((q = list_pop(&c->Generic))) {
                  if (q[0] == xtype[0] && q[1] == xtype[1]) {
                        i = handle_generic_compare(c, q, pdata, n);
                        if (i != -1)
                              return i;
                  } else
                        mem_free(q);
            }
      }

      offset = 0;
      if (xtype[0] == DNS_T_A[0] && xtype[1] == DNS_T_A[1]) {
            /* A record test */
            if (c->A) {
                  if (n == 0) return 1;
                  if (n != 4) return 0;
                  while ((q = list_pop(&c->A))) {
                        /* Clib */
                        if (memcmp(q, pdata, 4) == 0) {
                              mem_free(q);
                              return 1;
                        }
                        mem_free(q);
                  }
            }
            return 0;
      } else if (xtype[0] == DNS_T_MX[0] && xtype[1] == DNS_T_MX[1]) {
            /* ignore preference (it's easier) */
            offset = 2;
            r = &c->MX;
      } else if (xtype[0] == DNS_T_CNAME[0] && xtype[1] == DNS_T_CNAME[1]) {
            r = &c->CNAME;
      } else if (xtype[0] == DNS_T_TXT[0] && xtype[1] == DNS_T_TXT[1]) {
            r = &c->TXT;
      } else if (xtype[0] == DNS_T_PTR[0] && xtype[1] == DNS_T_PTR[1]) {
            r = &c->PTR;
      } else {
            return 0;
      }

      if (!*r) return 0;
      if (n == 0) return 1;
      while ((q = list_pop(r))) {
            for (i = offset; i < n; i++) {
                  if (q[i] != pdata[i])
                        break;
            }
            mem_free(q);
            if (i == n)
                  return 1;
      }
      return 0;
}
static void start_next_update_operation(dns_ctx *c)
{
      str_t s;
      char *p,*d;
      int r;

next_l:
      p = list_pop(&c->sec_update);
      if (!p) {
            /* all done? */
            response_rcode(c, DNS_R_NOERROR);
            complete_phase(c, '+');
            return;
      }

      d = p+6;
      r = -1;
      dns_to_name(s, d, 0);
      /* XXX this will actually do the work */
      if (p[0] == DNS_C_ANY[0] && p[1] == DNS_C_ANY[1]) {
            if (p[2] == DNS_T_ANY[0] && p[2] == DNS_T_ANY[1]) {
                  /* delete all rrs from a name */
                  warning("NS-UPDATE: Delete all RR's from: %s", str(s));
            } else {
                  /* delete all of an rr */
                  warning("NS-UPDATE: Delete all RR of a type from: %s", str(s));
            }
      } else if (p[0] == DNS_C_NONE[0] && p[1] == DNS_C_NONE[1]) {
            /* delete rr from rrset */
            if (p[2] == DNS_T_ANY[0] && p[2] == DNS_T_ANY[1]) {
                  response_rcode(c, DNS_R_FORMERR);
                  complete_phase(c, 'C');
                  return;
            }
            warning("NS-UPDATE: Delete an RR in an rrset from: %s", str(s));
      } else {
            /* add rrset */
            warning("NS-UPDATE: Add an RR to: %s", str(s));
      }
      if (r == -1) goto next_l;
}
static void ldapdns_process_update(dns_ctx *c)
{
      int err_code;
      int exists;
      str_t sa, sb;
      char *p;
      int r;

      if (!c->message) {
            goto start_next_l;
      }

      //printf("phase1\n");
      ldap_parse_result(c->c->ldap_con, c->message,
                  &err_code,
                  0,0,0,0,0);
      if (err_code != LDAP_SUCCESS) {
            if (err_code == LDAP_SERVER_DOWN) {
                  pthread_mutex_lock(&c->c->lock);
                  restart_ldap_connection(c->c);
                  pthread_mutex_unlock(&c->c->lock);
                  /* we need to resend query */
                  c->request_name = c->request_name_alloc;
                  do_zonesearch(c, c->request_name_alloc);
                  return;
            } else if (err_code == LDAP_TIMEOUT) {
                  /*response_refuse(c);*/
                  complete_phase(c, '?');
                  return;
            } else if (err_code == LDAP_NO_SUCH_OBJECT
            || err_code == LDAP_LOOP_DETECT
            || err_code == LDAP_UNWILLING_TO_PERFORM
            || err_code == LDAP_ALIAS_PROBLEM
            || err_code == LDAP_INVALID_SYNTAX
            || err_code == LDAP_INAPPROPRIATE_MATCHING
            || err_code == LDAP_NO_SUCH_ATTRIBUTE) {
                  /* okay did not exist... */
                  exists = 0;
            } else
                  fatal("ldap_result (nsupdate): %s", ldap_err2string(err_code));
      } else {
            exists = 1;

            /* we have results... process SOA/NS */
            ldap_load_dns_attributes(c, 0, 1);
      }
      p = list_pop(&c->sec_prereq);
      if (p) {
            /* test on exists */
            if (p[0] == DNS_C_NONE[0] && p[1] == DNS_C_NONE[1]) {
                  if (exists) {
                        if (p[2] == DNS_T_ANY[0] && p[3] == DNS_T_ANY[1]) {
                              /* nxdomain */
                              response_rcode(c, DNS_R_NXDOMAIN);
                              response_nxdomain(c);
                              complete_phase(c, 'N');
                              return;
                        } else if (rrset_test(c, p+2)) {
                              /* nxrrset */
                              response_rcode(c, DNS_R_NXRRSET);
                              response_nxdomain(c);
                              complete_phase(c, 'n');
                              return;
                        }
                  }
                  /* succeeded */
            } else if ((p[0] == DNS_C_IN[0] && p[1] == DNS_C_IN[1])
                        || (p[0] == DNS_C_ANY[0] && p[1] == DNS_C_ANY[1])) {
                  if (!exists) {
                        /* nxdomain */
                        response_rcode(c, DNS_R_YXDOMAIN);
                        response_nxdomain(c);
                        complete_phase(c, 'Y');
                        return;
                  }
                  if (p[2] != DNS_T_ANY[0] && p[3] != DNS_T_ANY[1]) {
                        if (!rrset_test(c, p+2)) {
                              /* yxrrset */
                              response_rcode(c, DNS_R_YXRRSET);
                              response_nxdomain(c);
                              complete_phase(c, 'y');
                              return;
                        }
                  }
            }
      }
      
start_next_l:
      cleanup_lists(c, 3); /* keep NS data (if we got any) */

      if (c->sec_prereq && c->sec_prereq->str) {
            p = c->sec_prereq->str + 6;
      } else if (c->sec_update && c->sec_update->str) {
            start_next_update_operation(c);
            return;
      } else {
            /* all done? */
            response_rcode(c, DNS_R_NOERROR);
            complete_phase(c, '+');
            return;
      }

retry_nsupdate_top_l:
      /* start request on domain name p */
      /* q is valid: safe */
      dns_to_name(sa, p, 0);
      name_to_ldap(sb, str(sa)); /* calls str_init sb */
      if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
            str_cat(sb, ", ");
            str_cat(sb, ldapdns.ldap_suffix);
      }

      pthread_mutex_lock(&c->c->lock);
      r = ldap_search(c->c->ldap_con,
                  str(sb),          /* base */
                  LDAP_SCOPE_BASE,  /* scope */
                  "(objectClass=*)",      /* ldap filter */
                  NULL,             /* all attributes */
                  0);               /* attrsonly */
      mem_free(str(sa));
      mem_free(str(sb));

      if (r == -1) {
            /* uh oh: possibly an out of memory error */
            restart_ldap_connection(c->c);
            pthread_mutex_unlock(&c->c->lock);
            goto retry_nsupdate_top_l;
      }

      //printf("sent query for %d (was %d)\n", r, c->message_id);
      c->message_id = r;
      c->phase = PHASE_NSUPDATE;

      c->c->message_sent++;
      //warning("nsupdate %d / %d", c->c->message_sent, c->c->message_wait);
      if (c->c->message_wait) pthread_cond_broadcast(&c->c->active);

      pthread_mutex_unlock(&c->c->lock);
}
static void engine_dns_answer_update(dns_ctx *c, int op, char header[12])
{
      char *q, qtype[2], qclass[2];
      char *z, ztype[2], zclass[2], zttl[4], zrd[2], zip[4];
      bin_t b;
      int i;

      /* flush this */
      while (list_pop(&c->sec_prereq));
      while (list_pop(&c->sec_update));

      /* query */
      q = 0;
      if (!dns_packet_getname(c, &q)) goto NOQ;
      if (!dns_packet_copy(c, qtype, 2)) goto NOQ;
      if (!dns_packet_copy(c, qclass, 2)) goto NOQ;
      if (!q) goto NOQ; /* offensive */

      if (c->request_name_alloc) {
            mem_free(c->request_name_alloc);
            c->request_name_alloc = 0;
      }
      c->request_name_alloc = q;

      /* in netbios mode, the sections are faked
       * we verify that the address being added/removed
       * is the one the client is on
       *
       * NB NAME REGISTRATION encodes data in the question+additional parts
       * NS UPDATE encodes data in the answer+authority parts
       *
       * kind of sick, yeah?
       */
      if (ldapdns.netbios
                  /* these numbers are magic to NBT :) */
                  && qtype[0] == 0
                        && (qtype[1] == 0x20 || qtype[1] == 0x21)
                  && qclass[0] == 0 && qclass[1] == 0x01) {
            /* we need to fast-forward to additional section
             *
             * skip len at position
             *
             */
            z = 0;
            if (!dns_packet_getname(c, &z)) goto NOQ;
            if (!z) goto NOQ;
            if (str_diff(z, q)) goto NOQ;
            if (!dns_packet_copy(c, ztype, 2)) goto NOQ;
            if (!dns_packet_copy(c, zclass, 2)) goto NOQ;

            if (ztype[0] != qtype[0]) goto NOQ;
            if (ztype[1] != qtype[1]) goto NOQ;
            if (zclass[0] != qclass[0]) goto NOQ;
            if (zclass[1] != qclass[1]) goto NOQ;

            if (!dns_packet_copy(c, zttl, 4)) goto NOQ;
            if (!dns_packet_copy(c, zrd, 2)) goto NOQ;
            if (zrd[0] != 0 && zrd[1] != 6) goto NOQ;

            /* load the flags */
            if (!dns_packet_copy(c, zrd, 2)) goto NOQ;

            /* requested node IP */
            if (!dns_packet_copy(c, zip, 4)) goto NOQ;

            /* standard protection: TODO make this possible from gateway */
            if (zip[0] != c->ip[0]) goto NOQ;
            if (zip[1] != c->ip[1]) goto NOQ;
            if (zip[2] != c->ip[2]) goto NOQ;
            if (zip[3] != c->ip[3]) goto NOQ;

            /* translate the name */
            if (!translate_netbios(c, q))
                  goto NOQ;

            /* *-* translate the sections *-* */

            if (op == DNS_O_RELEASE) {
                  /* UPDATE: NB record */
                  bin_init(b);
                  bin_addch(b, DNS_C_NONE[0]);
                  bin_addch(b, DNS_C_NONE[1]);
                  bin_addch(b, DNS_T_NB[0]);
                  bin_addch(b, DNS_T_NB[1]);
                  bin_addch(b, 0);bin_addch(b, 6);
                  bin_addch(b, zrd[0]);
                  bin_addch(b, zrd[1]);
                  bin_cat(b, zip, 4);
                  list_push(&c->sec_update, caddr(b));
      
                  /* UPDATE: A record */
                  bin_init(b);
                  bin_addch(b, DNS_C_NONE[0]);
                  bin_addch(b, DNS_C_NONE[1]);
                  bin_addch(b, DNS_T_A[0]);
                  bin_addch(b, DNS_T_A[1]);
                  bin_addch(b, 0);bin_addch(b, 4);
                  bin_cat(b, zip, 4);
                  list_push(&c->sec_update, caddr(b));
            } else {
                  if (op == DNS_O_UPDATE) {
                        /* NXRRSET; address record */
                        bin_init(b);
                        bin_addch(b, DNS_C_NONE[0]);
                        bin_addch(b, DNS_C_NONE[1]);
                        bin_addch(b, DNS_T_A[0]);
                        bin_addch(b, DNS_T_A[1]);
                        bin_addch(b, 0);bin_addch(b, 4);
                        bin_cat(b, c->ip, 4);
                        list_push(&c->sec_update, caddr(b));

                        /* NXRRSET: NB record */
                        bin_init(b);
                        bin_addch(b, DNS_C_NONE[0]);
                        bin_addch(b, DNS_C_NONE[1]);
                        bin_addch(b, DNS_T_NB[0]);
                        bin_addch(b, DNS_T_NB[1]);
                        bin_addch(b, 0);bin_addch(b, 6);
                        bin_addch(b, '\x00');   /* NB mode explicitly ignores this */
                        bin_addch(b, '\x00');
                        bin_cat(b, zip, 4);
                        list_push(&c->sec_update, caddr(b));
                  }

                  /* UPDATE: NB record */
                  bin_init(b);
                  bin_addch(b, DNS_C_IN[0]);
                  bin_addch(b, DNS_C_IN[1]);
                  bin_addch(b, DNS_T_NB[0]);
                  bin_addch(b, DNS_T_NB[1]);
                  bin_addch(b, 0);bin_addch(b, 6);
                  bin_addch(b, zrd[0]);
                  bin_addch(b, zrd[1]);
                  bin_cat(b, zip, 4);
                  list_push(&c->sec_update, caddr(b));
      
                  /* UPDATE: A record */
                  bin_init(b);
                  bin_addch(b, DNS_C_IN[0]);
                  bin_addch(b, DNS_C_IN[1]);
                  bin_addch(b, DNS_T_A[0]);
                  bin_addch(b, DNS_T_A[1]);
                  bin_addch(b, 0);bin_addch(b, 4);
                  bin_cat(b, zip, 4);
                  list_push(&c->sec_update, caddr(b));
            }
      
            /* mark this lookup as netbios */
            c->protnum = PROT_NETBIOS;
      } else {
            c->protnum = PROT_DNS;

            /* load sections */
            if (!do_update_section(c, q, header,
                        REQUEST_PRE, &c->sec_prereq)) goto NOQ;
            if (!do_update_section(c, q, header,
                        REQUEST_UPDATE, &c->sec_update)) goto NOQ;

            /* reverse these... (order MAY be important...) */
            list_reverse(&c->sec_prereq);
            list_reverse(&c->sec_update);
      }

      /* copy response... */
      c->response->used = 0;
      response_addbytes(c, header, 12);
      /* turn off question bit */
      c->response->buf[2] &= ~1;
      c->response->buf[2] |= 128;
      c->response->buf[2] &= ~(0x0F<<3);
      c->response->buf[2] |= 5<<3;
      c->response->buf[3] = 0;
      for (i = 4; i < 12; i++) c->response->buf[i] = 0;
      c->response->used = 12;
      response_id(c, header);

      /* okay, for each entry, start the appropriate ldap query */
      c->message = 0;
      c->phase = PHASE_NSUPDATE;
      ldapdns_process_update(c); /* this also starts up the request */
      return;
NOQ:
      c->response->used = 0;
      complete_phase(c, '/');
      return;
}
static void engine_dns_answer(dns_ctx *c, time_t now)
{
      /* first, we need to setup/parse the headers of the dns request */
      char header[12];
      int op;

      /* default serial is always */

      c->serial = now;
      c->soahack = 0;

      /* copy the header and make sure this is infact a question */
      if (!dns_packet_copy(c, header, 12)) goto NOQ;
      if (header[2] & 128) goto NOQ; /* response flag */

      /* do something with the opcode? */
      op = (header[2] >> 3) & 0xF;
      switch (op) {
      case DNS_O_QUERY:
            /* query; there must be exactly 1 query */
            if (header[4]) goto NOQ;
            if (header[5] != 1) goto NOQ;
            engine_dns_answer_query(c, header);
            return;
      case DNS_O_UPDATE:
      case DNS_O_RELEASE:
            if (!ldapdns.update)
                  goto NOTIMP;
            /* only one zone allowed (duh) */
            if (header[4]) goto NOQ;
            if (header[5] != 1) goto NOQ;
            engine_dns_answer_update(c, op, header);
            return;
      case DNS_O_NOTIFY:
            if (!ldapdns.notify)
                  goto NOTIMP;
            /* do we have a notify helper-program? */
            if (header[4]) goto NOQ;
            if (header[5] != 1) goto NOQ;
            engine_dns_answer_notify(c, header);
            return;

      case DNS_O_IQUERY:
      case DNS_O_STATUS:
            goto NOTIMP;
      };
NOTIMP:
      /* copy the entire query */
      c->response->used = 0;
      response_addbytes(c, c->request_buf, c->request_len);
      /* turn off question bit */
      c->response->buf[2] &= ~1;
      c->response->buf[3] &= ~128;
      response_rcode(c, DNS_R_NOTIMP);
      complete_phase(c, 'I');
      return;
NOQ:
      complete_phase(c, '/');
      return;
}
static void ldapdns_process_zonesearch(dns_ctx *c)
{
      int err_code;
      char *x;

      list_t saved_NS;
      unsigned long saved_soa[6];
      int saved_wantdie;
      int saved_adlen;

      /* process NS/SOA search results; possibly rerun */
      if (!c->message) {
            /* odd... no message waiting? */
            fatal("assertion: zonesearch processing without message!");
      }

      //printf("phase1\n");
      ldap_parse_result(c->c->ldap_con, c->message,
                  &err_code,
                  0,0,0,0,0);
      if (err_code != LDAP_SUCCESS) {
            if (err_code == LDAP_SERVER_DOWN) {
                  pthread_mutex_lock(&c->c->lock);
                  restart_ldap_connection(c->c);
                  pthread_mutex_unlock(&c->c->lock);
                  /* we need to resend query */
                  c->request_name = c->request_name_alloc;
                  do_zonesearch(c, c->request_name_alloc);
                  return;
            } else if (err_code == LDAP_TIMEOUT) {
                  /*response_refuse(c);*/
                  complete_phase(c, '?');
                  return;
            } else if (err_code == LDAP_NO_SUCH_OBJECT
            || err_code == LDAP_LOOP_DETECT
            || err_code == LDAP_UNWILLING_TO_PERFORM
            || err_code == LDAP_ALIAS_PROBLEM
            || err_code == LDAP_INVALID_SYNTAX
            || err_code == LDAP_INAPPROPRIATE_MATCHING
            || err_code == LDAP_NO_SUCH_ATTRIBUTE) {
                  /* doesn't exist... let's chop the base up */
                  //printf("shoten\n");

                  if (c->subreq) {
                        /* skip the first dot and try again */
                        if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
                              finish_subrequest(c);
                        } else {
                              c->subreq_in += ((*c->subreq_in)+1);
                              do_zonesearch(c, c->subreq_in);
                        }
                  } else if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
                        response_nxdomain(c);
                        complete_phase(c, '-');
                  } else {
                        c->request_name += ((*c->request_name)+1);
                        do_zonesearch(c, c->request_name);
                  }
                  return;
            }

            fatal("ldap_result (zonesearch): %s", ldap_err2string(err_code));
      }

      /* we have results... process SOA/NS */
      if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
            /* save list pointers */
            if (c->adlen > -1) {
                  saved_NS = c->NS;
                  saved_soa[0] = c->serial;
                  saved_soa[1] = c->refresh;
                  saved_soa[2] = c->retry;
                  saved_soa[3] = c->expire;
                  saved_soa[4] = c->minimum;
                  saved_soa[5] = c->ttl;
                  saved_wantdie = c->wantdie;

                  c->refresh = default_refresh;
                  c->retry = default_retry;
                  c->expire = default_expire;
                  c->minimum = default_minimum;
                  c->ttl = c->minimum;

                  c->NS = 0;
                  c->wantdie = 0;
                  c->serial = c->lastt;
            }
            saved_adlen = c->adlen;
            c->adlen = -1;
      }

      ldap_load_dns_attributes(c, &c->search_base, 0);

      if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
//          if (c->adlen > -1) 
            add_peer_ns(c, 1);

      }

      if (ldapdns.dn_mode == DN_MODE_LDAPDNS && c->adlen > -1
                  && saved_adlen > -1) {
            if (saved_adlen > c->adlen) {
                  /* okay, we already had a better shot... free the
                   * new ones and restore */
                  while ((x = list_pop(&c->NS))) mem_free(x);

                  c->NS = saved_NS;
                  c->serial = saved_soa[0];
                  c->refresh = saved_soa[1];
                  c->retry = saved_soa[2];
                  c->expire = saved_soa[3];
                  c->minimum = saved_soa[4];
                  c->ttl = saved_soa[5];
                  c->wantdie = saved_wantdie;
                  c->adlen = saved_adlen;

            } else {
                  /* this one is better... free the saved */
                  while ((x = list_pop(&saved_NS))) mem_free(x);
            }
      }
      if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
            /* more on the way... */
            if (ldap_msgtype(c->message) == LDAP_RES_SEARCH_RESULT)
                  return;
      }
      
      /* try lowering ourselves... */
      if (c->subreq) {
            //printf("lower ourselves\n");
            if (c->subreq_valid < c->subreq) {
                  if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
                        finish_subrequest(c);
                  } else {
                        c->subreq_in += ((*c->subreq_in)+1);
                        do_zonesearch(c, c->subreq_in);
                  }
                  return;
            }
      } else if (!c->NS) {
            /* still not good enough */
            /* skip the first dot and try again */
            if (ldapdns.dn_mode == DN_MODE_LDAPDNS) {
                  response_nxdomain(c);
                  complete_phase(c, '-');
            } else {
                  c->request_name += ((*c->request_name)+1);
                  do_zonesearch(c, c->request_name);
            }
            return;
      }

      /* okay, back up to original request and send it */
      if (c->subreq) {
            c->subreq_in = c->subreq_in_alloc;
            c->attr_wild = 0;
            do_attrsearch(c, c->subreq_in, 0);
      } else {
            //printf("okay, move back up to %s\n", c->request_name_alloc);
            c->request_name_zone = c->request_name;
            c->request_name = c->request_name_alloc;
            if (c->axfr) {
                  do_axfrsearch(c, c->request_name);
            } else {
                  c->attr_wild = 0;
                  do_attrsearch(c, c->request_name, 0);
            }
      }
}
static void inline push_subreq(dns_ctx *c, char *q)
{
      list_t lp;

      /* make sure we haven't done this guy yet (save ourselves time) */
      for (lp = c->subreq_done; lp; lp = lp->next) {
            if (str_equali(lp->str, q)) return;
      }

      /* add to the lists */
      list_push(&c->subreq_tries, str_dup(q));
}

static void inline
response_generic(dns_ctx *c, int ax, char *q, unsigned char *p, unsigned long ttl)
{
      unsigned short glen;
      int dlen;

      glen = *(unsigned short *)(p+2);
      if (ax) {
            if (!response_axstart(c, 0, q, p, DNS_C_IN, ttl)) {
                  fatal("could not construct generic");
            }
      } else {
            if (!response_rstart(c, q, p, ttl)) {
                  fatal("could not construct generic");
            }
      }
      p += 4;
      while (glen > 0) {
            if (*p == 0xFF) {
                  glen--; p++;
                  if (glen <= 0) {
                        fatal("could not construct generic");
                  }
                  if (*p == 0x00) {
                        if (!response_addbytes(c, "\377", 1)) {
                              fatal("could not construct generic");
                        }
                        glen--; p++;
                  } else {
                        /* dns-encoded name */
                        if (!response_addname(c, p)) {
                              fatal("could not construct generic");
                        }
                        dlen = dns_domain_length(p);
                        /* do we want to do subrequests? */
                        if (!ldapdns.no_additionals)
                              push_subreq(c, p);
                        glen -= dlen; p += dlen;
                  }
            } else {
                  if (!response_addbytes(c, p, 1)) {
                        fatal("could not construct generic");
                  }
                  glen--; p++;
            }
      }
}
static void inline add_peer_ns(dns_ctx *c, int inc) {
      list_t lp;
      char *x;

      if (inc) {
            /* then also attach peer_ns and self_ns */
            for (lp = ldapdns.peer_ns; lp; lp = lp->next) {
                  list_push(&c->NS, str_dup(lp->str));
            }
            if (ldapdns.self_ns)
                  list_push(&c->NS, str_dup(ldapdns.self_ns));
      }
      
      /* don't add nameservers unless we've already got one (including @) */
      if (c->NS) {
            /* make sure only unique nameservers are listed */
            ldapdns_list_unique(&c->NS);
            
            lp = 0;
            while ((x = list_pop(&c->NS))) {
                  if (x[0] == 1 && x[1] == '@' && x[2] == 0) {
                        mem_free(x);
                        continue;
                  }
                  list_push(&lp, x);
            }
            list_reverse(&lp);
            c->NS = lp;
      }
}
static void ldapdns_process_attrsearch(dns_ctx *c, int no_wild)
{
      int err_code;
      char *dat;
      list_t lp;
      int ext_records, selfmatch;
      int didany = 0;

      /* process NS/SOA search results; possibly rerun */
      if (!c->message) {
            /* odd... no message waiting? */
            fatal("assertion: attrsearch processing without message!");
      }

      //printf("attrsearch\n");
      ldap_parse_result(c->c->ldap_con, c->message,
                  &err_code,
                  0,0,0,0,0);
      if (err_code != LDAP_SUCCESS) {
            if (err_code == LDAP_SERVER_DOWN) {
                  pthread_mutex_lock(&c->c->lock);
                  restart_ldap_connection(c->c);
                  pthread_mutex_unlock(&c->c->lock);
                  if (no_wild) {
                        /* resend original */
                        do_simple_search(c, c->request_name);
                  } else
                  /* resend phase-2 query */
                  if (c->subreq) {
                        do_attrsearch(c, c->subreq_in, c->attr_wild);
                  } else {
                        do_attrsearch(c, c->request_name, c->attr_wild);
                  }
                  return;
            } else if (err_code == LDAP_TIMEOUT) {
                  /*response_refuse(c);*/
                  complete_phase(c, '?');
                  return;
            } else if (err_code == LDAP_NO_SUCH_OBJECT
            || err_code == LDAP_LOOP_DETECT
            || err_code == LDAP_UNWILLING_TO_PERFORM
            || err_code == LDAP_ALIAS_PROBLEM
            || err_code == LDAP_INVALID_SYNTAX
            || err_code == LDAP_INAPPROPRIATE_MATCHING
            || err_code == LDAP_NO_SUCH_ATTRIBUTE) {
                  /* doesn't exist... let's chop the base up */
                  /* and put it in wildmode */
                  //printf("did not exist?\n");
                  //
                  if (no_wild) {
                        /* this is a real failure */
                        response_refuse(c);
                        complete_phase(c, '-');
                        return;
                  }

                  /* skip the first dot and try again */
                  c->attr_wild = 1;
                  if (c->subreq) {
                        c->subreq_in += ((*c->subreq_in)+1);
                        do_attrsearch(c, c->subreq_in, 1);
                  } else {
                        c->request_name += ((*c->request_name)+1);
                        do_attrsearch(c, c->request_name, 1);
                  }
                  return;
            }

            fatal("ldap_result (zonesearch): %s", ldap_err2string(err_code));
      }

      /* we have results... load them into lists*/
      //printf("go in here?\n");
      ldap_load_dns_attributes(c, 0, 1);
      //printf("go out here?\n");

      /* randomize arecords if requested */
      switch (ldapdns.schedule_arecord) {
      case SCHEDULE_A_RANDOM:
            list_randomize(&c->A);
            break;
      }

      if (c->subreq) {
            //printf("eating addresses?\n");
            if (c->A) {
                  while ((dat = list_pop(&c->A))) {
                        if (!response_rstart(c, c->subreq_in_alloc,
                                          DNS_T_A, c->ttl)
                        || !response_addbytes(c, dat, 4)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ADDITIONAL);
                        mem_free(dat);
                  }
            }
            finish_subrequest(c);
            return;
      }

      /* makes it "easy" to kill records */
      if (c->wantdie) {
            response_refuse(c);
            complete_phase(c, '-');
            return;
      }

#define _eq2(a) (a[0] == c->request_record[0] && a[1] == c->request_record[1])
      if (_eq2(DNS_T_SOA)) {
            /* SOA needs: NS1, original query, and hostmaster */
            if (!c->NS) {
                  response_refuse(c);
                  complete_phase(c, '-');
                  return;
            }

            add_peer_ns(c, 0);
            response_aa(c, 1);

            if (!response_rstart(c, c->request_name_alloc, DNS_T_SOA, c->ttl)
            || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
            || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
            || !response_addulong(c, c->serial)
            || !response_addulong(c, c->refresh)
            || !response_addulong(c, c->retry)
            || !response_addulong(c, c->expire)
            || !response_addulong(c, c->minimum)) {
                  fatal("could not construct SOA");
            }
            response_rfinish(c, RESPONSE_ANSWER);
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone, DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }
            if (no_wild)
                  complete_phase(c, '+');
            else if (c->subreq_tries) {
                  c->subreq++;
                  finish_subrequest(c);
            } else
                  complete_phase(c, '+');
            return;
      }

      ext_records = 0;
      if (c->Generic) {
            /* Generic first */
            while ((dat = list_pop(&c->Generic))) {
                  if (dat[0] != DNS_T_NS[0] && dat[1] != DNS_T_NS[1])
                        ext_records++;

                  if (_eq2(dat)) {
                        response_generic(c, 0, c->request_name_alloc,
                                    dat, c->ttl);
                        response_rfinish(c, RESPONSE_ANSWER);
                  }

                  mem_free(dat);
            }
      }

      /* flip the AA bit */
      add_peer_ns(c, 0);
      response_aa(c, 1);

      /* address-style requests */
      if (_eq2(DNS_T_ANY)) {
            /* CNAME:A and MX */
            if (c->A) {
                  /* so we don't say it again */
                  if (!ldapdns.no_additionals)
                        list_push(&c->subreq_done,
                              str_dup(c->request_name_alloc));

                  /* pop off each entry and add it's response */
                  while ((dat = list_pop(&c->A))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_A, c->ttl)
                        || !response_addbytes(c, dat, 4)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        mem_free(dat);
                  }
            } else if (c->CNAME) {
                  while ((dat = list_pop(&c->CNAME))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_CNAME, c->ttl)
                        || !response_addname(c, dat)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        if (!ldapdns.no_additionals)
                              push_subreq(c, dat);
                        mem_free(dat);
                  }
            }

            if (c->PTR) {
                  /* this is silly */
                  while ((dat = list_pop(&c->PTR))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_PTR, c->ttl)
                        || !response_addname(c, dat)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        mem_free(dat);
                  }
            }

            if (c->MX) {
                  while ((dat = list_pop(&c->MX))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_MX, c->ttl)
                        || !response_addbytes(c, dat, 2)
                        || !response_addname(c, dat+2)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        if (!ldapdns.no_additionals)
                              push_subreq(c, dat+2);
                        mem_free(dat);
                  }
            }

            /* return a SOA */
            if ((no_wild && ldapdns.self_ns) || c->NS) {
                  if (!response_rstart(c, c->request_name_zone, DNS_T_SOA, c->ttl)
                  || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
                  || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
                  || !response_addulong(c, c->serial)
                  || !response_addulong(c, c->refresh)
                  || !response_addulong(c, c->retry)
                  || !response_addulong(c, c->expire)
                  || !response_addulong(c, c->minimum)) {
                        fatal("could not construct SOA");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }

            /* also included SOA */
            for (lp = c->NS; lp; lp = lp->next) {
                  dat = lp->str;
                  if (!response_rstart(c, c->request_name_zone,
                                    DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
            }

            /* also included SOA */
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone,
                                    DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }


      } else if (_eq2(DNS_T_A)) {
            /* CNAME:A */
            if (c->A) {
                  if (!ldapdns.no_additionals)
                        list_push(&c->subreq_done,
                                    str_dup(c->request_name_alloc));
                  while ((dat = list_pop(&c->A))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_A, c->ttl)
                        || !response_addbytes(c, dat, 4)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        didany++;
                        mem_free(dat);
                  }
            } else if (c->CNAME) {
                  while ((dat = list_pop(&c->CNAME))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_CNAME, c->ttl)
                        || !response_addname(c, dat)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        if (!ldapdns.no_additionals)
                              push_subreq(c, dat);
                        didany++;
                        mem_free(dat);
                  }
            }

            if (!didany && c->NS) {
                  /* if no answers, but we're NS, we give SOA */
                  if (!response_rstart(c, c->request_name_zone, DNS_T_SOA, c->ttl)
                  || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
                  || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
                  || !response_addulong(c, c->serial)
                  || !response_addulong(c, c->refresh)
                  || !response_addulong(c, c->retry)
                  || !response_addulong(c, c->expire)
                  || !response_addulong(c, c->minimum)) {
                        fatal("could not construct SOA");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }

            /* also included SOA */
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone,
                                    DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }

      } else if (ldapdns.netbios && _eq2(DNS_T_NB)) {
            if (c->A) {
                  /* okay, normally these things are generic.
                   * but we'll translate A records for them
                   */
                  while ((dat = list_pop(&c->A))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_NB, c->ttl)
                        /* netbios flags:
                         *    0x80 (unique)           'G'
                         *    0x20 P-node       'ont'
                         */
                        || !response_addbytes(c, "\x60\x00", 2)
                        || !response_addbytes(c, dat, 4)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        mem_free(dat);
                  }
            }

      } else if (_eq2(DNS_T_MX)) {
            /* CNAME:A:MX */
            selfmatch = 1;
            if (c->MX) {
                  selfmatch = 0;
                  while ((dat = list_pop(&c->MX))) {
                        if (!response_rstart(c, c->request_name_alloc,
                                          DNS_T_MX, c->ttl)
                        || !response_addbytes(c, dat, 2)
                        || !response_addname(c, dat+2)) {
                              fatal("could not construct address");
                        }
                        response_rfinish(c, RESPONSE_ANSWER);
                        if (str_equal(c->request_name_alloc, dat+2)) {
                              selfmatch = 2;
                        } else if (!ldapdns.no_additionals)
                              push_subreq(c, dat+2);
                        didany++;
                        mem_free(dat);
                  }
            }

            if (selfmatch == 1) {
                  if (c->A) {
                        if (!ldapdns.no_additionals)
                              list_push(&c->subreq_done,
                                          str_dup(c->request_name_alloc));
                        while ((dat = list_pop(&c->A))) {
                              if (!response_rstart(c, c->request_name_alloc,
                                                DNS_T_A, c->ttl)
                              || !response_addbytes(c, dat, 4)) {
                                    fatal("could not construct address");
                              }
                              didany++;
                              response_rfinish(c, RESPONSE_ANSWER);
                              mem_free(dat);
                        }
                  } else if (c->CNAME) {
                        while ((dat = list_pop(&c->CNAME))) {
                              if (!response_rstart(c, c->request_name_alloc,
                                                DNS_T_CNAME, c->ttl)
                              || !response_addname(c, dat)) {
                                    fatal("could not construct address");
                              }
                              response_rfinish(c, RESPONSE_ANSWER);
                              didany++;
                              if (!ldapdns.no_additionals)
                                    push_subreq(c, dat);
                              mem_free(dat);
                        }
                  }
            }
            if (!didany && c->NS) {
                  /* if no answers, but we're NS, we give SOA */
                  if (!response_rstart(c, c->request_name_zone, DNS_T_SOA, c->ttl)
                  || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
                  || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
                  || !response_addulong(c, c->serial)
                  || !response_addulong(c, c->refresh)
                  || !response_addulong(c, c->retry)
                  || !response_addulong(c, c->expire)
                  || !response_addulong(c, c->minimum)) {
                        fatal("could not construct SOA");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }

            /* also included SOA */
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone,
                                    DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }

            if (selfmatch == 2) {
                  if (c->A) {
                        if (!ldapdns.no_additionals)
                              list_push(&c->subreq_done,
                                          str_dup(c->request_name_alloc));
                        while ((dat = list_pop(&c->A))) {
                              if (!response_rstart(c, c->request_name_alloc,
                                                DNS_T_A, c->ttl)
                              || !response_addbytes(c, dat, 4)) {
                                    fatal("could not construct address");
                              }
                              response_rfinish(c, RESPONSE_ADDITIONAL);
                              mem_free(dat);
                        }
                  } else if (c->CNAME) {
                        while ((dat = list_pop(&c->CNAME))) {
                              if (!response_rstart(c, c->request_name_alloc,
                                                DNS_T_CNAME, c->ttl)
                              || !response_addname(c, dat)) {
                                    fatal("could not construct address");
                              }
                              response_rfinish(c, RESPONSE_ADDITIONAL);
                              if (!ldapdns.no_additionals)
                                    push_subreq(c, dat);
                              mem_free(dat);
                        }
                  }
            }

      } else if (_eq2(DNS_T_CNAME)) {
            while ((dat = list_pop(&c->CNAME))) {
                  if (!response_rstart(c, c->request_name_alloc, DNS_T_CNAME, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct CNAME");
                  }
                  didany++;
                  response_rfinish(c, RESPONSE_ANSWER);
                  mem_free(dat);
            }

            if (!didany && c->NS) {
                  /* if no answers, but we're NS, we give SOA */
                  if (!response_rstart(c, c->request_name_zone, DNS_T_SOA, c->ttl)
                  || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
                  || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
                  || !response_addulong(c, c->serial)
                  || !response_addulong(c, c->refresh)
                  || !response_addulong(c, c->retry)
                  || !response_addulong(c, c->expire)
                  || !response_addulong(c, c->minimum)) {
                        fatal("could not construct SOA");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }

            /* also included SOA */
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone,
                                    DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }

      } else if (_eq2(DNS_T_PTR)) {
            while ((dat = list_pop(&c->PTR))) {
                  if (!response_rstart(c, c->request_name_alloc,
                                    DNS_T_PTR, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct address");
                  }
                  didany++;
                  response_rfinish(c, RESPONSE_ANSWER);
                  mem_free(dat);
            }
            if (!didany && c->NS) {
                  /* if no answers, but we're NS, we give SOA */
                  if (!response_rstart(c, c->request_name_zone, DNS_T_SOA, c->ttl)
                  || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
                  || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
                  || !response_addulong(c, c->serial)
                  || !response_addulong(c, c->refresh)
                  || !response_addulong(c, c->retry)
                  || !response_addulong(c, c->expire)
                  || !response_addulong(c, c->minimum)) {
                        fatal("could not construct SOA");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }

            /* also included SOA */
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone,
                                    DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }
      } else if (_eq2(DNS_T_NS)) {
            /* NS returns both answer and authority (since we asked nicely) */
            for (lp = c->NS; lp; lp = lp->next) {
                  dat = lp->str;
                  if (!response_rstart(c, c->request_name_alloc, DNS_T_NS, c->ttl)
                  || !response_addname(c, lp->str)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone, DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }
      } else if (_eq2(DNS_T_TXT)) {
            /* TXT */
            while ((dat = list_pop(&c->TXT))) {
                  if (!response_rstart(c, c->request_name_alloc, DNS_T_TXT, c->ttl)
                  || !response_addbytes(c, dat, dns_domain_length(dat)-1)) {
                        fatal("could not construct TXT");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
                  didany++;
                  mem_free(dat);
            }
            if (!didany && c->NS) {
                  /* if no answers, but we're NS, we give SOA */
                  if (!response_rstart(c, c->request_name_zone, DNS_T_SOA, c->ttl)
                  || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : (c->NS ? c->NS->str : ""))
                  || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
                  || !response_addulong(c, c->serial)
                  || !response_addulong(c, c->refresh)
                  || !response_addulong(c, c->retry)
                  || !response_addulong(c, c->expire)
                  || !response_addulong(c, c->minimum)) {
                        fatal("could not construct SOA");
                  }
                  response_rfinish(c, RESPONSE_ANSWER);
            }
            while ((dat = list_pop(&c->NS))) {
                  if (!response_rstart(c, c->request_name_zone, DNS_T_NS, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  response_rfinish(c, RESPONSE_AUTHORITY);
                  if (!ldapdns.no_additionals && !ldapdns.no_additionals_ns)
                        push_subreq(c, dat);
                  mem_free(dat);
            }
      }

#undef _eq2
      if (no_wild)
            complete_phase(c, '+');
      else if (c->subreq_tries) {
            c->subreq++;
            finish_subrequest(c);
      } else
            complete_phase(c, '+');
      //printf("outta here\n");
}
static void ldapdns_process_axfrsearch_mid(dns_ctx *c, char *autodn)
{
      int err_code;
      char *dn, *dat;
      str_t rn, n;
      list_t y;

      if (autodn) {
            dn = autodn;
      } else {
            /* process multi-record results... blech */
            if (!c->message) {
                  /* odd... no message waiting? */
                  fatal("assertion: axfrsearch processing without message!");
            }
      
            //printf("phase3\n");
            ldap_parse_result(c->c->ldap_con, c->message,
                        &err_code,
                        0,0,0,0,0);
            if (err_code != LDAP_SUCCESS) {
                  if (err_code == LDAP_SERVER_DOWN) {
                        pthread_mutex_lock(&c->c->lock);
                        restart_ldap_connection(c->c);
                        pthread_mutex_unlock(&c->c->lock);
                        /* resend axfr-query */
                        do_axfrsearch(c, c->request_name);
                        return;
                  } else if (err_code == LDAP_TIMEOUT) {
                        /*response_refuse(c);*/
                        complete_phase(c, '?');
                        return;
                  } else if (err_code == LDAP_NO_SUCH_OBJECT
                  || err_code == LDAP_LOOP_DETECT
                  || err_code == LDAP_UNWILLING_TO_PERFORM
                  || err_code == LDAP_ALIAS_PROBLEM
                  || err_code == LDAP_INVALID_SYNTAX
                  || err_code == LDAP_INAPPROPRIATE_MATCHING
                  || err_code == LDAP_NO_SUCH_ATTRIBUTE) {
                        /* doesn't exist... VERY strange... */
                        //printf("did not exist?\n");
                        response_refuse(c);
                        complete_phase(c, '-');
                        return;
                  }
      
                  fatal("ldap_result (zonesearch): %s", ldap_err2string(err_code));
            }
      
            if (!ldap_load_dns_attributes(c, &dn, 0)) {
                  goto finish_axfr_l;
            }
      }
                  
      ldap_to_name(n, dn);
      mem_free(dn);

      if (! *(str(n))) /* invalid name? */
            goto pass_this_round_l;

      name_to_dns(rn, str(n));
      mem_free(str(n));

      /* nameservers */
      if (!autodn) {
            while ((dat = list_pop(&c->NS))) {
                  if (!response_axstart(c, 0, str(rn), DNS_T_NS, DNS_C_IN, c->ttl)
                  || !response_addname(c, dat)) {
                        fatal("could not construct NS");
                  }
                  mem_free(dat);
                  response_axfinish(c);
            }
      }

      /* addresses */
      while ((dat = list_pop(&c->A))) {
            if (!response_axstart(c, 0, str(rn), DNS_T_A, DNS_C_IN, c->ttl)
            || !response_addbytes(c, dat, 4)) {
                  fatal("could not construct address");
            }
            mem_free(dat);
            response_axfinish(c);
      }

      /* cnames */
      while ((dat = list_pop(&c->CNAME))) {
            if (!response_axstart(c, 0, str(rn),
                        DNS_T_CNAME, DNS_C_IN, c->ttl)
            || !response_addname(c, dat)) {
                  fatal("could not construct address");
            }
            response_axfinish(c);
            mem_free(dat);
      }

      /* exchanges */
      while ((dat = list_pop(&c->MX))) {
            if (!response_axstart(c, 0, str(rn),
                              DNS_T_MX, DNS_C_IN, c->ttl)
            || !response_addbytes(c, dat, 2)
            || !response_addname(c, dat+2)) {
                  fatal("could not construct address");
            }
            response_axfinish(c);
            mem_free(dat);
      }
      /* text */
      while ((dat = list_pop(&c->TXT))) {
            if (!response_axstart(c, 0, str(rn), DNS_T_TXT,
                              DNS_C_IN, c->ttl)
            || !response_addbytes(c, dat, dns_domain_length(dat)-1)) {
                  fatal("could not construct TXT");
            }
            response_axfinish(c);
            mem_free(dat);
      }
      /* pointers */
      while ((dat = list_pop(&c->PTR))) {
            if (!response_axstart(c, 0, str(rn), DNS_T_PTR,
                              DNS_C_IN, c->ttl)
            || !response_addname(c, dat)) {
                  fatal("could not construct PTR");
            }
            response_axfinish(c);
            mem_free(dat);
      }
      /* srv and everything else */
      while ((dat = list_pop(&c->Generic))) {
            response_generic(c, 1, c->request_name_alloc,
                        dat, c->ttl);
            response_axfinish(c);
            mem_free(dat);
      }

      mem_free(str(rn));

pass_this_round_l:
      c->still_using_message = 1;
      return;

finish_axfr_l:
      if (!response_axstart(c, 1, c->request_name, DNS_T_SOA, DNS_C_IN, c->ttl)
      || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : c->ns->str)
      || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
      || !response_addulong(c, c->serial)
      || !response_addulong(c, c->refresh)
      || !response_addulong(c, c->retry)
      || !response_addulong(c, c->expire)
      || !response_addulong(c, c->minimum)) {
            fatal("could not construct SOA");
      }
      while ((dat = list_pop(&c->ns))) {
            mem_free(dat);
      }
      c->ns = 0;
      response_axfinish(c);
      complete_phase(c, '+');
}
static void ldapdns_process_axfrsearch_start(dns_ctx *c)
{
      const char *attrs[11] = {
                        "aRecord",
                        "mXRecord",
                        "cNAMERecord",
                        "seeAlso",
                        "description",
                        "photo",
                        "mail",
                        "nSRecord",
                        "modifyTimestamp"
      };
      int r;
      int err_code;
      char *dn, *dat;
      list_t y;

      /* process multi-record results... blech */
      if (!c->message) {
            /* odd... no message waiting? */
            fatal("assertion: axfrsearch processing without message!");
      }

      //printf("phase3\n");
      ldap_parse_result(c->c->ldap_con, c->message,
                  &err_code,
                  0,0,0,0,0);
      if (err_code != LDAP_SUCCESS) {
            if (err_code == LDAP_SERVER_DOWN) {
                  pthread_mutex_lock(&c->c->lock);
                  restart_ldap_connection(c->c);
                  pthread_mutex_unlock(&c->c->lock);
                  /* resend axfr-query */
                  do_axfrsearch(c, c->request_name);
                  return;
            } else if (err_code == LDAP_TIMEOUT) {
                  /*response_refuse(c);*/
                  complete_phase(c, '?');
                  return;
            } else if (err_code == LDAP_NO_SUCH_OBJECT
            || err_code == LDAP_LOOP_DETECT
            || err_code == LDAP_UNWILLING_TO_PERFORM
            || err_code == LDAP_ALIAS_PROBLEM
            || err_code == LDAP_INVALID_SYNTAX
            || err_code == LDAP_INAPPROPRIATE_MATCHING
            || err_code == LDAP_NO_SUCH_ATTRIBUTE) {
                  /* doesn't exist... VERY strange... */
                  //printf("did not exist?\n");
                  response_refuse(c);
                  complete_phase(c, '-');
                  return;
            }

            fatal("ldap_result (zonesearch): %s", ldap_err2string(err_code));
      }

      /* we have results... load them into lists*/
      if (!ldap_load_dns_attributes(c, &dn, 1) || !c->NS) {
            response_refuse(c);
            complete_phase(c, '-');
            return;
      }

      add_peer_ns(c, 0);
//    response_aa(c, 1);

      /* add these nameservers to plan-for-authority */
      c->ns = c->NS; c->NS = 0;

      /* before you can call any axfr calls */
      response_axfr(c);

      /* SOA comes first...
       */
      if (!response_axstart(c, 1, c->request_name, DNS_T_SOA, DNS_C_IN, c->ttl)
      || !response_addname(c, ldapdns.self_ns ? ldapdns.self_ns : c->ns->str)
      || !response_addname(c, c->ADM ? c->ADM->str : ldapdns.hostmaster)
      || !response_addulong(c, c->serial)
      || !response_addulong(c, c->refresh)
      || !response_addulong(c, c->retry)
      || !response_addulong(c, c->expire)
      || !response_addulong(c, c->minimum)) {
            fatal("could not construct SOA");
      }
      response_axfinish(c);

retry_axfr_next_top_l:
      if (ldapdns.dn_mode == DN_MODE_RFC1279
                  || ldapdns.dn_mode == DN_MODE_MSDNS) {
            attrs[9] = (const char *)"dNSRecord";
            attrs[10] = (const char *)0;
      } else {
            attrs[9] = (const char *)0;
      }

      pthread_mutex_lock(&c->c->lock);
      r = ldap_search(c->c->ldap_con,
                  dn,               /* base */
                  LDAP_SCOPE_SUBTREE,     /* scope */
                  "(objectClass=*)",      /* ldap filter */
                  (char **)attrs,         /* attrs */
                  0);               /* attrsonly */

      if (r == -1) {
            /* uh oh: possibly an out of memory error */
            restart_ldap_connection(c->c);
            pthread_mutex_unlock(&c->c->lock);
            goto retry_axfr_next_top_l;
      }

      //printf("sent query for %d (was %d)\n", r, c->message_id);
      c->message_id = r;
      c->phase = PHASE_AXFRSEARCH;

      c->c->message_sent++;
      //warning("axfrfirst %d / %d", c->c->message_sent, c->c->message_wait);
      if (c->c->message_wait)
            pthread_cond_broadcast(&c->c->active);

      pthread_mutex_unlock(&c->c->lock);
      c->still_using_message = 0;

      return;
}

static void inline initialize_handler(int i, dns_ctx *c)
{
      c->n = i;
      if (pthread_mutex_init(&c->lock, NULL) != 0)
            cfatal("pthread_mutex_init(h%d): %s", i);

      c->sec_update = 0;
      c->sec_prereq = 0;
      c->sock = -1;
      c->swm = 0;
      c->axfr = 0;
      c->axfr_base = 0;
      c->ns = 0;
      c->phase = PHASE_IDLE;
      c->subreq = c->subreq_valid = 0;
      c->subreq_in = c->subreq_in_alloc = 0;
      c->response_names.b = 0;
      c->response_names.s = 0;
      c->request_name_alloc = 0;
      c->request_attr = 0;
      c->message = 0;
//    c->message_entry = 0;
      c->NS = 0;
      c->adlen = -1;
      c->wantdie = 0;
      c->subreq_tries = 0;
      c->subreq_done = 0;
      c->DNSRecord = c->A = c->CNAME = c->MX = c->SRV = c->TXT =
            c->ADM = c->PTR = c->Generic = 0;
      bin_init(c->response);
}
static void *one2one_msgwait_loop(void *o_void)
{
      struct timeval tv;
      LDAPMessage *result;
      dns_ctx *c;
      ldap_ctx *o;
      list_t lp;
      time_t now;
      int r;

      /* setup linkage */
      o = (ldap_ctx *)o_void;
      for (c = handler; c; c = c->next) {
            if (c->n == o->n) break;
      }
      if (!c) {
            fatal("Could not find matching 1:1 handler (%d)", o->n);
      }

      c->c = o;

      for (;;) {
            /* do upkeep */
            time(&now);
            tp_housekeeping((long *)&now);
            handle_messages();
            if (tp_read(c) < 1) continue;

            /* switch it */
            c->swm = 0;
            for (lp = ldapdns.swm; lp; lp = lp->next) {
                  if (!lp->str) continue;
                  if (lp->str[0] == 0x04) {
                        if (ipv4_in_subnet(lp->str+1, c->ip)) {
                              c->swm = lp->str + 9;
                              if (!ipv4_null(lp->str+1))
                                    break;
                        }
#ifdef HAVE_IPV6
                  } else if (lp->str[0] == 0x06) {
                        if (ipv6_in_subnet(lp->str+1, c->ip)) {
                              c->swm = lp->str + 33;
                              if (!ipv6_null(lp->str+1))
                                    break;
                        }
#endif
                  }
            }

            o->load++;

            /* start phase 1 */
            time(&c->lastt);
            engine_dns_answer(c, c->lastt);

            /* phase is now zonesearch */
            while (c->phase != PHASE_IDLE) {
                  tv.tv_usec = 0;
                  tv.tv_sec = 14;
                  pthread_mutex_lock(&o->lock);
                  r = ldap_result(o->ldap_con, c->message_id, 0,
                              &tv, &result);

                  if (r == -1) {
                        complete_phase(c, '?');
                        c->c = 0; /* give it up */
                        c->phase = PHASE_IDLE;
                        restart_ldap_connection(o);
                        pthread_mutex_unlock(&o->lock);
                        break;
                  }

                  pthread_mutex_unlock(&o->lock);
                  time(&now);

                  if (r == 0 && now - c->lastt > 12) {
                        pthread_mutex_lock(&log_lock);
                        warning("handler %d has waited too long", c->n);
                        pthread_mutex_unlock(&log_lock);

                        /* whoops; abort this biotch */
                        if (c->message_id > -1) {
                              pthread_mutex_lock(&o->lock);
                              /* we don't want to do this if we're
                               * using the openldap client-side
                               * cache
                               */
#ifdef ACCELERATE_CACHE
                              if (!ldapdns.accelerate_cache)
#endif
                                    ldap_abandon(o->ldap_con,
                                          c->message_id);
                              c->message_id = -1;
                              pthread_mutex_unlock(&o->lock);
                        }

                        complete_phase(c, '?');
                        break;
                  }

                  c->message = result;
                  c->still_using_message = 0;
                  switch (c->phase) {
                  case PHASE_ZONESEARCH:
                        ldapdns_process_zonesearch(c);
                        break;
                  case PHASE_ATTRSEARCH:
                        ldapdns_process_attrsearch(c, 0);
                        break;
                  case PHASE_AXFRFIRST:
                        ldapdns_process_axfrsearch_start(c);
                        break;
                  case PHASE_AXFRSEARCH:
                        ldapdns_process_axfrsearch_mid(c, 0);
                        break;
                  case PHASE_NSUPDATE:
                        ldapdns_process_update(c);
                        break;
                  case PHASE_SIMPLESEARCH:
                        ldapdns_process_attrsearch(c, 1);
                        break;
                  };
                  if (!c->still_using_message) {
                        if (ldap_msgfree(result) == -1) {
                              cfatal("ldap_msgfree: %s");
                        }
                        c->message = 0;
                        o->message_sent--;
                  }

                  c->lastt = now;
            }
      }
}
static void *dns_msgwait_loop(void *d_void)
{
      time_t now;
      int need_second_round, did_second_round;
      dns_ctx *c;
      int j, p, noavail;
      list_t lp;

      /* we don't do anything with d_void */
      for (;;) {
            /* do upkeep */
            time(&now);
            tp_housekeeping((long *)&now);
            handle_messages();
            pthread_mutex_lock(&handler_lock);
            c = handler;
            pthread_mutex_unlock(&handler_lock);
            need_second_round = did_second_round = 0;
            noavail = 1;
round_top_l:
            for (; c; c = c->next) {
                  if ((volatile)c->phase != PHASE_IDLE)
                        continue;
                  break;
            }
            if (!c) {
                  if (!noavail) {/* restart from top  */
                        if (need_second_round && !did_second_round) {
                              did_second_round = 1;
                              pthread_mutex_lock(&handler_lock);
                              c = handler;
                              pthread_mutex_unlock(&handler_lock);
                              noavail = 1;
                              goto round_top_l;
                        }
                        continue;
                  }

                  c = mem_alloc(sizeof(dns_ctx));
                  if (!c) {
                        cfatal("new_handler:mem_alloc: %s");
                  }
                  pthread_mutex_lock(&handler_lock);
                  initialize_handler(ldapdns.handlers, c);
                  ldapdns.handlers++;
                  c->next = handler;
                  c->prev = 0;
                  handler = c;
                  if (c->next) /* should never fail */
                        c->next->prev = c;
                  pthread_mutex_unlock(&handler_lock);
                  continue;
            }
            noavail = 0;
            /* this blocks until data can be read */
            //warning("tp read");
            switch (tp_read(c)) {
            case -1: /* they need a second round just-in-case */
                  need_second_round = 1;
                  /* fall through */
            case 0:
                  c = c->next;
                  goto round_top_l;
            };

            /* switch it */
            c->swm = 0;
            for (lp = ldapdns.swm; lp; lp = lp->next) {
                  if (!lp->str) continue;
                  if (lp->str[0] == 0x04) {
                        if (ipv4_in_subnet(lp->str+1, c->ip)) {
                              c->swm = lp->str + 9;
                              if (!ipv4_null(lp->str+1))
                                    break;
                        }
#ifdef HAVE_IPV6
                  } else if (lp->str[0] == 0x06) {
                        if (ipv6_in_subnet(lp->str+1, c->ip)) {
                              c->swm = lp->str + 33;
                              if (!ipv6_null(lp->str+1))
                                    break;
                        }
#endif
                  }
            }

            /* find the lowest-loaded thread */
            for (j = 0, p = -1; j < ldapdns.ldap_threads; j++) {
                  if (ldap_thread[j].load == 0) /* 0 is good :) */
                        break;
                  if (p == -1 || ldap_thread[p].load > ldap_thread[j].load)
                        p = j;
            }
            if (j == ldapdns.ldap_threads) {
                  j = p;
                  if (p == -1) {
                        /* this should never happen */
                        fatal("ldapdns.ldap_threads got zero'd somehow");
                  }
            }

            /* assign it to a thread */
            c->c = &ldap_thread[j];

            /* and increase the load */
            pthread_mutex_lock(&ldap_thread[j].load_lock);
            ldap_thread[j].load++;
            pthread_mutex_unlock(&ldap_thread[j].load_lock);

            /* start phase 1 */
            time(&c->lastt);
            engine_dns_answer(c, c->lastt);
            /* (phase will be set to PHASE_ZONESEARCH) */

            c = c->next;
            goto round_top_l;
      }

      return 0;
}
static void *ldap_msgwait_loop(void *c_void)
{
      LDAPMessage *result, *exresult;
      struct timeval tv;
      int r, i, msgid, answered;
      time_t now;
      ldap_ctx *c;
      dns_ctx *x;

      /* here's our context */
      c = c_void;

      /* message loop */
      for (answered = 0;;) {
            pthread_mutex_lock(&c->lock);
            //warning("%d answered", answered);
            c->message_sent -= answered; answered = 0;
did_restart_l:
            //warning("enter sent-block %d", c->message_sent);
            while (!c->message_sent) {
                  c->message_wait++;
                  pthread_cond_wait(&c->active, &c->lock);
                  c->message_wait--;
            }
            //warning("exit sent-block %d", c->message_sent);
            tv.tv_usec = 0;
            tv.tv_sec = 14;
            result = 0;
            //warning("ldap_result (wait)");
            r = ldap_result(c->ldap_con, LDAP_RES_ANY, 0, &tv, &result);
            //warning("ldap_result (done)");
            if (r == -1) {
                  /* it remains locked */
                  restart_ldap_connection(c);
                  goto did_restart_l;
            }
            pthread_mutex_unlock(&c->lock);

            if (r == 0)
                  msgid = -1;
            else {
                  msgid = ldap_msgid(result);
                  if (msgid == -1) {
                        /* err... */
                        ldap_msgfree(result);
                        pthread_mutex_lock(&log_lock);
                        warning("msgid on handler %d returned -1", i);
                        pthread_mutex_unlock(&log_lock);
                        continue;
                  }
            }

            time(&now);
            pthread_mutex_lock(&handler_lock);
            x = handler;
            pthread_mutex_unlock(&handler_lock);
            for (; x; x = x->next) {
                  /* not _our_ running handler */
                  if (x->c != c)
                        continue;

                  /* not a running handler */
                  if ((volatile)x->phase == PHASE_IDLE)
                        continue;
                  /* bind's resolver waits 10 seconds...
                   * we'll wait 12...
                   */
                  if (msgid != -1 && msgid == x->message_id) {
                        /* do nothing; fall though */
                        x->message = result;
                        msgid = -1;
                  } else if (now - x->lastt > 12) {
                        pthread_mutex_lock(&log_lock);
                        warning("handler %d has waited too long", x->n);
                        pthread_mutex_unlock(&log_lock);

                        /* whoops; abort this biotch */
                        if (x->message_id > -1) {
                              pthread_mutex_lock(&c->lock);
                              /* we don't want to do this if we're
                               * using the openldap client-side
                               * cache
                               */
#ifdef ACCELERATE_CACHE
                              if (!ldapdns.accelerate_cache)
#endif
                                    ldap_abandon(c->ldap_con,
                                          x->message_id);
                              x->message_id = -1;
                              c->message_sent--;
                              pthread_mutex_unlock(&c->lock);
                        }

                        complete_phase(x, '?');
                        continue;
                  } else if (x->message_id == -1) {
                        continue;
                  } else if (c->message_sent > 0) {
                        tv.tv_sec = 0;
                        tv.tv_usec = 0;
                        //pthread_mutex_lock(&c->lock);
                        exresult = 0;
                        r = ldap_result(c->ldap_con, x->message_id,
                                    0, &tv, &exresult);
                        if (r == 0) {
                              //pthread_mutex_unlock(&c->lock);
                              continue;
                        }
                        if (r == -1) {
                              pthread_mutex_lock(&c->lock);
                              restart_ldap_connection(c);
                              pthread_mutex_unlock(&c->lock);
                              goto did_restart_l;
                        }

                        x->message = exresult;
                        //pthread_mutex_unlock(&c->lock);
                        /* fall through */
                  } else {
                        continue;
                  }

                  x->still_using_message = 0;
                  switch (x->phase) {
                  case PHASE_ZONESEARCH:
                        ldapdns_process_zonesearch(x);
                        break;
                  case PHASE_ATTRSEARCH:
                        ldapdns_process_attrsearch(x, 0);
                        break;
                  case PHASE_AXFRFIRST:
                        ldapdns_process_axfrsearch_start(x);
                        break;
                  case PHASE_AXFRSEARCH:
                        ldapdns_process_axfrsearch_mid(x, 0);
                        break;
                  case PHASE_NSUPDATE:
                        ldapdns_process_update(x);
                        break;
                  case PHASE_SIMPLESEARCH:
                        ldapdns_process_attrsearch(x, 1);
                        break;
                  };
                  /* update lastt timer */
                  x->lastt = now;

                  if (!x->still_using_message) {
                        if (ldap_msgfree(x->message) == -1) {
                              cfatal("ldap_msgfree: %s");
                        }
                        x->message = 0;
                        answered++;
                  }
            }
      }
}
static void parse_read_search(char *d)
{
      char *x;
      str_t p;

      if (!d) return;

      x = d;
      while (*x && !isspace((unsigned int)*x)) x++;
      if (*x == 0) return;
      *x = 0; x++;
      while (isspace((unsigned int)*x)) x++;

      name_to_dns(p, d);

      switch (ht_store(&ldapdns.search, str(p), str_len(p), str_dup(x))) {
      case -1: cfatal("parse_read_search: %s");
      case 0: fatal("cannot (presently) search for %s twice", d);
      case 1: break;
      };
      mem_free(str(p)); /* ht_store makes it's own copy */
}
static unsigned long __search_hash(const void *str, unsigned len)
{
      const char *h;
      unsigned long d;
      unsigned i;

      d = 5431;
      for (i = 0, h = str; i < len; i++) {
            /* Clib */
            d = (d << 5) ^ (tolower((int)h[i]));
      }

      return d;
}
static int root_read_search(void)
{
      FILE *fp;
      str_t line;
      int c;

      ht_init(&ldapdns.search, 7, __search_hash);

      fp = fopen("search", "r");
      if (!fp)
            return 0;

      str_init(line);
      /* could hang if we attached to a device node... but it's in
       * initialization only, so np here
       */
      while ((c = fgetc(fp)) != EOF) {
            if (c == '\r' || c == '\n') {
                  if (str(line)[0] && str(line)[0] != '#') {
                        /* parse out this data */
                        parse_read_search(str(line));
                  }
                  str_copy(line, "");
            } else {
                  str_addch(line, c);
            }
      }
      fclose(fp);
      return 1;
}
static int root_read_sw(const char *filename, list_t *p)
{
      FILE *fp;
      str_t line;
      bin_t res;
      unsigned char *x, cidr[IP_LEN*2];
      int c;

      fp = fopen(filename, "r");
      if (!fp)
            return 0;

      str_init(line);
      bin_init(res);
      /* could hang if we attached to a device node... but it's in
       * initialization only, so np here
       */
      while ((c = fgetc(fp)) != EOF) {
            if (c == '\r' || c == '\n' || c == ' ' || c == '\t') {
                  if (str(line)[0] && str(line)[0] != '#') {
                        /* parse as follows */
                        /* key=cidr */
                        for (x = str(line); *x && *x != '='; x++);
                        if (*x == '=') {
                              *x = 0;
                              x++;
#ifdef HAVE_IPV6
                              if (ipv6_cidr(x, cidr)) {
                                    bin_copy(res, "\x06", 1);
                                    bin_cat(res, cidr, 32);
                                    bin_cat(res, str(line), str_len(line));
                                    bin_0(res);
                                    list_push(p, caddr(res));
                                    bin_init(res);
                              } else
#endif
                              if (ipv4_cidr(x, cidr)) {
                                    bin_copy(res, "\x04", 1);
                                    bin_cat(res, cidr, 8);
                                    bin_cat(res, str(line), str_len(line));
                                    bin_0(res);
                                    list_push(p, caddr(res));
                                    bin_init(res);
                              }
                        }

                  }
                  str_copy(line, "");
            } else {
                  str_addch(line, c);
            }
      }
      fclose(fp);
      return 1;
}

static int root_read(const char *filename, char **buf, int secure)
{
      str_t retbuf;
      FILE *fp;
      int c, sp;

      fp = fopen(filename, "r");
      if (!fp)
            return 0;

      if (secure) {
            struct stat sb;

            if (fstat(fileno(fp), &sb) == -1) {
                  fclose(fp);
                  return 0;
            }
            if (sb.st_mode & 0377) {
                  fclose(fp);
                  fatal("$ROOT/%s must have mode 0400", filename);
            }
      }

      str_init(retbuf);
      sp = 0;
      /* could hang if we attached to a device node... but it's in
       * initialization only, so np here
       */
      while ((c = fgetc(fp)) != EOF) {
            if (c == '\r' || c == '\n' || c == ' ' || c == '\t') {
                  sp = 1;
            } else {
                  if (sp) {
                        sp = 0;
                        str_addch(retbuf, ' ');
                  }
                  str_addch(retbuf, c);
            }
      }
      fclose(fp);
      *buf = str(retbuf);
      return 1;
}
static int gc_ldap_cache(void *ignore)
{
      register int i;

#ifdef ACCELERATE_CACHE
      if (!ldapdns.accelerate_cache)
#endif
            return 0;

#ifdef ACCELERATE_CACHE
      pthread_mutex_lock(&host_lock);
      for (i = 0; i < ldapdns.ldap_threads; i++) {
            pthread_mutex_lock(&ldap_thread[i].lock);
            ldap_destroy_cache(ldap_thread[i].ldap_con);
            ldap_enable_cache(ldap_thread[i].ldap_con, ldapdns.accelerate_cache, 0);
            pthread_mutex_unlock(&ldap_thread[i].lock);
      }
      pthread_mutex_unlock(&host_lock);
#endif

      return 1;
}
int main(int argc, char *argv[])
{
      char *x, *y, *h;
      int i, j, p;
      static pthread_attr_t helper_thread_attr;
      str_t retbuf, hbuf;
      char spbuf[32];
      pthread_t dummy;
      list_t lp;
      bin_t res;

      /* try and get logging up first */
      x = env_get("LOG");
      log_init(x);

      /* allow self-supervise */
      x = env_get("SUPERVISE");
      if (x) {
            supervise(x);
      }

      /* setup signals */
      signal(SIGSTOP, handle_signal);
      signal(SIGCONT, handle_signal);
      signal(SIGTERM, handle_signal);
      signal(SIGHUP, handle_signal);
      signal(SIGINT, handle_signal);

      /* load AXFR early */
      x = env_get("AXFR");
      if (!x)
            x = env_get("LDAP_AXFR");
      if (x && *x) {
            /* save it in dns form... i'm just silly like that */
            name_to_dns(retbuf, x);
            ldapdns.axfr_base = str(retbuf);
      } else
            ldapdns.axfr_base = 0;

      if (pthread_attr_init(&helper_thread_attr) != 0)
            cfatal("pthread_attr_init: %s");
      if (pthread_attr_setdetachstate(&helper_thread_attr,
                        PTHREAD_CREATE_DETACHED))
            cfatal("pthread_attr_setdetachstate: %s");

      ldapdns.ldap_threads = -1;
      ldapdns.dns_threads = -1;
      x = env_get("THREADS");
      if (x) {
            ldapdns.ldap_threads = atoi(x);
            if (ldapdns.ldap_threads > 1) {
                  ldapdns.dns_threads = ldapdns.ldap_threads / 2;
                  if (ldapdns.ldap_threads % 2 == 1)
                        ldapdns.dns_threads++;
            }
      }

      x = env_get("DEFAULT_REFRESH"); if (x) default_refresh = atoi(x);
      x = env_get("DEFAULT_RETRY"); if (x) default_retry = atoi(x);
      x = env_get("DEFAULT_EXPIRE"); if (x) default_expire = atoi(x);
      x = env_get("DEFAULT_MINIMUM"); if (x) default_minimum = atoi(x);

      x = env_get("LDAP_THREADS");
      if (x) ldapdns.ldap_threads = atoi(x);

      x = env_get("DNS_THREADS");
      if (x) ldapdns.dns_threads = atoi(x);

      if (ldapdns.ldap_threads < 1)
            ldapdns.ldap_threads = 1;
      if (ldapdns.dns_threads < 1)
            ldapdns.dns_threads = 1;

      ldapdns.handlers = (ldapdns.ldap_threads + ldapdns.dns_threads)*2;
      if (ldapdns.handlers < 128) ldapdns.handlers = 128;

      x = env_get("HANDLERS");
      if (x) {
            j = atoi(x);
            if (j > 0)
                  ldapdns.handlers = j;
      }

      /* setup TCP options */
      x = env_get("TIMEOUT_TCP");
      if (!x)
            x = env_get("TIMEOUT");
      if (x) {
            /* barg */
            ldapdns.timeout_tcp = atoi(x);
      } else
            ldapdns.timeout_tcp = 0;

      x = env_get("ALWAYS_HANGUP_TCP");
      if (!x)
            x = env_get("ALWAYS_HANGUP");
      ldapdns.always_hangup = (x ? 1 : 0);

      /* get listening socket */
      tp_initialize();
            /* !!! tcpserver modifies ldapdns.handlers */

      if (ldapdns.handlers == 1) {
            /* special handler mode... */
            ldapdns.one2one_mode = 1;
            if (ldapdns.dns_threads > ldapdns.ldap_threads) {
                  /* silly... */
                  ldapdns.handlers = ldapdns.ldap_threads = ldapdns.dns_threads;
            } else {
                  ldapdns.handlers = ldapdns.dns_threads = ldapdns.ldap_threads;
            }
      } else {
            ldapdns.one2one_mode = 0;
      }

      ldap_thread = mem_alloc(ldapdns.ldap_threads * sizeof(ldap_ctx));
      if (!ldap_thread)
            cfatal("thread:mem_alloc: %s");
      for (i = 0; i <ldapdns.ldap_threads; i++) {
            ldap_thread[i].ldap_con = 0;

            /* one2one mode doesn't need these ... */
            if (pthread_mutex_init(&ldap_thread[i].lock, NULL) != 0)
                  cfatal("pthread_mutex_init(t%d): %s", i);
            if (pthread_mutex_init(&ldap_thread[i].load_lock, NULL) != 0)
                  cfatal("pthread_mutex_init(t%d): %s", i);
            if (pthread_cond_init(&ldap_thread[i].active, NULL) != 0)
                  cfatal("pthread_cond_init(t%d): %s", i);
      }
      if (pthread_mutex_init(&log_lock, NULL) != 0)
            cfatal("pthread_mutex_init(log_lock): %s");
      if (pthread_mutex_init(&host_lock, NULL) != 0)
            cfatal("pthread_mutex_init(host_lock): %s");
      if (pthread_mutex_init(&handler_lock, NULL) != 0)
            cfatal("pthread_mutex_init(handler_lock): %s");
      if (pthread_mutex_init(&engine_message_mutex, NULL) != 0)
            cfatal("pthread_mutex_init(pause_lock): %s");
      if (pthread_cond_init(&engine_pause_cond, NULL) != 0)
            cfatal("pthread_cond_init(pause_cond): %s");

      x = env_get("ROOT");
      if (!x)
            fatal("$ROOT not set");
      if (chdir(x) == -1)
            cfatal("unable to chdir to %s: %s", x);

      /* 0 is ok. */
      ldapdns.notify = env_get("HELPER_NOTIFY");

      ldapdns.dn_mode = DN_MODE_COSINE;
      x = env_get("SCHEMA");
      if (x) {
            if (str_equali(x, "rfc1279"))
                  ldapdns.dn_mode = DN_MODE_RFC1279;
            else if (str_equal(x, "msdns") || str_equal(x, "ad"))
                  ldapdns.dn_mode = DN_MODE_MSDNS;
            else if (str_equal(x, "cosine") || str_equali(x, "ldapdns1")
                              || str_equali(x, "ldapdns-1"))
                  ldapdns.dn_mode = DN_MODE_COSINE;
            else if (str_equal(x, "ldapdns") || str_equali(x, "ldapdns2")
                              || str_equali(x, "ldapdns-2"))
                  ldapdns.dn_mode = DN_MODE_LDAPDNS;
            else
                  fatal("$SCHEMA set to an invalid setting");
      } else {
            x = env_get("RFC1279");
            if (x)
                  ldapdns.dn_mode = DN_MODE_RFC1279;
            else {
                  x = env_get("DNSRECORD");
                  if (x) {
                        if (str_equali(x, "rfc1279"))
                              ldapdns.dn_mode = DN_MODE_RFC1279;
                        else if (str_equal(x, "msdns") || str_equal(x, "ad"))
                              ldapdns.dn_mode = DN_MODE_MSDNS;
                        else
                              fatal("$DNSRECORD set to an invalid setting");
                  }
            }
      }

      x = env_get("NSUPDATE");
      if (x) {
            name_to_dns(retbuf, x);
            ldapdns.update = str(retbuf);
      } else
            ldapdns.update = 0;

      x = env_get("LDAP_SUFFIX");
      if (!x)
            ldapdns.ldap_suffix = "";
      else {
            /* ldap_suffix is dynamically generated */
            str_init(retbuf);
            str_copy(retbuf, x);
            ldapdns.ldap_suffix = str(retbuf);
            
            /* x came from env; so it's ASCIIZ */
            lp = ldap_into_parts(x);
            /* reverse the list: the interesting stuff is at the other end */
            list_reverse(&lp);
            y = list_pop(&lp);
            mem_free(y);

            y = list_pop(&lp);
            /* test for the microsoft DNS suffix */
            if (y && str_equali(y, "cn=system")) {
                  mem_free(y);
                  y = list_pop(&lp);
                  if (y && str_equali(y, "cn=microsoftdns")) {
                        mem_free(y);
                        ldapdns.dn_mode = DN_MODE_MSDNS;
                  } else
                        mem_free(y);
            } else
                  mem_free(y);
            while ((y = list_pop(&lp)))
                  mem_free(y);
      }

      /* this is a performance thing; PDNS claims 10k requests,
       * i think it's a bad idea...
       */
      x = env_get("NO_ADDITIONALS");
      if (!x) {
            x = env_get("NO_ADDITIONALS_NS");
            if (x)
                  ldapdns.no_additionals = ldapdns.no_additionals_ns = 1;
            else
                  ldapdns.no_additionals = ldapdns.no_additionals_ns = 0;
      } else {
            ldapdns.no_additionals = 1;
            ldapdns.no_additionals_ns = 0;
      }

      ldapdns.schedule_arecord = SCHEDULE_A_NONE;
      x = env_get("SCHEDULE_ARECORD");
      if (x) {
            if (str_equali(x, "random")) {
                  ldapdns.schedule_arecord = SCHEDULE_A_RANDOM;
            }
      }

      /* this should be fine. */
      srand(time(NULL)^(getpid() << 5)^(getppid()));

      /* mwahahahah */
      x = env_get("NETBIOS");
      if (x)
            ldapdns.netbios = 1;
      else
            ldapdns.netbios = 0;

      /* self nameserver */
      ldapdns.self_ns = 0;
      x = env_get("NS_SELF");
      if (!x) x = env_get("NSSELF");
      if (!x) x = env_get("SELFNS");
      if (!x) x = env_get("SELF_NS");
      if (x) {
            str_init(retbuf);
            name_to_dns(retbuf, x);
            ldapdns.self_ns = str(retbuf);
      }

      /* this adds nameservers */
      ldapdns.peer_ns = 0;
      x = env_get("NS");
      if (!x) {
            for (i = 1;; i++) {
                  sprintf(spbuf, "NS%d", i);
                  x = env_get(spbuf);
                  if (!x) break;
                  str_init(retbuf);
                  name_to_dns(retbuf, x);
                  list_push(&ldapdns.peer_ns, str(retbuf));
            }
      } else {
            for (i = j = 0; x[i]; i++) {
                  if (x[i] == '\r' || x[i] == '\n' || x[i] == ' '
                              || x[i] == '\t') {
                        if (j < i-1) {
                              str_init(retbuf);
                              str_catb(retbuf, x+j, i-j);

                              str_init(hbuf);
                              name_to_dns(hbuf, str(retbuf));
                              mem_free(str(retbuf));

                              list_push(&ldapdns.peer_ns, str(hbuf));
                        }
                        j = i+1;
                  }
            }
            if (j < i-1) {
                  str_init(retbuf);
                  name_to_dns(retbuf, x+j);
                  list_push(&ldapdns.peer_ns, str(retbuf));
            }
      }

      /* you know what you doing... */
      ldapdns.accelerate_cache = 0;
      x = env_get("ACCELERATE_CACHE");
      if (!x)
            x = env_get("CACHE");
      if (x)
            ldapdns.accelerate_cache = atoi(x);
      if (ldapdns.accelerate_cache < 0)
            ldapdns.accelerate_cache = 0;
#ifdef ACCELERATE_CACHE
      if (ldapdns.accelerate_cache) {
            /* register garbage collection */
            mem_register_gc(0, gc_ldap_cache);
      }
#endif

      handler = mem_alloc(ldapdns.handlers * sizeof(dns_ctx));
      if (!handler)
            cfatal("handler:mem_alloc: %s");
      for (i = 0; i < ldapdns.handlers; i++) {
            /* flush handlers */
            dns_ctx *c = &handler[i];
            initialize_handler(i, c);

            c->prev = (i == 0 ? 0 : &handler[i-1]);
            c->next = (i == ldapdns.handlers-1 ? 0 : &handler[i+1]);
      }

/* core */
      x = env_get("HOSTMASTER");
      if (!x)
            fatal("$HOSTMASTER not set");
      str_init(retbuf);
      name_to_dns_fix(retbuf, x, 2);
      ldapdns.hostmaster = str(retbuf);

      ldapdns.auth_mode = AUTH_MODE_ANONYMOUS;
      x = env_get("LDAP_AUTH");
      if (x) {
            if (str_equali(x, "simple"))
                  ldapdns.auth_mode = AUTH_MODE_SIMPLE;
            else if (str_equali(x, "sasl"))
                  ldapdns.auth_mode = AUTH_MODE_SASL;
      }

      x = env_get("LDAP_SASL");
      if (x) {
            ldapdns.auth_mode = AUTH_MODE_SASL;
            ldapdns.ldap_name = x;
      } else {
            x = env_get("LDAP_AUTH_NAME");
            if (!x)
                  x = env_get("LDAP_BINDDN");
            if (x && *x) {
                  ldapdns.ldap_name = x;
                  /* fixup for no $LDAP_AUTH */
                  if (ldapdns.auth_mode == AUTH_MODE_ANONYMOUS)
                        ldapdns.auth_mode = AUTH_MODE_SIMPLE;
            }
      }

      x = env_get("RELATIVE_NAMES");
      ldapdns.relative_names = (x ? 1 : 0);

      if (!root_read("password", &ldapdns.ldap_cred, 1)) {
            if (ldapdns.auth_mode == AUTH_MODE_SIMPLE) {
                  /* no password file */
                  fatal("$ROOT/password is empty");
            }
      }
      /* load the search table (if present) */
      root_read_search();

      /* read switch values (if present) */
      ldapdns.swm = 0;
      root_read_sw("switch", &ldapdns.swm);
      ldapdns.swaxfr = 0;
      root_read_sw("axfr", &ldapdns.swaxfr);

      /* try both host and hosts */
      x = env_get("LDAP_HOSTS");
      if (!x)
            x = env_get("LDAP_HOST");
      if (!x)
            fatal("$LDAP_HOST not set");

      /* load connection host/strings */
      ldapdns.hosts = 0;
      str_init(retbuf);
      str_copy(retbuf, x);
      for (h = str(retbuf); h && *h;) { /* on init: np if it infinites */
            int uri=0;
            for (x = h; *x && *x != ',' && *x != ' '
                        && *x != ';' && *x != '\t'; x++);
            if (*x) {
                  *x = 0;
                  x++;
            } else
                  x = 0;

            p = LDAP_PORT;
            for (y = h; *y && *y != ':'; y++);
            if (*y && *y == ':') {
                  if (*(y+1) && *(y+1) == '/') { /* detect ldap:// uri */
                        uri=1;
                  } else {
                        *y = 0;
                        y++;
                        p = atoi(y);
                        if (p == -1)
                              p = LDAP_PORT;
                  }
            }

            str_init(hbuf);
            str_copy(hbuf, h);
            if (!uri) {
                  str_addch(hbuf, ':');
                  sprintf(spbuf, "%d", p);
                  str_cat(hbuf, spbuf);
            }

            list_push(&ldapdns.hosts, str(hbuf));
            h = x;
      }

      if (!ldapdns.hosts) {
            fatal("No hosts were loaded");
      }

      j = 1;
      for (i = 0, lp=ldapdns.hosts; i < ldapdns.ldap_threads; lp = lp->next) {
            if (!lp) {
                  lp = ldapdns.hosts;
                  if (!j) {
                        fatal("cannot contact any LDAP servers (thread %d)", i);
                  }
                  j = 0;
            }

            if (start_ldap_connection(&ldap_thread[i], lp->str) != -1) {
                  i++;
                  j = 1;
            }
      }
      /* save last used */
      host_lp = lp;
      if (!lp) host_lp = ldapdns.hosts;

      /* chroot */
      if (chroot(".") == -1)
            cfatal("chroot: %s");

      x = env_get("UID");
      if (!x)
            fatal("$UID not set");
      i = atoi(x);
      if (i < 0)
            fatal("$UID invalid (not numeric)");
      x = env_get("GID");
      if (!x)
            fatal("$GID not set");
      j = atoi(x);
      if (j < 0)
            fatal("$GID invalid (not numeric)");

      x = env_get("I_AM_STUPID_LET_ME_RUN_LDAPDNS_AS_ROOT");
      if (x)
            warning("doing something very stupid (running as root)");
      else if (i == 0 || j == 0)
            fatal("refuse to run ldapdns as root");

      /* drop privs */
      if (j != getgid() && setgid(j) == -1) cfatal("setgid: %s");
      if (i != getuid() && setuid(i) == -1) cfatal("setuid: %s");

      /* setup signals */
      signal(SIGPIPE, SIG_IGN); /* i ignore this... but it's okay... */

      eph = pthread_self();
      if (ldapdns.one2one_mode) {
#ifdef ACCELERATE_CACHE
            if (ldapdns.accelerate_cache)
                  log(log_info, "ldap client caching enabled!");
#endif

            log(log_info, "starting ldapdns %s (1:1/%d)", VERSION,
                        ldapdns.handlers);

            /* alternate message loop */
            for (i = 0; i < ldapdns.dns_threads-1; i++) {
                  ldap_thread[i].n = i;
                  if (pthread_create(&ldap_thread[i].id, &helper_thread_attr,
                              one2one_msgwait_loop, &ldap_thread[i]) != 0) {
                        cfatal("pthread_create(%d): %s", i);
                  }
            }
            /* use the main thread for something :) */
            ldap_thread[i].n = i;
            ldap_thread[i].id = pthread_self();
            one2one_msgwait_loop(&ldap_thread[i]);
            return 0;
      }

      /* start helper thread */
      for (i = 0; i < ldapdns.ldap_threads; i++) {

            /* save index */
            ldap_thread[i].n = i;
            /* null these out here */
            ldap_thread[i].message_wait = 0;
            ldap_thread[i].message_sent = 0;
            if (pthread_create(&ldap_thread[i].id, &helper_thread_attr,
                              ldap_msgwait_loop, &ldap_thread[i]) != 0) {
                  cfatal("pthread_create(%d): %s", i);
            }
      }

      if (ldapdns.accelerate_cache)
            log(log_info, "ldap client caching enabled!");

      log(log_info, "starting ldapdns %s (%d:%d/%d)", VERSION,
                        ldapdns.ldap_threads, ldapdns.dns_threads,
                        ldapdns.handlers);

      for (i = 0; i < ldapdns.dns_threads-1; i++) {
            if (pthread_create(&dummy, &helper_thread_attr,
                              dns_msgwait_loop, 0) != 0) {
                  cfatal("pthread_create(%d): %s", i);
            }
            bin_init(res);
            bin_copy(res, (char *)&dummy, sizeof(pthread_t));
            list_push(&other_threads, caddr(res));
      }
      dns_msgwait_loop(0);
      return 0; /* never reached */
}


Generated by  Doxygen 1.6.0   Back to index