587 řádky
19 KiB
C++
587 řádky
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);
|
|
}
|
|
|
|
}
|