dnsd-name
Version:
Dynamic authoritative name server for Iris services
270 lines (208 loc) • 7.11 kB
JavaScript
/*
* This module handles all IO called on the cache (currently Redis)
*/
const url = require('url');
const factory = require('./redis');
const async = require('async');
const tld = require('tldjs');
const LruCache = require('./lru');
const to = require('./to');
class Cache {
constructor(config, options) {
this.config = config;
this.log = function (msg) {
if (options.logHandler)
options.logHandler.log(msg);
else
console.log(msg);
};
this.client = new factory(config);
this.client.on('error', function (err) {
this.log('DriverError ' + err);
}.bind(this));
this.lru = new LruCache();
this.lru.enabled = {
size: 100000,
ttl: 60
};
}
queryCache(query) {
return this.lru.get(query);
}
addToCache(query, backends, ttl) {
this.lru.set(query, backends, ttl * 1000 || 15 * 1000);
}
getDomainsLookup(hostname) {
let parts = hostname.split('.');
let result = [hostname];
let n;
// Prevent abusive lookups
while (parts.length > 6) {
parts.shift();
}
while (parts.length > 1) {
parts.shift();
n = parts.join('.');
result.push('*.' + n);
}
result.push('*');
return result;
}
readFromCache(hostKey, type, explicit = false) {
let self = this;
let backends = this.lru.get(type + ':' + hostKey);
if (backends) {
return Promise.resolve(backends.slice(0));
}
return new Promise(async function (resolve, reject) {
// The entry is not in the LRU cache, let's do a request on Redis
self.client.read(explicit ? [hostKey] : self.getDomainsLookup(hostKey), type, function (err, rows) {
if (err) {
return reject(err)
}
backends = rows.shift();
while (rows.length && !backends.length) {
backends = rows.shift();
}
if (!backends.length) {
return resolve([])
}
for (var i = 0,
j = backends.length; i < j; i++) {
try {
backends[i] = JSON.parse(backends[i])
} catch (e) {
}
}
self.lru.set(type + ':' + hostKey, backends, (backends[0] && backends[0].ttl * 1000) || 6000);
resolve(backends.slice(0));
});
})
}
async getDnsFromHostType(host, type) {
var self = this;
return new Promise(async function (resolve, reject) {
let [err, backends] = await to(self.readFromCache(host, type))
if (err) {
return reject(err)
}
resolve(backends)
});
}
getNSList(name) {
let self = this;
return new Promise(async function (resolve, reject) {
let [err, backends] = await to(self.readFromCache(name, 'NS', true))
if (err) {
return reject(err)
}
resolve(backends)
});
}
getSOAList(name) {
let self = this;
return new Promise(async function (resolve, reject) {
let nameservers,
err,
backends;
[err, backends] = await to(self.readFromCache(name, 'SOA', true))
if (err) {
return reject(err)
}
async function setSoa(server) {
[err, nameservers] = await to(self.getNSList('nameserver'))
if (err) {
return reject(err)
}
let nameserver = nameservers.shift();
var soa = {
"name": server.name,
"ttl": server.ttl,
"primary": nameserver.name,
"admin": server.admin,
"serial": server.serial,
"refresh": server.refresh,
"retry": server.retry,
"expiration": server.expiration,
"minimum": server.minimum
};
resolve(soa);
}
if (backends.length === 0) {
[err, backends] = await to(self.readFromCache('nameserver', 'SOA', true))
setSoa(backends.shift());
} else {
setSoa(backends.shift());
}
});
}
lookupQuestion(question, cb) {
let self = this;
return new Promise(async function (resolve, reject) {
let [err, records] = await to(self.getDnsFromHostType(question.name, question.type))
if (err) {
return reject(err)
}
let results = [];
for (let record of records) {
results.push({
name: question.name,
type: question.type,
zone: tld.getDomain(question.name),
ttl: record.ttl,
priority: record.priority,
data: record.data
})
}
resolve(results);
})
}
lookupCNAME(question) {
let self = this;
let answers = [];
return new Promise(async function (resolve, reject) {
let [err, cnameRecords] = await to(self.getDnsFromHostType(question.name, 'CNAME'))
if (err) {
return reject(err)
}
for (let host of cnameRecords) {
answers.push({
name: question.name,
type: 'CNAME',
zone: tld.getDomain(question.name),
ttl: host.ttl,
priority: host.priority,
data: host.data
});
try {
let records = await self.getDnsFromHostType(host.data, question.type)
for (var i = 0; i < records.length; i++) {
answers.push(records[i]);
}
} catch (err) {
}
}
resolve(answers)
});
};
getAnswerList(questions) {
let self = this;
return new Promise(async function (resolve, reject) {
let records;
try {
records = await Promise.all(questions.map(function (question) {
return self.lookupQuestion(question)
}))
} catch (err) {
return reject(err);
}
if (!records) {
return resolve([])
}
resolve(records.reduce(function (prev, curr) {
return prev.concat(curr);
}));
});
}
}
module.exports = Cache;