veriname/ares/dns_parser.cc

587 lines
19 KiB
C++

#include "dns_parser.h"
#include <node.h>
#include <v8.h>
#include <ares.h>
#include <ares_dns.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#ifdef __OpenBSD__
# ifndef ns_t_a
# include <nameser.h>
# endif
#endif // __OpenBSD__
namespace dns_parser {
static v8::Persistent<v8::String> question_symbol, answer_symbol, authority_symbol, additional_symbol;
static v8::Persistent<v8::String> type_symbol, ntype_symbol, class_symbol, nclass_symbol, ttl_symbol, rdata_symbol, text_symbol, data_symbol;
static v8::Persistent<v8::String> priority_symbol, weight_symbol, port_symbol, name_symbol, exchange_symbol, target_symbol;
static v8::Persistent<v8::String> mname_symbol, rname_symbol, serial_symbol, refresh_symbol, retry_symbol, expire_symbol, minimum_symbol;
static v8::Persistent<v8::String> space_symbol;
typedef int (*ParseRRData)(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r);
struct RR_TYPE {
const char *s;
int n;
ParseRRData parse_in; /* only parse class IN */
v8::Persistent<v8::String> symbol;
};
struct RR_CLASS {
const char *s;
int n;
v8::Persistent<v8::String> symbol;
};
static int parse_char_string(const unsigned char *&rr_data, int &rr_len, v8::Local<v8::String> &s) {
if (rr_len <= 0) return ARES_EBADRESP;
int len = (unsigned int) rr_data[0];
rr_data++; rr_len--;
if (len > rr_len) return ARES_EBADRESP;
s = v8::String::New((const char*) rr_data, len);
rr_data += len; rr_len -= len;
return ARES_SUCCESS;
}
static int ParseRRData_A(const unsigned char *, int , const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
if (rr_len == sizeof(struct in_addr)) {
struct in_addr a;
memcpy(&a, rr_data, sizeof(a));
char ip[INET_ADDRSTRLEN];
ip[0] = '\0';
::inet_ntop(AF_INET, &a, ip, INET_ADDRSTRLEN);
v8::Local<v8::String> address = v8::String::New(ip);
r->Set(text_symbol, address);
return ARES_SUCCESS;
}
return ARES_EBADRESP;
}
static int ParseRRData_AAAA(const unsigned char *, int , const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
if (rr_len == sizeof(struct in6_addr)) {
struct in6_addr a;
memcpy(&a, rr_data, sizeof(a));
char ip[INET6_ADDRSTRLEN];
ip[0] = '\0';
::inet_ntop(AF_INET6, &a, ip, INET6_ADDRSTRLEN);
v8::Local<v8::String> address = v8::String::New(ip);
r->Set(text_symbol, address);
return ARES_SUCCESS;
}
return ARES_EBADRESP;
}
static int ParseRRData_MX(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
int status;
if (rr_len < 2) return ARES_EBADRESP;
uint16_t priority;
memcpy(&priority, rr_data, sizeof(priority));
priority = ntohs(priority);
rr_data += sizeof(priority); rr_len -= 2;
char *hostname;
long len;
status = ares_expand_name(rr_data, abuf, alen, &hostname, &len);
if (status != ARES_SUCCESS) return status;
v8::Local<v8::String> v8_hostname = v8::String::New(hostname);
free(hostname);
if (len != rr_len) return ARES_EBADRESP;
v8::Local<v8::Integer> v8_priority = v8::Integer::New(priority);
r->Set(priority_symbol, v8_priority);
r->Set(exchange_symbol, v8_hostname);
v8::Local<v8::String> text = v8::String::Concat(v8_priority->ToString(), space_symbol);
text = v8::String::Concat(text, v8_hostname);
r->Set(text_symbol, text);
return ARES_SUCCESS;
}
static int ParseRRData_NS(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
int status;
char *hostname;
long len;
status = ares_expand_name(rr_data, abuf, alen, &hostname, &len);
if (status != ARES_SUCCESS) return status;
v8::Local<v8::String> v8_hostname = v8::String::New(hostname);
free(hostname);
if (len != rr_len) return ARES_EBADRESP;
r->Set(text_symbol, v8_hostname);
return ARES_SUCCESS;
}
static int ParseRRData_SOA(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
int status;
long len;
char *mname; /* primary NS */
status = ares_expand_name(rr_data, abuf, alen, &mname, &len);
if (status != ARES_SUCCESS) return status;
v8::Local<v8::String> v8_mname = v8::String::New(mname);
free(mname);
if (len > rr_len) return ARES_EBADRESP;
rr_data += len; rr_len -= len;
char *rname; /* owner mailbox */
status = ares_expand_name(rr_data, abuf, alen, &rname, &len);
if (status != ARES_SUCCESS) return status;
v8::Local<v8::String> v8_rname = v8::String::New(rname);
free(rname);
if (len > rr_len) return ARES_EBADRESP;
rr_data += len; rr_len -= len;
if (rr_len != 20) return ARES_EBADRESP;
uint32_t serial, refresh, retry, expire, minimum;
memcpy(&serial, rr_data, sizeof(uint32_t)); serial = ntohl(serial); rr_data += sizeof(uint32_t);
memcpy(&refresh, rr_data, sizeof(uint32_t)); refresh = ntohl(refresh); rr_data += sizeof(uint32_t);
memcpy(&retry, rr_data, sizeof(uint32_t)); retry = ntohl(retry); rr_data += sizeof(uint32_t);
memcpy(&expire, rr_data, sizeof(uint32_t)); expire = ntohl(expire); rr_data += sizeof(uint32_t);
memcpy(&minimum, rr_data, sizeof(uint32_t)); minimum = ntohl(minimum);
v8::Local<v8::Integer>
v8_serial = v8::Integer:: New(serial),
v8_refresh = v8::Integer:: New(refresh),
v8_retry = v8::Integer:: New(retry),
v8_expire = v8::Integer:: New(expire),
v8_minimum = v8::Integer:: New(minimum);
v8::Local<v8::String> text = v8::String::Concat(v8_mname, space_symbol);
text = v8::String::Concat(text, v8_rname); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_serial->ToString()); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_refresh->ToString()); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_retry->ToString()); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_expire->ToString()); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_minimum->ToString());
r->Set(text_symbol, text);
r->Set(mname_symbol, v8_mname);
r->Set(rname_symbol, v8_rname);
r->Set(serial_symbol, v8_serial);
r->Set(refresh_symbol, v8_refresh);
r->Set(retry_symbol, v8_retry);
r->Set(expire_symbol, v8_expire);
r->Set(minimum_symbol, v8_minimum);
return ARES_SUCCESS;
}
static int ParseRRData_TXT(const unsigned char *, int , const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
int status, i;
if (0 == rr_len) return ARES_EBADRESP;
v8::Local<v8::Array> list = v8::Array::New();
v8::Local<v8::String> s, text = v8::String::New(""), quote = v8::String::NewSymbol("\"");
for (i = 0; 0 < rr_len; i++) {
status = parse_char_string(rr_data, rr_len, s);
if (ARES_SUCCESS != status) return status;
list->Set(v8::Integer::New(i), s);
if (i != 0) text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, quote);
/* TODO: escaping */
text = v8::String::Concat(text, s);
text = v8::String::Concat(text, quote);
}
r->Set(data_symbol, list);
r->Set(text_symbol, text);
return ARES_SUCCESS;
}
static int ParseRRData_SRV(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local<v8::Object> &r) {
int status;
if (rr_len < 6) return ARES_EBADRESP;
uint16_t priority, weight, port;
memcpy(&priority, rr_data, sizeof(uint16_t)); priority = ntohl(priority); rr_data += sizeof(uint16_t); rr_len -= sizeof(uint16_t);
memcpy(&weight, rr_data, sizeof(uint16_t)); weight = ntohl(weight); rr_data += sizeof(uint16_t); rr_len -= sizeof(uint16_t);
memcpy(&port, rr_data, sizeof(uint16_t)); port = ntohl(port); rr_data += sizeof(uint16_t); rr_len -= sizeof(uint16_t);
char *target;
long len;
status = ares_expand_name(rr_data, abuf, alen, &target, &len);
if (status != ARES_SUCCESS) return status;
v8::Local<v8::String> v8_target = v8::String::New(target);
free(target);
if (len != rr_len) return ARES_EBADRESP;
v8::Local<v8::Integer>
v8_priority = v8::Integer:: New(priority),
v8_weight = v8::Integer:: New(weight),
v8_port = v8::Integer:: New(port);
r->Set(priority_symbol, v8_priority);
r->Set(weight_symbol, v8_weight);
r->Set(port_symbol, v8_port);
r->Set(target_symbol, v8_target);
v8::Local<v8::String> text = v8::String::Concat(v8_priority->ToString(), space_symbol);
text = v8::String::Concat(text, v8_weight->ToString()); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_port->ToString()); text = v8::String::Concat(text, space_symbol);
text = v8::String::Concat(text, v8_target);
r->Set(text_symbol, text);
return ARES_SUCCESS;
}
/* indented ones are deprecated/experimental */
static RR_TYPE rr_types[] = {
{ "A", 1, ParseRRData_A, v8::Persistent<v8::String>() },
{ "NS", 2, ParseRRData_NS, v8::Persistent<v8::String>() },
{ "MD", 3, 0, v8::Persistent<v8::String>() },
{ "MF", 4, 0, v8::Persistent<v8::String>() },
{ "CNAME", 5, ParseRRData_NS, v8::Persistent<v8::String>() }, /* same RRDATA as NS */
{ "SOA", 6, ParseRRData_SOA, v8::Persistent<v8::String>() },
{ "MB", 7, 0, v8::Persistent<v8::String>() },
{ "MG", 8, 0, v8::Persistent<v8::String>() },
{ "MR", 9, 0, v8::Persistent<v8::String>() },
{ "NULL", 10, 0, v8::Persistent<v8::String>() },
{ "WKS", 11, 0, v8::Persistent<v8::String>() },
{ "PTR", 12, ParseRRData_NS, v8::Persistent<v8::String>() }, /* same RRDATA as NS */
{ "HINFO", 13, 0, v8::Persistent<v8::String>() },
{ "MINFO", 14, 0, v8::Persistent<v8::String>() },
{ "MX", 15, ParseRRData_MX, v8::Persistent<v8::String>() },
{ "TXT", 16, ParseRRData_TXT, v8::Persistent<v8::String>() },
{ "RP", 17, 0, v8::Persistent<v8::String>() },
{ "AFSDB", 18, 0, v8::Persistent<v8::String>() },
{ "SIG", 24, 0, v8::Persistent<v8::String>() },
{ "KEY", 25, 0, v8::Persistent<v8::String>() },
{ "AAAA", 28, ParseRRData_AAAA, v8::Persistent<v8::String>() },
{ "LOC", 29, 0, v8::Persistent<v8::String>() },
{ "SRV", 33, ParseRRData_SRV, v8::Persistent<v8::String>() },
{ "NAPTR", 35, 0, v8::Persistent<v8::String>() },
{ "KX", 36, 0, v8::Persistent<v8::String>() },
{ "CERT", 37, 0, v8::Persistent<v8::String>() },
{ "DNAME", 39, 0, v8::Persistent<v8::String>() },
{ "OPT", 41, 0, v8::Persistent<v8::String>() },
{ "APL", 42, 0, v8::Persistent<v8::String>() },
{ "DS", 43, 0, v8::Persistent<v8::String>() },
{ "SSHFP", 44, 0, v8::Persistent<v8::String>() },
{ "IPSECKEY", 45, 0, v8::Persistent<v8::String>() },
{ "RRSIG", 46, 0, v8::Persistent<v8::String>() },
{ "NSEC", 47, 0, v8::Persistent<v8::String>() },
{ "DNSKEY", 48, 0, v8::Persistent<v8::String>() },
{ "DHCID", 49, 0, v8::Persistent<v8::String>() },
{ "NSEC3", 50, 0, v8::Persistent<v8::String>() },
{ "NSEC3PARAM", 51, 0, v8::Persistent<v8::String>() },
{ "HIP", 55, 0, v8::Persistent<v8::String>() },
{ "SPF", 99, 0, v8::Persistent<v8::String>() },
{ "TKEY", 249, 0, v8::Persistent<v8::String>() },
{ "TSIG", 250, 0, v8::Persistent<v8::String>() },
{ "IXFR", 251, 0, v8::Persistent<v8::String>() },
{ "AXFR", 252, 0, v8::Persistent<v8::String>() },
{ "MAILB", 253, 0, v8::Persistent<v8::String>() },
{ "MAILA", 254, 0, v8::Persistent<v8::String>() },
{ "ANY", 255, 0, v8::Persistent<v8::String>() }, /* aka "*" */
{ "TA", 32768, 0, v8::Persistent<v8::String>() },
{ "DLV", 32769, 0, v8::Persistent<v8::String>() }
};
static RR_CLASS rr_classes[] = {
{ "IN", 1, v8::Persistent<v8::String>() }, /* the Internet */
{ "CS", 2, v8::Persistent<v8::String>() }, /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
{ "CH", 3, v8::Persistent<v8::String>() }, /* the CHAOS class */
{ "HS", 4, v8::Persistent<v8::String>() }, /* Hesiod [Dyer 87] */
{ "ANY", 255, v8::Persistent<v8::String>() } /* same value as in rr_type, so no name conflict */
};
void Initialize(v8::Handle<v8::Object> target) {
v8::HandleScope scope;
using namespace v8;
int r = ares_library_init(ARES_LIB_INIT_ALL);
if (0 != r) {
// TODO
// ThrowException(Exception::Error(String::New(ares_strerror(r))));
assert(r == 0);
}
question_symbol = NODE_PSYMBOL("question");
answer_symbol = NODE_PSYMBOL("answer");
authority_symbol = NODE_PSYMBOL("authority");
additional_symbol = NODE_PSYMBOL("additional");
type_symbol = NODE_PSYMBOL("type");
ntype_symbol = NODE_PSYMBOL("ntype");
class_symbol = NODE_PSYMBOL("class");
nclass_symbol = NODE_PSYMBOL("nclass");
ttl_symbol = NODE_PSYMBOL("ttl");
rdata_symbol = NODE_PSYMBOL("rdata");
text_symbol = NODE_PSYMBOL("text");
data_symbol = NODE_PSYMBOL("data");
priority_symbol = NODE_PSYMBOL("priority");
weight_symbol = NODE_PSYMBOL("weight");
port_symbol = NODE_PSYMBOL("port");
name_symbol = NODE_PSYMBOL("name");
exchange_symbol = NODE_PSYMBOL("exchange");
target_symbol = NODE_PSYMBOL("target");
mname_symbol = NODE_PSYMBOL("mname");
rname_symbol = NODE_PSYMBOL("rname");
serial_symbol = NODE_PSYMBOL("serial");
refresh_symbol = NODE_PSYMBOL("refresh");
retry_symbol = NODE_PSYMBOL("retry");
expire_symbol = NODE_PSYMBOL("expire");
minimum_symbol = NODE_PSYMBOL("minimum");
space_symbol = NODE_PSYMBOL(" ");;
v8::Local<v8::Object> types = v8::Object::New();
for (unsigned int i = 0; i < sizeof(rr_types)/sizeof(rr_types[0]); i++) {
rr_types[i].symbol = NODE_PSYMBOL(rr_types[i].s);
target->Set(rr_types[i].symbol, v8::Integer::New(rr_types[i].n));
}
for (unsigned int i = 0; i < sizeof(rr_classes)/sizeof(rr_classes[0]); i++) {
rr_classes[i].symbol = NODE_PSYMBOL(rr_classes[i].s);
target->Set(rr_classes[i].symbol, v8::Integer::New(rr_classes[i].n));
}
}
static inline const char *ares_errno_string(int errorno) {
#define ERRNO_CASE(e) case ARES_##e: return #e;
switch (errorno) {
ERRNO_CASE(SUCCESS)
ERRNO_CASE(ENODATA)
ERRNO_CASE(EFORMERR)
ERRNO_CASE(ESERVFAIL)
ERRNO_CASE(ENOTFOUND)
ERRNO_CASE(ENOTIMP)
ERRNO_CASE(EREFUSED)
ERRNO_CASE(EBADQUERY)
ERRNO_CASE(EBADNAME)
ERRNO_CASE(EBADFAMILY)
ERRNO_CASE(EBADRESP)
ERRNO_CASE(ECONNREFUSED)
ERRNO_CASE(ETIMEOUT)
ERRNO_CASE(EOF)
ERRNO_CASE(EFILE)
ERRNO_CASE(ENOMEM)
ERRNO_CASE(EDESTRUCTION)
ERRNO_CASE(EBADSTR)
ERRNO_CASE(EBADFLAGS)
ERRNO_CASE(ENONAME)
ERRNO_CASE(EBADHINTS)
ERRNO_CASE(ENOTINITIALIZED)
ERRNO_CASE(ELOADIPHLPAPI)
ERRNO_CASE(EADDRGETNETWORKPARAMS)
ERRNO_CASE(ECANCELLED)
default:
assert(0 && "Unhandled c-ares errno");
return "(UNKNOWN)";
}
}
v8::Local<v8::Value> AresException(int status) {
v8::Local<v8::String> code = v8::String::NewSymbol(ares_errno_string(status));
v8::Local<v8::String> message = v8::String::NewSymbol(ares_strerror(status));
v8::Local<v8::String> cons1 = v8::String::Concat(code, v8::String::NewSymbol(", "));
v8::Local<v8::String> cons2 = v8::String::Concat(cons1, message);
v8::Local<v8::Value> e = v8::Exception::Error(cons2);
v8::Local<v8::Object> obj = e->ToObject();
obj->Set(v8::String::NewSymbol("errno"), v8::Integer::New(status));
return e;
}
static void set_type(v8::Local<v8::Object> &r, int rr_type) {
r->Set(ntype_symbol, v8::Integer::New(rr_type));
for (unsigned int i = 0; i < sizeof(rr_types)/sizeof(rr_types[0]); i++) {
if (rr_type == rr_types[i].n) {
r->Set(type_symbol, rr_types[i].symbol);
break;
}
}
}
static void set_class(v8::Local<v8::Object> &r, int rr_class) {
r->Set(nclass_symbol, v8::Integer::New(rr_class));
for (unsigned int i = 0; i < sizeof(rr_classes)/sizeof(rr_classes[0]); i++) {
if (rr_class == rr_classes[i].n) {
r->Set(class_symbol, rr_classes[i].symbol);
break;
}
}
}
static int parse_read_questions(const unsigned char *abuf, int alen, const unsigned char * &aptr, int count, v8::Local<v8::Array> list) {
int status;
long len;
/* skip each question */
for (int i = 0; i < count; i++) {
int rr_type, rr_class;
char *rr_name;
v8::Local<v8::Object> r = v8::Object::New();
/* Decode the RR up to the data field. */
status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
if (status != ARES_SUCCESS) return status;
aptr += len;
if (aptr + QFIXEDSZ > abuf + alen) {
free(rr_name);
return ARES_EBADRESP;
}
rr_type = DNS_RR_TYPE(aptr);
rr_class = DNS_RR_CLASS(aptr);
aptr += QFIXEDSZ;
if (aptr > abuf + alen) {
free(rr_name);
return ARES_EBADRESP;
}
r->Set(name_symbol, v8::String::New(rr_name));
set_type(r, rr_type);
set_class(r, rr_class);
free(rr_name);
list->Set(v8::Integer::New(i), r);
}
return ARES_SUCCESS;
}
static int parse_read_answers(const unsigned char *abuf, int alen, const unsigned char * &aptr, int count, v8::Local<v8::Array> list) {
int status;
long len;
/* Examine each resource record (RR) in turn. */
for (int i = 0; i < count; i++) {
int rr_type, rr_class, rr_len, rr_ttl;
char *rr_name;
const unsigned char *rr_data;
v8::Local<v8::Object> r = v8::Object::New();
/* Decode the RR up to the data field. */
status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
if (status != ARES_SUCCESS) break;
aptr += len;
if (aptr + RRFIXEDSZ > abuf + alen) {
free(rr_name);
return ARES_EBADRESP;
}
rr_type = DNS_RR_TYPE(aptr);
rr_class = DNS_RR_CLASS(aptr);
rr_ttl = DNS_RR_TTL(aptr);
rr_len = DNS_RR_LEN(aptr);
aptr += RRFIXEDSZ;
rr_data = aptr;
aptr += rr_len;
if (aptr > abuf + alen) {
free(rr_name);
return ARES_EBADRESP;
}
r->Set(name_symbol, v8::String::New(rr_name));
set_class(r, rr_class);
r->Set(ttl_symbol, v8::Integer::New(rr_ttl));
free(rr_name);
r->Set(ntype_symbol, v8::Integer::New(rr_type));
for (unsigned int t = 0; t < sizeof(rr_types)/sizeof(rr_types[0]); t++) {
if (rr_type == rr_types[t].n) {
r->Set(type_symbol, rr_types[t].symbol);
if (C_IN == rr_class && rr_types[t].parse_in) {
status = rr_types[t].parse_in(abuf, alen, rr_data, rr_len, r);
if (status != ARES_SUCCESS) return status;
}
break;
}
}
r->Set(rdata_symbol, v8::String::New((const char*) rr_data, rr_len));
list->Set(v8::Integer::New(i), r);
}
return ARES_SUCCESS;
}
v8::Local<v8::Value> parse_dns_response(const unsigned char *abuf, int alen, int *pstatus) {
unsigned int qdcount, ancount, nscount, arcount;
int status;
const unsigned char *aptr;
if (pstatus) *pstatus = ARES_SUCCESS;
v8::Local<v8::Array> qd = v8::Array::New(), an = v8::Array::New(), ns = v8::Array::New(), ar = v8::Array::New();
v8::Local<v8::Object> msg = v8::Object::New();
/* Give up if abuf doesn't have room for a header. */
if (alen < HFIXEDSZ) {
status = ARES_EBADRESP;
goto error;
}
/* Fetch the question and answer count from the header. */
qdcount = DNS_HEADER_QDCOUNT(abuf);
ancount = DNS_HEADER_ANCOUNT(abuf);
nscount = DNS_HEADER_NSCOUNT(abuf);
arcount = DNS_HEADER_ARCOUNT(abuf);
if (qdcount != 1) {
status = ARES_EBADRESP;
goto error;
}
aptr = abuf + HFIXEDSZ;
status = parse_read_questions(abuf, alen, aptr, qdcount, qd);
if (status != ARES_SUCCESS) goto error;
status = parse_read_answers(abuf, alen, aptr, ancount, an);
if (status != ARES_SUCCESS) goto error;
status = parse_read_answers(abuf, alen, aptr, nscount, ns);
if (status != ARES_SUCCESS) goto error;
status = parse_read_answers(abuf, alen, aptr, arcount, ar);
if (status != ARES_SUCCESS) goto error;
msg->Set(question_symbol, qd);
msg->Set(answer_symbol, an);
msg->Set(authority_symbol, ns);
msg->Set(additional_symbol, ar);
return msg;
error:
if (pstatus) *pstatus = status;
return AresException(status);
}
}