Files
smmapdfw/smmapdfw/libsmmapdfw/htdns.c
whottgen ae73ee5094 fix
2007-06-05 10:16:33 +00:00

604 lines
15 KiB
C

/*
Copyright (C) 2004, Wolfgang Hottgenroth
This file is part of smmapdfw.
smmapdfw is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
smmapdfw is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with smmapdfw. If not, write to the Free Software Foundation, 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <sys/types.h>
#include <netinet/in.h>
#define BIND_8_COMPAT
#include <arpa/nameser.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#if HAVE_LIBDJBDNS
#include <dns.h>
#include <djbdns/alloc.h>
#include <pthread.h>
#else
#include <resolv.h>
#endif
#include "htdns.h"
#include "htmalloc.h"
#if ! HAVE_LIBDJBDNS
extern int h_errno;
/* extern struct state _res; */
#endif
union answer_u {
HEADER hdr;
u_char buf[PACKETSZ+1];
};
typedef union answer_u answer_t;
#define get32_x(b, o) htonl((b[o+3] << 24) + (b[o+2] << 16) + (b[o+1] << 8) + b[o])
#define get32(b, o) ((b[o] << 24) + (b[o+1] << 16) + (b[o+2] << 8) + b[o+3])
#define get16(b, o) ((b[o] << 8) + b[o+1])
#if HAVE_LIBDJBDNS
pthread_mutex_t dns_transmit_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
static int get_domain_name(answer_t *answer, int offset, char **name) {
int start, len, i, offset2;
char *name_buf, *name_buf2, *remember_name_buf;
name_buf = NULL;
while (0 != (len = answer->buf[offset++])) {
if (0xC0 == (len & 0xC0)) {
if (NULL != name) { /* if we don't need the result, we don't need to recurse, since a ... */
offset2 = ((len & ~0xC0) << 8) + answer->buf[offset++];
get_domain_name(answer, offset2, &name_buf2);
remember_name_buf = name_buf;
name_buf = (char*) htrealloc(name_buf, ((NULL != name_buf) ? strlen(name_buf) : 0) + strlen(name_buf2) + 1);
if (NULL == remember_name_buf) {
name_buf[0] = '\0';
}
strcat(name_buf, name_buf2);
free(name_buf2);
*name = name_buf;
} else {
offset++; /* ... a recursion pointer is always two octets long ... */
}
return offset; /* ... and is always the final part of a name */
} else {
start = offset;
offset += len;
if (NULL != name) {
i = (NULL != name_buf) ? strlen(name_buf) : 0;
name_buf = (char*) htrealloc(name_buf, i+len+2);
strncpy(name_buf + i, answer->buf + start, len);
name_buf[i+len] = '\0';
strcat(name_buf, ".");
}
}
}
if (NULL != name) {
if (name_buf != NULL) /* we can do this of course only if there is something */
name_buf[strlen(name_buf)-1] = '\0'; /* remove the final dot */
*name = name_buf;
}
return offset;
}
static int get_mx_rdata(answer_t *answer, int offset, int len, mx_rdata_t **resp) {
*resp = (mx_rdata_t*) htmalloc(sizeof(mx_rdata_t));
(*resp)->preference = get16(answer->buf, offset);
(*resp)->exchange = NULL;
get_domain_name(answer, offset+2, &(*resp)->exchange);
(*resp)->type = T_MX;
return 0;
}
static void free_mx_rdata(mx_rdata_t *resp) {
free(resp->exchange);
free(resp);
}
static int get_a_rdata(answer_t *answer, int offset, int len, a_rdata_t **resp) {
*resp = (a_rdata_t*) htmalloc(sizeof(a_rdata_t));
(*resp)->address = htonl(get32(answer->buf, offset));
(*resp)->type = T_A;
return 0;
}
static int get_cname_rdata(answer_t *answer, int offset, int len, cname_rdata_t **resp) {
*resp = (cname_rdata_t*) htmalloc(sizeof(cname_rdata_t));
(*resp)->cname = NULL;
get_domain_name(answer, offset, &(*resp)->cname);
(*resp)->type = T_CNAME;
return 0;
}
static void free_cname_rdata(cname_rdata_t *resp) {
free(resp->cname);
free(resp);
}
static void free_a_rdata(a_rdata_t *resp) {
free(resp);
}
static int get_rdata(answer_t *answer, int type, int offset, int len, void **resp) {
switch (type) {
case T_MX:
get_mx_rdata(answer, offset, len, (mx_rdata_t**)resp);
break;
case T_A:
get_a_rdata(answer, offset, len, (a_rdata_t**)resp);
break;
case T_CNAME:
get_cname_rdata(answer, offset, len, (cname_rdata_t**)resp);
break;
default:
syslog(LOG_ERR, "type %d unsupported\n", type);
}
return 0;
}
static void free_rdata(void *resp) {
rdata_t *d = (rdata_t*)resp;
switch (d->type) {
case T_MX:
free_mx_rdata(resp);
break;
case T_A:
free_a_rdata(resp);
break;
case T_CNAME:
free_cname_rdata(resp);
break;
default:
syslog(LOG_ERR, "type %d unsupported\n", d->type);
}
}
void free_rrs(void **resp) {
void **rdata;
for (rdata = resp; *rdata != NULL; rdata++) {
free_rdata(*rdata);
}
free(resp);
}
static void** get_rrs(char *domain, int qtype) {
unsigned int res, i, cnt, len, offset, x, y, rdlength;
answer_t *answer;
unsigned int class, type, ttl;
char *name;
void **rdata, **rdata2;
#if HAVE_LIBDJBDNS
struct dns_transmit dt = {0};
char *q = NULL;
#endif
syslog(LOG_DEBUG, "get_rrs: looking up domain %s, qtype %d", domain, qtype);
#if HAVE_LIBDJBDNS
{
char s[64];
int flagrecursive = 1;
char t[2];
char localip[4] = "\0\0\0\0";
iopause_fd x[1];
struct taia deadline;
struct taia stamp;
t[0] = '\0';
t[1] = qtype;
if (dns_resolvconfip(s) == -1) {
syslog(LOG_ERR, "failure in dns_resolvconfip: %d, %s\n",
errno, strerror(errno));
return NULL;
}
syslog(LOG_DEBUG, "get_rrs: (1) q = %p", q);
dns_domain_fromdot(&q, domain, strlen(domain));
syslog(LOG_DEBUG, "get_rrs: (2) q = %p", q);
pthread_mutex_lock(&dns_transmit_mutex);
res = dns_transmit_start(&dt, s, flagrecursive, q, t, localip);
syslog(LOG_DEBUG, "get_rrs: (3) q = %p", q);
pthread_mutex_unlock(&dns_transmit_mutex);
if (res == -1) {
syslog(LOG_ERR, "failure in dns_transmit_start: %d, %s\n",
errno, strerror(errno));
return NULL;
}
for (;;) {
int r;
taia_now(&stamp);
taia_uint(&deadline,120);
taia_add(&deadline, &deadline, &stamp);
pthread_mutex_lock(&dns_transmit_mutex);
dns_transmit_io(&dt, x, &deadline);
pthread_mutex_unlock(&dns_transmit_mutex);
iopause(x, 1, &deadline, &stamp);
pthread_mutex_lock(&dns_transmit_mutex);
r = dns_transmit_get(&dt, x, &stamp);
pthread_mutex_unlock(&dns_transmit_mutex);
if (-1 == r) {
syslog(LOG_ERR, "failure in dns_transmit_get: %d, %s\n",
errno, strerror(errno));
pthread_mutex_lock(&dns_transmit_mutex);
dns_transmit_free(&dt);
pthread_mutex_unlock(&dns_transmit_mutex);
return NULL;
}
if (1 == r)
break;
}
answer = (answer_t*) dt.packet;
}
#else
{
answer_t lanswer;
syslog(LOG_DEBUG, "get_rrs: before res_search");
res = res_search(domain, C_IN, qtype, (u_char*) &lanswer, sizeof(answer_t));
if (-1 == res) {
syslog(LOG_ERR, "get_rrs: error in res_search: h_errno=%d, errno=%d, (%s)",
h_errno, errno, strerror(errno));
return NULL;
}
answer = &lanswer;
}
#endif
cnt = sizeof(HEADER);
#if 1
syslog(LOG_DEBUG, "id: %d\n", answer->hdr.id);
syslog(LOG_DEBUG, "qr: %d\n", answer->hdr.qr);
syslog(LOG_DEBUG, "oc: %d\n", answer->hdr.opcode);
syslog(LOG_DEBUG, "aa: %d\n", answer->hdr.aa);
syslog(LOG_DEBUG, "tc: %d\n", answer->hdr.tc);
syslog(LOG_DEBUG, "rd: %d\n", answer->hdr.rd);
syslog(LOG_DEBUG, "ra: %d\n", answer->hdr.ra);
// syslog(LOG_DEBUG, "ad: %d\n", answer->hdr.ad);
// syslog(LOG_DEBUG, "cd: %d\n", answer->hdr.cd);
syslog(LOG_DEBUG, "rc: %d\n", answer->hdr.rcode);
syslog(LOG_DEBUG, "qdcount: %d\n", ntohs(answer->hdr.qdcount));
syslog(LOG_DEBUG, "ancount: %d\n", ntohs(answer->hdr.ancount));
syslog(LOG_DEBUG, "nscount: %d\n", ntohs(answer->hdr.nscount));
syslog(LOG_DEBUG, "arcount: %d\n", ntohs(answer->hdr.arcount));
#endif
/* query section */
for (y = 0; y < ntohs(answer->hdr.qdcount); y++) {
cnt = get_domain_name(answer, cnt, NULL);
type = get16(answer->buf, cnt);
cnt += 2;
class = get16(answer->buf, cnt);
cnt += 2;
}
/* answer section */
rdata = (void**)htmalloc(sizeof(void*) * (ntohs(answer->hdr.ancount)+1));
for (y = 0; y < ntohs(answer->hdr.ancount); y++) {
cnt = get_domain_name(answer, cnt, NULL);
type = get16(answer->buf, cnt);
syslog(LOG_DEBUG, "get_rrs: answer type %d", type);
//if (type != qtype)
// printf("answer type mismatch: %d != %d\n", type, qtype);
/* assert(type==qtype); */
cnt += 2;
class = get16(answer->buf, cnt);
cnt += 2;
ttl = get32(answer->buf, cnt);
cnt += 4;
rdlength = get16(answer->buf, cnt);
cnt += 2;
rdata2 = rdata+y;
get_rdata(answer, type, cnt, rdlength, rdata2);
cnt += rdlength;
}
rdata2 = rdata+y;
*((void**)rdata2) = NULL;
#if HAVE_LIBDJBDNS
pthread_mutex_lock(&dns_transmit_mutex);
dns_transmit_free(&dt);
pthread_mutex_unlock(&dns_transmit_mutex);
syslog(LOG_DEBUG, "get_rrs: (4) q = %p", q);
alloc_free(q);
q = NULL;
#endif
return rdata;
}
mx_rdata_t** get_mx_rrs(char *domain) {
if (domain == NULL)
return NULL;
rdata_t **rdata = (rdata_t**) get_rrs(domain, T_MX);
if (NULL == rdata) {
return NULL;
}
if (NULL == *rdata) {
free(rdata);
return NULL;
}
rdata_t **rdata2;
mx_rdata_t **mx_rdata = NULL;
int i = 0; // one for the termination
for (rdata2 = rdata; *rdata2 != NULL; rdata2++) {
if ((*rdata2)->type == T_MX) {
mx_rdata = (mx_rdata_t**) htrealloc(mx_rdata, sizeof(mx_rdata_t*) * (i + 2));
*(mx_rdata + i) = (mx_rdata_t*) *rdata2;
i++;
} else {
/* it's not, free it */
free_rdata(*rdata2);
}
}
/* free the old container */
free(rdata);
/* terminate the new container */
if (mx_rdata != NULL)
*((int**)(mx_rdata + i)) = NULL;
return mx_rdata;
}
a_rdata_t** inner_get_a_rrs(char *domain, int *depth) {
//printf("depth: %d, domain: %s\n", *depth, domain);
if (*depth > 5) {
//printf("loop\n");
return NULL;
}
if (domain == NULL)
return NULL;
cname_rdata_t **cname_rdata;
rdata_t **rdata = (rdata_t**) get_rrs(domain, T_A);
if (NULL == rdata) {
return NULL;
}
if (NULL == *rdata) {
free(rdata);
return NULL;
}
rdata_t **rdata2;
a_rdata_t **a_rdata = NULL;
int i = 0; // one for the termination
for (rdata2 = rdata; *rdata2 != NULL; rdata2++) {
//printf(" depth: %d, rdata2: %p\n", *depth, rdata2);
if ((*rdata2)->type == T_A) {
a_rdata = (a_rdata_t**) htrealloc(a_rdata, sizeof(a_rdata_t*) * (i + 2));
*(a_rdata + i) = *rdata2;
i++;
} else if ((*rdata2)->type == T_CNAME) {
cname_rdata_t *cname_rdata = (cname_rdata_t*) *rdata2;
(*depth)++;
a_rdata_t **a_rdata_recurse = inner_get_a_rrs(cname_rdata->cname, depth);
if ((NULL != a_rdata_recurse) && (NULL != *a_rdata_recurse)) {
a_rdata_t **a_rdata2;
for (a_rdata2 = a_rdata_recurse; *a_rdata2 != NULL; a_rdata2++) {
a_rdata = (a_rdata_t**) htrealloc(a_rdata, sizeof(a_rdata_t*) * (i + 2));
*(a_rdata + i) = *a_rdata2;
i++;
}
free(a_rdata_recurse);
} else if (NULL != a_rdata_recurse) {
free(a_rdata_recurse);
}
free_rdata(cname_rdata);
} else {
/* it's not, free it */
free_rdata(*rdata2);
}
}
/* free the old container */
free(rdata);
/* terminate the new container */
if (a_rdata != NULL)
*((int**)(a_rdata + i)) = NULL;
return a_rdata;
}
a_rdata_t** get_a_rrs(char *domain) {
int i = 0;
return inner_get_a_rrs(domain, &i);
}
#define min(A,B) ((A<B) ? A : B)
mx_rdata_t** get_best_mx_rrs(char *domain) {
if (domain == NULL)
return NULL;
int i = 0;
mx_rdata_t **all_mx_rrs = get_mx_rrs(domain);
if (NULL == all_mx_rrs)
return NULL;
/* how much are there at all and what is the minimum preference */
int min_pref = 10000000;
int all_cnt = 0;
mx_rdata_t **mx_rdata2;
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++) {
all_cnt++;
min_pref = min(min_pref, (*mx_rdata2)->preference);
}
//printf("all_cnt: %d, min_pref: %d\n", all_cnt, min_pref);
/* how much are there of the minimum preference */
int best_cnt = 0;
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++)
if ((*mx_rdata2)->preference == min_pref)
best_cnt++;
//printf("best_cnt: %d\n", best_cnt);
if (all_cnt == best_cnt) {
/* all of them are minimum */
return all_mx_rrs;
} else {
/* space for the minimum pref rr's */
mx_rdata_t **best_mx_rrs = (mx_rdata_t**) htmalloc(sizeof(mx_rdata_t*) * (best_cnt+1));
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++) {
if ((*mx_rdata2)->preference == min_pref) {
/* is a minimum one, keep it */
*(best_mx_rrs+i) = *mx_rdata2;
i++;
} else {
/* it's not, free it */
free_rdata(*mx_rdata2);
}
}
/* free the old container */
free(all_mx_rrs);
/* terminate the new container */
*((int**)(best_mx_rrs+i)) = NULL;
return best_mx_rrs;
}
}
/* #define _TEST_MODE_ */
#ifdef _TEST_MODE_
int main(int argc, char **argv) {
char default_domain[] = "test.de";
char *domain = default_domain;
mx_rdata_t **mx_rdata, **mx_rdata2;
a_rdata_t **a_rdata, **a_rdata2;
int a;
if (argc > 1) {
domain = argv[1];
}
openlog("htdns-test", LOG_PID, LOG_LOCAL2);
printf("before get_mx_rrs: %s\n", domain);
mx_rdata = get_mx_rrs(domain);
for (mx_rdata2 = mx_rdata; *mx_rdata2 != NULL; mx_rdata2++) {
printf("preference: %d, exchange: %s\n", (*mx_rdata2)->preference, (*mx_rdata2)->exchange);
}
free_rrs((void**)mx_rdata);
printf("------------------\n");
mx_rdata = get_best_mx_rrs(domain);
for (mx_rdata2 = mx_rdata; *mx_rdata2 != NULL; mx_rdata2++) {
printf("preference: %d, exchange: %s\n", (*mx_rdata2)->preference, (*mx_rdata2)->exchange);
a_rdata = get_a_rrs((*mx_rdata2)->exchange);
for (a_rdata2 = a_rdata; *a_rdata2 != NULL; a_rdata2++) {
a = (*a_rdata2)->address;
printf("address: %04x\n", a);
printf(" %d.%d.%d.%d\n", (a&0xff000000)>>24, (a&0x00ff0000)>>16, (a&0x0000ff00)>>8, a&0x000000ff);
}
free_rrs((void**)a_rdata);
}
free_rrs((void**)mx_rdata);
/* printf("------------------\n"); */
/* a_rdata = get_a_rrs("www.microsoft.com.nsatc.net"); */
/* for (a_rdata2 = a_rdata; *a_rdata2 != NULL; a_rdata2++) { */
/* a = (*a_rdata2)->address; */
/* printf("address: %04x\n", a); */
/* printf(" %d.%d.%d.%d\n", (a&0xff000000)>>24, (a&0x00ff0000)>>16, (a&0x0000ff00)>>8, a&0x000000ff); */
/* } */
/* free_rrs((void**)a_rdata); */
closelog();
}
#endif /* _TEST_MODE_ */