├── README ├── examples ├── authorative.js ├── proxy.js └── zonewalk.js └── lib └── ndns.js /README: -------------------------------------------------------------------------------- 1 | see examples/ -------------------------------------------------------------------------------- /examples/authorative.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'), puts = sys.puts; 2 | var dgram = require('dgram'); 3 | var ndns = require('../lib/ndns'); 4 | var server = ndns.createServer('udp4'); 5 | var client = ndns.createClient('udp4'); 6 | 7 | var BIND_PORT = 53; 8 | 9 | server.on("request", function(req, res) { 10 | res.setHeader(req.header); 11 | 12 | for (var i = 0; i < req.q.length; i++) 13 | res.addQuestion(req.q[i]); 14 | 15 | if (req.q.length > 0) { 16 | var name = req.q[0].name; 17 | if (name == ".") 18 | name = ""; 19 | res.header.qr = 1; 20 | res.header.ra = 1; 21 | res.header.rd = 0; 22 | res.header.ancount = 3; 23 | res.header.nscount = 4; 24 | res.header.arcount = 5; 25 | res.addRR(name, 1, "IN", "SOA", 26 | "hostmaster." + name, 27 | "hostmaster." + name, 28 | 1, 2, 3, 4, 5); 29 | res.addRR(name, 2, "IN", "TXT", "Hello World"); 30 | res.addRR(name, 3, "IN", "MX", 10, "mail." + name); 31 | res.addRR(name, 4, "IN", "NS", "ns1." + name); 32 | res.addRR(name, 5, "IN", "NS", "ns2." + name); 33 | res.addRR(name, 6, "IN", "NS", "ns3." + name); 34 | res.addRR(name, 7, "IN", "NS", "ns4." + name); 35 | res.addRR("mail." + name, 8, "IN", "A", "127.0.0.1"); 36 | res.addRR("ns1." + name, 9, "IN", "A", "127.0.0.1"); 37 | res.addRR("ns2." + name, 10, "IN", "A", "127.0.0.2"); 38 | res.addRR("ns3." + name, 11, "IN", "A", "127.0.0.3"); 39 | res.addRR("ns4." + name, 12, "IN", "A", "127.0.0.4"); 40 | } 41 | res.send(); 42 | }); 43 | 44 | server.bind(BIND_PORT); 45 | -------------------------------------------------------------------------------- /examples/proxy.js: -------------------------------------------------------------------------------- 1 | var ndns = require('../lib/ndns'); 2 | var server = ndns.createServer('udp4'); 3 | var client = ndns.createClient('udp4'); 4 | 5 | var LOCAL_PORT = 53; 6 | var REMOTE_HOST = "4.2.2.1" 7 | var REMOTE_PORT = 53; 8 | 9 | server.on("request", function(req, res) { 10 | var c_req = client.request(REMOTE_PORT, REMOTE_HOST); 11 | c_req.on("response", function (c_res) { 12 | res.send(c_res); 13 | }); 14 | c_req.send(req); 15 | }); 16 | 17 | server.bind(LOCAL_PORT); 18 | client.bind(); 19 | -------------------------------------------------------------------------------- /examples/zonewalk.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'), puts = sys.puts; 2 | var ndns = require('../lib/ndns'); 3 | var resolver = ndns.createClient('udp4'); 4 | 5 | function walk (a_root_servers_net, domain) { 6 | puts(domain); 7 | var req = resolver.request(53, a_root_servers_net); 8 | 9 | req.setHeader({ 10 | id: 1992, 11 | rd: 1, 12 | qdcount: 1}); 13 | req.addQuestion (domain, "NSEC", "IN"); 14 | req.send(); 15 | 16 | req.on("response", function (res) { 17 | var rr; 18 | for (var i = 0; i < res.rr.length; i++) { 19 | rr = res.rr[i]; 20 | if (rr.typeName == "NSEC") { 21 | walk(a_root_servers_net, rr.rdata.next_domain_name); 22 | break; 23 | } 24 | } 25 | }); 26 | } 27 | 28 | require('dns').resolve4("A.ROOT-SERVERS.NET", function (err, addrs) { 29 | if (err) throw err; 30 | if (addrs.length > 0) 31 | walk(addrs[0], "."); 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /lib/ndns.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | 3 | var debug; 4 | var debugLevel = parseInt(process.env.NODE_DEBUG, 16); 5 | if(debugLevel & 0x4) { 6 | debug = function (x) { sys.error('NDNS: ' + x); }; 7 | } else { 8 | debug = function () { }; 9 | } 10 | 11 | var dgram = require('dgram'); 12 | var events = require('events'); 13 | var Buffer = require('buffer').Buffer; 14 | 15 | var FreeList = require('freelist').FreeList; 16 | 17 | var ns_packsiz = 512; // Default UDP Packet size 18 | var ns_maxdname = 1025; // Maximum domain name 19 | var ns_maxmsg = 65535; // Maximum message size 20 | var ns_maxcdname = 255; // Maximum compressed domain name 21 | var ns_maxlabel = 63; // Maximum compressed domain label 22 | var ns_hfixedsz = 12; // Bytes of fixed data in header 23 | var ns_qfixedsz = 4; // Bytes of fixed data in query 24 | var ns_rrfixedsz = 10; // Bytes of fixed data in r record 25 | var ns_int32sz = 4; // Bytes of data in a u_int32_t 26 | var ns_int16sz = 2; // Bytes of data in a u_int16_t 27 | var ns_int8sz = 1; // Bytes of data in a u_int8_t 28 | var ns_inaddrsz = 4; // IPv4 T_A 29 | var ns_in6addrsz = 16; // IPv6 T_AAAA 30 | var ns_cmprsflgs = 0xc0;// Flag bits indicating name compression. 31 | var ns_defaultport = 53;// For both UDP and TCP. 32 | 33 | function enum (obj) { 34 | for (key in obj) { 35 | global[key] = obj[key]; 36 | } 37 | return obj; 38 | } 39 | 40 | var ns_sect = enum({ 41 | 'ns_s_qd': 0, // Query: Question. 42 | 'ns_s_zn': 0, // Update: Zone. 43 | 'ns_s_an': 1, // Query: Answer. 44 | 'ns_s_pr': 1, // Update: Prerequisites. 45 | 'ns_s_ns': 2, // Query: Name servers. 46 | 'ns_s_ud': 2, // Query: Update. 47 | 'ns_s_ar': 3, // Query|Update: Additional records. 48 | 'ns_s_max': 4, 49 | }); 50 | 51 | var ns_flag = enum({ 52 | 'ns_f_qr': 0, // Question/Response. 53 | 'ns_f_opcode': 1, // Operation code. 54 | 'ns_f_aa': 2, // Authorative Answer. 55 | 'ns_f_tc': 3, // Truncation occured. 56 | 'ns_f_rd': 4, // Recursion Desired. 57 | 'ns_f_ra': 5, // Recursion Available. 58 | 'ns_f_z': 6, // MBZ 59 | 'ns_f_ad': 7, // Authentic Data (DNSSEC) 60 | 'ns_f_cd': 8, // Checking Disabled (DNSSEC) 61 | 'ns_f_rcode': 9, // Response code. 62 | 'ns_f_max': 10, 63 | }); 64 | 65 | // Currently defined opcodes. 66 | var ns_opcode = enum({ 67 | 'ns_o_query': 0, // Standard query. 68 | 'ns_o_iquery': 1, // Inverse query (deprecated/unsupported). 69 | 'ns_o_status': 2, // Name server status query (unsupported). 70 | // Opcode 3 is undefined/reserved 71 | 'ns_o_notify': 4, // Zone change notification. 72 | 'ns_o_update': 5, // Zone update message. 73 | }); 74 | 75 | // Currently defined response codes 76 | var ns_rcode = enum({ 77 | 'ns_r_noerror': 0, // No error occured. 78 | 'ns_r_formerr': 1, // Format error. 79 | 'ns_r_servfail': 2, // Server failure. 80 | 'ns_r_nxdomain': 3, // Name error. 81 | 'ns_r_notimpl': 4, // Unimplemented. 82 | 'ns_r_refused': 5, // Operation refused. 83 | // These are for BIND_UPDATE 84 | 'ns_r_yxdomain': 6, // Name exists 85 | 'ns_r_yxrrset': 7, // RRset exists 86 | 'ns_r_nxrrset': 8, // RRset does not exist 87 | 'ns_r_notauth': 9, // Not authoritative for zone 88 | 'ns_r_notzone': 10, // Zone of record different from zone section 89 | 'ns_r_max': 11, 90 | // The following are EDNS extended rcodes 91 | 'ns_r_badvers': 16, 92 | // The following are TSIG errors 93 | 'ns_r_badsig': 16, 94 | 'ns_r_badkey': 17, 95 | 'ns_r_badtime': 18, 96 | }); 97 | 98 | // BIND_UPDATE 99 | var ns_update_operation = enum({ 100 | 'ns_oup_delete': 0, 101 | 'ns_oup_add': 1, 102 | 'ns_oup_max': 2, 103 | }); 104 | 105 | var NS_TSIG = enum({ 106 | 'NS_TSIG_FUDGE': 300, 107 | 'NS_TSIG_TCP_COUNT': 100, 108 | 'NS_TSIG_ALG_HMAC_MD5': "HMAC-MD5.SIG-ALG.REG.INT", 109 | 110 | 'NS_TSIG_ERROR_NO_TSIG': -10, 111 | 'NS_TSIG_ERROR_NO_SPACE': -11, 112 | 'NS_TSIG_ERROR_FORMERR': -12, 113 | }); 114 | 115 | // Currently defined type values for resources and queries. 116 | var ns_type = enum({ 117 | 'ns_t_invalid': 0, // Cookie. 118 | 'ns_t_a': 1, // Host address. 119 | 'ns_t_ns': 2, // Authoritative server. 120 | 'ns_t_md': 3, // Mail destination. 121 | 'ns_t_mf': 4, // Mail forwarder. 122 | 'ns_t_cname': 5, // Canonical name. 123 | 'ns_t_soa': 6, // Start of authority zone. 124 | 'ns_t_mb': 7, // Mailbox domain name. 125 | 'ns_t_mg': 8, // Mail group member. 126 | 'ns_t_mr': 9, // Mail rename name. 127 | 'ns_t_null': 10, // Null resource record. 128 | 'ns_t_wks': 11, // Well known service. 129 | 'ns_t_ptr': 12, // Domain name pointer. 130 | 'ns_t_hinfo': 13, // Host information. 131 | 'ns_t_minfo': 14, // Mailbox information. 132 | 'ns_t_mx': 15, // Mail routing information. 133 | 'ns_t_txt': 16, // Text strings. 134 | 'ns_t_rp': 17, // Responsible person. 135 | 'ns_t_afsdb': 18, // AFS cell database. 136 | 'ns_t_x25': 19, // X_25 calling address. 137 | 'ns_t_isdn': 20, // ISDN calling address. 138 | 'ns_t_rt': 21, // Router. 139 | 'ns_t_nsap': 22, // NSAP address. 140 | 'ns_t_ns_nsap_ptr': 23, // Reverse NSAP lookup (deprecated) 141 | 'ns_t_sig': 24, // Security signature. 142 | 'ns_t_key': 25, // Security key. 143 | 'ns_t_px': 26, // X.400 mail mapping. 144 | 'ns_t_gpos': 27, // Geographical position (withdrawn). 145 | 'ns_t_aaaa': 28, // Ip6 Address. 146 | 'ns_t_loc': 29, // Location Information. 147 | 'ns_t_nxt': 30, // Next domain (security) 148 | 'ns_t_eid': 31, // Endpoint identifier. 149 | 'ns_t_nimloc': 32, // Nimrod Locator. 150 | 'ns_t_srv': 33, // Server Selection. 151 | 'ns_t_atma': 34, // ATM Address 152 | 'ns_t_naptr': 35, // Naming Authority PoinTeR 153 | 'ns_t_kx': 36, // Key Exchange 154 | 'ns_t_cert': 37, // Certification Record 155 | 'ns_t_a6': 38, // IPv6 Address (deprecated, use ns_t_aaaa) 156 | 'ns_t_dname': 39, // Non-terminal DNAME (for IPv6) 157 | 'ns_t_sink': 40, // Kitchen sink (experimental) 158 | 'ns_t_opt': 41, // EDNS0 option (meta-RR) 159 | 'ns_t_apl': 42, // Address prefix list (RFC3123) 160 | 'ns_t_ds': 43, // Delegation Signer 161 | 'ns_t_sshfp': 44, // SSH Fingerprint 162 | 'ns_t_ipseckey': 45,// IPSEC Key 163 | 'ns_t_rrsig': 46, // RRSet Signature 164 | 'ns_t_nsec': 47, // Negative Security 165 | 'ns_t_dnskey': 48, // DNS Key 166 | 'ns_t_dhcid': 49, // Dynamic host configuartion identifier 167 | 'ns_t_nsec3': 50, // Negative security type 3 168 | 'ns_t_nsec3param': 51, // Negative security type 3 parameters 169 | 'ns_t_hip': 55, // Host Identity Protocol 170 | 'ns_t_spf': 99, // Sender Policy Framework 171 | 'ns_t_tkey': 249, // Transaction key 172 | 'ns_t_tsig': 250, // Transaction signature. 173 | 'ns_t_ixfr': 251, // Incremental zone transfer. 174 | 'ns_t_axfr': 252, // Transfer zone of authority. 175 | 'ns_t_mailb': 253, // Transfer mailbox records. 176 | 'ns_t_maila': 254, // Transfer mail agent records. 177 | 'ns_t_any': 255, // Wildcard match. 178 | 'ns_t_zxfr': 256, // BIND-specific, nonstandard. 179 | 'ns_t_dlv': 32769, // DNSSEC look-aside validation. 180 | 'ns_t_max': 65536 181 | }); 182 | exports.ns_type = ns_type; 183 | 184 | // Values for class field 185 | var ns_class = enum({ 186 | 'ns_c_invalid': 0, // Cookie. 187 | 'ns_c_in': 1, // Internet. 188 | 'ns_c_2': 2, // unallocated/unsupported. 189 | 'ns_c_chaos': 3, // MIT Chaos-net. 190 | 'ns_c_hs': 4, // MIT Hesoid. 191 | // Query class values which do not appear in resource records 192 | 'ns_c_none': 254, // for prereq. sections in update requests 193 | 'ns_c_any': 255, // Wildcard match. 194 | 'ns_c_max': 65535, 195 | }); 196 | exports.ns_class = ns_class; 197 | 198 | // DNSSEC constants. 199 | var ns_key_types = enum({ 200 | 'ns_kt_rsa': 1, // key type RSA/MD5 201 | 'ns_kt_dh': 2, // Diffie Hellman 202 | 'ns_kt_dsa': 3, // Digital Signature Standard (MANDATORY) 203 | 'ns_kt_private': 4 // Private key type starts with OID 204 | }); 205 | 206 | var ns_cert_type = enum({ 207 | 'cert_t_pkix': 1, // PKIX (X.509v3) 208 | 'cert_t_spki': 2, // SPKI 209 | 'cert_t_pgp': 3, // PGP 210 | 'cert_t_url': 253, // URL private type 211 | 'cert_t_oid': 254 // OID private type 212 | }); 213 | 214 | // Flags field of the KEY RR rdata 215 | 216 | 217 | var ns_type_elt = 0x40; //edns0 extended label type 218 | var dns_labeltype_bitstring = 0x41; 219 | var digitvalue = [ 220 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 221 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 222 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 48 223 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 64 224 | -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 225 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 226 | -1, 12, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 227 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 228 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 229 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 231 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 232 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 233 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 234 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 235 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 256 236 | ]; 237 | 238 | var hexvalue = [ 239 | "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 240 | "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 241 | "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 242 | "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 243 | "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 244 | "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 245 | "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 246 | "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 247 | "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 248 | "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 249 | "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 250 | "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 251 | "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 252 | "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", 253 | "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 254 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", 255 | ]; 256 | 257 | var digits = "0123456789"; 258 | var ns_flagdata = [ 259 | { mask: 0x8000, shift: 15 }, // qr. 260 | { mask: 0x7800, shift: 11 }, // opcode. 261 | { mask: 0x0400, shift: 10 }, // aa. 262 | { mask: 0x0200, shift: 9 }, // tc. 263 | { mask: 0x0100, shift: 8 }, // rd. 264 | { mask: 0x0080, shift: 7 }, // ra. 265 | { mask: 0x0040, shift: 6 }, // z. 266 | { mask: 0x0020, shift: 5 }, // ad. 267 | { mask: 0x0010, shift: 4 }, // cd. 268 | { mask: 0x000f, shift: 0 }, // rcode. 269 | { mask: 0x0000, shift: 0 }, // expansion (1/6). 270 | { mask: 0x0000, shift: 0 }, // expansion (2/6). 271 | { mask: 0x0000, shift: 0 }, // expansion (3/6). 272 | { mask: 0x0000, shift: 0 }, // expansion (4/6). 273 | { mask: 0x0000, shift: 0 }, // expansion (5/6). 274 | { mask: 0x0000, shift: 0 }, // expansion (6/6). 275 | ]; 276 | 277 | var res_opcodes = [ 278 | "QUERY", 279 | "IQUERY", 280 | "CQUERYM", 281 | "CQUERYU", // experimental 282 | "NOTIFY", // experimental 283 | "UPDATE", 284 | "6", 285 | "7", 286 | "8", 287 | "9", 288 | "10", 289 | "11", 290 | "12", 291 | "13", 292 | "ZONEINIT", 293 | "ZONEREF", 294 | ]; 295 | var res_sectioncodes = [ 296 | "ZONE", 297 | "PREREQUISITES", 298 | "UPDATE", 299 | "ADDITIONAL", 300 | ]; 301 | 302 | var p_class_syms = { 303 | 1: "IN", 304 | 3: "CHAOS", 305 | 4: "HESOID", 306 | 254: "ANY", 307 | 255: "NONE" 308 | }; 309 | 310 | var p_default_section_syms = { 311 | 0: "QUERY", 312 | 1: "ANSWER", 313 | 2: "AUTHORITY", 314 | 3: "ADDITIONAL" 315 | }; 316 | 317 | var p_key_syms = { 318 | 1: ["RSA", "RSA KEY with MD5 hash"], 319 | 2: ["DH", "Diffie Hellman"], 320 | 3: ["DSA", "Digital Signature Algorithm"], 321 | 4: ["PRIVATE", "Algorithm obtained from OID"] 322 | }; 323 | 324 | var p_cert_syms = { 325 | 1: ["PKIX", "PKIX (X.509v3) Certificate"], 326 | 2: ["SKPI", "SPKI Certificate"], 327 | 3: ["PGP", "PGP Certificate"], 328 | 253: ["URL", "URL Private"], 329 | 254: ["OID", "OID Private"] 330 | }; 331 | 332 | var p_type_syms = { 333 | 1: "A", 334 | 2: "NS", 335 | 3: "MD", 336 | 4: "MF", 337 | 5: "CNAME", 338 | 6: "SOA", 339 | 7: "MB", 340 | 8: "MG", 341 | 9: "MR", 342 | 10: "NULL", 343 | 11: "WKS", 344 | 12: "PTR", 345 | 13: "HINFO", 346 | 14: "MINFO", 347 | 15: "MX", 348 | 16: "TXT", 349 | 17: "RP", 350 | 18: "AFSDB", 351 | 19: "X25", 352 | 20: "ISDN", 353 | 21: "RT", 354 | 22: "NSAP", 355 | 23: "NSAP_PTR", 356 | 24: "SIG", 357 | 25: "KEY", 358 | 26: "PX", 359 | 27: "GPOS", 360 | 28: "AAAA", 361 | 29: "LOC", 362 | 30: "NXT", 363 | 31: "EID", 364 | 32: "NIMLOC", 365 | 33: "SRV", 366 | 34: "ATMA", 367 | 35: "NAPTR", 368 | 36: "KX", 369 | 37: "CERT", 370 | 38: "A6", 371 | 39: "DNAME", 372 | 40: "SINK", 373 | 41: "OPT", 374 | 42: "APL", 375 | 43: "DS", 376 | 44: "SSHFP", 377 | 45: "IPSECKEY", 378 | 46: "RRSIG", 379 | 47: "NSEC", 380 | 48: "DNSKEY", 381 | 49: "DHCID", 382 | 50: "NSEC3", 383 | 51: "NSEC3PARAM", 384 | 55: "HIP", 385 | 99: "SPF", 386 | 249: "TKEY", 387 | 250: "TSIG", 388 | 251: "IXFR", 389 | 252: "AXFR", 390 | 253: "MAILB", 391 | 254: "MAILA", 392 | 255: "ANY", 393 | 32769: "DLV", 394 | 256: "ZXFR", 395 | }; 396 | 397 | 398 | 399 | var p_rcode_syms = { 400 | 0: ["NOERROR", "no error"], 401 | 1: ["FORMERR", "format error"], 402 | 2: ["SERVFAIL", "server failed"], 403 | 3: ["NXDOMAIN", "no such domain name"], 404 | 4: ["NOTIMP", "not implemented"], 405 | 5: ["REFUSED", "refused"], 406 | // These are for BIND_UPDATE 407 | 6: ["YXDOMAIN", "domain name exist"], 408 | 7: ["YXRRSET", "rrset exists"], 409 | 8: ["NXRRSET", "rrset doesn't exist"], 410 | 9: ["NOTAUTH", "not authoritative"], 411 | 10: ["NOTZONE", "not in zone"], 412 | 11: ["", ""], 413 | // The following are EDNS extended rcodes 414 | // The following are TSIG errors 415 | 16: ["BADSIG", "bad signature"], 416 | 17: ["BADKEY", "bad key"], 417 | 18: ["BADTIME", "bad time"] 418 | }; 419 | 420 | var n_type_syms = {}; 421 | for (var k in p_type_syms) 422 | n_type_syms[p_type_syms[k]] = k; 423 | 424 | var n_class_syms = {}; 425 | for (var k in p_class_syms) 426 | n_class_syms[p_class_syms[k]] = k; 427 | 428 | function Ptr () { 429 | this.p = (arguments.length == 1) ? arguments[0] : null; 430 | } 431 | exports.Ptr = Ptr; 432 | 433 | Ptr.prototype.get = function () { 434 | return this.p; 435 | }; 436 | 437 | Ptr.prototype.set = function (val) { 438 | return this.p = val; 439 | }; 440 | 441 | function ns_name_ntop(src, dst, dstsiz) { 442 | var cp; 443 | var dn, eom; 444 | var c; 445 | var n; 446 | var l; 447 | 448 | cp = 0; 449 | dn = 0; 450 | eom = dstsiz; 451 | 452 | while((n = src[cp++]) != 0) { 453 | if((n & ns_cmprsflgs) == ns_cmprsflgs) { 454 | /* some kind of compression pointer */ 455 | errno.set('EMSGSIZE'); 456 | return (-1); 457 | } 458 | if(dn != 0) { 459 | if(dn >= eom) { 460 | errno.set('EMSGSIZE'); 461 | return (-1); 462 | } 463 | dst[dn++] = 0x2e; /* '.' */ 464 | } 465 | if ((l = labellen(src, cp - 1)) < 0) { 466 | errno.set('EMSGSIZE'); 467 | return (-1); 468 | } 469 | if(dn + l >= eom) { 470 | errno.set('EMSGSIZE'); 471 | return (-1); 472 | } 473 | if((n & ns_cmprsflgs) == ns_type_elt) { 474 | var m; 475 | 476 | if(n != dns_labeltype_bitstring) { 477 | /* labellen should reject this case */ 478 | return (-1); 479 | } 480 | var cpp = new Ptr(cp); 481 | if ((m = decode_bitstring(src, cpp, dst, dn, eom)) < 0) { 482 | errno.set('EMSGSIZE'); 483 | return (-1); 484 | } 485 | cp = cpp.get(); 486 | dn += m; 487 | continue; 488 | } 489 | for(; l > 0; l--) { 490 | c = src[cp++]; 491 | if(special(c)) { 492 | if(dn + 1 >= eom) { 493 | errno.set('EMSGSIZE'); 494 | return (-1); 495 | } 496 | dst[dn++] = 0x5c; /* '\\' */ 497 | dst[dn++] = c; 498 | } 499 | else if(!printable(c)) { 500 | if(dn + 3 >= eom) { 501 | errno.set('EMSGSIZE'); 502 | return (-1); 503 | } 504 | dst[dn++] = 0x5c; /* '\\' */ 505 | dst[dn++] = digits[c / 100]; 506 | dst[dn++] = digits[(c % 100) / 10]; 507 | dst[dn++] = digits[c % 10]; 508 | } 509 | else { 510 | if(dn >= eom) { 511 | errno.set('EMSGSIZE'); 512 | return (-1); 513 | } 514 | dst[dn++] = c; 515 | } 516 | } 517 | } 518 | if (dn == 0) { 519 | if (dn >= eom) { 520 | errno.set('EMSGSIZE'); 521 | return (-1); 522 | } 523 | dst[dn++] = 0x2e; // '.' 524 | } 525 | if (dn >= eom) { 526 | errno.set('EMSGSIZE'); 527 | return (-1); 528 | } 529 | dst[dn] = 0; 530 | return (dn); 531 | } 532 | exports.ns_name_ntop = ns_name_ntop; 533 | 534 | function ns_name_pton (src, dst, dstsiz) { 535 | return ns_name_pton2(src, dst, dstsiz, null); 536 | } 537 | exports.ns_name_pton = ns_name_pton; 538 | 539 | function ns_name_pton2(src, dst, dstsiz, dstlenp) { 540 | var label, bp, epm 541 | var c, n, escaped, e = 0; 542 | var cp; 543 | 544 | escaped = 0; 545 | bp = 0; 546 | eom = dstsiz; 547 | label = bp++; 548 | 549 | var srcn = 0; 550 | var done = false; // instead of goto 551 | while ((c = src[srcn++]) != 0) { 552 | if (escaped) { 553 | if (c == 91) { // '['; start a bit string label 554 | if ((cp = strchr(src, srcn, 93)) == null) { // ']' 555 | errno.set('EINVAL'); 556 | return(-1); 557 | } 558 | var srcp = new Ptr(srcn); 559 | var bpp = new Ptr(bp); 560 | var labelp = new Ptr(label); 561 | if ((e = encode_bitstring (src, srcp, cp + 2, 562 | labelp, dst, bpp, eom) 563 | != 0)) { 564 | errno.set(e); 565 | return(-1); 566 | } 567 | label = labelp.get(); 568 | bp = bpp.get(); 569 | srcn = srcp.get(); 570 | escaped = 0; 571 | label = bp++; 572 | if ((c = src[srcn++]) == 0) { 573 | done = true; 574 | break; 575 | } 576 | } 577 | else if ((cp = digits.indexOf(String.fromCharCode(c))) != -1) { 578 | n = (cp * 100); 579 | if ((c = src[srcn++]) || 580 | (cp = digits.indexOf(String.fromCharCode(c))) == -1) { 581 | errno.set('EMSGSIZE'); 582 | return (-1); 583 | } 584 | n += (cp) * 10; 585 | if ((c = src[srcn++]) == 0 || 586 | (cp = digits.indexOf(String.fromCharCode(c))) == -1) { 587 | errno.set('EMSGSIZE'); 588 | return (-1); 589 | } 590 | n += cp; 591 | if (n > 255) { 592 | errno.set('EMSGSIZE'); 593 | return (-1); 594 | } 595 | c = n; 596 | } 597 | escaped = 0; 598 | } else if (c == 92) { // '\\' 599 | escaped = 1; 600 | continue; 601 | } else if (c == 46) { // '.' 602 | c = (bp - label - 1) 603 | if ((c & ns_cmprsflgs) != 0) { // label too big 604 | errno.set('EMSGSIZE'); 605 | return (-1); 606 | } 607 | if (label >= eom) { 608 | errno.set('EMSGSIZE'); 609 | return (-1); 610 | } 611 | dst[label] = c; 612 | // Fully qualified? 613 | if (src[srcn] == 0) { 614 | if (c != 0) { 615 | if (bp >= eom) { 616 | errno.set('EMSGSIZE'); 617 | return (-1); 618 | } 619 | dst[bp++] = 0; 620 | } 621 | if ((bp) > ns_maxcdname) { 622 | errno.set('EMSGSIZE'); 623 | return (-1); 624 | } 625 | if (dstlenp != null) { 626 | dstlenp.set(bp); 627 | } 628 | return (1); 629 | } 630 | if (c == 0 || src[srcn] == 46) { // '.' 631 | errno.set('EMSGSIZE'); 632 | return (-1); 633 | } 634 | label = bp++; 635 | continue; 636 | } 637 | if (bp >= eom) { 638 | errno.set('EMSGSIZE'); 639 | return (-1); 640 | } 641 | dst[bp++] = c; 642 | } 643 | if (!done) { 644 | c = (bp - label - 1); 645 | if ((c & ns_cmprsflgs) != 0) { 646 | errno.set('EMSGSIZE'); 647 | return (-1); 648 | } 649 | } 650 | // done: 651 | if (label >= eom) { 652 | errno.set('EMSGSIZE'); 653 | return (-1); 654 | } 655 | dst[label] = c; 656 | if (c != 0) { 657 | if (bp >= eom) { 658 | errno.set('EMSGSIZE'); 659 | return (-1); 660 | } 661 | dst[bp++] = 0; 662 | } 663 | if (bp > ns_maxcdname) { // src too big 664 | errno.set('EMSGSIZE'); 665 | return (-1); 666 | } 667 | if (dstlenp != null) { 668 | dstlenp.set(bp); 669 | } 670 | return (0); 671 | } 672 | 673 | function strchr (src, off, n) { 674 | while (off < buf.length && buf[off] != 0) { 675 | if (buf[off] == n) 676 | return off; 677 | off++; 678 | } 679 | return null; 680 | } 681 | 682 | function ns_name_unpack (msg, offset, len, dst, dstsiz) { 683 | return ns_name_unpack2 (msg, offset, len, dst, dstsiz, null); 684 | } 685 | exports.ns_name_unpack = ns_name_unpack; 686 | 687 | function ns_name_unpack2 (msg, offset, len, dst, dstsiz, dstlenp) { 688 | var n, l; 689 | 690 | var llen = -1; 691 | var checked = 0; 692 | var dstn = 0; 693 | var srcn = offset; 694 | var dstlim = dstsiz; 695 | var eom = offset + len; 696 | if(srcn < 0 || srcn >= eom) { 697 | errno.set('EMSGSIZE'); 698 | return (-1); 699 | } 700 | /* Fetch next label in domain name */ 701 | while((n = msg[srcn++]) != 0 && !isNaN(srcn)) { 702 | /* Check for indirection */ 703 | switch(n & ns_cmprsflgs) { 704 | case 0: 705 | case ns_type_elt: 706 | /* Limit checks */ 707 | 708 | if((l = labellen(msg, srcn - 1)) < 0) { 709 | errno.set('EMSGSIZE'); 710 | return (-1); 711 | } 712 | if(dstn + l + 1 >= dstlim || srcn + l >= eom) { 713 | errno.set('EMSGSIZE'); 714 | return (-1); 715 | } 716 | checked += l + 1; 717 | dst[dstn++] = n; 718 | msg.copy(dst, dstn, srcn, srcn + l); 719 | dstn += l; 720 | srcn += l; 721 | break; 722 | 723 | case ns_cmprsflgs: 724 | if(srcn >= eom) { 725 | errno.set('EMSGSIZE'); 726 | return (-1); 727 | } 728 | if(llen < 0) { 729 | llen = (srcn - offset) + 1; 730 | } 731 | 732 | srcn = (((n & 0x3F) * 256) | (msg[srcn] & 0xFF)); 733 | 734 | if(srcn < 0 || srcn >= eom) { /* Out of range */ 735 | errno.set('EMSGSIZE'); 736 | return (-1); 737 | } 738 | 739 | checked += 2; 740 | /* check for loops in compressed name */ 741 | if(checked >= eom) { 742 | errno.set('EMSGSIZE'); 743 | return (-1); 744 | } 745 | break; 746 | 747 | default: 748 | errno.set('EMSGSIZE'); 749 | return (-1); // flag error 750 | } 751 | } 752 | dst[dstn] = 0; 753 | if (dstlenp != null) 754 | dstlenp.set(dstn); 755 | if(llen < 0) 756 | llen = srcn - offset; 757 | return (llen); 758 | } 759 | 760 | function ns_name_pack (src, dst, dstn, dstsiz, dnptrs, lastdnptr) { 761 | var dstp; 762 | var cpp, lpp, eob, msgp; 763 | var srcp; 764 | var n, l, first = 1; 765 | 766 | srcp = 0; 767 | dstp = dstn; 768 | eob = dstp + dstsiz; 769 | lpp = cpp = null; 770 | var ndnptr = 0; 771 | if (dnptrs != null) { 772 | msg = dst; 773 | //if ((msg = dnptrs[ndnptr++]) != null) { 774 | for (cpp = 0; dnptrs[cpp] != null; cpp++); 775 | lpp = cpp; // end of list to search 776 | //} 777 | } else 778 | msg = null; 779 | 780 | // make sure the domain we are about to add is legal 781 | l = 0; 782 | do { 783 | var l0; 784 | 785 | n = src[srcp]; 786 | if ((n & ns_cmprsflgs) == ns_cmprsflgs) { 787 | errno.set('EMSGSIZE'); 788 | return (-1); 789 | } 790 | if ((l0 = labellen(src, srcp)) < 0) { 791 | errno.set('EINVAL'); 792 | return (-1); 793 | } 794 | l += l0 + 1; 795 | if (l > ns_maxcdname) { 796 | errno.set('EMSGSIZE'); 797 | return (-1); 798 | } 799 | srcp += l0 + 1; 800 | } while (n != 0); 801 | 802 | // from here on we need to reset compression pointer array on error 803 | srcp = 0; 804 | var cleanup = false; // instead of goto 805 | do { 806 | // look to see if we can use pointers 807 | n = src[srcp]; 808 | if (n != 0 && msg != null) { 809 | l = dn_find(src, srcp, msg, dnptrs, ndnptr, lpp); 810 | if (l >= 0) { 811 | if (dstp + 1 >= eob) { 812 | cleanup = true; 813 | break; 814 | } 815 | dst[dstp++] = (l >> 8) | ns_cmprsflgs; 816 | dst[dstp++] = l & 0xff; 817 | return (dstp - dstn); 818 | } 819 | // Not found, save it. 820 | if (lastdnptr != null && cpp < lastdnptr - 1 && 821 | (dstp) < 0x4000 && first) { 822 | dnptrs[cpp++] = dstp; 823 | dnptrs[cpp++] = null; 824 | first = 0; 825 | } 826 | } 827 | // copy label to buffer 828 | if ((n & ns_cmprsflgs) == ns_cmprsflgs) { 829 | // should not happen 830 | cleanup = true; 831 | break; 832 | } 833 | n = labellen(src, srcp); 834 | if (dstp + 1 + n >= eob) { 835 | cleanup = true; 836 | break; 837 | } 838 | src.copy(dst, dstp, srcp, srcp + (n + 1)); 839 | srcp += n + 1; 840 | dstp += n + 1; 841 | 842 | } while (n != 0); 843 | 844 | if (dstp > eob || 845 | // cleanup: 846 | cleanup) { 847 | if (msg != null) { 848 | dnptrs[lpp] = null; 849 | } 850 | errno.set('EMSGSIZE'); 851 | return (-1); 852 | } 853 | return (dstp - dstn); 854 | } 855 | exports.ns_name_pack = ns_name_pack; 856 | 857 | function ns_name_skip (b, ptrptr, eom) { 858 | var cp; 859 | var n; 860 | var l; 861 | 862 | cp = ptrptr.get(); 863 | while (cp < eom && (n = b[cp++]) != 0) { 864 | switch (n & ns_cmprsflgs) { 865 | case 0: // normal case, n == len 866 | cp += n; 867 | continue; 868 | case ns_type_elt: // edns0 extended label 869 | if ((l = labellen(b, cp - 1)) < 0) { 870 | errno.set('EMSGSIZE'); 871 | return (-1); 872 | } 873 | cp += l; 874 | continue; 875 | case ns_cmprsflgs: // indirection 876 | cp++; 877 | break; 878 | default: // illegal type 879 | errno.set('EMSGSIZE'); 880 | return (-1); 881 | } 882 | break; 883 | } 884 | if (cp > eom) { 885 | errno.set('EMSGSIZE'); 886 | return (-1); 887 | } 888 | ptrptr.set(cp); 889 | return (0); 890 | } 891 | exports.ns_name_skip = ns_name_skip; 892 | 893 | function special(ch) { 894 | switch(ch) { 895 | case 0x22: /* '"' */ 896 | case 0x2E: /* '.' */ 897 | case 0x3B: /* ';' */ 898 | case 0x5C: /* '\\' */ 899 | case 0x28: /* '(' */ 900 | case 0x29: /* ')' */ 901 | /* special modifiers in the zone file */ 902 | case 0x40: /* '@' */ 903 | case 0x24: /* '$' */ 904 | return (1); 905 | default: 906 | return (0); 907 | } 908 | } 909 | 910 | function printable (ch) { 911 | return (ch > 0x20 && ch < 0x7F); 912 | } 913 | 914 | function mklower (ch) { 915 | if (ch >= 0x41 && ch <= 0x5A) 916 | return (ch + 0x20); 917 | return (ch); 918 | } 919 | 920 | function dn_find(src, domain, msg, dnptrs, ndnptr, lastdnptr) { 921 | var dn, cp, sp; 922 | var cpp; 923 | var n; 924 | 925 | var next = false; // instead of goto 926 | for (cpp = ndnptr; cpp < lastdnptr; cpp++) { 927 | sp = dnptrs[cpp]; 928 | // 929 | // terminate search on: 930 | // root label 931 | // compression pointer 932 | // unusable offset 933 | // 934 | while (msg[sp] != 0 && (msg[sp] & ns_cmprsflgs) == 0 && 935 | (sp) < 0x4000) { 936 | dn = domain; 937 | cp = sp; 938 | while ((n = msg[cp++]) != 0) { 939 | // 940 | // check for indirection 941 | // 942 | switch (n & ns_cmprsflgs) { 943 | case 0: // normal case, n == len 944 | n = labellen(msg, cp - 1) // XXX 945 | if (n != src[dn++]) { 946 | next = true; 947 | break; 948 | } 949 | for (null; n > 0; n--) { 950 | if (mklower(src[dn++]) != 951 | mklower(msg[cp++])) { 952 | next = true; 953 | break; 954 | } 955 | } 956 | if (next) { 957 | break; 958 | } 959 | // Is next root for both ? 960 | if (src[dn] == 0 && msg[cp] == 0) { 961 | return (sp); 962 | } 963 | if (src[dn]) { 964 | continue; 965 | } 966 | next = true; 967 | break; 968 | case ns_cmprsflgs: // indirection 969 | cp = (((n & 0x3f) * 256) | msg[cp]); 970 | break; 971 | 972 | default: // illegal type 973 | errno.set('EMSGSIZE'); 974 | return (-1); 975 | } 976 | if (next) { 977 | break; 978 | } 979 | } 980 | sp += msg[sp] + 1; 981 | if (next) 982 | next = false; 983 | } 984 | } 985 | errno.set('ENOENT'); 986 | return (-1); 987 | } 988 | exports.dn_find = dn_find; 989 | 990 | function decode_bitstring (b, cpp, d, dn, eom) { 991 | var cp = cpp.get(); 992 | var beg = dn, tc; 993 | var b, blen, plen, i; 994 | 995 | if ((blen = (b[cp] & 0xff)) == 0) 996 | blen = 256; 997 | plen = (blen + 3) / 4; 998 | plen += "\\[x/]".length + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); 999 | if (dn + plen >= eom) 1000 | return (-1); 1001 | 1002 | cp++; 1003 | i = d.write("\\[x", dn); 1004 | if (i != 3) 1005 | return (-1); 1006 | dn += i; 1007 | for (b = blen; b > 7; b -= 8, cp++) { 1008 | if (dn + 2 >= eom) 1009 | return (-1); 1010 | } 1011 | } 1012 | exports.decode_bitstring = decode_bitstring; 1013 | 1014 | function encode_bitstring (src, bp, end, labelp, dst, dstp, eom) { 1015 | var afterslash = 0; 1016 | var cp = bp.get(); 1017 | var tp; 1018 | var c; 1019 | var beg_blen; 1020 | var end_blen = null; 1021 | var value = 0, count = 0, tbcount = 0, blen = 0; 1022 | 1023 | beg_blen = end_blen = null; 1024 | 1025 | // a bitstring must contain at least two bytes 1026 | if (end - cp < 2) 1027 | return errno.EINVAL; 1028 | 1029 | // currently, only hex strings are supported 1030 | if (src[cp++] != 120) // 'x' 1031 | return errno.EINVAL; 1032 | if (!isxdigit((src[cp]) & 0xff)) // reject '\[x/BLEN]' 1033 | return errno.EINVAL; 1034 | 1035 | var done = false; 1036 | for (tp = dstp.get() + 1; cp < end && tp < eom; cp++) { 1037 | switch (c = src[cp++]) { 1038 | case 93: // ']' 1039 | if (afterslash) { 1040 | if (beg_blen == null) 1041 | return errno.EINVAL; 1042 | blen = strtol(src, beg_blen, 10); 1043 | // todo: 1044 | // if ( char after string == ']' ) 1045 | // return errno.EINVAL; 1046 | } 1047 | if (count) 1048 | dst[tp++] = ((value << 4) & 0xff); 1049 | cp++; // skip ']' 1050 | done = true; 1051 | break; 1052 | case 47: // '/' 1053 | afterslash = 1; 1054 | break; 1055 | default: 1056 | if (afterslash) { 1057 | if (!isxdigit(c&0xff)) 1058 | return errno.EINVAL; 1059 | if (beg_blen == null) { 1060 | 1061 | if (c == 48) { // '0' 1062 | // blen never begins with 0 1063 | return errno.EINVAL; 1064 | } 1065 | beg_blen = cp; 1066 | } 1067 | } else { 1068 | if (!isxdigit(c&0xff)) 1069 | return errno.EINVAL; 1070 | value <<= 4; 1071 | value += digitvalue[c]; 1072 | count += 4; 1073 | tbcount += 4; 1074 | if (tbcount > 256) 1075 | return errno.EINVAL; 1076 | if (count == 8) { 1077 | dst[tp++] = value; 1078 | count = 0; 1079 | } 1080 | } 1081 | break; 1082 | } 1083 | if (done) { 1084 | break; 1085 | } 1086 | } 1087 | // done: 1088 | if (cp >= end || tp >= eom) 1089 | return errno.EMSGSIZE; 1090 | // bit length validation: 1091 | // If a is present, the number of digits in the 1092 | // MUST be just sufficient to contain the number of bits specified 1093 | // by the . If there are insufficient bits in a final 1094 | // hexadecimal or octal digit, they MUST be zero. 1095 | // RFC2673, Section 3.2 1096 | if (blen && (blen > 0)) { 1097 | var traillen; 1098 | 1099 | if (((blen + 3) & ~3) != tbcount) 1100 | return errno.EINVAL; 1101 | traillen = tbcount - blen; // between 0 and 3 1102 | if (((value << (8 - traillen)) & 0xFF) != 0) 1103 | return errno.EINVAL; 1104 | } 1105 | else 1106 | blen = tbcount; 1107 | if (blen == 256) 1108 | blen = 0; 1109 | 1110 | // encode the type and the significant bit fields 1111 | src[labelp.get()] = dns_labeltype_bitstring; 1112 | dst[dstp.get()] = blen; 1113 | 1114 | bp.set(cp); 1115 | dstp.set(tp); 1116 | 1117 | return (0); 1118 | } 1119 | exports.encode_bitstring = encode_bitstring; 1120 | 1121 | function isxdigit (ch) { 1122 | return ((ch >= 48 && ch <= 57) 1123 | || (ch >= 97 && ch <= 102) 1124 | || (ch >= 65 && ch <= 70)); 1125 | } 1126 | 1127 | function isspace (ch) { 1128 | return (ch == 32 || ch == 12 || ch == 10 || ch == 13 || ch == 9 || ch == 12); 1129 | } 1130 | 1131 | function strtol (b, off, end, base) { 1132 | // todo: port from C 1133 | return parseInt(b.toString(off, end), base); 1134 | } 1135 | 1136 | function labellen (b, off) { 1137 | var bitlen; 1138 | var l = b[off]; 1139 | 1140 | if((l & ns_cmprsflgs) == ns_cmprsflgs) { 1141 | return (-1); 1142 | } 1143 | if((l & ns_cmprsflgs) == ns_type_elt) { 1144 | if(l == dns_labeltype_bitstring) { 1145 | bitlen = b[off + 1]; 1146 | if(bitlen == 0) { 1147 | bitlen = 256; 1148 | } 1149 | return (1 + (bitlen + 7) / 8); 1150 | } 1151 | } 1152 | return (l); 1153 | } 1154 | 1155 | var errno = { 1156 | val: { 1157 | "ENOENT": 2, 1158 | "EINVAL": 22, 1159 | "EMSGSIZE": 90, 1160 | }, 1161 | errno: null, 1162 | set: function (name) { 1163 | if (typeof name === 'string' && this.val[name]) { 1164 | this.errno = name; 1165 | } 1166 | }, 1167 | get: function () { 1168 | return this.errno; 1169 | }, 1170 | }; 1171 | exports.errno = errno; 1172 | 1173 | function DNSParser(buf, start, end) { 1174 | if (arguments.length < 3) { 1175 | this.initialized = false; 1176 | return; 1177 | } 1178 | 1179 | if (!(buf instanceof Buffer)) { 1180 | throw new Error("Argument should be a buffer"); 1181 | } 1182 | if (start > buf.length) { 1183 | throw new Error("Offset is out of bounds"); 1184 | } 1185 | if (end > buf.length) { 1186 | throw new Error("Length extends beyond buffer"); 1187 | } 1188 | 1189 | this.buf = buf; 1190 | this.bufStart = start; 1191 | this.bufEnd = end; 1192 | 1193 | this.parseStart = 0; 1194 | this.parseEnd = 0; 1195 | 1196 | this.initialized = true; 1197 | 1198 | this.err = false; 1199 | } 1200 | exports.DNSParser = DNSParser; 1201 | 1202 | DNSParser.prototype.reinitialize = function() { 1203 | DNSParser.apply (this, arguments); 1204 | }; 1205 | 1206 | DNSParser.prototype.parseMessage = function () { 1207 | var qdcount, ancount, nscount, arcount, rrcount; 1208 | 1209 | // todo: streaming parser 1210 | if(typeof this.onMessageBegin === 'function') 1211 | this.onMessageBegin (); 1212 | 1213 | try { 1214 | this.skipHeader(this.onHeader); 1215 | } catch (err) { this.err = err; return; } 1216 | 1217 | qdcount = this.buf[this.parseStart-8] * 256 + this.buf[this.parseStart-7]; 1218 | ancount = this.buf[this.parseStart-6] * 256 + this.buf[this.parseStart-5]; 1219 | nscount = this.buf[this.parseStart-4] * 256 + this.buf[this.parseStart-3]; 1220 | arcount = this.buf[this.parseStart-2] * 256 + this.buf[this.parseStart-1]; 1221 | rrcount = ancount + nscount + arcount; 1222 | 1223 | for (var i = 0; i < qdcount; i++) 1224 | try { 1225 | this.skipQuestion(this.onQuestion); 1226 | } catch (err) { this.err = err; return; } 1227 | 1228 | for (var i = 0; i < rrcount; i++) { 1229 | if (i == 0 && typeof this.onAnswerBegin === 'function') 1230 | this.onAnswerBegin(); 1231 | 1232 | else if (i == ancount && typeof this.onAuthorityBegin === 'function') 1233 | this.onAuthorityBegin(); 1234 | 1235 | else if (i == ancount + nscount && typeof this.onAdditionalBegin === 'function') 1236 | this.onAdditionalBegin(); 1237 | 1238 | try { 1239 | this.skipRR(this.onRR); 1240 | } catch (err) { this.err = err; return; } 1241 | } 1242 | 1243 | if(typeof this.onMessageComplete === 'function') 1244 | this.onMessageComplete (); 1245 | } 1246 | 1247 | DNSParser.prototype.skipHeader = function (cb) { 1248 | this.parseEnd = this.parseStart + ns_hfixedsz; 1249 | if (this.parseEnd > this.bufEnd) 1250 | throw new Error(); 1251 | 1252 | if (typeof cb === 'function') 1253 | cb (this.buf, this.parseStart, this.parseEnd); 1254 | 1255 | this.parseStart = this.parseEnd; 1256 | }; 1257 | 1258 | DNSParser.prototype.skipQuestion = function (cb) { 1259 | var ptr = new Ptr(this.parseStart); 1260 | if (ns_name_skip(this.buf, ptr, this.bufEnd) != 0) 1261 | throw new Error(); 1262 | 1263 | this.parseEnd = ptr.get() + ns_qfixedsz; 1264 | if (this.parseEnd > this.bufEnd) 1265 | throw new Error(); 1266 | 1267 | if (typeof cb === 'function') 1268 | cb (this.buf, this.parseStart, this.parseEnd); 1269 | 1270 | this.parseStart = this.parseEnd; 1271 | }; 1272 | 1273 | DNSParser.prototype.skipRR = function (cb) { 1274 | var rrcount; 1275 | var ptr = new Ptr(this.parseStart); 1276 | 1277 | if (ns_name_skip(this.buf, ptr, this.bufEnd) != 0) 1278 | throw new Error(); 1279 | 1280 | this.parseEnd = ptr.get() + ns_rrfixedsz; 1281 | if (this.parseEnd > this.bufEnd) 1282 | throw new Error(); 1283 | 1284 | this.parseEnd += this.buf[this.parseEnd - 2] * 256 + this.buf[this.parseEnd - 1]; 1285 | if (this.parseEnd > this.bufEnd) 1286 | throw new Error(); 1287 | 1288 | if (typeof cb === 'function') 1289 | cb (this.buf, this.parseStart, this.parseEnd); 1290 | 1291 | this.parseStart = this.parseEnd; 1292 | }; 1293 | 1294 | DNSParser.prototype._cdname = new Buffer(ns_maxcdname); 1295 | 1296 | DNSParser.prototype._dname = new Buffer(ns_maxdname); 1297 | 1298 | DNSParser.prototype._string = new Buffer(ns_maxdname); 1299 | 1300 | DNSParser.prototype.parseName = function () { 1301 | var n, len; 1302 | 1303 | if ((n = ns_name_unpack(this.buf, this.parseStart, this.parseEnd - this.parseStart, this._dname, this._dname.length)) == -1) 1304 | throw new Error(); 1305 | if ((len = ns_name_ntop(this._dname, this._string, this._string.length)) == -1) 1306 | throw new Error(); 1307 | 1308 | this.parseStart += n; 1309 | return this._string.toString('ascii', 0, len); 1310 | }; 1311 | 1312 | DNSParser.prototype.parseUInt8 = function () { 1313 | if (this.parseStart + 1 > this.parseEnd) 1314 | throw new Error(); 1315 | this.parseStart++; 1316 | return this.buf[this.parseStart-1]; 1317 | }; 1318 | 1319 | DNSParser.prototype.parseUInt16 = function () { 1320 | if (this.parseStart + 2 > this.parseEnd) 1321 | throw new Error(); 1322 | this.parseStart += 2; 1323 | return this.buf[this.parseStart-2] * 256 + this.buf[this.parseStart-1]; 1324 | }; 1325 | 1326 | DNSParser.prototype.parseUInt32 = function () { 1327 | if (this.parseStart + 4 > this.parseEnd) 1328 | throw new Error(); 1329 | this.parseStart += 4; 1330 | return (this.buf[this.parseStart-4] * 16777216 + 1331 | this.buf[this.parseStart-3] * 65536 + 1332 | this.buf[this.parseStart-2] * 256 + 1333 | this.buf[this.parseStart-1] ); 1334 | }; 1335 | 1336 | DNSParser.prototype.parseHeader = function (header) { 1337 | var tmp; 1338 | header.id = this.parseUInt16(); 1339 | tmp = this.parseUInt16(); 1340 | header.qr = (tmp & 0x8000) >> 15; 1341 | header.opcode = (tmp & 0x7800) >> 11; 1342 | header.aa = (tmp & 0x0400) >> 10; 1343 | header.tc = (tmp & 0x0200) >> 9; 1344 | header.rd = (tmp & 0x0100) >> 8; 1345 | header.ra = (tmp & 0x0080) >> 7; 1346 | header.z = (tmp & 0x0040) >> 6; 1347 | header.ad = (tmp & 0x0020) >> 5; 1348 | header.cd = (tmp & 0x0010) >> 4; 1349 | header.rcode = (tmp & 0x000f) >> 0; 1350 | 1351 | header.qdcount = this.parseUInt16(); 1352 | header.ancount = this.parseUInt16(); 1353 | header.nscount = this.parseUInt16(); 1354 | header.arcount = this.parseUInt16(); 1355 | }; 1356 | 1357 | DNSParser.prototype.parseQuestion = function (question) { 1358 | question.name = this.parseName(); 1359 | question.type = this.parseUInt16(); 1360 | question.class = this.parseUInt16(); 1361 | question.typeName = p_type_syms[question.type]; 1362 | question.className = p_class_syms[question.class]; 1363 | }; 1364 | 1365 | DNSParser.prototype.parseA = function () { 1366 | if (this.parseStart + 4 > this.parseEnd) 1367 | throw new Error(); 1368 | this.parseStart += 4; 1369 | return [this.buf[this.parseStart-4], 1370 | this.buf[this.parseStart-2], 1371 | this.buf[this.parseStart-1], 1372 | this.buf[this.parseStart-1]].join('.'); 1373 | }; 1374 | 1375 | function BufferReference (buf, start, end) { 1376 | if (!(buf instanceof Buffer)) { 1377 | throw new Error("Argument should be a buffer"); 1378 | } 1379 | if (start > end) { 1380 | throw new Error("Start extends beyond end"); 1381 | } 1382 | if (start > buf.length) { 1383 | throw new Error("Offset is out of bounds"); 1384 | } 1385 | if (end > buf.length) { 1386 | throw new Error("Length extends beyond buffer"); 1387 | } 1388 | this.buf = buf; 1389 | this.start = start; 1390 | this.end = end; 1391 | }; 1392 | exports.BufferReference = BufferReference; 1393 | 1394 | BufferReference.prototype.toString = function () { 1395 | return this.buf.toString('ascii', this.start, this.end); 1396 | }; 1397 | 1398 | BufferReference.prototype.toBuffer = function () { 1399 | return this.buf.slice(this.start, this.end); 1400 | }; 1401 | 1402 | DNSParser.prototype.parseSOA = function (soa) { 1403 | soa.mname = this.parseName(); 1404 | soa.rname = this.parseName(); 1405 | soa.serial = this.parseUInt32(); 1406 | soa.refresh = this.parseUInt32(); 1407 | soa.retry = this.parseUInt32(); 1408 | soa.expire = this.parseUInt32(); 1409 | soa.minimum = this.parseUInt32(); 1410 | 1411 | soa[0] = soa.mname; 1412 | soa[1] = soa.rname; 1413 | soa[2] = soa.serial; 1414 | soa[3] = soa.refresh; 1415 | soa[4] = soa.retry; 1416 | soa[5] = soa.expire; 1417 | soa[6] = soa.minimum; 1418 | soa.length = 7; 1419 | 1420 | return soa; 1421 | }; 1422 | 1423 | DNSParser.prototype.parseMX = function (mx) { 1424 | mx.preference = this.parseUInt16(); 1425 | mx.exchange = this.parseName(); 1426 | 1427 | mx[0] = mx.preference; 1428 | mx[1] = mx.exchange; 1429 | mx.length = 2; 1430 | 1431 | return mx; 1432 | }; 1433 | 1434 | DNSParser.prototype.parseAAAA = function () { 1435 | if (this.parseStart + 16 > this.parseEnd) 1436 | throw new Error(); 1437 | this.parseStart += 16; 1438 | return [(hexvalue[this.buf[this.parseStart-16]]+ 1439 | hexvalue[this.buf[this.parseStart-15]]), 1440 | (hexvalue[this.buf[this.parseStart-14]]+ 1441 | hexvalue[this.buf[this.parseStart-13]]), 1442 | (hexvalue[this.buf[this.parseStart-12]]+ 1443 | hexvalue[this.buf[this.parseStart-11]]), 1444 | (hexvalue[this.buf[this.parseStart-10]]+ 1445 | hexvalue[this.buf[this.parseStart-9]]), 1446 | (hexvalue[this.buf[this.parseStart-8]]+ 1447 | hexvalue[this.buf[this.parseStart-7]]), 1448 | (hexvalue[this.buf[this.parseStart-6]]+ 1449 | hexvalue[this.buf[this.parseStart-5]]), 1450 | (hexvalue[this.buf[this.parseStart-4]]+ 1451 | hexvalue[this.buf[this.parseStart-3]]), 1452 | (hexvalue[this.buf[this.parseStart-2]]+ 1453 | hexvalue[this.buf[this.parseStart-1]])].join(":"); 1454 | } 1455 | 1456 | DNSParser.prototype.parseNSEC = function (nsec) { 1457 | nsec.next_domain_name = this.parseName(); 1458 | nsec.type_bit_maps = new BufferReference (this.buf, this.parseStart, this.parseEnd); 1459 | 1460 | nsec[0] = nsec.next_domain_name; 1461 | nsec[1] = nsec.type_bit_maps; 1462 | nsec.length = 2; 1463 | 1464 | this.parseStart = this.parseEnd; 1465 | }; 1466 | 1467 | function Rdata () { 1468 | } 1469 | 1470 | Rdata.prototype.length = 0; 1471 | 1472 | DNSParser.prototype.parseRR = function (rr) { 1473 | var parseEnd; 1474 | rr.name = this.parseName(); 1475 | rr.type = this.parseUInt16(); 1476 | rr.class = this.parseUInt16(); 1477 | rr.ttl = this.parseUInt32(); 1478 | rr.rdlength = this.parseUInt16(); 1479 | 1480 | rr.typeName = p_type_syms[rr.type]; 1481 | rr.className = p_class_syms[rr.class]; 1482 | 1483 | if (this.parseStart + rr.rdlength != this.parseEnd) 1484 | throw new Error(); 1485 | 1486 | rr.rdata = new Rdata(); 1487 | rr.rdata.length = 1; 1488 | 1489 | switch (rr.type) { 1490 | case 1: // a 1491 | rr.rdata.a = this.parseA(); 1492 | rr.rdata[0] = rr.rdata.a; 1493 | break; 1494 | case 2: // ns 1495 | rr.rdata.ns = this.parseName(); 1496 | rr.rdata[0] = rr.rdata.ns; 1497 | break; 1498 | case 5: // cname 1499 | rr.rdata.cname = this.parseName(); 1500 | rr.rdata[0] = rr.rdata.cname; 1501 | break; 1502 | case 6: // soa 1503 | this.parseSOA(rr.rdata); 1504 | break; 1505 | case 12: // ptr 1506 | rr.rdata.ptrdname = this.parseName(); 1507 | rr.rdata[0] = rr.rdata.ptrdname; 1508 | break; 1509 | case 15: // mx 1510 | this.parseMX(rr.rdata); 1511 | break; 1512 | case 16: // txt 1513 | rr.rdata.txt = new BufferReference (this.buf, this.parseStart, this.parseEnd); 1514 | //rr.rdata.txt = this.buf.slice(this.parseStart, this.parseEnd); 1515 | rr.rdata[0] = rr.rdata.txt; 1516 | this.parseStart += rr.rdlength; 1517 | break; 1518 | case 28: // aaaa 1519 | rr.rdata.aaaa = this.parseAAAA(); 1520 | rr.rdata[0] = rr.rdata.aaaa; 1521 | break; 1522 | case 47: // nsec 1523 | this.parseNSEC(rr.rdata); 1524 | break; 1525 | default: 1526 | rr.rdata = new BufferReference(this.parseStart, this.parseEnd); 1527 | break; 1528 | } 1529 | 1530 | if (this.parseStart != this.parseEnd) 1531 | throw new Error(); 1532 | }; 1533 | 1534 | DNSParser.prototype.finish = function () { 1535 | if (arguments.length == 3 && (arguments[0] instanceof Buffer)){ 1536 | this.parseOnce.apply(this, arguments); 1537 | } 1538 | }; 1539 | 1540 | function DNSWriter (buf, start, end) { 1541 | if (arguments.length < 3) { 1542 | this.initialized = false; 1543 | return; 1544 | } 1545 | 1546 | if (!(buf instanceof Buffer)) { 1547 | throw new Error("Argument should be a buffer"); 1548 | } 1549 | if (start > end) { 1550 | throw new Error("Start extends beyond end"); 1551 | } 1552 | if (start > buf.length) { 1553 | throw new Error("Offset is out of bounds"); 1554 | } 1555 | if (end > buf.length) { 1556 | throw new Error("Length extends beyond buffer"); 1557 | } 1558 | 1559 | this.dnptrs = new Array(20); 1560 | this.dnptrs[0] = null; 1561 | this.lastdnptr = this.dnptrs.length; 1562 | 1563 | this.rdstart = 0; 1564 | this.trstart = 0; 1565 | 1566 | this.buf = buf; 1567 | this.bufStart = start; 1568 | this.bufEnd = end; 1569 | 1570 | this.writeStart = 0; 1571 | this.writeEnd = this.bufEnd; 1572 | 1573 | this.initialized = true; 1574 | 1575 | this.truncated = false; 1576 | } 1577 | exports.DNSWriter = DNSWriter; 1578 | 1579 | DNSWriter.prototype.reinitialize = function() { 1580 | DNSWriter.apply (this, arguments); 1581 | }; 1582 | 1583 | DNSWriter.prototype.startRdata = function () { 1584 | if (this.truncated) 1585 | return; 1586 | 1587 | this.writeUInt16(0); 1588 | this.rdstart = this.writeStart; 1589 | }; 1590 | 1591 | DNSWriter.prototype.endRdata = function () { 1592 | if (this.truncated) 1593 | return; 1594 | 1595 | var rdlength = this.writeStart - this.rdstart; 1596 | this.buf[this.rdstart-2] = (rdlength >> 8) & 0xff; 1597 | this.buf[this.rdstart-1] = (rdlength) & 0xff; 1598 | }; 1599 | 1600 | DNSWriter.prototype.startTruncate = function () { 1601 | if (this.truncated) 1602 | return; 1603 | 1604 | this.trstart = this.writeStart; 1605 | }; 1606 | 1607 | DNSWriter.prototype.endTruncate = function () { 1608 | debug('DNSWriter.prototype.endTruncate'); 1609 | // todo: figure out truncate 1610 | this.writeStart = this.trstart; 1611 | }; 1612 | 1613 | DNSWriter.prototype._cdname = new Buffer(ns_maxcdname); 1614 | 1615 | DNSWriter.prototype._dname = new Buffer(ns_maxdname); 1616 | 1617 | DNSWriter.prototype.writeNameBuffer = function (name) { 1618 | if (this.truncated) 1619 | return; 1620 | 1621 | var n, len; 1622 | 1623 | if ((len = ns_name_pton(name, this._dname, this._dname.length)) == -1) { 1624 | if (errno.get() == 'EMSGSIZE') { 1625 | this.truncated = true; 1626 | return; 1627 | } 1628 | throw new Error("ns_name_pton"); 1629 | } 1630 | if ((n = ns_name_pack(this._dname, this.buf, this.writeStart, this.writeEnd - this.writeStart, this.dnptrs, this.lastdnptr)) == -1) { 1631 | if (errno.get() == 'EMSGSIZE') { 1632 | this.truncated = true; 1633 | return; 1634 | } 1635 | throw new Error("ns_name_pack"); 1636 | } 1637 | this.writeStart += n; 1638 | }; 1639 | 1640 | DNSWriter.prototype._string = new Buffer(ns_maxdname); 1641 | 1642 | DNSWriter.prototype.writeNameString = function (name) { 1643 | if (this.truncated) 1644 | return; 1645 | 1646 | var len; 1647 | // copy string to buffer 1648 | len = this._string.write(name); 1649 | if (len == this._string.length) 1650 | throw new Error("Name string is too long"); 1651 | 1652 | this._string[len] = 0; // terminate string 1653 | 1654 | this.writeNameBuffer(this._string); 1655 | }; 1656 | 1657 | DNSWriter.prototype.writeName = function (name) { 1658 | if (typeof name === 'string') 1659 | this.writeNameString(name); 1660 | else if (name instanceof Buffer) { 1661 | this.writeNameBuffer(name); 1662 | } 1663 | }; 1664 | 1665 | DNSWriter.prototype.writeUInt8 = function (uint) { 1666 | if (this.truncated) 1667 | return; 1668 | 1669 | if (this.writeStart + 1 > this.writeEnd) 1670 | this.truncated = true; 1671 | else { 1672 | this.buf[this.writeStart++] = (uint) & 0xff; 1673 | } 1674 | }; 1675 | 1676 | DNSWriter.prototype.writeUInt16 = function (uint) { 1677 | if (this.truncated) 1678 | return; 1679 | 1680 | if (this.writeStart + 2 > this.writeEnd) 1681 | this.truncated = true; 1682 | else { 1683 | this.buf[this.writeStart++] = (uint >> 8) & 0xff; 1684 | this.buf[this.writeStart++] = (uint >> 0) & 0xff; 1685 | } 1686 | }; 1687 | 1688 | DNSWriter.prototype.writeUInt32 = function (uint) { 1689 | if (this.truncated) 1690 | return; 1691 | 1692 | if (this.writeStart + 4 > this.writeEnd) 1693 | this.truncated = true; 1694 | else { 1695 | this.buf[this.writeStart++] = (uint >> 24) & 0xff; 1696 | this.buf[this.writeStart++] = (uint >> 16) & 0xff; 1697 | this.buf[this.writeStart++] = (uint >> 8) & 0xff; 1698 | this.buf[this.writeStart++] = (uint >> 0) & 0xff; 1699 | } 1700 | }; 1701 | 1702 | DNSWriter.prototype.writeHeader = function (header) { 1703 | var tmp = 0; 1704 | tmp = 0; 1705 | tmp |= (header.qr << 15) & 0x8000; 1706 | tmp |= (header.opcode << 11) & 0x7800; 1707 | tmp |= (header.aa << 10) & 0x0400; 1708 | tmp |= (header.tc << 9) & 0x0200; 1709 | tmp |= (header.rd << 8) & 0x0100; 1710 | tmp |= (header.ra << 7) & 0x0080; 1711 | tmp |= (header.z << 6) & 0x0040; 1712 | tmp |= (header.ad << 5) & 0x0020; 1713 | tmp |= (header.cd << 4) & 0x0010; 1714 | tmp |= (header.rcode << 0) & 0x000f; 1715 | 1716 | this.writeUInt16(header.id); 1717 | this.writeUInt16(tmp); 1718 | this.writeUInt16(header.qdcount); 1719 | this.writeUInt16(header.ancount); 1720 | this.writeUInt16(header.nscount); 1721 | this.writeUInt16(header.arcount); 1722 | }; 1723 | 1724 | DNSWriter.prototype.writeQuestion = function (question) { 1725 | debug('DNSWriter.prototype.writeQuestion'); 1726 | this.writeName(question.name); 1727 | this.writeUInt16(question.type); 1728 | this.writeUInt16(question.class); 1729 | }; 1730 | 1731 | DNSWriter.prototype.writeBuffer = function (buf) { 1732 | if (this.truncated) 1733 | return; 1734 | 1735 | if (this.writeStart + buf.length > this.writeEnd) 1736 | this.truncated = true; 1737 | else { 1738 | buf.copy(this.buf, this.writeStart, 0, buf.length); 1739 | this.writeStart += buf.length; 1740 | } 1741 | }; 1742 | 1743 | DNSWriter.prototype.writeString = function (str) { 1744 | if (this.truncated) 1745 | return; 1746 | 1747 | if (this.writeString + Buffer.byteLength(str, 'ascii') > this.writeEnd) 1748 | this.truncated = true; 1749 | else { 1750 | this.writeStart += this.buf.write(str, this.writeStart); 1751 | } 1752 | }; 1753 | 1754 | DNSWriter.prototype.writeA = function (a) { 1755 | var tmp; 1756 | 1757 | if (this.truncated) 1758 | return; 1759 | 1760 | if (this.writeStart + 4 > this.writeEnd) 1761 | this.truncated = true; 1762 | else { 1763 | tmp = a.split('.'); 1764 | this.buf[this.writeStart++] = tmp[0]; 1765 | this.buf[this.writeStart++] = tmp[1]; 1766 | this.buf[this.writeStart++] = tmp[2]; 1767 | this.buf[this.writeStart++] = tmp[3]; 1768 | } 1769 | }; 1770 | 1771 | DNSWriter.prototype.writeSOA = function (soa) { 1772 | debug('DNSWriter.prototype.writeSOA'); 1773 | this.writeName(soa[0]); // mname 1774 | this.writeName(soa[1]); // rname 1775 | this.writeUInt32(soa[2]); // serial 1776 | this.writeUInt32(soa[3]); // refresh 1777 | this.writeUInt32(soa[4]); // retry 1778 | this.writeUInt32(soa[5]); // expire 1779 | this.writeUInt32(soa[6]); // minumum 1780 | }; 1781 | 1782 | DNSWriter.prototype.writeMX = function (mx) { 1783 | this.writeUInt16(mx[0]); // preference 1784 | this.writeName(mx[1]); // exchange 1785 | }; 1786 | 1787 | DNSWriter.prototype.writeAAAA = function (aaaa) { 1788 | if (this.truncated) 1789 | return; 1790 | 1791 | var n, tmp; 1792 | 1793 | if (this.writeStart + 16 > this.writeEnd) { 1794 | this.truncated = true; 1795 | return; 1796 | } 1797 | tmp = aaaa.split(":"); 1798 | if (tmp.length != 8) 1799 | throw new Error("IPV6 String must have exactly 7 colons"); 1800 | for (var i = 0; i < 8; i++) 1801 | this.writeUInt16(parseInt(tmp[i], 16)); 1802 | }; 1803 | 1804 | DNSWriter.prototype.writeRR = function (rr) { 1805 | debug('DNSWriter.prototype.writeRR'); 1806 | 1807 | this.writeName(rr.name); 1808 | this.writeUInt16(rr.type); 1809 | this.writeUInt16(rr.class); 1810 | this.writeUInt32(rr.ttl); 1811 | 1812 | this.startRdata(); 1813 | 1814 | if (rr.type == 1) { // a 1815 | this.writeA(rr.rdata[0]); 1816 | } 1817 | else if (rr.type == 2) { // ns 1818 | this.writeName(rr.rdata[0]); 1819 | } 1820 | else if (rr.type == 5) { // cname 1821 | this.writeName(rr.rdata[0]); 1822 | } 1823 | else if (rr.type == 6) { // soa 1824 | this.writeSOA(rr.rdata); 1825 | } 1826 | else if (rr.type == 12) { // ptr 1827 | this.writeName(rr.rdata[0]); 1828 | } 1829 | else if (rr.type == 15) { // mx 1830 | this.writeMX(rr.rdata); 1831 | } 1832 | else if (rr.type == 16) { // txt 1833 | this.writeUInt8(rr.rdata[0].length); 1834 | if (typeof rr.rdata[0] === 'string') 1835 | this.writeString(rr.rdata[0]); 1836 | else if (rr.rdata[0] instanceof Buffer) 1837 | this.writeBuffer(rr.rdata[0]); 1838 | } 1839 | else if (rr.type == 28) { // aaaa 1840 | this.writeAAAA(rr.rdata[0]); 1841 | } 1842 | else { 1843 | if (typeof rr.rdata[0] === 'string') 1844 | this.writeString(rr.rdata[0]); 1845 | else if (rr.rdata[0] instanceof Buffer) 1846 | this.writeBuffer(rr.rdata[0]); 1847 | } 1848 | 1849 | this.endRdata(); 1850 | }; 1851 | 1852 | DNSWriter.prototype.writeMessage = function (message) { 1853 | this.writeHeader(message.header); 1854 | 1855 | for (var i = 0; i < message.q.length; i++) 1856 | this.writeQuestion(message.q[i]); 1857 | 1858 | this.startTruncate(); 1859 | 1860 | for (var i = 0; i < message.rr.length; i++) { 1861 | this.writeRR(message.rr[i]); 1862 | } 1863 | 1864 | if (this.truncated) 1865 | this.endTruncate(); 1866 | }; 1867 | 1868 | var parsers = new FreeList('parsers', 1000, function() { 1869 | var parser = new DNSParser(); 1870 | 1871 | parser.onMessageBegin = function () { 1872 | debug('parser.onMessageBegin'); 1873 | 1874 | parser.incoming = new IncomingMessage(parser.socket, parser.rinfo); 1875 | } 1876 | parser.onHeader = function () { 1877 | debug('parser.onHeader'); 1878 | 1879 | try { 1880 | parser.parseHeader(parser.incoming.header); 1881 | } catch (err) { parser.onError (err); } 1882 | }; 1883 | parser.onQuestion = function () { 1884 | debug('parser.onQuestion'); 1885 | 1886 | try { 1887 | parser.parseQuestion(parser.incoming.q.add()); 1888 | } catch (err) { parser.onError (err); } 1889 | }; 1890 | parser.onAnswerBegin = function () { 1891 | debug('parser.onAnswerBegin'); 1892 | }; 1893 | parser.onAuthorityBegin = function () { 1894 | debug('parser.onAuthorityBegin'); 1895 | }; 1896 | parser.onAdditionalBegin = function () { 1897 | debug('parser.onAdditionalBegin'); 1898 | }; 1899 | parser.onRR = function () { 1900 | debug('parser.onRR'); 1901 | 1902 | try { 1903 | parser.parseRR(parser.incoming.rr.add()); 1904 | } catch (err) { parser.onError (err); } 1905 | }; 1906 | parser.onMessageComplete = function () { 1907 | debug('parser.onMessageComplete'); 1908 | 1909 | parser.onIncoming(parser.incoming); 1910 | }; 1911 | 1912 | return parser; 1913 | }); 1914 | 1915 | function MessageHeader () { 1916 | } 1917 | 1918 | MessageHeader.prototype.id = 0; 1919 | MessageHeader.prototype.qr = 0; 1920 | MessageHeader.prototype.opcode = 0; 1921 | MessageHeader.prototype.aa = 0; 1922 | MessageHeader.prototype.tc = 0; 1923 | MessageHeader.prototype.rd = 0; 1924 | MessageHeader.prototype.ra = 0; 1925 | MessageHeader.prototype.a = 0; 1926 | MessageHeader.prototype.ad = 0; 1927 | MessageHeader.prototype.cd = 0; 1928 | MessageHeader.prototype.rcode = 0; 1929 | 1930 | MessageHeader.prototype.set = function (obj) { 1931 | for (var k in obj) 1932 | this[k] = obj[k]; 1933 | }; 1934 | 1935 | function MessageRecord () { 1936 | } 1937 | 1938 | MessageRecord.prototype.set = MessageHeader.prototype.set; 1939 | 1940 | function MessageObject () { 1941 | this.length = 0; 1942 | } 1943 | 1944 | MessageObject.prototype.add = function () { 1945 | var obj = this[this.length++] = new MessageRecord(); 1946 | if (arguments.length > 0) 1947 | obj.set(arguments[0]); 1948 | return obj; 1949 | }; 1950 | 1951 | function Message (socket, rinfo) { 1952 | events.EventEmitter.call(this); 1953 | 1954 | this.socket = socket; 1955 | this.rinfo = rinfo; 1956 | 1957 | this.length = 0; 1958 | this.header = new MessageHeader(); 1959 | this.q = new MessageObject(); 1960 | this.rr = new MessageObject(); 1961 | } 1962 | sys.inherits(Message, events.EventEmitter); 1963 | exports.Message = Message; 1964 | 1965 | Message.prototype.addRR = function (name, ttl, className, typeName) { 1966 | if (arguments.length >= 4) { 1967 | if (n_type_syms.hasOwnProperty(typeName.toUpperCase()) && 1968 | n_class_syms.hasOwnProperty(className.toUpperCase())) { 1969 | var rr = this.rr.add(); 1970 | rr.name = name; 1971 | rr.ttl = ttl 1972 | rr.type = n_type_syms[typeName.toUpperCase()]; 1973 | rr.class = n_class_syms[className.toUpperCase()]; 1974 | rr.rdata = Array.prototype.slice.call(arguments, 4); 1975 | return rr; 1976 | } 1977 | } 1978 | }; 1979 | 1980 | Message.prototype.setHeader = function (obj) { 1981 | for (k in obj) 1982 | this.header[k] = obj[k]; 1983 | }; 1984 | 1985 | Message.prototype.addQuestion = function (name, typeName, className) { 1986 | var question; 1987 | if (arguments.length == 1 && typeof arguments[0] === 'object') { 1988 | this.q.add(arguments[0]); 1989 | } 1990 | else { 1991 | if (typeof name !== 'string' && 1992 | !(name instanceof Buffer)) { 1993 | throw new Error ("Name argument should be string or buffer") 1994 | } 1995 | if (n_type_syms.hasOwnProperty(typeName.toUpperCase()) && 1996 | n_class_syms.hasOwnProperty(className.toUpperCase())) { 1997 | question = this.q.add(); 1998 | question.name = name; 1999 | question.type = n_type_syms[typeName.toUpperCase()]; 2000 | question.class = n_class_syms[className.toUpperCase()]; 2001 | } 2002 | } 2003 | }; 2004 | 2005 | function IncomingMessage (socket, rinfo) { 2006 | Message.call(this, socket, rinfo); 2007 | }; 2008 | sys.inherits(IncomingMessage, Message); 2009 | exports.IncomingMessage = IncomingMessage; 2010 | 2011 | function OutgoingMessage (socket, rinfo) { 2012 | Message.call(this, socket, rinfo); 2013 | this.maxSend = 512; 2014 | } 2015 | sys.inherits(OutgoingMessage, Message); 2016 | exports.OutgoingMessage = OutgoingMessage; 2017 | 2018 | OutgoingMessage.prototype._Buffer = new Buffer(ns_maxmsg); 2019 | 2020 | OutgoingMessage.prototype._Writer = new DNSWriter(); 2021 | 2022 | OutgoingMessage.prototype.setMaxSend = function (n) { 2023 | if (n > ns_maxmsg) 2024 | throw new Error ("Size must be < 65535"); 2025 | 2026 | this.maxSend = n; 2027 | }; 2028 | 2029 | OutgoingMessage.prototype.send = function (message) { 2030 | debug('ServerResponse.prototype.send'); 2031 | 2032 | if (arguments.length == 0) 2033 | message = this; 2034 | 2035 | this._Writer.reinitialize (this._Buffer, 0, Math.min(this.maxSend, this._Buffer.length)); 2036 | 2037 | this._Writer.writeMessage(message); 2038 | 2039 | 2040 | this.socket.send(this._Buffer, 0, this._Writer.writeStart, this.rinfo.port, this.rinfo.address, function (err, bytesSent) { 2041 | debug (err || 'bytesSent: ' + bytesSent); 2042 | }); 2043 | }; 2044 | 2045 | function ServerResponse (req) { 2046 | OutgoingMessage.call(this, req.socket, req.rinfo); 2047 | } 2048 | sys.inherits(ServerResponse, OutgoingMessage); 2049 | exports.ServerResponse = ServerResponse; 2050 | 2051 | function ClientRequest(client, socket, port, host) { 2052 | OutgoingMessage.call(this, socket, { port: port, address: host }); 2053 | 2054 | this.client = client; 2055 | this.socket = socket; 2056 | 2057 | this.port = port; 2058 | this.host = host; 2059 | } 2060 | sys.inherits(ClientRequest, OutgoingMessage); 2061 | exports.ClientRequest = ClientRequest; 2062 | 2063 | ClientRequest.prototype.send = function (message) { 2064 | debug('ClientRequest.prototype.send'); 2065 | 2066 | if (arguments.length == 0) 2067 | message = this; 2068 | 2069 | this.client.pending.push({ 2070 | time: new Date(), 2071 | request: this, 2072 | id: message.header.id, 2073 | rinfo: this.rinfo 2074 | }); 2075 | 2076 | this._Writer.reinitialize (this._Buffer, 0, Math.min(this._Buffer.length, this.maxSend)); 2077 | 2078 | this._Writer.writeMessage(message); 2079 | 2080 | this.socket.send(this._Buffer, 0, this._Writer.writeStart, this.rinfo.port, this.rinfo.address, function (err, bytesSent) { 2081 | debug (err || 'bytesSent: ' + bytesSent); 2082 | }); 2083 | }; 2084 | 2085 | function Server(type, requestListener) { 2086 | dgram.Socket.call(this, type); 2087 | 2088 | if(requestListener) { 2089 | this.on("request", requestListener); 2090 | } 2091 | 2092 | this.on("message", messageListener); 2093 | }; 2094 | sys.inherits(Server, dgram.Socket); 2095 | exports.Server = Server; 2096 | 2097 | Server.prototype._Parser = parsers.alloc(); 2098 | 2099 | exports.createServer = function() { 2100 | var type = 'udp4'; 2101 | var requestListener = null; 2102 | if(typeof arguments[0] === 'string') { 2103 | type = arguments[0]; 2104 | if(typeof arguments[1] === 'function') { 2105 | requestListener = arguments[1]; 2106 | } 2107 | } 2108 | else if(typeof arguments[0] === 'function') { 2109 | requestListener = arguments[0]; 2110 | } 2111 | return new Server(type, requestListener); 2112 | }; 2113 | 2114 | function messageListener(msg, rinfo) { 2115 | var self = this; 2116 | 2117 | debug("new message"); 2118 | 2119 | this._Parser.reinitialize(msg, 0, msg.length); 2120 | this._Parser.socket = this; 2121 | this._Parser.rinfo = rinfo; 2122 | 2123 | this._Parser.onIncoming = function (req) { 2124 | var res = new ServerResponse(req); 2125 | self.emit('request', req, res); 2126 | }; 2127 | this._Parser.onError = debug; 2128 | 2129 | this._Parser.parseMessage(); 2130 | } 2131 | 2132 | function Client(type, responseListener) { 2133 | dgram.Socket.call(this, type); 2134 | 2135 | this.pending = []; 2136 | 2137 | if (responseListener) { 2138 | this.on("response", responseListener); 2139 | } 2140 | 2141 | this.on("message", clientMessageListener); 2142 | this.bind(); 2143 | } 2144 | sys.inherits(Client, dgram.Socket); 2145 | exports.Client = Client; 2146 | 2147 | Client.prototype.request = function (port, host) { 2148 | var req = new ClientRequest(this, this, port, host); 2149 | return req; 2150 | }; 2151 | 2152 | Client.prototype.defaultType = 'udp4'; 2153 | 2154 | Client.prototype.parser = parsers.alloc(); 2155 | 2156 | exports.createClient = function() { 2157 | var type = this.defaultType; 2158 | var responseListener = null; 2159 | if(typeof arguments[0] === 'string') { 2160 | type = arguments[0]; 2161 | if(typeof arguments[1] === 'function') { 2162 | responseListener = arguments[1]; 2163 | } 2164 | } 2165 | else if(typeof arguments[0] === 'function') { 2166 | requestListener = arguments[0]; 2167 | } 2168 | return new Client(type, responseListener); 2169 | }; 2170 | 2171 | function clientMessageListener(msg, rinfo) { 2172 | var self = this; 2173 | 2174 | debug("new message"); 2175 | 2176 | this.parser.reinitialize(msg, 0, msg.length); 2177 | this.parser.socket = this; 2178 | this.parser.rinfo = rinfo; 2179 | 2180 | this.parser.onIncoming = function (res) { 2181 | var i, item; 2182 | self.emit("response", res); 2183 | for (i = 0; i < self.pending.length; i++) { 2184 | item = self.pending[i]; 2185 | if (item.id == res.header.id && 2186 | item.rinfo.address == rinfo.address && 2187 | item.rinfo.port == rinfo.port) { 2188 | 2189 | item.request.emit("response", res); 2190 | self.pending.splice(i, 1); 2191 | } 2192 | } 2193 | }; 2194 | this.parser.onError = debug; 2195 | 2196 | this.parser.parseMessage(); 2197 | } 2198 | --------------------------------------------------------------------------------