forked from coolaj86/digd.js
		
	making room for API
This commit is contained in:
		
							parent
							
								
									b5ec1f7982
								
							
						
					
					
						commit
						59980dfa60
					
				
							
								
								
									
										13
									
								
								bin/digd.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								bin/digd.js
									
									
									
									
									
								
							| @ -373,7 +373,7 @@ cli.main(function (args, cli) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     require('../lib/dns-store').query(path.resolve(cli.input), query, function (err, resp) { | ||||
|     function respondWithResults(err, resp) { | ||||
| 
 | ||||
|       if (err) { console.log('[DEV] answer not found in local db, recursing'); console.error(err); recurse(); return; } | ||||
| 
 | ||||
| @ -383,7 +383,16 @@ cli.main(function (args, cli) { | ||||
|       if (!cli.norecurse && query.header.rd) { resp.header.ra = 1; } | ||||
| 
 | ||||
|       sendResponse(resp); | ||||
|     }); | ||||
|     } | ||||
| 
 | ||||
|     var engine; | ||||
|     try { | ||||
|       engine = require('../lib/store.json.js').create({ filepath: path.resolve(cli.input) }); | ||||
|     } catch(e) { | ||||
|       respondWithResults(e); | ||||
|       return; | ||||
|     } | ||||
|     require('../lib/digd.js').query(engine, query, respondWithResults); | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
|  | ||||
| @ -10,110 +10,102 @@ var NOERROR = 0; | ||||
| var NXDOMAIN = 3; | ||||
| var REFUSED = 5; | ||||
| 
 | ||||
| function getRecords(db, qname, cb) { | ||||
| function getRecords(engine, qname, cb) { | ||||
|   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) { | ||||
| 
 | ||||
|     if ('string' !== typeof r.name) { | ||||
|       return false; | ||||
|     } | ||||
|   return engine.getRecords({ name: qname }, function (err, myRecords) { | ||||
|     if (err) { cb(err); return; } | ||||
| 
 | ||||
|     // TODO use IN in masterquest (or implement OR)
 | ||||
|     // Only return single-level wildcard?
 | ||||
|     if (qname === r.name || ('*.' + qname.split('.').slice(1).join('.')) === r.name) { | ||||
|       return true; | ||||
|     } | ||||
|   }); | ||||
|     function checkCount() { | ||||
|       var ready; | ||||
| 
 | ||||
|   function checkCount() { | ||||
|     var ready; | ||||
|       count -= 1; | ||||
|       ready = count <= 0; | ||||
| 
 | ||||
|     count -= 1; | ||||
|     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 (!ready) { | ||||
|         return; | ||||
|       } | ||||
|       if ('*' === r.name[0]) { | ||||
|         hasWild = true; | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     if (hasMatch) { | ||||
|       myRecords = myRecords.filter(function (r) { | ||||
|         if ('*' !== r.name[0]) { return true; } | ||||
|         return !delMe[r.id]; | ||||
|       }); | ||||
|     } | ||||
|     /* | ||||
|     // no need to filter out records if wildcard is used
 | ||||
|     else { | ||||
|       records = records.filter(function (r) { | ||||
|         if ('*' === r.name[0]) { return true; } | ||||
| 
 | ||||
|       // 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; | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|     */ | ||||
| 
 | ||||
|     cb(null, myRecords); | ||||
|   } | ||||
| 
 | ||||
|   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]; | ||||
|       if (hasMatch) { | ||||
|         myRecords = myRecords.filter(function (r) { | ||||
|           if ('*' !== r.name[0]) { return true; } | ||||
|         }); | ||||
|       } | ||||
|       /* | ||||
|       // 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); | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|     }; | ||||
|   } | ||||
|     }); | ||||
| 
 | ||||
|   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; | ||||
|       } | ||||
|     if (!myRecords.length) { | ||||
|       checkCount(); | ||||
|     } | ||||
| 
 | ||||
|     checkCount(); | ||||
|   }); | ||||
| 
 | ||||
|   if (!myRecords.length) { | ||||
|     checkCount(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function dbToResourceRecord(r) { | ||||
|   return { | ||||
|     name: r.name | ||||
|   , typeName: r.type // NS
 | ||||
|   , typeName: r.typeName || r.type // NS
 | ||||
|   , className: 'IN' | ||||
|   , ttl: r.ttl || 300 | ||||
| 
 | ||||
| @ -147,7 +139,7 @@ function dbToResourceRecord(r) { | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function getNs(db, ds, results, cb) { | ||||
| function getNs(engine, ds, results, cb) { | ||||
|   console.log('[DEV] getNs entered with domains', ds); | ||||
| 
 | ||||
|   var d = ds.shift(); | ||||
| @ -161,7 +153,7 @@ function getNs(db, ds, results, cb) { | ||||
| 
 | ||||
|   var qn = d.id.toLowerCase(); | ||||
| 
 | ||||
|   return getRecords(db, qn, function (err, records) { | ||||
|   return getRecords(engine, qn, function (err, records) { | ||||
|     if (err) { cb(err); return; } | ||||
| 
 | ||||
|     records.forEach(function (r) { | ||||
| @ -187,16 +179,16 @@ function getNs(db, ds, results, cb) { | ||||
|     }); | ||||
| 
 | ||||
|     if (!results.authority.length) { | ||||
|       return getNs(db, ds, results, cb); | ||||
|       return getNs(engine, 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()); | ||||
|       return -1 !== engine.primaryNameservers.indexOf(ns.data.toLowerCase()); | ||||
|     })) { | ||||
|       results.authority.length = 0; | ||||
|       results.authority.push(domainToSoa(db, d)); | ||||
|       results.authority.push(domainToSoa(engine.primaryNameservers, d)); | ||||
|       results.header.rcode = NXDOMAIN; | ||||
|     } | ||||
|     cb(null, results); | ||||
| @ -204,8 +196,8 @@ function getNs(db, ds, results, cb) { | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function domainToSoa(db, domain) { | ||||
|   var nameservers = domain.vanityNs || db.primaryNameservers; | ||||
| function domainToSoa(primaryNameservers, domain) { | ||||
|   var nameservers = domain.vanityNs || primaryNameservers; | ||||
| 
 | ||||
|   var index = Math.floor(Math.random() * nameservers.length) % nameservers.length; | ||||
|   var nameserver = nameservers[index]; | ||||
| @ -245,20 +237,20 @@ function domainToSoa(db, domain) { | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function getSoa(db, domain, results, cb, answerSoa) { | ||||
| function getSoa(primaryNameservers, domain, results, cb, answerSoa) { | ||||
|   console.log('[DEV] getSoa entered'); | ||||
| 
 | ||||
|   if (!answerSoa) { | ||||
|     results.authority.push(domainToSoa(db, domain)); | ||||
|     results.authority.push(domainToSoa(primaryNameservers, domain)); | ||||
|   } else { | ||||
|     results.answer.push(domainToSoa(db, domain)); | ||||
|     results.answer.push(domainToSoa(primaryNameservers, domain)); | ||||
|   } | ||||
| 
 | ||||
|   cb(null, results); | ||||
|   return; | ||||
| } | ||||
| 
 | ||||
| module.exports.query = function (input, query, cb) { | ||||
| module.exports.query = function (engine, query, cb) { | ||||
|   /* | ||||
|   var fs = require('fs'); | ||||
| 
 | ||||
| @ -271,11 +263,7 @@ module.exports.query = function (input, query, cb) { | ||||
|   }); | ||||
|   */ | ||||
| 
 | ||||
|   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")); | ||||
| @ -337,46 +325,48 @@ module.exports.query = function (input, query, cb) { | ||||
|     console.log('[DEV] answerSoa?', answerSoa); | ||||
|     console.log('[DEV] qnames'); | ||||
|     console.log(qnames); | ||||
|     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; | ||||
|     } | ||||
|     return engine.getSoas({ names: qnames}, function (err, myDomains) { | ||||
|       console.log('[SOA] looking for', qnames, 'and proudly serving', err, myDomains); | ||||
|       if (err) { cb(err); 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); | ||||
|     } | ||||
| 
 | ||||
|     return getNs(db, /*myDomains.slice(0)*/qnames.map(function (qn) { return { id: qn }; }), results, function (err, results) { | ||||
|       //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; | ||||
|       // 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 was sorted such that the longest was first
 | ||||
|       return getSoa(db, myDomains[0], results, cb); | ||||
|       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(engine.primaryNameservers, myDomains[0], results, cb, answerSoa); | ||||
|       } | ||||
| 
 | ||||
|       return getNs(engine, /*myDomains.slice(0)*/qnames.map(function (qn) { return { id: qn }; }), results, function (err, results) { | ||||
|         //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(engine.primaryNameservers, myDomains[0], results, cb); | ||||
| 
 | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
| @ -385,7 +375,7 @@ module.exports.query = function (input, query, cb) { | ||||
|   } | ||||
| 
 | ||||
|   //console.log('[DEV] QUERY NAME', qname);
 | ||||
|   return getRecords(db, qname, function (err, someRecords) { | ||||
|   return getRecords(engine, qname, function (err, someRecords) { | ||||
|     var myRecords; | ||||
|     var nsRecords = []; | ||||
| 
 | ||||
| @ -410,7 +400,7 @@ module.exports.query = function (input, query, cb) { | ||||
|       // NOTE: I think that the issue here is EXTERNAL vs INTERNAL vanity NS
 | ||||
|       // We _should_ reply for EXTERNAL vanity NS... but not when it's listed on the SOA internally?
 | ||||
|       // It's surrounding the problem of what if I do sub domain delegation to the same server.
 | ||||
|       if (-1 === db.primaryNameservers.indexOf(r.data.toLowerCase())) { | ||||
|       if (-1 === engine.primaryNameservers.indexOf(r.data.toLowerCase())) { | ||||
|         console.log("It's a vanity NS"); | ||||
|         return false; | ||||
|       } | ||||
							
								
								
									
										37
									
								
								lib/store.json.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/store.json.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| module.exports.create = function (opts) { | ||||
|   // opts = { filepath };
 | ||||
|   var engine = { db: null }; | ||||
| 
 | ||||
|   var db = require(opts.filepath); | ||||
| 
 | ||||
|   engine.primaryNameservers = db.primaryNameservers; | ||||
|   engine.getSoas = function (query, cb) { | ||||
|     var myDomains = db.domains.filter(function (d) { | ||||
|       return -1 !== query.names.indexOf(d.id.toLowerCase()); | ||||
|     }); | ||||
|     process.nextTick(function () { | ||||
|       cb(null, myDomains); | ||||
|     }); | ||||
|   }; | ||||
|   engine.getRecords = function (query, cb) { | ||||
|     var myRecords = db.records.slice(0).filter(function (r) { | ||||
| 
 | ||||
|       if ('string' !== typeof r.name) { | ||||
|         return false; | ||||
|       } | ||||
| 
 | ||||
|       // TODO use IN in masterquest (or implement OR)
 | ||||
|       // Only return single-level wildcard?
 | ||||
|       if (query.name === r.name || ('*.' + query.name.split('.').slice(1).join('.')) === r.name) { | ||||
|         return true; | ||||
|       } | ||||
|     }); | ||||
|     process.nextTick(function () { | ||||
|       cb(null, myRecords); | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   return engine; | ||||
| }; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user