Initial stab at DNS code, including:
* new check tool for querying MX records, * dns test harness
This commit is contained in:
@@ -144,6 +144,7 @@ include_subdirs := \
|
||||
src/apps/queuetool \
|
||||
src/apps/storetool \
|
||||
src/apps/storetool/demo \
|
||||
src/apps/testtool \
|
||||
src/www \
|
||||
zoneinfo \
|
||||
man \
|
||||
@@ -212,6 +213,7 @@ include src/apps/mdbtool/Bongo.rules
|
||||
include src/apps/queuetool/Bongo.rules
|
||||
include src/apps/storetool/Bongo.rules
|
||||
include src/apps/storetool/demo/Bongo.rules
|
||||
include src/apps/testtool/Bongo.rules
|
||||
include src/www/Bongo.rules
|
||||
include zoneinfo/Bongo.rules
|
||||
include man/Bongo.rules
|
||||
|
||||
@@ -670,6 +670,7 @@ src/apps/mdbtool/Makefile
|
||||
src/apps/queuetool/Makefile
|
||||
src/apps/storetool/Makefile
|
||||
src/apps/storetool/demo/Makefile
|
||||
src/apps/testtool/Makefile
|
||||
src/libs/cal/Makefile
|
||||
src/libs/cal/tests/Makefile
|
||||
src/libs/calcmd/Makefile
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
|
||||
#ifndef XPLDNS_H
|
||||
#define XPLDNS_H
|
||||
|
||||
#include <xplutil.h>
|
||||
|
||||
#define XPLDNS_NAMELEN 256
|
||||
|
||||
typedef enum {
|
||||
XPLDNS_INTERNAL_ERROR = -1, // internal error, see errno
|
||||
XPLDNS_SUCCESS = 0,
|
||||
XPLDNS_NOT_FOUND = 1, // authoritative host not found
|
||||
XPLDNS_TRY_AGAIN = 2, // host not found or server failure
|
||||
XPLDNS_DNS_ERROR = 3, // query refused/other non-recoverable
|
||||
XPLDNS_NO_DATA = 4, // name valid, no records exist
|
||||
XPLDNS_FAIL = 100,
|
||||
XPLDNS_FAIL_CNAME_LOOP = 101
|
||||
} XplDns_ResultCode;
|
||||
|
||||
typedef enum {
|
||||
XPLDNS_RR_A = 0x0001,
|
||||
XPLDNS_RR_CNAME = 0x0005,
|
||||
XPLDNS_RR_SOA = 0x0006,
|
||||
XPLDNS_RR_PTR = 0x000C,
|
||||
XPLDNS_RR_MX = 0x000F,
|
||||
XPLDNS_RR_TXT = 0x0010
|
||||
} XplDns_RecordType;
|
||||
|
||||
typedef struct {
|
||||
char name[XPLDNS_NAMELEN + 1]; // Domain name on MX record
|
||||
char mxname[XPLDNS_NAMELEN + 1]; // address of record
|
||||
unsigned short preference; // Preference of record
|
||||
} XplDns_MxRecord;
|
||||
|
||||
typedef struct {
|
||||
char name[XPLDNS_NAMELEN + 1]; // Domain name on A record
|
||||
int address; // IP address of record
|
||||
} XplDns_ARecord;
|
||||
|
||||
typedef struct {
|
||||
char name[XPLDNS_NAMELEN + 1]; // Domain name on PTR record
|
||||
} XplDns_PtrRecord;
|
||||
|
||||
typedef struct {
|
||||
char name[XPLDNS_NAMELEN + 1]; // Domain name on CNAME
|
||||
char cname[XPLDNS_NAMELEN + 1]; // what it points to
|
||||
} XplDns_CnameRecord;
|
||||
|
||||
typedef struct {
|
||||
char mname[XPLDNS_NAMELEN + 1]; // source host
|
||||
char rname[XPLDNS_NAMELEN + 1]; // contact e-mail
|
||||
unsigned long serial; // serial number
|
||||
unsigned long refresh; // refresh time (secs)
|
||||
unsigned long retry; // retry time (secs)
|
||||
unsigned long expire; // expiry time (secs)
|
||||
unsigned long minimum; // minimum TTL (secs)
|
||||
} XplDns_SoaRecord;
|
||||
|
||||
typedef struct {
|
||||
char txt[XPLDNS_NAMELEN + 1]; // Text attached to record
|
||||
} XplDns_TxtRecord;
|
||||
|
||||
typedef union {
|
||||
XplDns_ARecord A;
|
||||
XplDns_CnameRecord CNAME;
|
||||
XplDns_MxRecord MX;
|
||||
XplDns_PtrRecord PTR;
|
||||
XplDns_SoaRecord SOA;
|
||||
XplDns_TxtRecord TXT;
|
||||
} XplDns_Record;
|
||||
|
||||
typedef struct _XplDns_RecordList XplDns_RecordList;
|
||||
|
||||
struct _XplDns_RecordList {
|
||||
XplDns_RecordList *next;
|
||||
XplDns_RecordType type;
|
||||
XplDns_Record record;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
XplDns_ResultCode status;
|
||||
XplDns_RecordList *list;
|
||||
} XplDns_Result;
|
||||
|
||||
#define XPLDNS_MAX_IP_IN_LIST 20
|
||||
typedef struct{
|
||||
XplDns_ResultCode status;
|
||||
int ip_list[XPLDNS_MAX_IP_IN_LIST + 1];
|
||||
int number;
|
||||
} XplDns_IpList;
|
||||
|
||||
typedef struct {
|
||||
XplDns_ResultCode status;
|
||||
int preference;
|
||||
char current_mx[XPLDNS_NAMELEN + 1];
|
||||
XplDns_Result *_mx_result;
|
||||
XplDns_RecordList *_mx_current;
|
||||
} XplDns_MxLookup;
|
||||
|
||||
int XplDnsInit(void);
|
||||
void XplDnsResultFree(XplDns_Result *result);
|
||||
XplDns_Result * XplDnsLookupIP(const char *domain);
|
||||
XplDns_MxLookup *XplDnsNewMxLookup(const char *domain);
|
||||
XplDns_IpList * XplDnsNextMxLookupIpList(XplDns_MxLookup *mx);
|
||||
void XplDnsFreeMxLookup(XplDns_MxLookup *mx);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
# -*- Makefile -*-
|
||||
sbin_PROGRAMS += bongo-testtool
|
||||
|
||||
bongo_testtool_SOURCES := \
|
||||
$(conf_defaults) \
|
||||
src/apps/testtool/config.h \
|
||||
src/apps/testtool/testtool.c
|
||||
|
||||
bongo_testtool_CPPFLAGS := $(AM_CPPFLAGS) \
|
||||
$(GNUTLS_CFLAGS)
|
||||
|
||||
bongo_testtool_LDADD := \
|
||||
libbongonmap.la \
|
||||
libbongomsgapi.la \
|
||||
libbongoconnio.la \
|
||||
libbongomemmgr.la \
|
||||
libbongoutil.la \
|
||||
libbongoxpl.la \
|
||||
$(PTHREAD_LIBS) \
|
||||
$(GNUTLS_LIBS) \
|
||||
$(ALL_LIBS)
|
||||
|
||||
src/apps/testtool/all: bongo-testtool
|
||||
src/apps/testtool/clean: clean
|
||||
src/apps/testtool/install:
|
||||
@cd $(top_builddir) && $(MAKE) $(MFLAGS) sbin_PROGRAMS=bongo-testtool install-sbinPROGRAMS
|
||||
@@ -0,0 +1,17 @@
|
||||
# -*- Makefile -*-
|
||||
#
|
||||
# Do not edit!
|
||||
#
|
||||
# (unless this is Makefile.am.subdir)
|
||||
#
|
||||
# This file is a copy of the toplevel Makefile.am.subdir. If changes
|
||||
# need to be made, edit that file, and make update-makefiles, and
|
||||
# check the new files in.
|
||||
#
|
||||
# This is a skeletal automake file. The real automake fules for
|
||||
# building the targets for this directory can be found in the
|
||||
# Bongo.rules file. This file is simply here so that 'make all' can
|
||||
# work in each subdirectory.
|
||||
|
||||
all clean install:
|
||||
@cd $(top_builddir) && $(MAKE) $(MFLAGS) $(subdir)/$@
|
||||
@@ -0,0 +1,4 @@
|
||||
// private header
|
||||
|
||||
void usage(void);
|
||||
void LookupMxRecords(const char *domain);
|
||||
@@ -0,0 +1,112 @@
|
||||
/* This program is free software, licensed under the terms of the GNU GPL.
|
||||
* See the Bongo COPYING file for full details
|
||||
* Copyright (c) 2007 Alex Hudson
|
||||
*/
|
||||
|
||||
#include <xpl.h>
|
||||
#include <xpldns.h>
|
||||
#include <msgapi.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <bongo-buildinfo.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <libintl.h>
|
||||
#define _(x) gettext(x)
|
||||
|
||||
void
|
||||
usage(void) {
|
||||
char *text =
|
||||
"bongo-testtool - Test parts of the Bongo system.\n\n"
|
||||
"Usage: bongo-testtool [command]\n\n"
|
||||
"Commands:\n"
|
||||
" checkmx <domain> Search for mail exchangers\n"
|
||||
"";
|
||||
|
||||
XplConsolePrintf("%s", text);
|
||||
}
|
||||
|
||||
void
|
||||
LookupMxRecords(const char *domain)
|
||||
{
|
||||
XplDns_MxLookup *mx;
|
||||
XplDns_IpList *list;
|
||||
|
||||
mx = XplDnsNewMxLookup(domain);
|
||||
if (!mx || mx->status != XPLDNS_SUCCESS) {
|
||||
XplConsolePrintf(_("ERROR: Unable to resolve MX records on %s"), domain);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME. We shouldn't try to relay to MXes who are lower preference
|
||||
// than we are, if we're in the list (to prevent mail loops).
|
||||
// In practice, this means stopping as soon as come to an IP we
|
||||
// recognise as being ours.
|
||||
while((list = XplDnsNextMxLookupIpList(mx)) != NULL) {
|
||||
int x;
|
||||
|
||||
XplConsolePrintf(_("%s (preference: %d):\n"),
|
||||
mx->current_mx, mx->preference);
|
||||
for (x = 0; x < list->number; x++) {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_addr.s_addr = list->ip_list[x];
|
||||
XplConsolePrintf(" - %s\n", inet_ntoa(addr.sin_addr));
|
||||
}
|
||||
MemFree(list);
|
||||
}
|
||||
|
||||
XplDnsFreeMxLookup(mx);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
int next_arg = 0;
|
||||
int command = 0;
|
||||
int startup = 0;
|
||||
|
||||
if (!MemoryManagerOpen("bongo-testtool")) {
|
||||
XplConsolePrintf(_("ERROR: Failed to initialize memory manager\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// parse options
|
||||
while (++next_arg < argc && argv[next_arg][0] == '-') {
|
||||
char *arg = argv[next_arg];
|
||||
printf(_("Unrecognized option: %s\n"), argv[next_arg]);
|
||||
}
|
||||
|
||||
// parse command
|
||||
if (next_arg < argc) {
|
||||
if (!strcmp(argv[next_arg], "checkmx")) {
|
||||
command = 1;
|
||||
} else {
|
||||
printf(_("Unrecognized command: %s\n"), argv[next_arg]);
|
||||
}
|
||||
} else {
|
||||
printf(_("ERROR: No command specified\n"));
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XplInit();
|
||||
|
||||
if (XplSetRealUser(MsgGetUnprivilegedUser()) < 0) {
|
||||
printf(_("bongo-testtool: Could not drop to unprivileged user '%s'\n"), MsgGetUnprivilegedUser());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
switch(command) {
|
||||
case 1:
|
||||
next_arg++;
|
||||
if (next_arg >= argc) {
|
||||
printf(_("Usage: checkmx <domain>\n"));
|
||||
} else {
|
||||
LookupMxRecords(argv[next_arg]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MemoryManagerClose("bongo-testtool");
|
||||
exit(0);
|
||||
}
|
||||
+462
-2
@@ -1,10 +1,470 @@
|
||||
// Xpl DNS resolution routines.
|
||||
|
||||
#include <xpl.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#include "dns.h"
|
||||
#include "config.h"
|
||||
|
||||
BOOL
|
||||
int
|
||||
XplDnsInit()
|
||||
{
|
||||
return FALSE;
|
||||
// res_init() may set errno, so we clear it and then test later
|
||||
errno = 0;
|
||||
(void) res_init();
|
||||
|
||||
if (errno != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_XplDns_ParseName(const char *response, const int response_len, const char *name, char *buffer, int buffer_len)
|
||||
{
|
||||
const char *ptr;
|
||||
int len = 0;
|
||||
int result = 0;
|
||||
|
||||
if (buffer_len < 2) return -1; // out of space
|
||||
len = buffer_len - strlen(buffer);
|
||||
if (len <= 0) return -1; // out of space
|
||||
|
||||
ptr = name;
|
||||
while ((response <= ptr) && (ptr < (response + response_len))) {
|
||||
if (*ptr == '\0') {
|
||||
// we've reached the end
|
||||
ptr++;
|
||||
break;
|
||||
}
|
||||
if ((*ptr & 0xc0) == 0xc0) {
|
||||
int result;
|
||||
int newptr;
|
||||
// this is a pointer to a previous label
|
||||
newptr = (*ptr++ & 0x3f) * 256;
|
||||
newptr += *ptr++;
|
||||
if (newptr == (ptr - response - 2))
|
||||
return -4; // cyclical pointer; bad.
|
||||
result = _XplDns_ParseName(response, response_len, (response + newptr), buffer, buffer_len);
|
||||
if (result <= 0) return result;
|
||||
break;
|
||||
} else {
|
||||
// append the next label if we have room in the buffer
|
||||
if ((*ptr + 1) < len) {
|
||||
strncat(buffer, ptr + 1, *ptr);
|
||||
strncat(buffer, ".", 1);
|
||||
len -= *ptr + 1;
|
||||
ptr += *ptr + 1;
|
||||
} else {
|
||||
// out of room!
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = ptr - name;
|
||||
if ((result < 0) || (result > response_len))
|
||||
// a big mistake?
|
||||
return -2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define IS_MX(r) ((r)->type == XPLDNS_RR_MX)
|
||||
|
||||
void
|
||||
_XplDnsResult_AppendRecord(XplDns_Result *result, XplDns_RecordList *item)
|
||||
{
|
||||
// this doesn't always append: if the item is an MX record, it will
|
||||
// be inserted before MX records of higher preference, or appended.
|
||||
// Thus, the record list always has MX records in order.
|
||||
// TOOD: Equal preference MX records are sorted in reply order (e.g.,
|
||||
// round-robin). We're supposed to randomise them.
|
||||
XplDns_RecordList *end;
|
||||
|
||||
item->next = NULL;
|
||||
|
||||
if (result->list == NULL) {
|
||||
// no items in list
|
||||
result->list = item;
|
||||
} else { //if (result->list->next == NULL) {
|
||||
// one item already in list
|
||||
end = result->list;
|
||||
// if this is an MX RR, see if we can insert before the first
|
||||
// item in our list
|
||||
if (IS_MX(item) && IS_MX(end)) {
|
||||
// TODO: if pref==pref, then we should randomise
|
||||
// See RFC 2821 #5
|
||||
if (end->record.MX.preference >= item->record.MX.preference) {
|
||||
item->next = result->list;
|
||||
result->list = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (end->next != NULL) {
|
||||
// see if we can insert before a later MX record
|
||||
if (IS_MX(item) && IS_MX(end->next)) {
|
||||
// See above TODO re: randomisation.
|
||||
if (end->next->record.MX.preference >=
|
||||
item->record.MX.preference) {
|
||||
// insert here!
|
||||
item->next = end->next;
|
||||
end->next = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
end = end->next;
|
||||
}
|
||||
end->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_XplDnsResult_PrintResults(XplDns_Result *result)
|
||||
{
|
||||
XplDns_RecordList *item;
|
||||
|
||||
if (result == NULL) return;
|
||||
item = result->list;
|
||||
|
||||
printf ("\n== START OF DNS RECORD LIST\n");
|
||||
while (item != NULL) {
|
||||
struct sockaddr_in addr;
|
||||
|
||||
switch (item->type) {
|
||||
case XPLDNS_RR_A:
|
||||
addr.sin_addr.s_addr = item->record.A.address;
|
||||
printf("%s IN A %s\n", item->record.A.name, inet_ntoa(addr.sin_addr));
|
||||
break;
|
||||
case XPLDNS_RR_CNAME:
|
||||
printf("%s IN CNAME %s\n", item->record.CNAME.name, item->record.CNAME.cname);
|
||||
break;
|
||||
case XPLDNS_RR_MX:
|
||||
printf("%s IN MX %d %s\n",
|
||||
item->record.MX.name,
|
||||
item->record.MX.preference,
|
||||
item->record.MX.mxname);
|
||||
break;
|
||||
default:
|
||||
printf("Unknown record.\n");
|
||||
break;
|
||||
}
|
||||
item = item->next;
|
||||
}
|
||||
printf ("== END OF RESULTS\n");
|
||||
}
|
||||
|
||||
void
|
||||
XplDnsResultFree(XplDns_Result *result)
|
||||
{
|
||||
XplDns_RecordList *item = result->list;
|
||||
XplDns_RecordList *last;
|
||||
|
||||
if (result == NULL) return;
|
||||
|
||||
while (item != NULL) {
|
||||
last = item;
|
||||
item = item->next;
|
||||
MemFree(last);
|
||||
}
|
||||
MemFree(result);
|
||||
}
|
||||
|
||||
XplDns_MxLookup *
|
||||
XplDnsNewMxLookup(const char *domain)
|
||||
{
|
||||
// Lazy MX resolver.
|
||||
// Should comply with RFC 2821 #5
|
||||
XplDns_MxLookup *result;
|
||||
XplDns_Result *mx_query;
|
||||
char mail_domain[XPLDNS_NAMELEN + 1];
|
||||
int cnames_found = 0;
|
||||
|
||||
result = MemMalloc0(sizeof(XplDns_MxLookup));
|
||||
if (! result) return NULL;
|
||||
|
||||
strncpy(mail_domain, domain, XPLDNS_NAMELEN);
|
||||
|
||||
// we come back to this point if we encounter CNAMEs before MX RRs
|
||||
restart_query:
|
||||
mx_query = _XplDns_Query(mail_domain, XPLDNS_RR_MX);
|
||||
|
||||
if (mx_query->status == XPLDNS_NO_DATA) {
|
||||
// domain exists, but no MX records
|
||||
XplDns_Result *a_query;
|
||||
|
||||
a_query = _XplDns_Query(domain, XPLDNS_RR_A);
|
||||
if (a_query->status == XPLDNS_SUCCESS) {
|
||||
// A RR exists, so let's fake an MX record for it
|
||||
XplDnsResultFree(mx_query);
|
||||
XplDnsResultFree(a_query);
|
||||
mx_query = _XplDns_QueryFakeMx(domain);
|
||||
}
|
||||
} else if (mx_query->status == XPLDNS_SUCCESS) {
|
||||
// check we don't have any CNAME in results
|
||||
XplDns_RecordList *record = mx_query->list;
|
||||
|
||||
while (record) {
|
||||
if (record->type == XPLDNS_RR_CNAME) {
|
||||
// restart our query on new domain
|
||||
strncpy(mail_domain, record->record.CNAME.cname,
|
||||
XPLDNS_NAMELEN);
|
||||
XplDnsResultFree(mx_query);
|
||||
|
||||
cnames_found++;
|
||||
if (cnames_found < 6) {
|
||||
goto restart_query;
|
||||
} else {
|
||||
result->status = XPLDNS_FAIL_CNAME_LOOP;
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
record = record->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result->status = mx_query->status;
|
||||
result->_mx_result = mx_query;
|
||||
result->_mx_current = mx_query->list;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XplDns_IpList *
|
||||
XplDnsNextMxLookupIpList(XplDns_MxLookup *mx)
|
||||
{
|
||||
XplDns_IpList *result;
|
||||
XplDns_Result *lookup;
|
||||
XplDns_RecordList *record;
|
||||
const char *mail_exchanger = NULL;
|
||||
|
||||
if (mx->_mx_current == NULL) return NULL;
|
||||
result = MemMalloc(sizeof(XplDns_IpList));
|
||||
if (! result) return NULL;
|
||||
|
||||
result->status = XPLDNS_TRY_AGAIN;
|
||||
result->number = 0;
|
||||
|
||||
if (mx->_mx_current->type == XPLDNS_RR_MX)
|
||||
// want to make sure we're looking at the right records.
|
||||
mail_exchanger = mx->_mx_current->record.MX.mxname;
|
||||
|
||||
// no decent records at this point?
|
||||
if (! mail_exchanger) return result;
|
||||
|
||||
strncpy(mx->current_mx, mail_exchanger, XPLDNS_NAMELEN);
|
||||
mx->preference = mx->_mx_current->record.MX.preference;
|
||||
mx->_mx_current = mx->_mx_current->next;
|
||||
|
||||
// look up the mail server's IP address(es)
|
||||
lookup = _XplDns_Query(mail_exchanger, XPLDNS_RR_A);
|
||||
|
||||
if (lookup->status != XPLDNS_SUCCESS) {
|
||||
// more failure... :/
|
||||
result->status = lookup->status;
|
||||
return result;
|
||||
}
|
||||
|
||||
record = lookup->list;
|
||||
while (record) {
|
||||
// FIXME: check for CNAME entries?
|
||||
if (record->type != XPLDNS_RR_A) {
|
||||
record = record->next;
|
||||
continue; // not interested in non-A records
|
||||
}
|
||||
|
||||
result->ip_list[result->number] = record->record.A.address;
|
||||
result->number++;
|
||||
if (result->number >= XPLDNS_MAX_IP_IN_LIST)
|
||||
break;
|
||||
record = record->next;
|
||||
}
|
||||
|
||||
XplDnsResultFree(lookup);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
XplDnsFreeMxLookup(XplDns_MxLookup *mx)
|
||||
{
|
||||
if (mx == NULL) return;
|
||||
|
||||
if (mx->_mx_result) {
|
||||
XplDnsResultFree(mx->_mx_result);
|
||||
mx->_mx_result = NULL;
|
||||
}
|
||||
|
||||
MemFree(mx);
|
||||
}
|
||||
|
||||
XplDns_Result *
|
||||
_XplDns_QueryFakeMx(const char *domain)
|
||||
{
|
||||
// when a domain has no MX RR, RFC 2821 says treat any A record
|
||||
// for that domain as having an implicit MX record. This creates
|
||||
// that record, as if it existed in DNS.
|
||||
XplDns_Result *result;
|
||||
XplDns_RecordList *list;
|
||||
|
||||
list = MemMalloc(sizeof(XplDns_RecordList));
|
||||
|
||||
strncpy(list->record.MX.name, domain, XPLDNS_NAMELEN);
|
||||
strncpy(list->record.MX.mxname, domain, XPLDNS_NAMELEN);
|
||||
list->record.MX.preference = 0;
|
||||
list->type = XPLDNS_RR_MX;
|
||||
|
||||
result = MemMalloc(sizeof(XplDns_Result));
|
||||
result->list = list;
|
||||
result->status = XPLDNS_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XplDns_Result *
|
||||
_XplDns_Query(const char *domain, XplDns_RecordType type)
|
||||
{
|
||||
XplDns_Result *result;
|
||||
char answer_buffer[4096];
|
||||
int answer_len = -1;
|
||||
|
||||
result = MemMalloc(sizeof(XplDns_Result));
|
||||
result->list = NULL;
|
||||
result->status = XPLDNS_FAIL;
|
||||
|
||||
// FIXME: is this re-entrant _everywhere_ ?
|
||||
answer_len = res_query(domain, ns_c_any, type, answer_buffer, 4096);
|
||||
|
||||
if (answer_len < 0) {
|
||||
// didn't get a sensible answer
|
||||
if (h_errno) {
|
||||
result->status = (XplDns_ResultCode) h_errno;
|
||||
return result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (answer_len == 0) {
|
||||
// no records in the answer
|
||||
result->status = XPLDNS_NO_DATA;
|
||||
return result;
|
||||
}
|
||||
|
||||
_XplDns_ParseQuery(answer_buffer, answer_len, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
_XplDns_ParseQuery(const char *answer_buffer, int answer_len, XplDns_Result *result)
|
||||
{
|
||||
XplDnsResponseHeader *header;
|
||||
const char *response, *response_end;
|
||||
int x;
|
||||
int res;
|
||||
int answers;
|
||||
|
||||
// try to parse the answer
|
||||
header = (XplDnsResponseHeader *)answer_buffer;
|
||||
response = answer_buffer + sizeof(XplDnsResponseHeader);
|
||||
response_end = answer_buffer + answer_len;
|
||||
|
||||
for (x = 0; x < ntohs(header->qdcount); x++) {
|
||||
int size;
|
||||
char buffer[1024];
|
||||
|
||||
buffer[0] = '\0';
|
||||
size = _XplDns_ParseName(answer_buffer, answer_len, response, buffer, 1000);
|
||||
// did we find the name correctly?
|
||||
if (size < 0) return;
|
||||
|
||||
// Skip QCODE / QCLASS
|
||||
response += size + 4;
|
||||
|
||||
// does the buffer extent look right?
|
||||
if (response >= response_end) return;
|
||||
}
|
||||
|
||||
for (x = 0; x < ntohs(header->ancount); x++) {
|
||||
XplDnsAnswer *dns_answer;
|
||||
XplDns_RecordList *item;
|
||||
char answer_domain[XPLDNS_NAMELEN + 1];
|
||||
|
||||
item = MemMalloc(sizeof(XplDns_RecordList));
|
||||
|
||||
answer_domain[0] = '\0';
|
||||
res = _XplDns_ParseName(answer_buffer, answer_len,
|
||||
response, answer_domain, XPLDNS_NAMELEN);
|
||||
if (res < 0) return; // couldn't parse name...
|
||||
|
||||
response += res;
|
||||
if (response >= response_end) return;
|
||||
|
||||
dns_answer = (XplDnsAnswer *)response;
|
||||
|
||||
response += sizeof(XplDnsAnswer);
|
||||
if (response >= response_end) return;
|
||||
|
||||
// TODO : check ntohs(ans->class) is right
|
||||
|
||||
switch(ntohs(dns_answer->rtype)) {
|
||||
case XPLDNS_RR_A: {
|
||||
item->type = XPLDNS_RR_A;
|
||||
XplDnsAnswerARecord *rec = (XplDnsAnswerARecord *)response;
|
||||
strncpy(item->record.A.name, answer_domain, XPLDNS_NAMELEN);
|
||||
item->record.A.address = rec->address;
|
||||
_XplDnsResult_AppendRecord(result, item);
|
||||
}
|
||||
break;
|
||||
case XPLDNS_RR_CNAME: {
|
||||
item->type = XPLDNS_RR_CNAME;
|
||||
|
||||
strncpy(item->record.CNAME.name, answer_domain,
|
||||
XPLDNS_NAMELEN);
|
||||
|
||||
item->record.CNAME.cname[0] = '\0';
|
||||
_XplDns_ParseName(answer_buffer, answer_len, response, item->record.CNAME.cname, XPLDNS_NAMELEN);
|
||||
_XplDnsResult_AppendRecord(result, item);
|
||||
|
||||
}
|
||||
break;
|
||||
case XPLDNS_RR_MX: {
|
||||
XplDnsAnswerMxRecord *rec = (XplDnsAnswerMxRecord *)response;
|
||||
int r;
|
||||
|
||||
strncpy(item->record.MX.name, answer_domain,
|
||||
XPLDNS_NAMELEN);
|
||||
item->type = XPLDNS_RR_MX;
|
||||
item->record.MX.preference = ntohs(rec->pref);
|
||||
|
||||
item->record.MX.mxname[0] = '\0';
|
||||
r = _XplDns_ParseName(answer_buffer,
|
||||
answer_len,
|
||||
response + sizeof(XplDnsAnswerMxRecord),
|
||||
item->record.MX.mxname,
|
||||
XPLDNS_NAMELEN);
|
||||
if (r >= 0) {
|
||||
_XplDnsResult_AppendRecord(result, item);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unknown type.
|
||||
MemFree(item);
|
||||
break;
|
||||
}
|
||||
|
||||
response += ntohs(dns_answer->size);
|
||||
if (response >= response_end) return;
|
||||
}
|
||||
|
||||
// don't make the result 'success' until we've really done everything
|
||||
result->status = XPLDNS_SUCCESS;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
// Test the Xpl DNS resolution code
|
||||
|
||||
#include <xpl.h>
|
||||
#include <xpldns.h>
|
||||
#include <memmgr.h>
|
||||
#include <bongocheck.h>
|
||||
|
||||
#ifdef BONGO_HAVE_CHECK
|
||||
|
||||
#define MEM_NAME "check-xpl-dns"
|
||||
|
||||
START_TEST(test1)
|
||||
{
|
||||
fail_unless(1==1);
|
||||
XplDns_MxLookup *mx;
|
||||
XplDns_IpList *list;
|
||||
|
||||
MemoryManagerOpen(MEM_NAME);
|
||||
|
||||
fail_unless(XplDnsInit() == 0);
|
||||
|
||||
mx = XplDnsNewMxLookup("bongo-project.org");
|
||||
|
||||
fail_unless(mx != NULL);
|
||||
fail_unless(mx->status == XPLDNS_SUCCESS);
|
||||
|
||||
XplDnsFreeMxLookup(mx);
|
||||
|
||||
MemoryManagerClose(MEM_NAME);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_CHECK_SUITE_SETUP("Testing the Xpl DNS routines")
|
||||
START_CHECK_SUITE_SETUP("Xpl DNS routine tests")
|
||||
CREATE_CHECK_CASE (tc_core , "Core" );
|
||||
CHECK_SUITE_ADD_CASE(top_suite, tc_core );
|
||||
CHECK_CASE_ADD_TEST (tc_core , test1 );
|
||||
|
||||
Reference in New Issue
Block a user