#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 */
}
syntax highlighted by Code2HTML, v. 0.9.1