310 lines
7.3 KiB
C
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_ */
|
|
|