Hello, I'm writting custom DNS server based on evdns. One problem I've ran into is that evdns doesn't support complex RR records where domain names are mixed with other data in RDATA section of the reply. For example both SOA and MX RRs are not supported as current code assumes that RRs either contain single domain name in RDATA or contain some raw binary data. The later option theoretically let's you to pack RDATA for say SOA RRs but it is bad idea as you cannot do label compression in this case and you have to duplicate a lot of code in your application which already exist in the library. So I've refactored the code to make easier to add support for serialization of RDATA section for other cases and implemented support for SOA. The refactored code can be now easily extended to support other RRs if needed but I didn't bother with other RRs as I only need SOA at the moment.
See the patch attached. Would be nice if it were integrated into libevent. -- Ilya Martynov, i...@iponweb.net CTO IPonWEB (UK) Ltd
diff -r 4887a6e3bbb7 3rdparty/libevent/evdns.c --- a/3rdparty/libevent/evdns.c Fri Mar 13 16:10:02 2009 +0300 +++ b/3rdparty/libevent/evdns.c Fri Mar 13 21:56:27 2009 +0300 @@ -150,9 +150,10 @@ /* which we bother recording */ #define TYPE_A EVDNS_TYPE_A -#define TYPE_CNAME 5 +#define TYPE_CNAME EVDNS_TYPE_CNAME #define TYPE_PTR EVDNS_TYPE_PTR #define TYPE_AAAA EVDNS_TYPE_AAAA +#define TYPE_SOA EVDNS_TYPE_SOA #define CLASS_INET EVDNS_CLASS_INET @@ -239,6 +240,40 @@ struct server_request *pending_replies; }; +/* This is an inefficient representation; only use it via the dnslabel_table_* + * functions, so that is can be safely replaced with something smarter later. */ +#define MAX_LABELS 128 +/* Structures used to implement name compression */ +struct dnslabel_entry { char *v; off_t pos; }; +struct dnslabel_table { + int n_labels; /* number of current entries */ + /* map from name to position in message */ + struct dnslabel_entry labels[MAX_LABELS]; +}; + +/* Represents RDATA part of a RR being built. */ +struct server_rdata { + int (*append)(unsigned char *buf, size_t buf_len, off_t j, struct server_rdata *rdata, struct dnslabel_table *table); + void (*free)(struct server_rdata *rdata); + + union { + struct { + void *data; + u16 datalen; + } raw; + char *name; + struct { + char *mname; + char *rname; + u32 serial; + u32 refresh; + u32 retry; + u32 expire; + u32 minimum; + } soa; + }; +}; + /* Represents part of a reply being built. (That is, a single RR.) */ struct server_reply_item { struct server_reply_item *next; /* next item in sequence. */ @@ -246,9 +281,7 @@ u16 type : 16; /* The RR type */ u16 class : 16; /* The RR class (usually CLASS_INET) */ u32 ttl; /* The RR TTL */ - char is_name; /* True iff data is a label */ - u16 datalen; /* Length of data; -1 if data is a label */ - void *data; /* The contents of the RR */ + struct server_rdata rdata; }; /* Represents a request that we've received as a DNS server, and holds */ @@ -1276,17 +1309,6 @@ } } -/* This is an inefficient representation; only use it via the dnslabel_table_* - * functions, so that is can be safely replaced with something smarter later. */ -#define MAX_LABELS 128 -/* Structures used to implement name compression */ -struct dnslabel_entry { char *v; off_t pos; }; -struct dnslabel_table { - int n_labels; /* number of current entries */ - /* map from name to position in message */ - struct dnslabel_entry labels[MAX_LABELS]; -}; - /* Initialize dnslabel_table. */ static void dnslabel_table_init(struct dnslabel_table *table) @@ -1486,16 +1508,113 @@ port->closing = 1; } -/* exported function */ -int -evdns_server_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data) +static int +raw_rdata_append(unsigned char *buf, size_t buf_len, off_t j, struct server_rdata *rdata, struct dnslabel_table *table) +{ + if (j+rdata->raw.datalen > (off_t)buf_len) + return -1; + + memcpy(buf+j, rdata->raw.data, rdata->raw.datalen); + + return j+rdata->raw.datalen; +} + +static void +raw_rdata_free(struct server_rdata *rdata) +{ + free(rdata->raw.data); +} + +static int +name_rdata_append(unsigned char *buf, size_t buf_len, off_t j, struct server_rdata *rdata, struct dnslabel_table *table) +{ + return dnsname_to_labels(buf, buf_len, j, rdata->name, strlen(rdata->name), table); +} + +static void +name_rdata_free(struct server_rdata *rdata) +{ + free(rdata->name); +} + +static int +soa_rdata_append(unsigned char *buf, size_t buf_len, off_t j, struct server_rdata *rdata, struct dnslabel_table *table) +{ + off_t r; + u32 _t32; /* used by the macros */ + + r = dnsname_to_labels(buf, buf_len, j, rdata->soa.mname, strlen(rdata->soa.mname), table); + if (r < 0) + goto overflow; + j = r; + + r = dnsname_to_labels(buf, buf_len, j, rdata->soa.rname, strlen(rdata->soa.rname), table); + if (r < 0) + goto overflow; + j = r; + + APPEND32(rdata->soa.serial); + APPEND32(rdata->soa.refresh); + APPEND32(rdata->soa.retry); + APPEND32(rdata->soa.expire); + APPEND32(rdata->soa.minimum); + + return j; + +overflow: + + return -1; +} + +static void +soa_rdata_free(struct server_rdata *rdata) +{ + free(rdata->soa.mname); + free(rdata->soa.rname); +} + +static struct server_reply_item * +evdns_server_reply_item_new(const char *name, int type, int class, int ttl) +{ + struct server_reply_item *item; + + item = malloc(sizeof(struct server_reply_item)); + if (!item) + return NULL; + item->next = NULL; + if (!(item->name = strdup(name))) { + free(item); + return NULL; + } + item->type = type; + item->dns_question_class = class; + item->ttl = ttl; + item->rdata.append = NULL; + item->rdata.free = NULL; + + return item; +} + +static void +evdns_server_reply_item_free(struct server_reply_item *item) +{ + if (!item) + return; + free(item->name); + if (!item->rdata.free) + item->rdata.free(&item->rdata); + free(item); +} + +static int +evdns_server_request_add_reply2(struct evdns_server_request *_req, int section, struct server_reply_item *item) { struct server_request *req = TO_SERVER_REQUEST(_req); - struct server_reply_item **itemp, *item; + struct server_reply_item **itemp; int *countp; if (req->response) /* have we already answered? */ - return (-1); + return -1; switch (section) { case EVDNS_ANSWER_SECTION: @@ -1511,47 +1630,50 @@ countp = &req->n_additional; break; default: - return (-1); + return -1; } while (*itemp) { itemp = &((*itemp)->next); - } - item = malloc(sizeof(struct server_reply_item)); - if (!item) - return -1; - item->next = NULL; - if (!(item->name = strdup(name))) { - free(item); - return -1; - } - item->type = type; - item->dns_question_class = class; - item->ttl = ttl; - item->is_name = is_name != 0; - item->datalen = 0; - item->data = NULL; - if (data) { - if (item->is_name) { - if (!(item->data = strdup(data))) { - free(item->name); - free(item); - return -1; - } - item->datalen = (u16)-1; - } else { - if (!(item->data = malloc(datalen))) { - free(item->name); - free(item); - return -1; - } - item->datalen = datalen; - memcpy(item->data, data, datalen); - } } *itemp = item; ++(*countp); return 0; +} + +/* exported function */ +int +evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data) +{ + struct server_reply_item *item ; + item = evdns_server_reply_item_new(name, type, class, ttl); + + if (!item) + return -1; + + if (data) { + if (is_name) { + item->rdata.append = name_rdata_append; + item->rdata.free = name_rdata_free; + item->rdata.name = NULL; + if (!(item->rdata.name = strdup(data))) { + evdns_server_reply_item_free(item); + return -1; + } + } else { + item->rdata.append = raw_rdata_append; + item->rdata.free = raw_rdata_free; + item->rdata.raw.data = NULL; + if (!(item->rdata.raw.data = malloc(datalen))) { + evdns_server_reply_item_free(item); + return -1; + } + item->rdata.raw.datalen = datalen; + memcpy(item->rdata.raw.data, data, datalen); + } + } + + return evdns_server_request_add_reply2(req, section, item); } /* exported function */ @@ -1603,6 +1725,39 @@ ttl, -1, 1, cname); } +/* exported function */ +int +evdns_server_request_add_soa_reply(struct evdns_server_request *req, int section, const char *name, const char *mname, const char *rname, unsigned serial, unsigned refresh, unsigned retry, unsigned expire, unsigned minimum, int ttl) +{ + struct server_reply_item *item; + item = evdns_server_reply_item_new(name, TYPE_SOA, CLASS_INET, ttl); + + if (!item) + return -1; + + item->rdata.append = soa_rdata_append; + item->rdata.free = soa_rdata_free; + item->rdata.soa.mname = NULL; + item->rdata.soa.rname = NULL; + + if (!(item->rdata.soa.mname = strdup(mname))) { + evdns_server_reply_item_free(item); + return -1; + } + + if (!(item->rdata.soa.rname = strdup(rname))) { + evdns_server_reply_item_free(item); + return -1; + } + + item->rdata.soa.serial = serial; + item->rdata.soa.refresh = refresh; + item->rdata.soa.retry = retry; + item->rdata.soa.expire = expire; + item->rdata.soa.minimum = minimum; + + return evdns_server_request_add_reply2(req, section, item); +} static int evdns_server_request_format_response(struct server_request *req, int err) @@ -1661,23 +1816,18 @@ APPEND16(item->type); APPEND16(item->dns_question_class); APPEND32(item->ttl); - if (item->is_name) { - off_t len_idx = j, name_start; - j += 2; - name_start = j; - r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table); + + off_t len_idx = j, data_start; + j += 2; + data_start = j; + if (item->rdata.append) { + r = item->rdata.append(buf, buf_len, j, &(item->rdata), &table); if (r < 0) goto overflow; j = r; - _t = htons( (short) (j-name_start) ); - memcpy(buf+len_idx, &_t, 2); - } else { - APPEND16(item->datalen); - if (j+item->datalen > (off_t)buf_len) - goto overflow; - memcpy(buf+j, item->data, item->datalen); - j += item->datalen; } + _t = htons( (short) (j-data_start) ); + memcpy(buf+len_idx, &_t, 2); item = item->next; } } @@ -1767,10 +1917,7 @@ victim = *list; while (victim) { next = victim->next; - free(victim->name); - if (victim->data) - free(victim->data); - free(victim); + evdns_server_reply_item_free(victim); victim = next; } *list = NULL; diff -r 4887a6e3bbb7 3rdparty/libevent/evdns.h --- a/3rdparty/libevent/evdns.h Fri Mar 13 16:10:02 2009 +0300 +++ b/3rdparty/libevent/evdns.h Fri Mar 13 21:56:27 2009 +0300 @@ -515,6 +515,7 @@ int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); +int evdns_server_request_add_soa_reply(struct evdns_server_request *req, int section, const char *name, const char *mname, const char *rname, unsigned serial, unsigned refresh, unsigned retry, unsigned expire, unsigned minimum, int ttl); int evdns_server_request_respond(struct evdns_server_request *req, int err); int evdns_server_request_drop(struct evdns_server_request *req);
_______________________________________________ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users