├── nodejs ├── DNS │ ├── IP │ │ ├── README.md │ │ ├── test.js │ │ ├── import.js │ │ └── ip2country.js │ ├── ndns-eec0a40 │ │ ├── README │ │ ├── examples │ │ │ ├── proxy.js │ │ │ ├── authorative.js │ │ │ ├── zonewalk.js │ │ │ └── redis-client.js │ │ ├── server.js │ │ ├── README.md │ │ └── lib │ │ │ ├── redis.js │ │ │ └── ndns.js │ ├── forwarder.js │ ├── test.js │ ├── whois-server.js │ ├── dns-server.js │ ├── punycode.js │ └── dns-api.js ├── dnshandler.js └── server.js └── drupal7-module ├── dns.info └── dns.module /nodejs/DNS/IP/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/README: -------------------------------------------------------------------------------- 1 | see examples/ -------------------------------------------------------------------------------- /drupal7-module/dns.info: -------------------------------------------------------------------------------- 1 | name = DNS Manager 2 | description = DNS Managemenet module 3 | core = 7.x 4 | package = "DNS" 5 | version = 0.1 6 | configure = admin/config/content/dns -------------------------------------------------------------------------------- /nodejs/DNS/IP/test.js: -------------------------------------------------------------------------------- 1 | var ip2country = require("./ip2country"); 2 | 3 | /* resolve and ouput country name for a test IP */ 4 | 5 | var ip = "195.50.209.246"; 6 | 7 | ip2country.resolve(ip, function(err, code){ 8 | if(code){ 9 | console.log(code+" - "+ip2country.countries[code]); // EE - Estonia 10 | ip2country.client.end(); 11 | } 12 | }); -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/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 | -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/examples/authorative.js: -------------------------------------------------------------------------------- 1 | var ndns = require('../lib/ndns'); 2 | var server = ndns.createServer('udp4'); 3 | 4 | var ns_c = ndns.ns_c; 5 | var ns_t = ndns.ns_t; 6 | 7 | server.on("request", function(req, res) { 8 | res.setHeader(req.header); 9 | res.header.qr = 1; 10 | res.header.aa = 1; 11 | res.header.rd = 0; 12 | res.header.ra = 0; 13 | res.header.ancount = 0; 14 | for (var i = 0; i < req.q.length; i++) { 15 | res.q.add(req.q[i]); 16 | res.addRR(req.q[i].name, 0, ns_c.in, ns_t.txt, "hello, world"); 17 | res.header.ancount++; 18 | } 19 | res.send(); 20 | }); 21 | server.bind(53); 22 | -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/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 | -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/examples/redis-client.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var redis = require('../lib/redis'); 3 | 4 | var db = redis.createClient(6379, "127.0.0.1"); 5 | 6 | var data = ""; 7 | 8 | db.onCommandType = function (commandType) { 9 | }; 10 | db.onMBulkLength = function (len) { 11 | }; 12 | db.onBulkLength = function (len) { 13 | }; 14 | db.onData = function (b, start, end) { 15 | data += b.toString("ascii", start, end); 16 | }; 17 | db.onDataEnd = function () { 18 | var info = {}; 19 | data.split("\r\n").map(function (line) { return line.split(":"); }); 20 | data.split("\r\n").forEach(function (line) { 21 | var keyval = line.split(":"); 22 | if (keyval.length == 2) { 23 | info[keyval[0]] = keyval[1]; 24 | } 25 | }); 26 | sys.puts('INFO: ' + sys.inspect(info)); 27 | data = ""; 28 | datalen = null; 29 | db.end(); 30 | }; 31 | db.onMBulkEnd = function () { 32 | }; 33 | 34 | db.query("info"); 35 | -------------------------------------------------------------------------------- /nodejs/DNS/forwarder.js: -------------------------------------------------------------------------------- 1 | var dnsapi = require("./dns-api"), 2 | utillib = require("util"); 3 | 4 | // check if a forward is needed or run callback 5 | module.exports = function(req, res, callback){ 6 | 7 | var hostname = req.headers.host, 8 | ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.connection.socket.remoteAddress; 9 | 10 | // resolve WEBFWD records 11 | dnsapi.resolve(hostname, "WEBFWD", ip, function(err, records){ 12 | if(records){ 13 | // use first web forwarder 14 | for(var i=0; i undefined 5 | * - callback (Function): callback function 6 | * 7 | * Imports IP data to Redis 8 | **/ 9 | module.exports = function(callback){ 10 | var client = redis.createClient() 11 | 12 | console.log("Start import..."); 13 | 14 | var ip_ranges = require("./all"), 15 | i=ip_ranges.length; 16 | 17 | console.log("Data loaded..."); 18 | 19 | setInterval(function(){ 20 | console.log("Rows left "+i); 21 | }, 10*1000); 22 | 23 | function addeach(){ 24 | var row = ip_ranges[--i]; 25 | client.zadd("iptable", Number(row[0]), row[1]+":"+row[2], function(err, res){ 26 | if(err){ 27 | return callback(err); 28 | } 29 | if(i){ 30 | process.nextTick(addeach); 31 | }else{ 32 | console.log("Ready!"); 33 | client && client.end(); 34 | callback(null, true); 35 | } 36 | }); 37 | } 38 | 39 | client.del("iptable", function(err, res){ 40 | if(err){ 41 | client && client.end(); 42 | return callback(err); 43 | } 44 | console.log("Cleaned table, starting inserts..."); 45 | addeach(); 46 | }); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/server.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var ndns = require('../lib/ndns'); 3 | var redis = require('../lib/redis'); 4 | 5 | var server = ndns.createServer('udp4'); 6 | 7 | var debug; 8 | var debugLevel = parseInt(process.env.NODE_DEBUG, 16); 9 | if(debugLevel & 0x4) { 10 | debug = function (x) { sys.error('redis: ' + x); }; 11 | } else { 12 | debug = function () { }; 13 | } 14 | 15 | var outgoing = []; 16 | 17 | var type = null, mbulklen = null, bulklen = null; 18 | var mbulk = [], data = ""; 19 | 20 | var db = redis.createClient(6379, "127.0.0.1"); 21 | db.onCommandType = function (commandType) { 22 | debug('db.onCommandType'); 23 | type = commandType; 24 | }; 25 | db.onMBulkLength = function (len) { 26 | debug('db.onMBulkLength'); 27 | mbulklen = len; 28 | }; 29 | db.onBulkLength = function (len) { 30 | debug('db.onBulkLength'); 31 | bulklen = len; 32 | }; 33 | db.onData = function (b, start, end) { 34 | debug('db.onData'); 35 | data += b.toString("ascii", start, end); 36 | }; 37 | db.onDataEnd = function () { 38 | debug('db.onDataEnd'); 39 | debug('data: ' + data); 40 | if (mbulklen) { 41 | mbulk.push(data); 42 | if (mbulk.length == 5) { 43 | var res = outgoing[0]; 44 | res.addRR(mbulk[0], mbulk[1], mbulk[2], mbulk[3], mbulk[4]); 45 | res.header.ancount++; 46 | while (mbulk.length) mbulk.pop(); 47 | } 48 | } 49 | data = ""; 50 | bulklen = null; 51 | }; 52 | db.onMBulkEnd = function () { 53 | debug('db.onMBulkEnd'); 54 | var res = outgoing.shift(); 55 | if (res.header.ancount == 0) { 56 | res.header.rcode = ndns.ns_rcode.nxdomain; 57 | } 58 | res.send(); 59 | mbulklen = null; 60 | }; 61 | 62 | server.on("request", function(req, res) { 63 | var key = ""; 64 | 65 | for (var i = 0; i < req.q.length; i++) { 66 | res.q.add(req.q[i]); 67 | key += req.q[i].name + "," + req.q[i].type.toString() + "," + req.q[i].class.toString(); 68 | } 69 | 70 | res.setHeader(req.header); 71 | res.header.qr = 1; 72 | res.header.aa = 1; 73 | res.header.rd = 0; 74 | res.header.ra = 0; 75 | 76 | if (key.length) { 77 | outgoing.push(res); 78 | db.query("lrange", key, 0, -1); 79 | } else { 80 | // nxdomain 81 | res.header.rcode = ndns.ns_rcode.nxdomain; 82 | res.send(); 83 | } 84 | }); 85 | server.bind(5353); 86 | -------------------------------------------------------------------------------- /nodejs/DNS/whois-server.js: -------------------------------------------------------------------------------- 1 | var dnsapi = require("./dns-api"), 2 | net = require('net'), 3 | punycode = require('./punycode'), 4 | server_name = "domains hosted at dns.kreata.ee"; 5 | 6 | var templates = { 7 | not_found: "% This Whois Server contains information on\r\n% "+server_name+"\r\n\r\n% ERROR:404: no entries found\r\n%\r\n% No entries found.", 8 | error_msg: "% This Whois Server contains information on\r\n% "+server_name+"\r\n\r\n% ERROR:500: {errname}\r\n%\r\n% {error}", 9 | found: "% This Whois Server contains information on\r\n% "+server_name+"\r\n\r\n"+ 10 | "domain: {domain}\r\n"+ 11 | "registered: {created}\r\n"+ 12 | "changed: {updated}\r\n\r\n% Registrant:\r\n"+ 13 | "name: {owner}\r\n"+ 14 | "e-mail: {email}\r\n\r\n% Nameservers:\r\n{ns}" 15 | } 16 | 17 | var server = net.createServer(function (socket) { 18 | socket.on("data", function(data){ 19 | var search_term = data.toString("ascii").trim(); 20 | whois(search_term, function(err, response){ 21 | if(err){ 22 | return socket.end(templates.error_msg. 23 | replace("{errname}", (err.name || "Error")). 24 | replace("{error}", (err.message || err))); 25 | } 26 | socket.end(response); 27 | }); 28 | }); 29 | }); 30 | 31 | function whois(search_term, callback){ 32 | search_term = search_term && search_term.trim() || ""; 33 | dnsapi.zones.find(search_term,function(err, zone){ 34 | if(err){ 35 | return callback(err); 36 | } 37 | if(zone){ 38 | var ns = [], 39 | owner = zone.whois && zone.whois.fname+" "+zone.whois.lname || zone.owner, 40 | email = zone.whois && zone.whois.email || "Not Disclosed"; 41 | if(zone.records && zone.records.NS){ 42 | for(var i=0; i node example.js 31 | Server running at 0.0.0.0:5300 32 | 33 | All of the examples in the documentation can be run similarly. 34 | 35 | ## ndns 36 | 37 | To use the ndns server and client one must require('ndns'). 38 | 39 | DNS request messages are represented by an object like this: 40 | 41 | { header: 42 | { id: 39545 43 | , qr: 0 44 | , opcode: 0 45 | , aa: 0 46 | , tc: 0 47 | , rd: 1 48 | , ra: 0 49 | , z: 0 50 | , ad: 0 51 | , cd: 0 52 | , rcode: 0 53 | , qdcount: 1 54 | , ancount: 0 55 | , nscount: 0 56 | , arcount: 0 57 | } 58 | , q: 59 | { '0': 60 | { name: 'example.com' 61 | , type: 1 62 | , class: 1 63 | } 64 | , length: 1 65 | } 66 | , rr: 67 | { '0': 68 | { name: 'example.com' 69 | , ttl: 3600 70 | , class: 1 71 | , type: 1 72 | , rdata: '127.0.0.1' 73 | } 74 | '1': 75 | { name: 'example.com' 76 | , ttl: 3600 77 | , class: 1 78 | , type: 1 79 | , rdata: '127.0.0.2' 80 | } 81 | , length: 2 82 | } 83 | } 84 | 85 | ## ndns.Server 86 | 87 | This is a dgram.Socket with the following events: 88 | 89 | ### Event: 'request' 90 | function (request, response) {} 91 | 92 | request is an instance of ndns.ServerRequest and response is an instance of 93 | ndns.ServerResponse 94 | 95 | ### ndns.createServer(type, requestListener) 96 | Return a new dns server object 97 | 98 | The requestListener is a function which is automatially added to the 'request' 99 | event. 100 | 101 | For documentation on dgram.Socket, see http://nodejs.org/api.html#dgram-267 102 | 103 | ## ndns.ServerRequest 104 | 105 | This object is created internally by a DNS-server, not by the user, and passed 106 | as the first argument to a 'request' listener 107 | 108 | ## ndns.ServerResponse 109 | 110 | This object is created internally by a DNS-server, not by the user. It is 111 | passed as the second argument to the 'request' event. 112 | 113 | ### response.setHeader(headers) 114 | Sets the response header to the request. 115 | 116 | Example #1: 117 | response.setHeader(request.header); 118 | response.header.qr = 1; 119 | response.header.qa = 1; 120 | 121 | Example #2: 122 | response.setHeader({ 123 | id: 0, 124 | qr: 0, 125 | rd: 1, 126 | qdcount: 1}); 127 | 128 | Valid keys `id`, `qr`, `opcode`, `aa`, `tc`, `rd`, `ra`, `z`, `ad`, `cd`, 129 | `rcode`, `qdcount`, `ancount`, `nscount` and `arcount`. 130 | 131 | This method can be called any number of times and must be called before 132 | `response.send()` is called; 133 | 134 | ### response.addQuestion(name, class, type) 135 | 136 | -------------------------------------------------------------------------------- /nodejs/dnshandler.js: -------------------------------------------------------------------------------- 1 | var dnslib = require("./DNS/dns-api.js"), 2 | urllib = require("url"), 3 | whois_server = require("./DNS/whois-server"); 4 | 5 | module.exports = function(req, res, data){ 6 | 7 | var url = urllib.parse(req.url, true); 8 | 9 | if(!url.query.user && url.pathname!="/api/dns/whois"){ 10 | return send(req, res, "Invalid user"); 11 | } 12 | 13 | var payload = false; 14 | try{ 15 | var payload = JSON.parse(data); 16 | }catch(E){} 17 | 18 | switch(url.pathname){ 19 | case "/api/dns/list": 20 | list_domains(url.query.user, send.bind(this, req, res)); 21 | break; 22 | case "/api/dns/add": 23 | add_domain(url.query.domain, url.query.user, { 24 | fname: url.query.fname, 25 | lname: url.query.lname, 26 | email: url.query.email, 27 | default_ip: url.query.default_ip, 28 | use_ga_mx: url.query.use_ga_mx 29 | }, send.bind(this, req, res)); 30 | break; 31 | case "/api/dns/remove": 32 | remove_domain(url.query.domain, url.query.user, send.bind(this, req, res)); 33 | break; 34 | case "/api/dns/records": 35 | list_records(url.query.domain, url.query.user, send.bind(this, req, res)); 36 | break; 37 | case "/api/dns/update": 38 | add_record(url.query.domain, url.query.user, payload, send.bind(this, req, res)); 39 | break; 40 | case "/api/dns/remove-record": 41 | remove_record(url.query.domain, url.query.user, url.query.rid, send.bind(this, req, res)); 42 | break; 43 | case "/api/dns/whois": 44 | whois_server.request(url.query.domain, send.bind(this, req, res)); 45 | break; 46 | default: 47 | send(req, res, "Unknown service"); 48 | } 49 | } 50 | 51 | 52 | function list_domains(owner, callback){ 53 | dnslib.zones.list(owner, callback); 54 | } 55 | 56 | function add_domain(domain_name, owner, options, callback){ 57 | if(!domain_name){ 58 | return callback("Domain name not specified"); 59 | } 60 | dnslib.zones.add(domain_name, owner, options, callback); 61 | } 62 | 63 | function remove_domain(domain_name, owner, callback){ 64 | if(!domain_name){ 65 | return callback("Domain name not specified"); 66 | } 67 | dnslib.zones.remove(domain_name, owner, callback); 68 | } 69 | 70 | function list_records(domain_name, owner, callback){ 71 | if(!domain_name){ 72 | return callback("Domain name not specified"); 73 | } 74 | dnslib.records.list(domain_name, owner, callback); 75 | } 76 | 77 | function add_record(domain_name, owner, record, callback){ 78 | if(!domain_name){ 79 | return callback("Domain name not specified"); 80 | } 81 | dnslib.records.add(domain_name, owner, record, callback); 82 | } 83 | 84 | function remove_record(domain_name, owner, rid, callback){ 85 | if(!domain_name){ 86 | return callback("Domain name not specified"); 87 | } 88 | dnslib.records.remove(domain_name, owner, rid, callback); 89 | } 90 | 91 | function send(req, res, err, data){ 92 | 93 | res.setHeader("Content-type","application/json; charset=utf-8"); 94 | if(err){ 95 | res.writeHead(200); 96 | res.end(JSON.stringify({"status": "error", "errormsg": err.message || err})); 97 | return; 98 | } 99 | res.writeHead(200); 100 | 101 | res.end(JSON.stringify({ 102 | "status": "OK", 103 | "data": data 104 | })); 105 | 106 | } -------------------------------------------------------------------------------- /nodejs/server.js: -------------------------------------------------------------------------------- 1 | 2 | var https = require('https'), 3 | http = require('http'), 4 | fs = require('fs'), 5 | config = require("./config"), 6 | static_handler = require("./static"), 7 | punycode = require("./modules/punycode"), 8 | start_dns_server = require("./DNS/dns-server"), 9 | whois_server = require("./DNS/whois-server"), 10 | forwarder = require("./DNS/forwarder"), 11 | dns_api = require("./dnshandler"); 12 | 13 | process.on('uncaughtException',function(err){ 14 | try{ 15 | console.log("Unexpected Error\n"+err.message+"\n"+err.stack); 16 | log("error", "["+Date()+"] Unexpected Error "+err.message); 17 | }catch(E){ 18 | console.log("Catastrophic failure!") 19 | } 20 | }); 21 | 22 | http.createServer(webserver).listen(80, HTTP_Ready); 23 | start_dns_server(); 24 | whois_server.start(); 25 | 26 | function HTTP_Ready(err){ 27 | if(err){ 28 | return console.log("Error setting up listener on port 80\n"+err.message); 29 | } 30 | console.log("Listening on port 80"); 31 | https.createServer(config.certificates, webserver).listen(443, HTTPS_Ready); 32 | } 33 | 34 | function HTTPS_Ready(err){ 35 | if(err){ 36 | return console.log("Error setting up listener on port 443\n"+err.message); 37 | } 38 | process.setgid("node"); 39 | process.setuid("node"); 40 | 41 | console.log("Listening on port 443"); 42 | log("access"," ["+Date()+"] Server started"); 43 | } 44 | 45 | function redirect(req, res){ 46 | 47 | var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.connection.socket.remoteAddress; 48 | 49 | if(config.deny.url.indexOf(req.url)>=0 || config.deny.ip.indexOf(ip)>=0){ 50 | res.writeHead(410); 51 | res.end(); 52 | return; 53 | } 54 | 55 | log("access","["+Date()+"] 302 to "+ip+" from "+req.url); 56 | res.setHeader("Location","https://node.ee"+req.url); 57 | res.writeHead(302); 58 | res.end(); 59 | } 60 | 61 | function c_webserver(req, res, data){ 62 | var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.connection.socket.remoteAddress; 63 | 64 | if(config.deny.url.indexOf(req.url)>=0 || config.deny.ip.indexOf(ip)>=0){ 65 | res.writeHead(410); 66 | res.end(); 67 | return; 68 | } 69 | 70 | if(req.url.match(/^\/api\/(?:dns|whois)/)){ 71 | log("access","["+Date()+"] 200 to "+ip+" from "+req.url); 72 | return dns_api(req, res, data); 73 | } 74 | 75 | if(req.url=="/dnsmanager"){ 76 | req.url = "/dnsmanager.html"; 77 | } 78 | 79 | if(req.url=="/"){ 80 | log("access","["+Date()+"] 200 to "+ip+" from "+req.url); 81 | res.setHeader("Content-type","text/html; charset=utf-8"); 82 | res.writeHead(200); 83 | res.end(""+(punycode.ToUnicode(req.headers.host) || "node.ee")+"

hi!

"+(punycode.ToUnicode(req.headers.host) || "node.ee")+" is a node.js instance ("+process.version+", "+process.getuid()+":"+process.getgid()+")

"); 84 | return; 85 | } 86 | 87 | if(!req.url.match(/\.\./)){ 88 | static_handler.serve(req, res, config.directories["static"]+req.url, function(err){ 89 | if(err){ 90 | return log("error","["+Date()+"] 404 to "+ip+" from "+req.url); 91 | } 92 | log("access","["+Date()+"] 200 to "+ip+" from "+req.url); 93 | }); 94 | return; 95 | }else{ 96 | log("error","["+Date()+"] 404 to "+ip+" from "+req.url); 97 | res.writeHead(404); 98 | res.end("not found\n"); 99 | return; 100 | } 101 | } 102 | 103 | function webserver(req, res){ 104 | var data = ""; 105 | req.on("data", function(chunk){ 106 | data += chunk.toString("utf-8"); 107 | }); 108 | req.on("end", function(){ 109 | forwarder(req, res, function(err, status){ 110 | c_webserver(req, res, data); 111 | }); 112 | }); 113 | } 114 | 115 | function log(type, message){ 116 | fs.open(config.directories.logs+'/'+type+'.log', 'a', function(err, fd){ 117 | if(err){ 118 | return console.log("Could not open log file :S\n"+err.message); 119 | } 120 | var buf = new Buffer(message.trim()+"\n","utf-8"); 121 | fs.write(fd, buf, 0, buf.length, null, function(err, written){ 122 | if(err){ 123 | console.log("Failed to write to log file :S\n"+err.message); 124 | } 125 | fs.close(fd); 126 | }); 127 | }); 128 | } -------------------------------------------------------------------------------- /nodejs/DNS/dns-server.js: -------------------------------------------------------------------------------- 1 | var ndns = require ('./ndns-eec0a40/lib/ndns'), 2 | fs = require("fs"), 3 | utillib = require("util"), 4 | dnsapi = require("./dns-api"), 5 | punycode = require("./punycode"); 6 | 7 | dnsapi.db.openDB(function(){}); 8 | 9 | module.exports = function(){ 10 | 11 | var server = ndns.createServer('udp4'), 12 | ns_c = ndns.ns_c, 13 | ns_t = ndns.ns_t, 14 | reqnr = 0; 15 | 16 | var tcount = 0, 17 | tsec = 60; 18 | 19 | setInterval(function(){ 20 | var m = (tcount/tsec).toFixed(2); 21 | console.log(tcount+" requests in "+tsec+" seconds, total "+reqnr+", "+m+" req/s avg"); 22 | tcount = 0; 23 | },tsec*1000); 24 | 25 | 26 | server.on("request", function(req, res) { 27 | 28 | reqnr++; 29 | tcount++; 30 | res.setHeader(req.header); 31 | res.header.qr = req.q.length; 32 | res.header.aa = 0; 33 | res.header.rd = 0; 34 | res.header.ra = 0; 35 | res.header.ancount = 0; 36 | res.header.arcount = 0; 37 | res.header.nscount = 0; 38 | 39 | var req_i=0; 40 | 41 | manageDomain(); 42 | 43 | function manageDomain(){ 44 | var data = req.q[req_i++], name, value; 45 | res.q.add(data); 46 | 47 | dnsapi.resolve(data.name, data.typeName, req.rinfo.address, function(err, records){ 48 | 49 | records.answer && records.answer.forEach(function(record){ 50 | res.addRR.apply(res, getValue(records, record)); 51 | res.header.ancount += 1; 52 | res.header.aa += 1; 53 | }); 54 | 55 | records.authority && records.authority.forEach(function(record){ 56 | res.addRR.apply(res, getValue(records, record)); 57 | res.header.nscount += 1; 58 | res.header.aa += 1; 59 | }); 60 | 61 | records.additional && records.additional.forEach(function(record){ 62 | res.addRR.apply(res, getValue(records, record)); 63 | res.header.arcount += 1; 64 | res.header.aa += 1; 65 | }); 66 | 67 | if(req.q.length>=req_i){ 68 | if(!records.answer || !records.answer.length){ 69 | res.header.rcode = ndns.ns_rcode.nxdomain; 70 | } 71 | res.send(); 72 | }else{ 73 | manageDomain(); 74 | } 75 | }); 76 | } 77 | 78 | }); 79 | server.bind(53); 80 | console.log("DNS server started on UDP port 53"); 81 | 82 | function normalizeHost(name, hostname){ 83 | if(name=="@" || !name){ 84 | return hostname; 85 | }else{ 86 | return name+"."+hostname; 87 | } 88 | } 89 | 90 | function getValue(records, record){ 91 | var name, value; 92 | if(record.hostname=="@" || !record.hostname){ 93 | name = records.hostname; 94 | }else{ 95 | name = record.hostname+"."+records.hostname; 96 | } 97 | 98 | switch(record.record.type){ 99 | case "A": 100 | value = [record.record.value[0] || "127.0.0.1"]; 101 | break; 102 | case "AAAA": 103 | value = [record.record.value[0] || "0000:0000:0000:0000:0000:0000:0000:0001"]; 104 | break; 105 | case "CNAME": 106 | case "NS": 107 | value = [punycode.ToASCII(record.record.value[0].replace("@", records.hostname) || "")]; 108 | break; 109 | case "MX": 110 | value = [record.record.value[1] || 10, punycode.ToASCII(record.record.value[0].replace("@", records.hostname) || "")]; 111 | break; 112 | case "SRV": 113 | value = [0, 0, record.record.value[1] || 10, punycode.ToASCII(record.record.value[0].replace("@", records.hostname) || "")]; 114 | break; 115 | } 116 | //[data.name, 60, ns_c["in"], ns_t.a, domain.records["A"][i].value 117 | value.unshift(ns_t[record.record.type.toLowerCase()]); 118 | value.unshift(ns_c["in"]); 119 | value.unshift(record.record.ttl || 600); 120 | if(record.record.type!="SRV"){ 121 | value.unshift(punycode.ToASCII(name)); 122 | }else{ 123 | // SRV records include _ symbols 124 | value.unshift(name); 125 | } 126 | return value; 127 | } 128 | 129 | function log(message){ 130 | message = "["+Date()+"]\n"+message+"\n"; 131 | fs.open('request.log', 'a', function(err, fd){ 132 | if(err){ 133 | return console.log("Could not open log file :S\n"+err.message); 134 | } 135 | var buf = new Buffer(message.trim()+"\n","utf-8"); 136 | fs.write(fd, buf, 0, buf.length, null, function(err, written){ 137 | if(err){ 138 | console.log("Failed to write to log file :S\n"+err.message); 139 | } 140 | fs.close(fd); 141 | }); 142 | }); 143 | } 144 | 145 | } -------------------------------------------------------------------------------- /nodejs/DNS/IP/ip2country.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | 3 | /** 4 | * ip2country.client -> Object 5 | * 6 | * Connection to Redis 7 | **/ 8 | exports.client = redis.createClient(); 9 | 10 | /** 11 | * ip2country.resolve(ip, callback) -> undefined 12 | * - ip (String): IP to resolve 13 | * - callback (Function): callback function 14 | * 15 | * Resolves a ctwo letter country code for an IP address. For a complete 16 | * country name check ip2country.countries 17 | **/ 18 | exports.resolve = function(ip, callback){ 19 | var numbers = ip.split(".").map(function(octet){return parseInt(octet,10);}), 20 | code = numbers[0]*16777216 + numbers[1]*65536 + numbers[2]*256 + numbers[3], 21 | data; 22 | exports.client.zrevrangebyscore("iptable", code, "-inf", "LIMIT", 0, 1, function(err, res){ 23 | if(err){ 24 | return callback(err); 25 | } 26 | if(!res || !res.length){ 27 | return callback(null, false); 28 | } 29 | data = res[0].split(":"); 30 | callback(null, code<=Number(data[0])&&data[1]!="ZZ"?data[1]:false); 31 | }); 32 | } 33 | 34 | /** 35 | * ip2country.import_data(callback) -> undefined 36 | * - callback (Function): callback function 37 | * 38 | * Imports IP table to Redis 39 | **/ 40 | exports.import_data = function(callback){ 41 | require("./import")(callback); 42 | } 43 | 44 | /** 45 | * ip2country.countries -> Object 46 | * 47 | * Hash to map two letter country codes with country names 48 | **/ 49 | exports.countries = { 50 | "AD": "Andorra", 51 | "AE": "United Arab Emirates", 52 | "AF": "Afghanistan", 53 | "AG": "Antigua And Barbuda", 54 | "AI": "Anguilla", 55 | "AL": "Albania", 56 | "AM": "Armenia", 57 | "AN": "Netherlands Antilles", 58 | "AO": "Angola", 59 | "AQ": "Antarctica", 60 | "AR": "Argentina", 61 | "AS": "American Samoa", 62 | "AT": "Austria", 63 | "AU": "Australia", 64 | "AW": "Aruba", 65 | "AZ": "Azerbaijan", 66 | "BA": "Bosnia And Herzegovina", 67 | "BB": "Barbados", 68 | "BD": "Bangladesh", 69 | "BE": "Belgium", 70 | "BF": "Burkina Faso", 71 | "BG": "Bulgaria", 72 | "BH": "Bahrain", 73 | "BI": "Burundi", 74 | "BJ": "Benin", 75 | "BM": "Bermuda", 76 | "BN": "Brunei Darussalam", 77 | "BO": "Bolivia", 78 | "BR": "Brazil", 79 | "BS": "Bahamas", 80 | "BT": "Bhutan", 81 | "BW": "Botswana", 82 | "BY": "Belarus", 83 | "BZ": "Belize", 84 | "CA": "Canada", 85 | "CD": "The Democratic Republic Of The Congo", 86 | "CF": "Central African Republic", 87 | "CG": "Congo", 88 | "CH": "Switzerland", 89 | "CI": "Cote D'ivoire", 90 | "CK": "Cook Islands", 91 | "CL": "Chile", 92 | "CM": "Cameroon", 93 | "CN": "China", 94 | "CO": "Colombia", 95 | "CR": "Costa Rica", 96 | "CS": "Serbia And Montenegro", 97 | "CU": "Cuba", 98 | "CV": "Cape Verde", 99 | "CY": "Cyprus", 100 | "CZ": "Czech Republic", 101 | "DE": "Germany", 102 | "DJ": "Djibouti", 103 | "DK": "Denmark", 104 | "DM": "Dominica", 105 | "DO": "Dominican Republic", 106 | "DZ": "Algeria", 107 | "EC": "Ecuador", 108 | "EE": "Estonia", 109 | "EG": "Egypt", 110 | "ER": "Eritrea", 111 | "ES": "Spain", 112 | "ET": "Ethiopia", 113 | "EU": "European Union", 114 | "FI": "Finland", 115 | "FJ": "Fiji", 116 | "FK": "Falkland Islands (Malvinas)", 117 | "FM": "Federated States Of Micronesia", 118 | "FO": "Faroe Islands", 119 | "FR": "France", 120 | "GA": "Gabon", 121 | "GB": "United Kingdom", 122 | "GD": "Grenada", 123 | "GE": "Georgia", 124 | "GF": "French Guiana", 125 | "GH": "Ghana", 126 | "GI": "Gibraltar", 127 | "GL": "Greenland", 128 | "GM": "Gambia", 129 | "GN": "Guinea", 130 | "GP": "Guadeloupe", 131 | "GQ": "Equatorial Guinea", 132 | "GR": "Greece", 133 | "GS": "South Georgia And The South Sandwich Islands", 134 | "GT": "Guatemala", 135 | "GU": "Guam", 136 | "GW": "Guinea-Bissau", 137 | "GY": "Guyana", 138 | "HK": "Hong Kong", 139 | "HN": "Honduras", 140 | "HR": "Croatia", 141 | "HT": "Haiti", 142 | "HU": "Hungary", 143 | "ID": "Indonesia", 144 | "IE": "Ireland", 145 | "IL": "Israel", 146 | "IN": "India", 147 | "IO": "British Indian Ocean Territory", 148 | "IQ": "Iraq", 149 | "IR": "Islamic Republic Of Iran", 150 | "IS": "Iceland", 151 | "IT": "Italy", 152 | "JM": "Jamaica", 153 | "JO": "Jordan", 154 | "JP": "Japan", 155 | "KE": "Kenya", 156 | "KG": "Kyrgyzstan", 157 | "KH": "Cambodia", 158 | "KI": "Kiribati", 159 | "KM": "Comoros", 160 | "KN": "Saint Kitts And Nevis", 161 | "KR": "Republic Of Korea", 162 | "KW": "Kuwait", 163 | "KY": "Cayman Islands", 164 | "KZ": "Kazakhstan", 165 | "LA": "Lao People's Democratic Republic", 166 | "LB": "Lebanon", 167 | "LC": "Saint Lucia", 168 | "LI": "Liechtenstein", 169 | "LK": "Sri Lanka", 170 | "LR": "Liberia", 171 | "LS": "Lesotho", 172 | "LT": "Lithuania", 173 | "LU": "Luxembourg", 174 | "LV": "Latvia", 175 | "LY": "Libyan Arab Jamahiriya", 176 | "MA": "Morocco", 177 | "MC": "Monaco", 178 | "MD": "Republic Of Moldova", 179 | "MG": "Madagascar", 180 | "MH": "Marshall Islands", 181 | "MK": "The Former Yugoslav Republic Of Macedonia", 182 | "ML": "Mali", 183 | "MM": "Myanmar", 184 | "MN": "Mongolia", 185 | "MO": "Macao", 186 | "MP": "Northern Mariana Islands", 187 | "MQ": "Martinique", 188 | "MR": "Mauritania", 189 | "MT": "Malta", 190 | "MU": "Mauritius", 191 | "MV": "Maldives", 192 | "MW": "Malawi", 193 | "MX": "Mexico", 194 | "MY": "Malaysia", 195 | "MZ": "Mozambique", 196 | "NA": "Namibia", 197 | "NC": "New Caledonia", 198 | "NE": "Niger", 199 | "NF": "Norfolk Island", 200 | "NG": "Nigeria", 201 | "NI": "Nicaragua", 202 | "NL": "Netherlands", 203 | "NO": "Norway", 204 | "NP": "Nepal", 205 | "NR": "Nauru", 206 | "NU": "Niue", 207 | "NZ": "New Zealand", 208 | "OM": "Oman", 209 | "PA": "Panama", 210 | "PE": "Peru", 211 | "PF": "French Polynesia", 212 | "PG": "Papua New Guinea", 213 | "PH": "Philippines", 214 | "PK": "Pakistan", 215 | "PL": "Poland", 216 | "PR": "Puerto Rico", 217 | "PS": "Palestinian Territory", 218 | "PT": "Portugal", 219 | "PW": "Palau", 220 | "PY": "Paraguay", 221 | "QA": "Qatar", 222 | "RE": "Reunion", 223 | "RO": "Romania", 224 | "RU": "Russian Federation", 225 | "RW": "Rwanda", 226 | "SA": "Saudi Arabia", 227 | "SB": "Solomon Islands", 228 | "SC": "Seychelles", 229 | "SD": "Sudan", 230 | "SE": "Sweden", 231 | "SG": "Singapore", 232 | "SI": "Slovenia", 233 | "SK": "Slovakia (Slovak Republic)", 234 | "SL": "Sierra Leone", 235 | "SM": "San Marino", 236 | "SN": "Senegal", 237 | "SO": "Somalia", 238 | "SR": "Suriname", 239 | "ST": "Sao Tome And Principe", 240 | "SV": "El Salvador", 241 | "SY": "Syrian Arab Republic", 242 | "SZ": "Swaziland", 243 | "TD": "Chad", 244 | "TF": "French Southern Territories", 245 | "TG": "Togo", 246 | "TH": "Thailand", 247 | "TJ": "Tajikistan", 248 | "TK": "Tokelau", 249 | "TL": "Timor-Leste", 250 | "TM": "Turkmenistan", 251 | "TN": "Tunisia", 252 | "TO": "Tonga", 253 | "TR": "Turkey", 254 | "TT": "Trinidad And Tobago", 255 | "TV": "Tuvalu", 256 | "TW": "Taiwan Province Of China", 257 | "TZ": "United Republic Of Tanzania", 258 | "UA": "Ukraine", 259 | "UG": "Uganda", 260 | "US": "United States", 261 | "UY": "Uruguay", 262 | "UZ": "Uzbekistan", 263 | "VA": "Holy See (Vatican City State)", 264 | "VC": "Saint Vincent And The Grenadines", 265 | "VE": "Venezuela", 266 | "VG": "Virgin Islands", 267 | "VI": "Virgin Islands", 268 | "VN": "Viet Nam", 269 | "VU": "Vanuatu", 270 | "WS": "Samoa", 271 | "YE": "Yemen", 272 | "YT": "Mayotte", 273 | "YU": "Serbia And Montenegro (Formally Yugoslavia)", 274 | "ZA": "South Africa", 275 | "ZM": "Zambia", 276 | "ZW": "Zimbabwe", 277 | "ZZ": "Reserved" 278 | }; -------------------------------------------------------------------------------- /nodejs/DNS/ndns-eec0a40/lib/redis.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('nredis: ' + x); }; 7 | } else { 8 | debug = function () { }; 9 | } 10 | 11 | var net = require('net'); 12 | var events = require('events'); 13 | var Buffer = require('buffer').Buffer; 14 | 15 | var FreeList = require('freelist').FreeList; 16 | 17 | var parsers = new FreeList('parsers', 1000, function () { 18 | var parser = new Parser(); 19 | 20 | parser.onCommandType = function () { 21 | parser.socket.onCommandType.apply(this, arguments); 22 | }; 23 | parser.onMBulkLength = function () { 24 | parser.socket.onMBulkLength.apply(this, arguments); 25 | }; 26 | parser.onBulkLength = function () { 27 | parser.socket.onBulkLength.apply(this, arguments); 28 | }; 29 | parser.onData = function () { 30 | parser.socket.onData.apply(this, arguments); 31 | }; 32 | parser.onDataEnd = function () { 33 | parser.socket.onDataEnd.apply(this, arguments); 34 | }; 35 | parser.onMBulkEnd = function () { 36 | parser.socket.onMBulkEnd.apply(this, arguments); 37 | }; 38 | 39 | return parser; 40 | }); 41 | 42 | var sym = 0; 43 | function Symbol (string) { 44 | this.sym = sym++; 45 | this.string = string; 46 | } 47 | 48 | Symbol.prototype.toString = function () { 49 | return this.string; 50 | }; 51 | 52 | function Counter () { 53 | this.value = 0; 54 | this.negative = false; 55 | }; 56 | exports.Counter = Counter; 57 | 58 | Counter.prototype.base = 10; 59 | 60 | Counter.prototype.read = function (c) { 61 | if (c >= 48 && c <= 57) { // >= '0' && <= '9' 62 | this.value *= this.base; 63 | this.value += (c - 48); 64 | } else if (c == 45) { // '-' 65 | this.negative = true; 66 | } 67 | }; 68 | 69 | Counter.prototype.reinitialize = function () { 70 | Counter.call(this); 71 | }; 72 | 73 | Counter.prototype.getValue = function () { 74 | return this.negative ? -1 : this.value; 75 | }; 76 | 77 | function Parser () { 78 | this.commandType = null; 79 | this.parseState = this.parseState_command; 80 | this.parseError = null; 81 | 82 | this.commandStart = 0; 83 | 84 | this.onCommandType = null; 85 | this.onBulkLength = null; 86 | this.onData = null; 87 | this.onDataEnd = null; 88 | 89 | this._counter = new Counter(); 90 | this.datalen = null; 91 | this.mbulklen = null; 92 | } 93 | exports.Parser = Parser; 94 | 95 | Parser.prototype.commandType_error = new Symbol("error"); 96 | Parser.prototype.commandType_singleline = new Symbol("single line"); 97 | Parser.prototype.commandType_bulk = new Symbol("bulk"); 98 | Parser.prototype.commandType_mbulk = new Symbol("multibulk"); 99 | Parser.prototype.commandType_integer = new Symbol("integer"); 100 | 101 | Parser.prototype.parseState_command = new Symbol("command"); 102 | Parser.prototype.parseState_bulk = new Symbol("bulk"); 103 | Parser.prototype.parseState_bulkdata = new Symbol("bulkdata"); 104 | Parser.prototype.parseState_mbulk = new Symbol("mbulk"); 105 | Parser.prototype.parseState_line = new Symbol("line"); 106 | Parser.prototype.parseState_r = new Symbol("r"); 107 | Parser.prototype.parseState_n = new Symbol("n"); 108 | 109 | Parser.prototype.reinitialize = function () { 110 | this.commandType = null; 111 | this.parseState = this.parseState_command; 112 | this.parseError = null; 113 | 114 | this.commandStart = 0; 115 | 116 | this._counter.reinitialize(); 117 | this.datalen = null; 118 | this.mbulklen = null; 119 | }; 120 | 121 | Parser.prototype.parse = function (b, start, end) { 122 | if (this.parseError) 123 | return; 124 | var i = start, c = 0; 125 | if (start > end) 126 | throw new Error ("start should < end"); 127 | if (end > b.length) 128 | throw new Error ("end extends beyond buffer"); 129 | this.commandStart = i; 130 | for (i = start; i < end; i++) { 131 | if (this.datalen) { 132 | --this.datalen; 133 | continue; 134 | } 135 | c = b[i]; 136 | switch(this.parseState) { 137 | case this.parseState_command: 138 | switch (c) { 139 | case 45: // - 140 | this.commandType = this.commandType_error; 141 | this.parseState = this.parseState_line; 142 | break; 143 | case 43: // + 144 | this.commandType = this.commandType_singleline; 145 | this.parseState = this.parseState_line; 146 | break; 147 | case 36: // $ 148 | this.commandType = this.commandType_bulk; 149 | this.parseState = this.parseState_bulk; 150 | break; 151 | case 42: // * 152 | this.commandType = this.commandType_mbulk; 153 | this.parseState = this.parseState_mbulk; 154 | break; 155 | case 58: // : 156 | this.commandType = this.commandType_integer; 157 | this.parseState = this.parseState_line; 158 | break; 159 | default: 160 | this.parseError = true; 161 | return; 162 | } 163 | this.onCommandType (this.commandType); 164 | this.commandStart = i + 1; 165 | break; 166 | case this.parseState_mbulk: 167 | case this.parseState_bulk: 168 | if (c != 13) { 169 | this._counter.read(c); 170 | } else { 171 | if (this.mbulklen > 0) { 172 | --this.mbulklen; 173 | } 174 | this.parseState = this.parseState_n; 175 | } 176 | break; 177 | case this.parseState_bulkdata: 178 | if (this.datalen == 0) { 179 | if (i > this.commandStart) 180 | this.onData(b, this.commandStart, i); 181 | this.onDataEnd(); 182 | this.parseState = this.parseState_n; 183 | 184 | if (this.mbulklen == 0) { 185 | this.onMBulkEnd(); 186 | this.mbulklen = null; 187 | } 188 | } else { 189 | --this.datalen; 190 | } 191 | break; 192 | case this.parseState_mbulk: 193 | if (c != 13) { 194 | this._counter.read(c); 195 | } else { 196 | this.parseState = this.parseState_n; 197 | } 198 | break; 199 | case this.parseState_line: 200 | if (c == 13) { // \r 201 | if (i > this.commandStart) 202 | this.onData (b, this.commandStart, i); 203 | this.onDataEnd(); 204 | this.parseState = this.parseState_n; 205 | } 206 | break; 207 | case this.parseState_r: 208 | if (c != 13) { // \r 209 | this.parseError = true; 210 | return; 211 | } 212 | this.parseState = this.parseState_n; 213 | break; 214 | case this.parseState_n: 215 | if (c != 10) { // \n 216 | this.parseError = true; 217 | return; 218 | } 219 | switch (this.commandType) { 220 | case this.commandType_mbulk: 221 | this.mbulklen = this._counter.getValue(); 222 | this.onMBulkLength(this.mbulklen); 223 | this._counter.reinitialize(); 224 | 225 | if (this.mbulklen == 0) { 226 | this.onMBulkEnd(); 227 | this.mbulklen = null; 228 | } 229 | 230 | this.parseState = this.parseState_command; 231 | break; 232 | case this.commandType_bulk: 233 | if (this.datalen == null) { 234 | this.datalen = this._counter.getValue(); 235 | this.onBulkLength(this.datalen); 236 | this._counter.reinitialize(); 237 | this.commandStart = i + 1; 238 | 239 | if (this.datalen == -1) { 240 | this.parseState = this.parseState_command; 241 | this.datalen = null; 242 | } else { 243 | this.parseState = this.parseState_bulkdata; 244 | } 245 | } else { 246 | this.datalen = null; 247 | this.parseState = this.parseState_command; 248 | } 249 | break; 250 | case this.commandType_error: 251 | case this.commandType_singleline: 252 | case this.commandType_integer: 253 | this.parseState = this.parseState_command; 254 | } 255 | break; 256 | } 257 | } 258 | switch (this.parseState) { 259 | case this.parseState_command: 260 | case this.parseState_bulk: 261 | break; 262 | case this.parseState_bulkdata: 263 | case this.parseState_line: 264 | if (i > this.commandStart) 265 | this.onData (b, this.commandStart, i); 266 | break; 267 | case this.parseState_mbulk: 268 | case this.parseState_r: 269 | case this.parseState_n: 270 | break; 271 | } 272 | }; 273 | 274 | Parser.prototype.finish = function () { 275 | 276 | }; 277 | 278 | function outgoingFlush (socket) { 279 | //var message = socket._outgoing[0]; 280 | var message = socket; 281 | 282 | if (!message) return; 283 | 284 | var ret; 285 | 286 | while (message.output.length) { 287 | var data = message.output.shift(); 288 | var encoding = message.outputEncodings.shift(); 289 | 290 | ret = socket.write(data, encoding); 291 | } 292 | 293 | // if (ret) message.emit('drain'); 294 | }; 295 | 296 | function Client () { 297 | net.Stream.call(this); 298 | var self = this; 299 | 300 | var parser; 301 | 302 | function initParser () { 303 | if (!parser) parser = parsers.alloc(); 304 | parser.reinitialize(); 305 | parser.socket = self; 306 | }; 307 | 308 | self.ondata = function (d, start, end) { 309 | debug('self.ondata'); 310 | parser.parse(d, start, end); 311 | if (parser.parseError) { 312 | self.destroy(new Error("syntax error")); 313 | } else { 314 | 315 | } 316 | }; 317 | 318 | this.on("connect", function () { 319 | debug('client connected'); 320 | initParser(); 321 | outgoingFlush(self); 322 | }); 323 | 324 | self.onend = function () { 325 | if (parser) parser.finish(); 326 | debug("self got end closing. readyState = " + self.readyState); 327 | self.end(); 328 | }; 329 | 330 | this.on("close", function (e) { 331 | if (e) return; 332 | 333 | // If there are more requests to handle, reconnect. 334 | // if (self._outgoing.length) { 335 | if (self.output.length) { 336 | self._reconnect(); 337 | } else if (parser) { 338 | parsers.free(parser); 339 | parser = null; 340 | } 341 | }); 342 | 343 | this.output = []; 344 | this.outputEncodings = []; 345 | // this._outgoing = []; 346 | }; 347 | sys.inherits(Client, net.Stream); 348 | exports.Client = Client; 349 | 350 | Client.prototype._reconnect = function () { 351 | if (this.readyState === "closed") { 352 | this.connect(this.port, this.host); 353 | } 354 | }; 355 | 356 | Client.prototype._buffer = function (data, encoding) { 357 | // Buffer 358 | if (data.length === 0) return; 359 | 360 | var length = this.output.length; 361 | 362 | if (length === 0 || typeof (data) != 'string') { 363 | this.output.push(data); 364 | encoding = encoding || "ascii"; 365 | this.outputEncodings.push(encoding); 366 | return false; 367 | } 368 | 369 | var lastEncoding = this.outputEncodings[length-1]; 370 | var lastData = this.output[length - 1]; 371 | 372 | if ((lastEncoding === encoding) || 373 | (!encoding && data.constructor === lastData.constructor)) { 374 | this.output[length-1] = lastData + data; 375 | return false; 376 | } 377 | 378 | this.output.push(data); 379 | encoding = encoding || "ascii"; 380 | this.outputEncodings.push(encoding); 381 | 382 | return false; 383 | }; 384 | 385 | Client.prototype.buf = function (data, encoding) { 386 | if (typeof data !== "string" 387 | && !Buffer.isBuffer(data) 388 | && !Array.isArray(data)) { 389 | throw new TypeError("first argument must be a string, Array, or Buffer"); 390 | } 391 | 392 | if (data.length === 0) return false; 393 | 394 | if (this.writable) { 395 | // There might be pending data in the this.output buffer 396 | while (this.output.length) { 397 | if (!this.writable) { 398 | this._buffer(data, encoding); 399 | return false; 400 | } 401 | var c = this.output.shift(); 402 | var e = this.outputEncodings.shift(); 403 | this.write(c, e); 404 | } 405 | 406 | // Directly write to socket. 407 | return this.write(data, encoding); 408 | } else { 409 | this._buffer(data, encoding); 410 | return false; 411 | } 412 | 413 | return ret; 414 | }; 415 | 416 | Client.prototype.query = function () { 417 | var query = "*" + arguments.length.toString() + "\r\n"; 418 | for (var i = 0; i < arguments.length; i++) { 419 | var arg = arguments[i].toString(); 420 | query += ("$" + Buffer.byteLength(arg, "utf8") + "\r\n" + 421 | arg + "\r\n"); 422 | } 423 | if (this.readyState === 'closed') this._reconnect(); 424 | this.buf(query); 425 | }; 426 | 427 | exports.createClient = function (port, host) { 428 | var c = new Client(); 429 | c.port = port; 430 | c.host = host; 431 | return c; 432 | }; 433 | -------------------------------------------------------------------------------- /nodejs/DNS/punycode.js: -------------------------------------------------------------------------------- 1 | //Javascript Punycode converter derived from example in RFC3492. 2 | //This implementation is created by some@domain.name and released into public domain 3 | module.exports = new function Punycode() { 4 | // This object converts to and from puny-code used in IDN 5 | // 6 | // punycode.ToASCII ( domain ) 7 | // 8 | // Returns a puny coded representation of "domain". 9 | // It only converts the part of the domain name that 10 | // has non ASCII characters. I.e. it dosent matter if 11 | // you call it with a domain that already is in ASCII. 12 | // 13 | // punycode.ToUnicode (domain) 14 | // 15 | // Converts a puny-coded domain name to unicode. 16 | // It only converts the puny-coded parts of the domain name. 17 | // I.e. it dosent matter if you call it on a string 18 | // that already has been converted to unicode. 19 | // 20 | // 21 | this.utf16 = { 22 | // The utf16-class is necessary to convert from javascripts internal character representation to unicode and back. 23 | decode:function(input){ 24 | var output = [], i=0, len=input.length,value,extra; 25 | while (i < len) { 26 | value = input.charCodeAt(i++); 27 | if ((value & 0xF800) === 0xD800) { 28 | extra = input.charCodeAt(i++); 29 | if ( ((value & 0xFC00) !== 0xD800) || ((extra & 0xFC00) !== 0xDC00) ) { 30 | throw new RangeError("UTF-16(decode): Illegal UTF-16 sequence"); 31 | } 32 | value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; 33 | } 34 | output.push(value); 35 | } 36 | return output; 37 | }, 38 | encode:function(input){ 39 | var output = [], i=0, len=input.length,value; 40 | while (i < len) { 41 | value = input[i++]; 42 | if ( (value & 0xF800) === 0xD800 ) { 43 | throw new RangeError("UTF-16(encode): Illegal UTF-16 value"); 44 | } 45 | if (value > 0xFFFF) { 46 | value -= 0x10000; 47 | output.push(String.fromCharCode(((value >>>10) & 0x3FF) | 0xD800)); 48 | value = 0xDC00 | (value & 0x3FF); 49 | } 50 | output.push(String.fromCharCode(value)); 51 | } 52 | return output.join(""); 53 | } 54 | } 55 | 56 | //Default parameters 57 | var initial_n = 0x80; 58 | var initial_bias = 72; 59 | var delimiter = "\x2D"; 60 | var base = 36; 61 | var damp = 700; 62 | var tmin=1; 63 | var tmax=26; 64 | var skew=38; 65 | var maxint = 0x7FFFFFFF; 66 | 67 | // decode_digit(cp) returns the numeric value of a basic code 68 | // point (for use in representing integers) in the range 0 to 69 | // base-1, or base if cp is does not represent a value. 70 | 71 | function decode_digit(cp) { 72 | return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base; 73 | } 74 | 75 | // encode_digit(d,flag) returns the basic code point whose value 76 | // (when used for representing integers) is d, which needs to be in 77 | // the range 0 to base-1. The lowercase form is used unless flag is 78 | // nonzero, in which case the uppercase form is used. The behavior 79 | // is undefined if flag is nonzero and digit d has no uppercase form. 80 | 81 | function encode_digit(d, flag) { 82 | return d + 22 + 75 * (d < 26) - ((flag != 0) << 5); 83 | // 0..25 map to ASCII a..z or A..Z 84 | // 26..35 map to ASCII 0..9 85 | } 86 | //** Bias adaptation function ** 87 | function adapt(delta, numpoints, firsttime ) { 88 | var k; 89 | delta = firsttime ? Math.floor(delta / damp) : (delta >> 1); 90 | delta += Math.floor(delta / numpoints); 91 | 92 | for (k = 0; delta > (((base - tmin) * tmax) >> 1); k += base) { 93 | delta = Math.floor(delta / ( base - tmin )); 94 | } 95 | return Math.floor(k + (base - tmin + 1) * delta / (delta + skew)); 96 | } 97 | 98 | // encode_basic(bcp,flag) forces a basic code point to lowercase if flag is zero, 99 | // uppercase if flag is nonzero, and returns the resulting code point. 100 | // The code point is unchanged if it is caseless. 101 | // The behavior is undefined if bcp is not a basic code point. 102 | 103 | function encode_basic(bcp, flag) { 104 | bcp -= (bcp - 97 < 26) << 5; 105 | return bcp + ((!flag && (bcp - 65 < 26)) << 5); 106 | } 107 | 108 | // Main decode 109 | this.decode=function(input,preserveCase) { 110 | // Dont use utf16 111 | var output=[]; 112 | var case_flags=[]; 113 | var input_length = input.length; 114 | 115 | var n, out, i, bias, basic, j, ic, oldi, w, k, digit, t, len; 116 | 117 | // Initialize the state: 118 | 119 | n = initial_n; 120 | i = 0; 121 | bias = initial_bias; 122 | 123 | // Handle the basic code points: Let basic be the number of input code 124 | // points before the last delimiter, or 0 if there is none, then 125 | // copy the first basic code points to the output. 126 | 127 | basic = input.lastIndexOf(delimiter); 128 | if (basic < 0) basic = 0; 129 | 130 | for (j = 0; j < basic; ++j) { 131 | if(preserveCase) case_flags[output.length] = ( input.charCodeAt(j) -65 < 26); 132 | if ( input.charCodeAt(j) >= 0x80) { 133 | throw new RangeError("Illegal input >= 0x80"); 134 | } 135 | output.push( input.charCodeAt(j) ); 136 | } 137 | 138 | // Main decoding loop: Start just after the last delimiter if any 139 | // basic code points were copied; start at the beginning otherwise. 140 | 141 | for (ic = basic > 0 ? basic + 1 : 0; ic < input_length; ) { 142 | 143 | // ic is the index of the next character to be consumed, 144 | 145 | // Decode a generalized variable-length integer into delta, 146 | // which gets added to i. The overflow checking is easier 147 | // if we increase i as we go, then subtract off its starting 148 | // value at the end to obtain delta. 149 | for (oldi = i, w = 1, k = base; ; k += base) { 150 | if (ic >= input_length) { 151 | throw RangeError ("punycode_bad_input(1)"); 152 | } 153 | digit = decode_digit(input.charCodeAt(ic++)); 154 | 155 | if (digit >= base) { 156 | throw RangeError("punycode_bad_input(2)"); 157 | } 158 | if (digit > Math.floor((maxint - i) / w)) { 159 | throw RangeError ("punycode_overflow(1)"); 160 | } 161 | i += digit * w; 162 | t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias; 163 | if (digit < t) { break; } 164 | if (w > Math.floor(maxint / (base - t))) { 165 | throw RangeError("punycode_overflow(2)"); 166 | } 167 | w *= (base - t); 168 | } 169 | 170 | out = output.length + 1; 171 | bias = adapt(i - oldi, out, oldi === 0); 172 | 173 | // i was supposed to wrap around from out to 0, 174 | // incrementing n each time, so we'll fix that now: 175 | if ( Math.floor(i / out) > maxint - n) { 176 | throw RangeError("punycode_overflow(3)"); 177 | } 178 | n += Math.floor( i / out ) ; 179 | i %= out; 180 | 181 | // Insert n at position i of the output: 182 | // Case of last character determines uppercase flag: 183 | if (preserveCase) { case_flags.splice(i, 0, input.charCodeAt(ic -1) -65 < 26);} 184 | 185 | output.splice(i, 0, n); 186 | i++; 187 | } 188 | if (preserveCase) { 189 | for (i = 0, len = output.length; i < len; i++) { 190 | if (case_flags[i]) { 191 | output[i] = (String.fromCharCode(output[i]).toUpperCase()).charCodeAt(0); 192 | } 193 | } 194 | } 195 | return this.utf16.encode(output); 196 | }; 197 | 198 | //** Main encode function ** 199 | 200 | this.encode = function (input,preserveCase) { 201 | //** Bias adaptation function ** 202 | 203 | var n, delta, h, b, bias, j, m, q, k, t, ijv, case_flags; 204 | 205 | if (preserveCase) { 206 | // Preserve case, step1 of 2: Get a list of the unaltered string 207 | case_flags = this.utf16.decode(input); 208 | } 209 | // Converts the input in UTF-16 to Unicode 210 | input = this.utf16.decode(input.toLowerCase()); 211 | 212 | var input_length = input.length; // Cache the length 213 | 214 | if (preserveCase) { 215 | // Preserve case, step2 of 2: Modify the list to true/false 216 | for (j=0; j < input_length; j++) { 217 | case_flags[j] = input[j] != case_flags[j]; 218 | } 219 | } 220 | 221 | var output=[]; 222 | 223 | 224 | // Initialize the state: 225 | n = initial_n; 226 | delta = 0; 227 | bias = initial_bias; 228 | 229 | // Handle the basic code points: 230 | for (j = 0; j < input_length; ++j) { 231 | if ( input[j] < 0x80) { 232 | output.push( 233 | String.fromCharCode( 234 | case_flags ? encode_basic(input[j], case_flags[j]) : input[j] 235 | ) 236 | ); 237 | } 238 | } 239 | 240 | h = b = output.length; 241 | 242 | // h is the number of code points that have been handled, b is the 243 | // number of basic code points 244 | 245 | if (b > 0) output.push(delimiter); 246 | 247 | // Main encoding loop: 248 | // 249 | while (h < input_length) { 250 | // All non-basic code points < n have been 251 | // handled already. Find the next larger one: 252 | 253 | for (m = maxint, j = 0; j < input_length; ++j) { 254 | ijv = input[j]; 255 | if (ijv >= n && ijv < m) m = ijv; 256 | } 257 | 258 | // Increase delta enough to advance the decoder's 259 | // state to , but guard against overflow: 260 | 261 | if (m - n > Math.floor((maxint - delta) / (h + 1))) { 262 | throw RangeError("punycode_overflow (1)"); 263 | } 264 | delta += (m - n) * (h + 1); 265 | n = m; 266 | 267 | for (j = 0; j < input_length; ++j) { 268 | ijv = input[j]; 269 | 270 | if (ijv < n ) { 271 | if (++delta > maxint) return Error("punycode_overflow(2)"); 272 | } 273 | 274 | if (ijv == n) { 275 | // Represent delta as a generalized variable-length integer: 276 | for (q = delta, k = base; ; k += base) { 277 | t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias; 278 | if (q < t) break; 279 | output.push( String.fromCharCode(encode_digit(t + (q - t) % (base - t), 0)) ); 280 | q = Math.floor( (q - t) / (base - t) ); 281 | } 282 | output.push( String.fromCharCode(encode_digit(q, preserveCase && case_flags[j] ? 1:0 ))); 283 | bias = adapt(delta, h + 1, h == b); 284 | delta = 0; 285 | ++h; 286 | } 287 | } 288 | 289 | ++delta, ++n; 290 | } 291 | return output.join(""); 292 | } 293 | 294 | this.ToASCII = (function ( domain ) { 295 | var domain_array = domain.split("."); 296 | var out = []; 297 | for (var i=0; i < domain_array.length; ++i) { 298 | var s = domain_array[i]; 299 | try{ 300 | out.push( 301 | s.match(/[^A-Za-z0-9-]/) ? 302 | "xn--" + this.encode(s) : 303 | s 304 | ); 305 | }catch(E){out.push(s);} 306 | } 307 | return out.join("."); 308 | }).bind(this); 309 | 310 | this.ToUnicode = (function ( domain ) { 311 | var domain_array = domain.split("."); 312 | var out = []; 313 | for (var i=0; i < domain_array.length; ++i) { 314 | var s = domain_array[i]; 315 | try{ 316 | out.push( 317 | s.match(/^xn--/) ? 318 | this.decode(s.slice(4)) : 319 | s 320 | ); 321 | }catch(E){out.push(s);} 322 | } 323 | return out.join("."); 324 | }).bind(this); 325 | }(); -------------------------------------------------------------------------------- /drupal7-module/dns.module: -------------------------------------------------------------------------------- 1 | array( 8 | 'title' => t('Administer DNS Manager'), 9 | 'description' => t('Perform administration tasks for DNS module.'), 10 | ), 11 | 'access dns' => array( 12 | 'title' => t('Access DNS contents'), 13 | 'description' => t('Use DNS Manager module.'), 14 | ) 15 | ); 16 | } 17 | 18 | function dns_menu() { 19 | $items = array(); 20 | 21 | $items['admin/config/content/dns'] = array( 22 | 'title' => 'DNS Manager', 23 | 'description' => 'Configuration for DNS Manager module', 24 | 'page callback' => 'drupal_get_form', 25 | 'page arguments' => array('dns_config_form'), 26 | 'access arguments' => array('administer dns'), 27 | 'type' => MENU_NORMAL_ITEM, 28 | ); 29 | 30 | $items['dns'] = array( 31 | 'title' => t('DNS Manager'), 32 | 'description' => 'Manage DNS records', 33 | 'page callback' => 'dns_domain_list', 34 | 'access arguments' => array('access dns'), 35 | 'type' => MENU_NORMAL_ITEM, 36 | 'menu_name' => 'navigation' 37 | ); 38 | 39 | $items['dns/list'] = array( 40 | 'title' => t('List'), 41 | 'page callback' => 'dns_domain_list', 42 | 'type' => MENU_DEFAULT_LOCAL_TASK, 43 | 'access arguments' => array('access dns'), 44 | 'parent' => 'dns', 45 | 'weight'=>-10, 46 | 'menu_name' => 'navigation' 47 | ); 48 | 49 | $items['dns/add'] = array( 50 | 'title' => t('Add'), 51 | 'page callback' => 'drupal_get_form', 52 | 'page arguments' => array('dns_domain_add_form'), 53 | 'type' => MENU_LOCAL_TASK, 54 | 'access arguments' => array('access dns'), 55 | 'parent' => 'dns', 56 | 'menu_name' => 'navigation' 57 | ); 58 | 59 | $items['dns/delete'] = array( 60 | 'page callback' => 'dns_domain_delete', 61 | 'access arguments' => array('access dns'), 62 | 'type' => MENU_CALLBACK, 63 | 'parent' => 'dns', 64 | 'menu_name' => 'navigation' 65 | ); 66 | 67 | $items['dns/edit'] = array( 68 | 'title' => t('Edit domain'), 69 | 'page callback' => 'dns_domain_edit', 70 | 'access arguments' => array('access dns'), 71 | 'type' => MENU_CALLBACK, 72 | 'parent' => 'dns', 73 | 'menu_name' => 'navigation' 74 | ); 75 | 76 | $items['whois'] = array( 77 | 'title' => t('Whois'), 78 | 'page callback' => 'dns_whois', 79 | 'access arguments' => array('access content'), 80 | 'type' => MENU_NORMAL_ITEM, 81 | 'menu_name' => 'navigation' 82 | ); 83 | 84 | $items['dns/remove-record'] = array( 85 | 'page callback' => 'dns_record_remove', 86 | 'access arguments' => array('access dns'), 87 | 'type' => MENU_CALLBACK, 88 | 'parent' => 'dns', 89 | 'menu_name' => 'navigation' 90 | ); 91 | 92 | return $items; 93 | } 94 | 95 | function dns_form_user_register_form_alter(&$form, &$form_state, $form_id) { 96 | // Modification for the form with the given form ID goes here. For example, if 97 | // FORM_ID is "user_register_form" this code would run only on the user 98 | // registration form. 99 | 100 | $form['first_name'] = array( 101 | '#type' => 'textfield', 102 | '#title' => t('First Name'), 103 | '#size' => 80, 104 | '#description' => t('Provide Your first name'), 105 | '#required' => TRUE 106 | ); 107 | 108 | $form['last_name'] = array( 109 | '#type' => 'textfield', 110 | '#title' => t('Last Name'), 111 | '#size' => 80, 112 | '#description' => t('Provide Your last name'), 113 | '#required' => TRUE 114 | ); 115 | } 116 | 117 | function dns_user_presave(&$edit, $account, $category) { 118 | if (isset($edit['first_name'])) { 119 | $edit['data']['first_name'] = $edit['first_name']; 120 | } 121 | if (isset($edit['last_name'])) { 122 | $edit['data']['last_name'] = $edit['last_name']; 123 | } 124 | } 125 | 126 | 127 | function dns_form_user_profile_form_alter(&$form, &$form_state, $form_id) { 128 | // Modification for the form with the given form ID goes here. For example, if 129 | // FORM_ID is "user_register_form" this code would run only on the user 130 | // registration form. 131 | $form['first_name'] = array( 132 | '#type' => 'textfield', 133 | '#title' => t('First Name'), 134 | '#size' => 80, 135 | '#default_value' => $form["#user"]->data["first_name"], 136 | '#description' => t('Provide Your first name'), 137 | '#required' => TRUE 138 | ); 139 | 140 | $form['last_name'] = array( 141 | '#type' => 'textfield', 142 | '#title' => t('Last Name'), 143 | '#size' => 80, 144 | '#default_value' => $form["#user"]->data["last_name"], 145 | '#description' => t('Provide Your last name'), 146 | '#required' => TRUE 147 | ); 148 | } 149 | 150 | function dns_domain_list(){ 151 | global $user; 152 | 153 | //echo "
".print_r($user,1)."
"; 154 | 155 | $header = array(array("data"=>"#","width"=>"30"), t("Zone name"),array("data"=>" ","width"=>"50")); 156 | $rows = array(); 157 | 158 | $domains = load_domains(); 159 | 160 | if($domains){ 161 | for($i=0;$i'.l($domains[$i]["_id"],"dns/edit",array("query"=>array("domain"=>$domains[$i]["_id"]))).'', 165 | array( 166 | "data"=>l(t("Delete"),"dns/delete",array("query"=>array("domain"=>$domains[$i]["_id"]))), 167 | "style"=>"text-align: center", 168 | "onclick"=>"return confirm('Are you sure?')" 169 | ) 170 | ); 171 | } 172 | } 173 | 174 | return theme_table(array( 175 | "header"=>$header, 176 | "rows"=>$rows, 177 | "attributes"=>array(), 178 | "caption"=>"Domain list for ".htmlspecialchars($user->name)."", 179 | "colgroups"=>array(), 180 | "sticky"=>true, 181 | "empty"=>t("No domains added, click ".l("here","dns/add")." to add one") 182 | )); 183 | } 184 | 185 | function dns_domain_delete(){ 186 | global $user; 187 | $domain = trim($_GET["domain"]); 188 | if(!$domain){ 189 | drupal_set_message(t("Invalid request"), 'error'); 190 | drupal_goto("dns"); 191 | } 192 | 193 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/remove?user=".urlencode($user->name)."&domain=".urlencode($domain))){ 194 | $response = @json_decode($data, true); 195 | if($response["status"]!="OK" || !$response["data"]){ 196 | form_set_error('dns_domain_name', $response["errormsg"]?$response["errormsg"]:t('Can\'t delete this domain - domain already removed or not listed')); 197 | }else{ 198 | drupal_set_message(t("Domain removed")); 199 | } 200 | }else{ 201 | form_set_error('dns_domain_name', t('Failed to check domain name')); 202 | } 203 | 204 | drupal_goto("dns"); 205 | } 206 | 207 | function dns_domain_edit(){ 208 | drupal_set_title("DNS Manager"); 209 | $domain = trim($_GET["domain"]); 210 | $time_start = microtime(true); 211 | $data = load_domain_info($domain); 212 | $time_end = microtime(true); 213 | $time = $time_end - $time_start; 214 | 215 | $types = array("A"=>"IPv4 address","AAAA"=>"IPv6 address", "CNAME"=>"Domain alias", "MX"=>"Mail server", "NS"=>"Name server", "SRV"=>"Service description", "WEBFWD"=>"Domain Rewrite"); 216 | 217 | $header = array(array("data"=>"#","width"=>"30"), t("Name"), t("TTL"), t("Value"), array("data"=>" ","width"=>"50")); 218 | $rows = array(); 219 | 220 | $tables = array( 221 | "A"=>"", 222 | "AAAA"=>"", 223 | "CNAME"=>"", 224 | "WEBFWD"=>"", 225 | "MX"=>"", 226 | "NS"=>"", 227 | "SRV"=>"" 228 | ); 229 | if($data){ 230 | 231 | foreach($data as $type => $block){ 232 | if(count($block)){ 233 | $i=0; 234 | $rows = array(); 235 | if(is_array($block)){ 236 | foreach($block as $row){ 237 | 238 | $value = ""; 239 | switch($type){ 240 | case "SRV": 241 | $value = join(" : ", $row["value"]); 242 | break; 243 | default: 244 | $srv = array_pop($row["value"]); 245 | array_unshift($row["value"], $srv); 246 | $value = join(" ", $row["value"]); 247 | } 248 | 249 | $rows[] = array( 250 | ++$i, 251 | htmlspecialchars($row["name"]!="@"?$row["name"]:$domain), 252 | intval($row["ttl"]), 253 | $value, 254 | array( 255 | "data"=>$row["frozen"]?"Informational":l("Delete", "dns/remove-record",array( 256 | "query"=>array( 257 | "row"=>intval($row["id"]), 258 | "domain"=>htmlspecialchars($domain) 259 | ) 260 | )), 261 | "style"=>"text-align: center", 262 | "onclick"=>$row["frozen"]?"":"return confirm('Are you sure?')" 263 | ) 264 | 265 | ); 266 | } 267 | 268 | 269 | if($i){ 270 | $tables[$type] = theme_table(array( 271 | "header"=>$header, 272 | "rows"=>$rows, 273 | "attributes"=>array(), 274 | "caption"=>"{$types["$type"]}".($type!="WEBFWD"?" ($type)":"")." records for ".htmlspecialchars($domain)."", 275 | "colgroups"=>array(), 276 | "empty"=>t("No records added") 277 | )); 278 | } 279 | } 280 | } 281 | } 282 | } 283 | 284 | $table = join("\n", $tables); 285 | 286 | $records_field = theme_fieldset(array( 287 | "element"=> array( 288 | "#title"=>"DNS records for ".htmlspecialchars($domain)."", 289 | "#description"=>"

Here are listed all the DNS records of ".htmlspecialchars($domain).". To use the DNS service, set the nameservers for your domain to:

  • ns11.node.ee
  • ns22.node.ee
", 290 | "#children"=>$table 291 | ) 292 | )); 293 | 294 | $add_form = drupal_render(drupal_get_form("dns_record_add_form")); 295 | 296 | return $records_field.$add_form; 297 | } 298 | 299 | function dns_record_add_form($form, &$form_state){ 300 | $domain = trim($_GET["domain"]); 301 | 302 | $form['general'] = array( 303 | '#type' => 'fieldset', 304 | '#title' => t('Add new record'), 305 | "#description"=>t(sprintf("Add a new DNS record for %s",htmlspecialchars($domain))), 306 | "#collapsible"=>TRUE, 307 | "#collapsed"=>!form_get_errors()?TRUE:FALSE 308 | ); 309 | 310 | $form['general']['name'] = array( 311 | '#type' => 'textfield', 312 | '#title' => t('Name'), 313 | '#size' => 80, 314 | '#default_value'=>"@", 315 | '#description' => t('Subdomain, for example www, unicode characters are allowed. Use @ for '.htmlspecialchars($domain).'
Regular expressions and wildcards are allowed. Enclose regular expressions in slashes (/regexp+/).'), 316 | '#required' => TRUE 317 | ); 318 | 319 | $form['general']['type'] = array( 320 | '#type' => 'select', 321 | '#title' => t('Type'), 322 | '#options' => array("A"=>"IP address", "CNAME"=>"Domain alias", "MX"=>"Mail server", "NS"=>"Name server", "SRV"=>"Service description", "WEBFWD"=>"Domain Rewrite"), 323 | '#default_value'=>"A", 324 | '#description' => t('Select record type'), 325 | '#required' => TRUE 326 | ); 327 | 328 | $form['general']['ttl'] = array( 329 | '#type' => 'textfield', 330 | '#title' => t('TTL'), 331 | '#size' => 10, 332 | '#default_value'=>"60", 333 | '#description' => t('Time To Live value, min. 1'), 334 | '#required' => FALSE 335 | ); 336 | 337 | $form['general']['priority'] = array( 338 | '#type' => 'textfield', 339 | '#title' => t('Priority or port'), 340 | '#size' => 10, 341 | '#description' => t('Mail server priority or Service description port'), 342 | '#required' => FALSE 343 | ); 344 | 345 | $form['general']['value'] = array( 346 | '#type' => 'textfield', 347 | '#title' => t('Value'), 348 | '#size' => 80, 349 | '#description' => t('Value, for example 127.0.0.1 or an IPv6 address. Use full hostname for CNAME, MX etc. or @'), 350 | '#required' => TRUE 351 | ); 352 | 353 | $form['general']['information'] = array( 354 | '#markup'=> '

Wildcard support
When using wildcards or regular expressions in the name field, substitutions for value can be used. '. 355 | 'When using wildcards, $1 indicates the whole match, $2 first wildcard, $3 second wildcard etc. '. 356 | 'When using regular expressions, $1 indicates the first capture group, $2 second capture group etc.

'. 357 | '

For example
Name: users.*, Value: $2.example.com converts users.test.'.htmlspecialchars($domain).' to test.example.com.
'. 358 | 'Name: /^r/, Value: example.com converts all subdomains beginning with r to example.com.

' 359 | ); 360 | 361 | $form['general']['submit'] = array( 362 | '#type' => 'submit', 363 | '#value' => t('Add record'), 364 | ); 365 | 366 | return $form; 367 | } 368 | 369 | function dns_record_add_form_validate($form, &$form_state) { 370 | global $user; 371 | 372 | $domain = trim($_GET["domain"]); 373 | $errors = false; 374 | 375 | if(!trim($form_state['values']['name'])){ 376 | form_set_error('name', t('Name is required!')); 377 | $errors = true; 378 | } 379 | 380 | if(!in_array($form_state['values']['type'], array("A", "AAAA", "CNAME", "NS", "MX", "SRV", "WEBFWD"))){ 381 | form_set_error('type', t('Type is required!')); 382 | $errors = true; 383 | } 384 | 385 | mb_regex_encoding('UTF-8'); 386 | 387 | $name = trim($form_state['values']['name']); 388 | if(substr($name,0,1)!="/" || substr($name,-1)!="/"){ 389 | if(mb_ereg_match(".*[^[:alnum:]\.\-\*_@]", $name)){ 390 | form_set_error('name', t('Name includes illegal characters!')); 391 | $errors = true; 392 | } 393 | } 394 | 395 | $value = trim($form_state['values']['value']); 396 | if(mb_ereg_match('.*[^[:alnum:]\.\-\*\:\$@]', $value)){ 397 | form_set_error('value', t('Value includes illegal characters!')); 398 | $errors = true; 399 | } 400 | 401 | $type = strtoupper(trim($form_state['values']['type'])); 402 | if($type=="A"){ 403 | //.match(/^(?:[a-f0-9]{0,4}:){4,}[a-f0-9]{0,4}$/) 404 | $value = strtolower($value); 405 | $ip4 = $ip6 = false; 406 | 407 | if(!($ip4 = !!preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $value)) && 408 | !($ip6 = !!preg_match('/^(?:[a-f0-9]{0,4}:){4,}[a-f0-9]{0,4}$/', $value))){ 409 | form_set_error('value', t('Invalid IP address')); 410 | $errors = true; 411 | }; 412 | if($ip6){ 413 | $type="AAAA"; 414 | } 415 | } 416 | 417 | $ttl = abs(intval(trim($form_state['values']['ttl']))); 418 | 419 | $record = array( 420 | "name"=> $name, 421 | "type"=> $type, 422 | "ttl"=> $ttl?$ttl:60, 423 | "value"=> array($value) 424 | ); 425 | 426 | if($record["type"]=="MX" || $record["type"]=="SRV"){ 427 | $record["value"][] = abs(intval(trim($form_state['values']['priority']))); 428 | } 429 | 430 | if(!$errors){ 431 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/update?user=".urlencode($user->name)."&domain=".urlencode($domain),json_encode($record))){ 432 | $response = @json_decode($data, true); 433 | 434 | if($response["status"]!="OK" || !$response["data"]){ 435 | form_set_error(null, $response["errormsg"]?$response["errormsg"]:t('Error saving data')); 436 | } 437 | 438 | }else{ 439 | form_set_error(null, t('Error connecting to server')); 440 | } 441 | } 442 | 443 | } 444 | 445 | function dns_record_add_form_submit($form, &$form_state) { 446 | drupal_set_message(t("Record added")); 447 | drupal_goto('dns/edit', array("query"=>array("domain"=>$_GET["domain"]))); 448 | } 449 | 450 | function dns_record_remove(){ 451 | global $user; 452 | $domain = trim($_GET["domain"]); 453 | $record = intval($_GET["row"]); 454 | 455 | if(!$record || !$domain){ 456 | drupal_set_message(t("Invalid request"), 'error'); 457 | if(!$domain){ 458 | drupal_goto("dns"); 459 | }else{ 460 | drupal_goto("dns/edit", array("query"=>array("domain"=>$domain))); 461 | } 462 | } 463 | 464 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/remove-record?user=".urlencode($user->name)."&domain=".urlencode($domain)."&rid=".urlencode($record))){ 465 | $response = @json_decode($data, true); 466 | if($response["status"]!="OK" || !$response["data"]){ 467 | drupal_set_message($response["errormsg"]?$response["errormsg"]:t("Error saving data"), 'error'); 468 | drupal_goto("dns/edit", array("query"=>array("domain"=>$domain))); 469 | } 470 | 471 | }else{ 472 | drupal_set_message(t("Error connecting to server"), 'error'); 473 | drupal_goto("dns/edit", array("query"=>array("domain"=>$domain))); 474 | } 475 | 476 | drupal_set_message(t("Record removed")); 477 | drupal_goto("dns/edit", array("query"=>array("domain"=>$domain))); 478 | } 479 | 480 | function dns_domain_add_form($form, &$form_state){ 481 | global $user; 482 | 483 | $form['general'] = array( 484 | '#type' => 'fieldset', 485 | '#title' => t('Zone info') 486 | ); 487 | 488 | $form['general']['dns_domain_name'] = array( 489 | '#type' => 'textfield', 490 | '#title' => t('Zone name'), 491 | '#size' => 80, 492 | '#description' => t('The name of the domain (without www), example: bookweed.com, unicode characters are allowed'), 493 | '#required' => TRUE 494 | ); 495 | 496 | $form['general']['dns_default_ip'] = array( 497 | '#type' => 'textfield', 498 | '#title' => t('IP'), 499 | '#size' => 80, 500 | '#description' => t('IP for the domain (optional)'), 501 | '#required' => FALSE 502 | ); 503 | 504 | $form['general']['dns_ga_mx'] = array( 505 | '#type' => 'checkbox', 506 | '#title' => t('Use Google Apps e-mail servers (optional)'), 507 | '#description' => t('Set up MX records pointing to Google Apps e-mail servers') 508 | ); 509 | 510 | 511 | 512 | $form['contacts'] = array( 513 | '#type' => 'fieldset', 514 | '#title' => t('Contact information'), 515 | '#description' => t('Public contact information for this domain') 516 | ); 517 | 518 | $form['contacts']['fname'] = array( 519 | '#type' => 'textfield', 520 | '#title' => t('First name'), 521 | '#default_value' =>$user->data["first_name"], 522 | '#size' => 80, 523 | '#required' => TRUE 524 | ); 525 | 526 | $form['contacts']['lname'] = array( 527 | '#type' => 'textfield', 528 | '#title' => t('Last name'), 529 | '#default_value' =>$user->data["last_name"], 530 | '#size' => 80, 531 | '#required' => TRUE 532 | ); 533 | 534 | $form['contacts']['email'] = array( 535 | '#type' => 'textfield', 536 | '#title' => t('E-mail address'), 537 | '#default_value' =>$user->mail, 538 | '#size' => 80, 539 | '#required' => TRUE 540 | ); 541 | 542 | 543 | $form['submit'] = array( 544 | '#type' => 'submit', 545 | '#value' => t('Add zone'), 546 | ); 547 | 548 | return $form; 549 | } 550 | 551 | function dns_domain_add_form_validate($form, &$form_state) { 552 | global $user; 553 | 554 | $errors = false; 555 | $domain = mb_strtolower(trim($form_state['values']['dns_domain_name'])); 556 | $default_ip = mb_strtolower(trim($form_state['values']['dns_default_ip'])); 557 | $use_ga_mx = $form_state['values']['dns_ga_mx']; 558 | $fname = trim($form_state['values']['fname']); 559 | $lname = trim($form_state['values']['lname']); 560 | $email = trim($form_state['values']['email']); 561 | 562 | 563 | if(!$domain){ 564 | form_set_error('dns_domain_name', t('Required field!')); 565 | $errors = true; 566 | } 567 | 568 | if(!$fname){ 569 | form_set_error('fname', t('Required field!')); 570 | $errors = true; 571 | } 572 | 573 | if(!$lname){ 574 | form_set_error('lname', t('Required field!')); 575 | $errors = true; 576 | } 577 | 578 | mb_regex_encoding('UTF-8'); 579 | if(mb_ereg_match(".*[^[:alnum:]\.\-]", $domain)){ 580 | form_set_error('dns_domain_name', t('Domain name includes illegal characters!')); 581 | $errors = true; 582 | } 583 | 584 | if($default_ip && !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $default_ip)){ 585 | form_set_error('dns_default_ip', t('Invalid IP address!')); 586 | $errors = true; 587 | } 588 | 589 | if(!$errors){ 590 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/add?user=".urlencode($user->name). 591 | "&fname=".urlencode($fname). 592 | "&lname=".urlencode($lname). 593 | "&email=".urlencode($email). 594 | "&domain=".urlencode($domain). 595 | "&default_ip=".urlencode($default_ip). 596 | "&use_ga_mx=".urlencode($use_ga_mx?"true":"") 597 | )){ 598 | $response = @json_decode($data, true); 599 | if($response["status"]!="OK" || !$response["data"]){ 600 | form_set_error('dns_domain_name', $response["errormsg"]?$response["errormsg"]:t('This domain name is already listed on the system')); 601 | } 602 | }else{ 603 | form_set_error(null, t('Failed to check domain name')); 604 | } 605 | } 606 | 607 | } 608 | 609 | function dns_domain_add_form_submit($form, &$form_state) { 610 | drupal_set_message(t("Domain added")); 611 | $form_state['redirect'] = 'dns/list'; 612 | } 613 | 614 | 615 | function dns_config_form($form, &$form_state){ 616 | $form['dns_api_access_point'] = array( 617 | '#type' => 'textfield', 618 | '#title' => t('DNS API Access point'), 619 | '#default_value' => variable_get('dns_api_access_point', "http://node.ee/api/dns"), 620 | '#size' => 80, 621 | '#description' => t('The API access point URL'), 622 | '#required' => TRUE, 623 | ); 624 | return system_settings_form($form); 625 | } 626 | 627 | function dns_config_form_validate($form, &$form_state){ 628 | $dns_api = $form_state['values']['dns_api_access_point']; 629 | if (!trim($dns_api)){ 630 | form_set_error('dns_api_access_point', t('Required field')); 631 | } 632 | } 633 | 634 | function load_domain_info($domain){ 635 | global $user; 636 | 637 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/records?user=".urlencode($user->name)."&domain=".urlencode($domain))){ 638 | $response = @json_decode($data, true); 639 | if($response["status"]=="OK" && isset($response["data"])){ 640 | return (array)$response["data"]; 641 | } 642 | } 643 | 644 | return false; 645 | } 646 | 647 | function load_domains(){ 648 | global $user; 649 | 650 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/list?user=".urlencode($user->name))){ 651 | $response = @json_decode($data, true); 652 | if($response["status"]=="OK" && $response["data"]){ 653 | return $response["data"]; 654 | } 655 | } 656 | 657 | return false; 658 | } 659 | 660 | function dns_whois(){ 661 | return theme_form(array( 662 | "element"=>array( 663 | "#method"=>"get", 664 | "#action"=>url("whois"), 665 | "#children"=>theme_fieldset(array( 666 | "element"=>array( 667 | "#title"=>"Search for domain", 668 | "#description"=>"Search from a whois server whois.node.ee", 669 | "#children" => theme_textfield(array( 670 | "element"=>array( 671 | "#name"=>"domain", 672 | "#title"=>"Domain name", 673 | "#value"=>$_GET["domain"], 674 | "#size"=>105 675 | ) 676 | )).theme_button(array( 677 | "element"=>array( 678 | "#button_type"=>"submit", 679 | "#name"=>"Search", 680 | "#value"=>"Search" 681 | ) 682 | )).theme_button(array( 683 | "element"=>array( 684 | "#button_type"=>"submit", 685 | "#submit"=>false, 686 | "#name"=>"Reset", 687 | "#value"=>"Reset", 688 | "#attributes"=>array( 689 | "onclick"=>"window.location.href='".url("whois")."';return false;" 690 | ) 691 | ) 692 | )) 693 | ) 694 | )).dns_check_whois() 695 | ) 696 | )); 697 | } 698 | 699 | function dns_check_whois(){ 700 | global $user; 701 | 702 | $domain = mb_strtolower(trim($_GET["domain"])); 703 | if(!$domain)return ""; 704 | 705 | 706 | 707 | if($data = load_from_url(variable_get('dns_api_access_point', "http://node.ee/api/dns")."/whois?domain=".urlencode($domain))){ 708 | $response = @json_decode($data, true); 709 | if($response["status"]=="OK" && $response["data"]){ 710 | return theme_fieldset(array( 711 | "element"=>array( 712 | "#title"=>"Results", 713 | "#children"=>"
".$response["data"]."
" 714 | ) 715 | )); 716 | } 717 | } 718 | 719 | return false; 720 | } 721 | 722 | function load_from_url($url, $postBody = false){ 723 | $ch = curl_init(); 724 | $options = array(CURLOPT_URL => $url, 725 | CURLOPT_USERAGENT => BOT_USERAGENT, 726 | CURLOPT_FOLLOWLOCATION => true, 727 | CURLOPT_AUTOREFERER => true, 728 | CURLOPT_NOBODY => false, 729 | CURLOPT_RETURNTRANSFER => true, 730 | CURLOPT_SSL_VERIFYPEER => false, 731 | CURLOPT_HEADER => false, 732 | CURLOPT_CONNECTTIMEOUT => 10 733 | ); 734 | if($postBody){ 735 | $options[CURLOPT_POST] = true; 736 | $options[CURLOPT_POSTFIELDS] = $postBody; 737 | } 738 | 739 | curl_setopt_array($ch, $options); 740 | $content = curl_exec($ch); 741 | $err = curl_errno($ch); 742 | $errmsg = curl_error($ch); 743 | $header = curl_getinfo($ch); 744 | $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); 745 | curl_close($ch); 746 | 747 | //echo "
";
748 |     //print_r(array($err, $content, $errmsg, $header, $status));
749 | 
750 |     if($err || substr($status,0,1) != "2"){
751 |         return false;
752 |     }
753 | 
754 |     return $content;
755 | }


--------------------------------------------------------------------------------
/nodejs/DNS/dns-api.js:
--------------------------------------------------------------------------------
  1 | var mongo = require("mongodb"),
  2 |     punycode = require("./punycode"),
  3 |     ip2country = require("./IP/ip2country"),
  4 |     utillib = require("util"),
  5 |     crypto = require("crypto");
  6 | 
  7 | var default_ns = ["ns11.node.ee", "ns22.node.ee"],
  8 |     forward_host = "forwarder.node.ee";
  9 | 
 10 | module.exports = dnsapi = {
 11 |     
 12 |     allowed_types: ["A", "AAAA", "CNAME", "MX", "NS", "SRV", "WEBFWD"],
 13 |     
 14 |     credentials: {
 15 |         
 16 |         create: function(user_name, user_data, callback){
 17 |             user_name = (user_name || "").trim();
 18 |             if(!callback && typeof user_data == "function"){
 19 |                 callback = user_data;
 20 |                 user_data = false;
 21 |             }
 22 |             user_data = user_data || {};
 23 |             if(!user_name){
 24 |                 return callback(new Error("Username not specified"));
 25 |             }
 26 |             this.get(user_name, function(err, user){
 27 |                 if(user){
 28 |                     return callback(new Error("Username already exists"));
 29 |                 }
 30 |                 var secret = sha1(Date.now()+user_name);
 31 |                 dnsapi.db.openCollection("credentials", function(err, collection){
 32 |                     collection.insert({
 33 |                         _id: user_name,
 34 |                         secret: secret,
 35 |                         data: user_data
 36 |                     });
 37 |                 });
 38 |                 callback(null, {
 39 |                     user: user_name,
 40 |                     secret: secret
 41 |                 });
 42 |             });
 43 |             
 44 |         },
 45 |         
 46 |         get: function(user_name, callback){
 47 |             user_name = (user_name || "").trim();
 48 |             dnsapi.db.openCollection("credentials", function(err, collection){
 49 |                 if(err){
 50 |                     return callback(err);
 51 |                 }
 52 |                 collection.find({_id: user_name}, function(err, cursor){
 53 |                     if(err){
 54 |                         return callback(err);
 55 |                     }
 56 |                     cursor.toArray(function(err, users) {
 57 |                         return callback(null, users && users[0]);
 58 |                     });
 59 |                 });
 60 |             });
 61 |         },
 62 |         
 63 |         verify: function(user_name, secret, domain_name){
 64 |             
 65 |         }
 66 |     },
 67 |     
 68 |     zones: {
 69 | 
 70 |         add: function(zone_name, owner, options, callback){
 71 |             zone_name = punycode.ToUnicode(zone_name.trim().toLowerCase());
 72 | 
 73 |             options = options || {};
 74 | 
 75 |             if(zone_name.match(/[^\w\.\-\*@\u0081-\uFFFF]/)){
 76 |                 return callback(new Error("Invalid characters in name"));
 77 |             }
 78 | 
 79 |             this.find(zone_name, (function(err, zone){
 80 |                 if(err){
 81 |                     return callback(err);
 82 |                 }
 83 |                 
 84 |                 if(zone){
 85 |                     if(zone._id==zone_name || zone.owner != owner){
 86 |                         return callback(new Error("This domain name is already listed in the system"));
 87 |                     }
 88 |                 }
 89 |                 
 90 |                 if(options.default_ip && (options.default_ip = options.default_ip.trim()) && !options.default_ip.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)){
 91 |                     return callback(new Error("Invalid IP address"));
 92 |                 }
 93 |                 
 94 |                 var records = {
 95 |                     _gen: 2,
 96 |                     NS: [
 97 |                         {
 98 |                             name: "@",
 99 |                             ttl: 600,
100 |                             id: 1,
101 |                             value: [default_ns[0]],
102 |                             frozen: true
103 |                         },
104 |                         {
105 |                             name: "@",
106 |                             ttl: 600,
107 |                             id: 2,
108 |                             value: [default_ns[1]],
109 |                             frozen: true
110 |                         }
111 |                     ]
112 |                 };
113 |                 
114 |                 if(options.default_ip){
115 |                     // main A record
116 |                     records["A"] = records["A"] || [];
117 |                     records["A"].push({
118 |                         name: "@",
119 |                         ttl: 60,
120 |                         id: ++records._gen,
121 |                         value: [options.default_ip]
122 |                     });
123 |                     // www CNAME record
124 |                     records["CNAME"] = records["CNAME"] || [];
125 |                     records["CNAME"].push({
126 |                         name: "www",
127 |                         ttl: 60,
128 |                         id: ++records._gen,
129 |                         value: ["@"]
130 |                     });
131 |                 }
132 |                 
133 |                 if(options.use_ga_mx){
134 |                     var mx_servers = [
135 |                         ["aspmx.l.google.com", 10],
136 |                         ["alt1.aspmx.l.google.com", 20],
137 |                         ["alt2.aspmx.l.google.com", 20],
138 |                         ["aspmx2.googlemail.com", 30],
139 |                         ["aspmx3.googlemail.com", 30],
140 |                         ["aspmx4.googlemail.com", 30],
141 |                         ["aspmx5.googlemail.com", 30]
142 |                     ];
143 |                     records["MX"] = records["MX"] || [];
144 |                     for(var i=0; i=0){
416 |                     parts = record.value[0].split(":"),
417 |                     needed = 8-parts.length;
418 |                     record.value[0] = record.value[0].replace("::", Array(needed+2).join(":0")+":");
419 |                 }
420 |                 // zero pad left
421 |                 parts = record.value[0].split(":");
422 |                 for(var i=0; i<8; i++){
423 |                     parts[i] = Array(4 - parts[i].length + 1).join("0") + parts[i];
424 |                 }
425 |                 record.value[0] = parts.join(":");
426 |             }
427 | 
428 |             record.value[0] = record.value[0].replace("@", zone_name);
429 | 
430 |             if(countries){
431 |                 record.countries = countries;
432 |                 record.priority += 2;
433 |             }
434 |             
435 |             record.ttl = ttl;
436 |             
437 |             this.list(zone_name, owner, (function(err, records){
438 |                 if(err){
439 |                     return callback(err);
440 |                 }
441 |                 if(!records){
442 |                     return callback(null, false);
443 |                 }
444 | 
445 |                 if(!records._gen){
446 |                     record.id = records._gen = 1;
447 |                 }else{
448 |                     record.id = ++records._gen;
449 |                 }
450 | 
451 |                 if(!records[type]){
452 |                     records[type] = [];
453 |                 }
454 |                 
455 |                 records[type].push(record);
456 |                 records[type] = records[type].sort(function(a,b){
457 |                     return (a.priority || 0) - (b.priority || 0);
458 |                 });
459 |                 this.save(zone_name, owner, records, callback);
460 |             }).bind(this));
461 |         }
462 |     },
463 |     
464 |     resolve: function(hostname, type, req_ip, callback){
465 |         
466 |         hostname = punycode.ToUnicode(hostname.trim().toLowerCase());
467 |         
468 |         this.zones.find(hostname, (function(err, zone){
469 |             if(err){
470 |                 return callback(err);
471 |             }
472 |             
473 |             var response = {
474 |                 hostname: zone._id,
475 |                 answer: [],
476 |                 authority: [],
477 |                 additional:[]
478 |             }
479 |             
480 |             if(!zone){
481 |                 return callback(null, response);
482 |             }
483 |             
484 |             if(("."+hostname).substr(-(zone._id.length+1))=="."+zone._id){
485 |                 hostname = hostname.substr(0, hostname.length - zone._id.length - 1);
486 |                 if(!hostname){
487 |                     hostname = "@";
488 |                 }
489 |             }
490 |     
491 |             ip2country.resolve(req_ip, (function(err, country){
492 |                 if(err){
493 |                     return callback(err);
494 |                 }
495 |                 this.resolver.records(hostname, zone, type, country, response, callback);
496 |             }).bind(this));
497 |             
498 |         }).bind(this));
499 |     },
500 |     
501 |     resolver: {
502 |         
503 |         records: function(hostname, zone, type, country, response, callback){
504 |             var data, ns_pointer = hostname;
505 |             
506 |             if(!zone.records){
507 |                 return callback(null, false);
508 |             }
509 |             
510 |             // A
511 |             data = this.dig_records("A", hostname, country, zone);
512 |             if(data.length){
513 |                 if(type=="A" || type=="ANY"){
514 |                     response.answer = response.answer.concat(data);
515 |                 }
516 |             }
517 |             
518 |             // CNAME
519 |             if(type=="CNAME"){
520 |                 data = this.dig_records("CNAME", hostname, country, zone);
521 |                 if(data.length){
522 |                     response.answer = response.answer.concat(data);
523 |                 }
524 |             }
525 |             
526 |             // WEBFWD
527 |             data = this.dig_records("WEBFWD", hostname, country, zone, false);
528 |             if(data.length){
529 |                 if(type!="WEBFWD"){
530 |                     data.forEach(function(record){
531 |                         record.record.type = "CNAME";
532 |                         record.record.value[0] = forward_host; 
533 |                     });
534 |                 }
535 |                 response.answer = response.answer.concat(data);
536 |             }
537 |             
538 |             // AAAA
539 |             if(type=="AAAA" || type=="ANY"){
540 |                 data = this.dig_records("AAAA", hostname, country, zone);
541 |                 if(data.length){
542 |                     response.answer = response.answer.concat(data);
543 |                 }
544 |             }
545 |             
546 |             // MX
547 |             if(type=="MX" || type=="ANY"){
548 |                 data = this.dig_records("MX", hostname, country, zone);
549 |                 if(data.length){
550 |                     response.answer = response.answer.concat(data);
551 |                 }
552 |             }
553 |             
554 |             // SRV
555 |             if(type=="SRV" || type=="ANY"){
556 |                 data = this.dig_records("SRV", hostname, country, zone);
557 |                 if(data.length){
558 |                     response.answer = response.answer.concat(data);
559 |                 }
560 |             }
561 |             
562 |             // NS
563 |             // use last A record if available instead of hostname
564 |             if(response.answer.length && response.answer[response.answer.length-1].record.type == "A"){
565 |                  ns_pointer = response.answer[response.answer.length-1].hostname || hostname;
566 |             }
567 |             if(ns_pointer || type=="ANY" || type=="NS"){
568 |                 data = this.dig_records("NS", ns_pointer, country, zone, true);
569 |                 if(data.length){
570 |                     if(type=="NS" || type=="ANY"){
571 |                         response.answer = response.answer.concat(data);
572 |                     }else{
573 |                         response.authority = response.authority.concat(data);
574 |                     }
575 |                 }
576 |             }
577 |             
578 |             callback(null, response);
579 |         },
580 |         
581 |         dig_records: function(type, hostname, country, zone, skip_digging, hop_count){
582 |             var response = [], record, re, value, matches, records = zone.records, hop_host;
583 | 
584 |             if(!hop_count)hop_count = 0;
585 | 
586 |             hostname = this.check_internal(hostname, zone) || hostname;
587 |             if(!hostname)return response;
588 | 
589 |             for(var i=0, len = records[type] && records[type].length || 0; i5){
627 |                 return response;
628 |             }
629 |             
630 |             if(!response.length && type!="CNAME" && type!="WEBFWD"){
631 |                 response = this.dig_records("CNAME", hostname, country, zone, skip_digging, hop_count+1);
632 |             }else if(!skip_digging){
633 |                 for(var i=0, len=response.length;i=0){
635 |                         if(hop_host = this.check_internal(response[i].record.value[0], zone)){
636 |                             response = response.concat(this.dig_records("A", hop_host, country, zone, false, hop_count+1));
637 |                         }
638 |                     }
639 |                 }
640 |             }
641 |             
642 |             return response;
643 |         },
644 |         
645 |         check_internal: function(hostname, zone){
646 |             if(("."+hostname).substr(-(zone._id.length+1))=="."+zone._id){
647 |                 hostname = hostname.substr(0, hostname.length - zone._id.length - 1);
648 |                 if(!hostname){
649 |                     hostname = "@";
650 |                 }
651 |                 return hostname;
652 |             }
653 |             return hostname=="@"?"@":false;
654 |         }
655 |         
656 |     },
657 |     
658 |     db: {
659 |         server: new mongo.Db(
660 |             "dns", // db collection_name 
661 |             new mongo.Server(
662 |                 "localhost", // server 
663 |                 mongo.Connection.DEFAULT_PORT, //port
664 |                 {auto_reconnect: true}
665 |             )
666 |         ),
667 |     
668 |         db_connection: false,
669 |     
670 |         openDB: function(callback){
671 |             this.server.open((function(err, db){
672 |                 if(err){
673 |                     return callback(err);
674 |                 }
675 |                 this.db_connection = db;
676 |                 callback(null, this.db_connection);
677 |             }).bind(this));
678 |         },
679 |     
680 |         openCollection: function(collection_name, callback, err){
681 |             if(err){
682 |                 return callback(err);
683 |             }
684 |             if(this.db_connection){
685 |                 this.db_connection.createCollection(collection_name, (function(err, collection){
686 |                     if(err){
687 |                         return callback(err);
688 |                     }
689 |                     callback(null, collection)
690 |                 }).bind(this));
691 |             }else{
692 |                 this.openDB(this.openCollection.bind(this, collection_name, callback));
693 |             }
694 |         },
695 |         
696 |         closeDB: function(){
697 |             if(this.db_connection){
698 |                 this.db_connection.close();
699 |             }
700 |             ip2country.client.end();
701 |         }
702 |     }
703 | }
704 | 
705 | function normalizeRecord(name, zone_name, value){
706 |     name = (name || "").replace(/^[\s\.]+|[\s\.]+$/g,"").toLowerCase();
707 | 
708 |     if(!Array.isArray(value)){
709 |         value = [value];
710 |     }
711 |     
712 |     if(("."+name).substr(-(zone_name.length+1))=="."+zone_name){
713 |         name = name.substr(0, name.length - zone_name.length - 1);
714 |         if(!name){
715 |             name = "@";
716 |         }
717 |     }
718 |     
719 |     if(String(value[0]).trim().toLowerCase() == zone_name){
720 |         value[0] = "@";
721 |     }
722 |     
723 |     var result = {name: name, value: value, priority: 3};
724 |     if(name=="*"){
725 |         result.regexp = "^((.*))$";
726 |         result.priority = 9;
727 |     }else if(name.length>1 && name.charAt(0)=="/" && name.charAt(name.length-1)=="/"){
728 |         result.regexp = "("+name.substr(1, name.length-2)+")";
729 |         result.priority = 5;
730 |     }else if(name.indexOf("*")>=0){
731 |         result.regexp = "^("+name.replace(/([-\.])/g, "\\$1").replace(/\*/g,"(.*?)")+")$";
732 |         result.priority = 7;
733 |     }
734 |     
735 |     if(result.regexp){
736 |         try{
737 |             new RegExp(result.regexp); // fails if invalid
738 |         }catch(E){
739 |             return false;
740 |         }
741 |     }
742 |     
743 |     return result;
744 | }
745 | 
746 | function sha1(str){
747 |     var c = crypto.createHash("sha1");
748 |     c.update(str);
749 |     return c.digest("base64");
750 | }


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


--------------------------------------------------------------------------------