| 
									
										
										
										
											2017-10-02 12:45:33 -06:00
										 |  |  | (function () { | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | module.exports.ask = function (query, cb) { | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-06 18:31:59 -06:00
										 |  |  | var NOERROR = 0; | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | var NXDOMAIN = 3; | 
					
						
							|  |  |  | var REFUSED = 5; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  | function getRecords(db, qname, cb) { | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |   var delMe = {}; | 
					
						
							|  |  |  |   var dns = require('dns'); | 
					
						
							|  |  |  |   // SECURITY XXX TODO var dig = require('dig.js/dns-request');
 | 
					
						
							|  |  |  |   var count; | 
					
						
							|  |  |  |   var myRecords = db.records.slice(0).filter(function (r) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |     if ('string' !== typeof r.name) { | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO use IN in masterquest (or implement OR)
 | 
					
						
							|  |  |  |     // Only return single-level wildcard?
 | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |     if (qname === r.name || ('*.' + qname.split('.').slice(1).join('.')) === r.name) { | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |   function checkCount() { | 
					
						
							| 
									
										
										
										
											2017-10-23 22:02:03 -06:00
										 |  |  |     var ready; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |     count -= 1; | 
					
						
							| 
									
										
										
										
											2017-10-23 22:02:03 -06:00
										 |  |  |     ready = count <= 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ready) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     myRecords = myRecords.filter(function (r) { | 
					
						
							|  |  |  |       return !delMe[r.id]; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // There are a number of ways to interpret the wildcard rules
 | 
					
						
							|  |  |  |     var hasWild = false; | 
					
						
							|  |  |  |     var hasMatch = false; | 
					
						
							|  |  |  |     myRecords.some(function (r) { | 
					
						
							|  |  |  |       if (qname === r.name) { | 
					
						
							|  |  |  |         hasMatch = true; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if ('*' === r.name[0]) { | 
					
						
							|  |  |  |         hasWild = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (hasMatch) { | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |       myRecords = myRecords.filter(function (r) { | 
					
						
							| 
									
										
										
										
											2017-10-23 22:02:03 -06:00
										 |  |  |         if ('*' !== r.name[0]) { return true; } | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-23 22:02:03 -06:00
										 |  |  |     /* | 
					
						
							|  |  |  |     // no need to filter out records if wildcard is used
 | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       records = records.filter(function (r) { | 
					
						
							|  |  |  |         if ('*' === r.name[0]) { return true; } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cb(null, myRecords); | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function getRecord(r) { | 
					
						
							|  |  |  |     // TODO allow multiple records to be returned(?)
 | 
					
						
							|  |  |  |     return function (err, addresses) { | 
					
						
							|  |  |  |       if (err || !addresses.length) { | 
					
						
							|  |  |  |         r.id = r.id || Math.random(); | 
					
						
							|  |  |  |         delMe[r.id] = true; | 
					
						
							|  |  |  |       } else if (addresses.length > 1) { | 
					
						
							|  |  |  |         r._address = addresses[Math.floor(Math.random() * addresses.length)]; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         r._address = addresses[0]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       checkCount(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   count = myRecords.length; | 
					
						
							|  |  |  |   myRecords.forEach(function (r) { | 
					
						
							|  |  |  |     if (r.aname && !r.address) { | 
					
						
							|  |  |  |       if ('A' === r.type) { | 
					
						
							|  |  |  |         // SECURITY XXX TODO dig.resolveJson(query, opts);
 | 
					
						
							|  |  |  |         dns.resolve4(r.aname, getRecord(r)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if ('AAAA' === r.type) { | 
					
						
							|  |  |  |         // SECURITY XXX TODO dig.resolveJson(query, opts);
 | 
					
						
							|  |  |  |         dns.resolve6(r.aname, getRecord(r)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     checkCount(); | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!myRecords.length) { | 
					
						
							|  |  |  |     checkCount(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function dbToResourceRecord(r) { | 
					
						
							|  |  |  |   return { | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |     name: r.name | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |   , typeName: r.type // NS
 | 
					
						
							|  |  |  |   , className: 'IN' | 
					
						
							|  |  |  |   , ttl: r.ttl || 300 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // SOA
 | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |   , "primary": "ns1.yahoo.com" | 
					
						
							|  |  |  |   , "admin": "hostmaster.yahoo-inc.com" | 
					
						
							|  |  |  |   , "serial": 2017092539 | 
					
						
							|  |  |  |   , "refresh": 3600 | 
					
						
							|  |  |  |   , "retry": 300 | 
					
						
							|  |  |  |   , "expiration": 1814400 | 
					
						
							|  |  |  |   , "minimum": 600 | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // A, AAAA
 | 
					
						
							| 
									
										
										
										
											2017-10-12 12:48:09 -06:00
										 |  |  |   , address: -1 !== [ 'A', 'AAAA' ].indexOf(r.type) ? (r._address || r.address || r.value) : undefined | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // CNAME, NS, PTR || TXT
 | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |   , data: -1 !== [ 'CNAME', 'NS', 'PTR', 'TXT' ].indexOf(r.type) ? (r.data || r.value || r.values) : undefined | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // MX, SRV
 | 
					
						
							|  |  |  |   , priority: r.priority | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MX
 | 
					
						
							|  |  |  |   , exchange: r.exchange | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // SRV
 | 
					
						
							|  |  |  |   , weight: r.weight | 
					
						
							|  |  |  |   , port: r.port | 
					
						
							|  |  |  |   , target: r.target | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getNs(db, ds, results, cb) { | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |   console.log('[DEV] getNs entered with domains', ds); | 
					
						
							| 
									
										
										
										
											2017-10-09 14:07:45 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |   var d = ds.shift(); | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |   console.log('[DEV] trying another one', d); | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!d) { | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |     results.header.rcode = NXDOMAIN; | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |     cb(null, results); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var qn = d.id.toLowerCase(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |   return getRecords(db, qn, function (err, records) { | 
					
						
							|  |  |  |     if (err) { cb(err); return; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     records.forEach(function (r) { | 
					
						
							|  |  |  |       if ('NS' !== r.type) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var ns = { | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |         name: r.name | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |       , typeName: r.type // NS
 | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |       , className: r.class || 'IN' | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |       , ttl: r.ttl || 300 | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |       , data: r.data || r.value || r.address | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log('got NS record:'); | 
					
						
							|  |  |  |       console.log(r); | 
					
						
							|  |  |  |       console.log(ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO what if this NS is one of the NS?
 | 
					
						
							|  |  |  |       // return SOA record instead
 | 
					
						
							|  |  |  |       results.authority.push(ns); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |     if (!results.authority.length) { | 
					
						
							|  |  |  |       return getNs(db, ds, results, cb); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // d.vanityNs should only be vanity nameservers (pointing to this same server)
 | 
					
						
							|  |  |  |     if (d.vanityNs || results.authority.some(function (ns) { | 
					
						
							|  |  |  |       console.log('[debug] ns', ns); | 
					
						
							|  |  |  |       return -1 !== db.primaryNameservers.indexOf(ns.data.toLowerCase()); | 
					
						
							|  |  |  |     })) { | 
					
						
							|  |  |  |       results.authority.length = 0; | 
					
						
							|  |  |  |       results.authority.push(domainToSoa(db, d)); | 
					
						
							|  |  |  |       results.header.rcode = NXDOMAIN; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |     cb(null, results); | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  | function domainToSoa(db, domain) { | 
					
						
							|  |  |  |   var nameservers = domain.vanityNs || db.primaryNameservers; | 
					
						
							| 
									
										
										
										
											2017-10-09 14:07:45 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   var index = Math.floor(Math.random() * nameservers.length) % nameservers.length; | 
					
						
							|  |  |  |   var nameserver = nameservers[index]; | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |   return { | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |     name: domain.id | 
					
						
							|  |  |  |   , typeName: 'SOA' | 
					
						
							|  |  |  |   , className: 'IN' | 
					
						
							|  |  |  |   , ttl: domain.ttl || 60 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // nameserver -- select an NS at random if they're all in sync
 | 
					
						
							|  |  |  |   , primary: nameserver | 
					
						
							|  |  |  |   , name_server: nameserver | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // admin -- email address or domain for admin
 | 
					
						
							| 
									
										
										
										
											2017-10-09 14:07:45 -06:00
										 |  |  |   , admin: domain.admin || ('admin.' + domain.id) | 
					
						
							|  |  |  |   , email_addr: domain.admin || ('admin.' + domain.id) | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // serial -- the version, for cache-busting of secondary nameservers. suggested format: YYYYMMDDnn
 | 
					
						
							| 
									
										
										
										
											2017-10-09 14:07:45 -06:00
										 |  |  |   , serial: domain.serial || Math.round((domain.updatedAt || domain.createdAt || 0) / 1000) | 
					
						
							|  |  |  |   , sn: domain.serial || Math.round((domain.updatedAt || domain.createdAt || 0) / 1000) | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // refresh -- only used when nameservers following the DNS NOTIFY spec talk
 | 
					
						
							|  |  |  |   , refresh: domain.refresh || 1800 | 
					
						
							|  |  |  |   , ref: domain.refresh || 1800 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // retry -- only used when nameservers following the DNS NOTIFY spec talk
 | 
					
						
							|  |  |  |   , retry: domain.retry || 600 | 
					
						
							|  |  |  |   , ret: domain.retry || 600 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // expiration -- how long other nameservers should continue when the primary goes down
 | 
					
						
							|  |  |  |   , expiration: domain.expiration || 2419200 | 
					
						
							|  |  |  |   , ex: domain.expiration || 2419200 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // minimum -- how long to cache a non-existent domain (also the default ttl for BIND)
 | 
					
						
							|  |  |  |   , minimum: domain.minimum || 5 | 
					
						
							|  |  |  |   , nx: domain.minimum || 5 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  | function getSoa(db, domain, results, cb, answerSoa) { | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |   console.log('[DEV] getSoa entered'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |   if (!answerSoa) { | 
					
						
							|  |  |  |     results.authority.push(domainToSoa(db, domain)); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     results.answer.push(domainToSoa(db, domain)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   cb(null, results); | 
					
						
							|  |  |  |   return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 12:45:33 -06:00
										 |  |  | module.exports.query = function (input, query, cb) { | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |   /* | 
					
						
							|  |  |  |   var fs = require('fs'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   fs.readFile(input, 'utf8', function (err, text) { | 
					
						
							|  |  |  |     if (err) { cb(err); return; } | 
					
						
							|  |  |  |     var records; | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       records = JSON.parse(text); | 
					
						
							|  |  |  |     } catch(e) { cb(e); return; } | 
					
						
							| 
									
										
										
										
											2017-10-02 12:45:33 -06:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |   */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var db; | 
					
						
							|  |  |  |   var qname; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     db = require(input); | 
					
						
							|  |  |  |   } catch(e) { cb(e); return; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!Array.isArray(query.question) || query.question.length < 1) { | 
					
						
							|  |  |  |     cb(new Error("query is missing question section")); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (1 !== query.question.length) { | 
					
						
							|  |  |  |     cb(new Error("query should have exactly one question (for now)")); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!query.question[0] || 'string' !== typeof query.question[0].name) { | 
					
						
							|  |  |  |     cb(new Error("query's question section should exist and have a String name property")); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   qname = query.question[0].name.toLowerCase(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var results = { | 
					
						
							|  |  |  |     header: { | 
					
						
							|  |  |  |       id: query.header.id   // same as request
 | 
					
						
							|  |  |  |     , qr: 1 | 
					
						
							|  |  |  |     , opcode: 0             // pretty much always 0 QUERY
 | 
					
						
							|  |  |  |     , aa: 1                 // TODO right now we assume that if we have the record, we're authoritative
 | 
					
						
							|  |  |  |                             // but in reality we could be hitting a cache and then recursing on a cache miss
 | 
					
						
							|  |  |  |     , tc: 0 | 
					
						
							|  |  |  |     , rd: query.header.rd   // duh
 | 
					
						
							|  |  |  |     , ra: 0                 // will be changed by cli.norecurse
 | 
					
						
							| 
									
										
										
										
											2017-10-06 18:31:59 -06:00
										 |  |  |     , rcode: NOERROR        // 0 NOERROR, 3 NXDOMAIN, 5 REFUSED
 | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-09 14:07:45 -06:00
										 |  |  |   , question: [ query.question[0] ], answer: [], authority: [], additional: [] | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |   function getNsAndSoa(getNsAlso, answerSoa) { | 
					
						
							|  |  |  |     // If the query is www.foo.delegated.example.com
 | 
					
						
							|  |  |  |     // and we have been delegated delegated.example.com
 | 
					
						
							|  |  |  |     // and delegated.example.com exists
 | 
					
						
							|  |  |  |     // but foo.delegated.example.com does not exist
 | 
					
						
							|  |  |  |     // what's the best strategy for returning the record?
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // What does PowerDNS do in these situations?
 | 
					
						
							|  |  |  |     // https://doc.powerdns.com/md/authoritative/backend-generic-mysql/
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // How to optimize:
 | 
					
						
							|  |  |  |     // Assume that if a record is being requested, it probably exists
 | 
					
						
							|  |  |  |     // (someone has probably published it somewhere)
 | 
					
						
							|  |  |  |     // If the record doesn't exist, then see if any of the domains are managed
 | 
					
						
							|  |  |  |     // [ 'www.john.smithfam.net', 'john.smithfam.net', 'smithfam.net', 'net' ]
 | 
					
						
							|  |  |  |     // Then if one of those exists, return the SOA record with NXDOMAIN
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var qarr = qname.split('.'); | 
					
						
							|  |  |  |     var qnames = []; | 
					
						
							|  |  |  |     while (qarr.length) { | 
					
						
							|  |  |  |       qnames.push(qarr.join('.').toLowerCase()); | 
					
						
							|  |  |  |       qarr.shift(); // first
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |     console.log('[DEV] getNsAlso?', getNsAlso); | 
					
						
							|  |  |  |     console.log('[DEV] answerSoa?', answerSoa); | 
					
						
							|  |  |  |     console.log('[DEV] qnames'); | 
					
						
							|  |  |  |     console.log(qnames); | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |     var myDomains = db.domains.filter(function (d) { | 
					
						
							|  |  |  |       return -1 !== qnames.indexOf(d.id.toLowerCase()); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // this should result in a REFUSED status
 | 
					
						
							|  |  |  |     if (!myDomains.length) { | 
					
						
							|  |  |  |       // REFUSED will have no records, so we could still recursion, if enabled
 | 
					
						
							|  |  |  |       results.header.rcode = REFUSED; | 
					
						
							|  |  |  |       cb(null, results); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     myDomains.sort(function (d1, d2) { | 
					
						
							|  |  |  |       if (d1.id.length > d2.id.length) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (d1.id.length < d2.id.length) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return 0; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     //console.log('sorted domains', myDomains);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!getNsAlso) { | 
					
						
							|  |  |  |       return getSoa(db, myDomains[0], results, cb, answerSoa); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |     return getNs(db, /*myDomains.slice(0)*/qnames.map(function (qn) { return { id: qn }; }), results, function (err, results) { | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |       //console.log('[DEV] getNs complete');
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (err) { cb(err, results); return; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // has NS records (or SOA record if NS records match the server itself)
 | 
					
						
							|  |  |  |       if (results.authority.length) { | 
					
						
							|  |  |  |         console.log(results); cb(null, results); return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // myDomains was sorted such that the longest was first
 | 
					
						
							|  |  |  |       return getSoa(db, myDomains[0], results, cb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ('SOA' === query.question[0].typeName) { | 
					
						
							|  |  |  |     return getNsAndSoa(false, true); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //console.log('[DEV] QUERY NAME', qname);
 | 
					
						
							|  |  |  |   return getRecords(db, qname, function (err, someRecords) { | 
					
						
							|  |  |  |     var myRecords; | 
					
						
							|  |  |  |     var nsRecords = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |     if (err) { cb(err); return; } | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |     // There are two special cases
 | 
					
						
							|  |  |  |     // NS records are returned as ANSWER for NS and ANY, and as AUTHORITY when an externally-delegated domain would return an SOA (no records)
 | 
					
						
							|  |  |  |     // SOA records are returned as ANSWER for SOA and ANY, and as AUTHORITY when no records are found, but the domain is controlled here
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 02:26:38 -06:00
										 |  |  |     console.log("[DEV] has", someRecords.length, "records"); | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |     // filter out NS (delegation) records, unless that is what is intended
 | 
					
						
							|  |  |  |     someRecords = someRecords.filter(function (r) { | 
					
						
							|  |  |  |       // If it's not an NS record, it's a potential result
 | 
					
						
							|  |  |  |       if ('NS' !== r.type && 'NS' !== r.typeName) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |       console.log("It's NS"); | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // If it's a vanity NS, it's not a valid NS for lookup
 | 
					
						
							|  |  |  |       if (-1 !== db.primaryNameservers.indexOf(r.data.toLowerCase())) { | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |         console.log("It's a vanity NS"); | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |         return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |       // If the query was for NS, it's a potential result
 | 
					
						
							|  |  |  |       if ('ANY' === query.question[0].typeName || 'NS' === query.question[0].typeName) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |       nsRecords.push(r); | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     myRecords = someRecords; | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |     if (255 !== query.question[0].type && 'ANY' !== query.question[0].typeName) { | 
					
						
							|  |  |  |       myRecords = myRecords.filter(function (r) { | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 19:17:34 -06:00
										 |  |  |         return ((r.type && r.type === query.question[0].type) | 
					
						
							|  |  |  |           || (r.type && r.type === query.question[0].typeName) | 
					
						
							|  |  |  |           || (r.typeName && r.typeName === query.question[0].typeName) | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |     if (myRecords.length) { | 
					
						
							|  |  |  |       myRecords.forEach(function (r) { | 
					
						
							|  |  |  |         results.answer.push(dbToResourceRecord(r)); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       results.header.rcode = NOERROR; | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |       //console.log('[DEV] ANSWER results', results);
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 12:14:44 -06:00
										 |  |  |       if (255 === query.question[0].type || 'ANY' === query.question[0].typeName) { | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |         getNsAndSoa(false, true); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-10-06 15:34:36 -06:00
										 |  |  |       cb(null, results); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |     else if (nsRecords.length) { | 
					
						
							|  |  |  |       nsRecords.forEach(function (r) { | 
					
						
							|  |  |  |         results.authority.push(dbToResourceRecord(r)); | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-10-18 18:24:41 -06:00
										 |  |  |       results.header.rcode = NOERROR; | 
					
						
							|  |  |  |       //console.log('[DEV] AUTHORITY results', results);
 | 
					
						
							|  |  |  |       cb(null, results); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-09 15:54:18 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 11:37:04 -06:00
										 |  |  |     console.log("[DEV] Gonna get NS and SOA"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // !myRecords.length
 | 
					
						
							|  |  |  |     getNsAndSoa(true); | 
					
						
							| 
									
										
										
										
											2017-10-02 12:45:33 -06:00
										 |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }()); |