#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 #include #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 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 */ }