Files
smmapdfw/smmapdfw/libsmmapdfw/dns.c
2004-09-27 13:38:53 +00:00

310 lines
7.3 KiB
C

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdio.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include "dns.h"
extern int h_errno;
/* extern struct state _res; */
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])
static int get_domain_name(answer_t *answer, int offset, char **name) {
int start, len, i, offset2;
char *name_buf, *name_buf2;
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);
name_buf = (char*) realloc(name_buf, ((NULL != name_buf) ? strlen(name_buf) : 0) + strlen(name_buf2) + 1);
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*) realloc(name_buf, i+len+2);
strncpy(name_buf + i, answer->buf + start, len);
name_buf[i+len] = '\0';
strcat(name_buf, ".");
}
}
}
if (NULL != name) {
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*) malloc(sizeof(mx_rdata_t));
(*resp)->preference = get16(answer->buf, offset);
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*) malloc(sizeof(a_rdata_t));
(*resp)->address = htonl(get32(answer->buf, offset));
(*resp)->type = T_A;
return 0;
}
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;
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;
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;
res = res_search(domain, C_IN, qtype, (u_char*) &answer, sizeof(answer_t));
if (-1 == res) {
return NULL;
}
cnt = sizeof(HEADER);
/* 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**)malloc(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);
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;
return rdata;
}
mx_rdata_t** get_mx_rrs(char *domain) {
return (mx_rdata_t**) get_rrs(domain, T_MX);
}
a_rdata_t** get_a_rrs(char *domain) {
return (a_rdata_t**) get_rrs(domain, T_A);
}
#define min(A,B) ((A<B) ? A : B)
mx_rdata_t** get_best_mx_rrs(char *domain) {
mx_rdata_t **all_mx_rrs, **mx_rdata2, **best_mx_rrs;
int min_pref = 10000000;
int all_cnt = 0;
int best_cnt = 0;
int i = 0;
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 */
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++) {
all_cnt++;
min_pref = min(min_pref, (*mx_rdata2)->preference);
}
/* how much are there of the minimum preference */
for (mx_rdata2 = all_mx_rrs; *mx_rdata2 != NULL; mx_rdata2++)
if ((*mx_rdata2)->preference == min_pref)
best_cnt++;
if (all_cnt == best_cnt) {
/* all of them are minimum */
return all_mx_rrs;
} else {
/* space for the minimum pref rr's */
best_mx_rrs = (mx_rdata_t**) malloc(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_mx_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];
}
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); */
}
#endif /* _TEST_MODE_ */