604 lines
15 KiB
C
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_ */
|
|
|