#include "dns_parser.h" #include #include #include #include #include #include #include #include #include #ifdef __OpenBSD__ # ifndef ns_t_a # include # endif #endif // __OpenBSD__ namespace dns_parser { static v8::Persistent question_symbol, answer_symbol, authority_symbol, additional_symbol; static v8::Persistent type_symbol, ntype_symbol, class_symbol, nclass_symbol, ttl_symbol, rdata_symbol, text_symbol, data_symbol; static v8::Persistent priority_symbol, weight_symbol, port_symbol, name_symbol, exchange_symbol, target_symbol; static v8::Persistent mname_symbol, rname_symbol, serial_symbol, refresh_symbol, retry_symbol, expire_symbol, minimum_symbol; static v8::Persistent space_symbol; typedef int (*ParseRRData)(const unsigned char *abuf, int alen, const unsigned char *rr_data, int rr_len, v8::Local &r); struct RR_TYPE { const char *s; int n; ParseRRData parse_in; /* only parse class IN */ v8::Persistent symbol; }; struct RR_CLASS { const char *s; int n; v8::Persistent symbol; }; static int parse_char_string(const unsigned char *&rr_data, int &rr_len, v8::Local &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 &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 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 &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 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 &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_hostname = v8::String::New(hostname); free(hostname); if (len != rr_len) return ARES_EBADRESP; v8::Local v8_priority = v8::Integer::New(priority); r->Set(priority_symbol, v8_priority); r->Set(exchange_symbol, v8_hostname); v8::Local 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 &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_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 &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_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_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_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 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 &r) { int status, i; if (0 == rr_len) return ARES_EBADRESP; v8::Local list = v8::Array::New(); v8::Local 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 &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_target = v8::String::New(target); free(target); if (len != rr_len) return ARES_EBADRESP; v8::Local 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 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() }, { "NS", 2, ParseRRData_NS, v8::Persistent() }, { "MD", 3, 0, v8::Persistent() }, { "MF", 4, 0, v8::Persistent() }, { "CNAME", 5, ParseRRData_NS, v8::Persistent() }, /* same RRDATA as NS */ { "SOA", 6, ParseRRData_SOA, v8::Persistent() }, { "MB", 7, 0, v8::Persistent() }, { "MG", 8, 0, v8::Persistent() }, { "MR", 9, 0, v8::Persistent() }, { "NULL", 10, 0, v8::Persistent() }, { "WKS", 11, 0, v8::Persistent() }, { "PTR", 12, ParseRRData_NS, v8::Persistent() }, /* same RRDATA as NS */ { "HINFO", 13, 0, v8::Persistent() }, { "MINFO", 14, 0, v8::Persistent() }, { "MX", 15, ParseRRData_MX, v8::Persistent() }, { "TXT", 16, ParseRRData_TXT, v8::Persistent() }, { "RP", 17, 0, v8::Persistent() }, { "AFSDB", 18, 0, v8::Persistent() }, { "SIG", 24, 0, v8::Persistent() }, { "KEY", 25, 0, v8::Persistent() }, { "AAAA", 28, ParseRRData_AAAA, v8::Persistent() }, { "LOC", 29, 0, v8::Persistent() }, { "SRV", 33, ParseRRData_SRV, v8::Persistent() }, { "NAPTR", 35, 0, v8::Persistent() }, { "KX", 36, 0, v8::Persistent() }, { "CERT", 37, 0, v8::Persistent() }, { "DNAME", 39, 0, v8::Persistent() }, { "OPT", 41, 0, v8::Persistent() }, { "APL", 42, 0, v8::Persistent() }, { "DS", 43, 0, v8::Persistent() }, { "SSHFP", 44, 0, v8::Persistent() }, { "IPSECKEY", 45, 0, v8::Persistent() }, { "RRSIG", 46, 0, v8::Persistent() }, { "NSEC", 47, 0, v8::Persistent() }, { "DNSKEY", 48, 0, v8::Persistent() }, { "DHCID", 49, 0, v8::Persistent() }, { "NSEC3", 50, 0, v8::Persistent() }, { "NSEC3PARAM", 51, 0, v8::Persistent() }, { "HIP", 55, 0, v8::Persistent() }, { "SPF", 99, 0, v8::Persistent() }, { "TKEY", 249, 0, v8::Persistent() }, { "TSIG", 250, 0, v8::Persistent() }, { "IXFR", 251, 0, v8::Persistent() }, { "AXFR", 252, 0, v8::Persistent() }, { "MAILB", 253, 0, v8::Persistent() }, { "MAILA", 254, 0, v8::Persistent() }, { "ANY", 255, 0, v8::Persistent() }, /* aka "*" */ { "TA", 32768, 0, v8::Persistent() }, { "DLV", 32769, 0, v8::Persistent() } }; static RR_CLASS rr_classes[] = { { "IN", 1, v8::Persistent() }, /* the Internet */ { "CS", 2, v8::Persistent() }, /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ { "CH", 3, v8::Persistent() }, /* the CHAOS class */ { "HS", 4, v8::Persistent() }, /* Hesiod [Dyer 87] */ { "ANY", 255, v8::Persistent() } /* same value as in rr_type, so no name conflict */ }; void Initialize(v8::Handle 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 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 AresException(int status) { v8::Local code = v8::String::NewSymbol(ares_errno_string(status)); v8::Local message = v8::String::NewSymbol(ares_strerror(status)); v8::Local cons1 = v8::String::Concat(code, v8::String::NewSymbol(", ")); v8::Local cons2 = v8::String::Concat(cons1, message); v8::Local e = v8::Exception::Error(cons2); v8::Local obj = e->ToObject(); obj->Set(v8::String::NewSymbol("errno"), v8::Integer::New(status)); return e; } static void set_type(v8::Local &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 &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 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 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 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 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 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 qd = v8::Array::New(), an = v8::Array::New(), ns = v8::Array::New(), ar = v8::Array::New(); v8::Local 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); } }