helper.runInTransaction(agent, function() { dns.resolveTxt('example.com', function(err, data) { t.notOk(err) t.ok(Array.isArray(data)) verifySegments(t, agent, 'dns.resolveTxt') }) })
export function resolveDatDNS (name, cb) { // is it a hash? if (DAT_HASH_REGEX.test(name)) { return cb(null, name) } // do a dns lookup debug('DNS TXT lookup for name:', name) dns.resolveTxt(name, (err, records) => { debug('DNS TXT results for', name, err || records) if (err) return cb(err) // scan the txt records for a dat URI for (var i=0; i < records.length; i++) { if (records[i][0].indexOf('dat://') === 0) { var urlp = url.parse(records[i][0]) if (DAT_HASH_REGEX.test(urlp.host)) { debug('DNS resolved', name, 'to', urlp.host) return cb(null, urlp.host) } debug('DNS TXT record failed:', records[i], 'Must be a dat://{hash} url') } } cb({ code: 'ENOTFOUND' }) }) }
function discoverRemote(req, remoteDomain, callback) { dns.resolveTxt(API_SRV_PREFIX + remoteDomain, function (err, addresses) { if (err) { callback(null); return; } if (addresses.length == 0) { callback(null); return; } //TODO Iterate over all addresses var firstAddress = addresses[0]; var txtRecord = splitTXTRecord(firstAddress); if (!txtRecord) { callback(null); return; } var host = txtRecord['host']; var port = txtRecord['port']; ping(host, port, function() { callback(null); }, function() { var protocol = txtRecord['protocol']; var path = txtRecord['path']; path = !path || path == '/' ? '' : path; callback(protocol + '://' + host + ':' + port + path + MEDIA_PROXY_ENDPOINT); }); }); }
/* Locates a Eureka host using DNS lookups. The DNS records are looked up by a naming convention and TXT records must be created according to the Eureka Wiki here: https://github.com/Netflix/eureka/wiki/Configuring-Eureka-in-AWS-Cloud Naming convention: txt.<REGION>.<HOST> */ locateEurekaHostUsingDns(callback = noop) { const currentServiceUrl = this.currentServiceUrl; const { host } = url.parse(currentServiceUrl); const { ec2Region } = this.config.eureka; if (!ec2Region) { return callback(new Error( 'EC2 region was undefined. ' + 'config.eureka.ec2Region must be set to resolve Eureka using DNS records.' )); } dns.resolveTxt(`txt.${ec2Region}.${host}`, (err, addresses) => { if (err) { return callback(new Error( `Error resolving eureka server list for region [${ec2Region}] using DNS: [${err}]` )); } const random = Math.floor(Math.random() * addresses[0].length); dns.resolveTxt(`txt.${addresses[0][random]}`, (resolveErr, results) => { if (resolveErr) { this.logger.warn('Failed to locate DNS record for Eureka', resolveErr); callback(new Error(`Error locating eureka server using DNS: [${resolveErr}]`)); } this.logger.debug('Found Eureka Server @ ', results); callback(null, [].concat(...results).shift()); }); }); }
server.get('/check/:ip/:endpoint', function(req, res) { var fs = require('fs'); var file = 'public/lists.json'; var lists = require('./public/lists'); var list_in_config = false; for (var i = 0; i < lists.length; i++) { if (lists[i].endpoint == req.params.endpoint) { list_in_config = true; break; } } if (!list_in_config) { res.json({ 'error': 'Unknown List'}); } dns.resolveTxt(rbl_hostname(req.params.ip, req.params.endpoint), function (err, addresses) { if (err) { if (err.code === 'ENOTFOUND') { // Not listed. res.json({ listed: false }); return; } else { res.json({ 'error': 'Unable to look up address' }); } } res.json({ 'listed': addresses }); }); });
module.exports = function(addr) { if (typeof addr !== 'string') return Q(addr); if (! addr.match(/^dnstxt/i)) return Q(addr); debug ('processing', addr); addr = addr.replace(/^dnstxt:\/\//, '') var end = addr.split('/').splice(1).join('/'); addr = addr.replace(/\/.*/, ''); var d = Q.defer(); dns.resolveTxt(addr, function (err, addresses) { if (err) { debug ('rejecting error', err); return d.reject(err); } if (!addresses || !addresses.length || !addresses[0] || !addresses[0].length) { debug('rejecting, addresses is malformed', addresses) return d.reject (addresses) } var ret = addresses[0][0] if (end) ret += '/' + end; debug ('resolving', ret); return d.resolve(ret); }); return d.promise; }
prepareDns(DnsServer.testResolveTXT(txt), function() { dns.resolveTxt("vertx.io", function(err, records) { vassert.assertTrue("Unexpected number of response records: " + records.length, 1 === records.length); vassert.assertTrue("Unexpected result: " + records[0], txt === records[0]); vassert.testComplete(); }); });
dns.resolveSrv(`_mongodb._tcp.${lookupAddress}`, (err, addresses) => { if (err) return callback(err); if (addresses.length === 0) { return callback(new MongoParseError('No addresses found at host')); } for (let i = 0; i < addresses.length; i++) { if (!matchesParentDomain(addresses[i].name, result.hostname, result.domainLength)) { return callback( new MongoParseError('Server record does not share hostname with parent URI') ); } } // Convert the original URL to a non-SRV URL. result.protocol = 'mongodb'; result.host = addresses.map(address => `${address.name}:${address.port}`).join(','); // Default to SSL true if it's not specified. if ( !('ssl' in options) && (!result.search || !('ssl' in result.query) || result.query.ssl === null) ) { result.query.ssl = true; } // Resolve TXT record and add options from there if they exist. dns.resolveTxt(lookupAddress, (err, record) => { if (err) { if (err.code !== 'ENODATA') { return callback(err); } record = null; } if (record) { if (record.length > 1) { return callback(new MongoParseError('Multiple text records not allowed')); } record = qs.parse(record[0].join('')); if (Object.keys(record).some(key => key !== 'authSource' && key !== 'replicaSet')) { return callback( new MongoParseError('Text record must only set `authSource` or `replicaSet`') ); } Object.assign(result.query, record); } // Set completed options back into the URL object. result.search = qs.stringify(result.query); const finalString = URL.format(result); parseConnectionString(finalString, options, callback); }); });
TEST(function test_resolveTxt(done) { var req = dns.resolveTxt('google.com', function(err, records) { if (err) throw err; assert.equal(records.length, 1); assert.equal(records[0].indexOf('v=spf1'), 0); done(); }); checkWrap(req); });
TEST(function test_resolveTxt(done) { const req = dns.resolveTxt('google.com', function(err, records) { assert.ifError(err); assert.strictEqual(records.length, 1); assert.ok(util.isArray(records[0])); assert.strictEqual(records[0][0].indexOf('v=spf1'), 0); done(); }); checkWrap(req); });
TEST(function test_resolveTxt_failure(done) { var req = dns.resolveTxt('something.invalid', function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); assert.strictEqual(result, undefined); done(); }); checkWrap(req); });
scriptLoader.on('command', ['wiki', 'wikipedia'], function (event) { if (event.params.length > 0) { dns.resolveTxt(event.params.join('_') + '.wp.dg.cx', function (error, txt) { if (!error) { event.channel.reply(event.user, txt[0]); } else { event.channel.reply(event.user, 'There were no results matching the query.'); scriptLoader.debug('[wiki] %s', error); } }); } else { event.user.notice('Use: !wikipedia <term>'); } });
namespace.run(function () { namespace.set('test', 2020); t.equal(namespace.get('test'), 2020, "state has been mutated"); dns.resolveTxt('newrelic.com', function (err, addresses) { t.notOk(err, "lookup succeeded"); t.ok(addresses.length > 0, "some results were found"); t.equal(namespace.get('test'), 2020, "mutated state has persisted to dns.resolveTxt's callback"); t.end(); }); });
dns.resolveTxt(`txt.${ec2Region}.${host}`, (err, addresses) => { if (err) { return callback(new Error( `Error resolving eureka server list for region [${ec2Region}] using DNS: [${err}]` )); } const random = Math.floor(Math.random() * addresses[0].length); dns.resolveTxt(`txt.${addresses[0][random]}`, (resolveErr, results) => { if (resolveErr) { this.logger.warn('Failed to locate DNS record for Eureka', resolveErr); callback(new Error(`Error locating eureka server using DNS: [${resolveErr}]`)); } this.logger.debug('Found Eureka Server @ ', results); callback(null, [].concat(...results).shift()); }); });
TEST(function test_resolveTxt_failure(done) { dnsPromises.resolveTxt(addresses.INVALID_HOST) .then(common.mustNotCall()) .catch(common.expectsError({ errno: 'ENOTFOUND' })); const req = dns.resolveTxt(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); assert.strictEqual(result, undefined); done(); }); checkWrap(req); });
TEST(async function test_resolveTxt(done) { function validateResult(result) { assert.ok(Array.isArray(result[0])); assert.strictEqual(result.length, 1); assert(result[0][0].startsWith('v=spf1')); } validateResult(await dnsPromises.resolveTxt(addresses.TXT_HOST)); const req = dns.resolveTxt(addresses.TXT_HOST, function(err, records) { assert.ifError(err); validateResult(records); done(); }); checkWrap(req); });
module.exports = (domain, opts, callback) => { dns.resolveTxt(domain, (err, records) => { if (err) { return callback(err, null) } // TODO: implement recursive option for (const record of records) { if (record[0].startsWith('dnslink=')) { return callback(null, record[0].substr(8, record[0].length - 1)) } } callback(new Error('domain does not have a txt dnslink entry')) }) }
exports.get_dns_results = function (zone, ip, done) { var plugin = this; var query = ip.split('.').reverse().join('.') + '.' + zone; // plugin.logdebug(plugin, "query: " + query); var timer = setTimeout(function () { return done(new Error('timeout'), zone, null); }, (plugin.cfg.main.timeout || 4) * 1000); dns.resolveTxt(query, function (err, addrs) { clearTimeout(timer); if (err) { plugin.logerror(plugin, "error: " + err + ' running: '+query); return done(err, zone); } if (!addrs || !addrs[0]) { return done(new Error('no results for ' + query), zone); } var first = addrs[0]; if (Array.isArray(first)) { // node 0.11 returns TXT records as an array of labels first = addrs[0].join(''); // concatenate the labels } plugin.logdebug(plugin, zone + " answers: " + addrs); var result; if (zone === 'origin.asn.cymru.com') { result = plugin.parse_cymru(first); } else if (zone === 'asn.routeviews.org') { result = plugin.parse_routeviews(addrs); } else if (zone === 'origin.asn.spameatingmonkey.net') { result = plugin.parse_monkey(first); } else { plugin.logerror(plugin, "unrecognized ASN provider: " + zone); } return done(null, zone, result); }); };
specify( '20161025 1e100.net', function( done ) { var selector = '20161025' var domain = '1e100.net' var dkimDomain = selector + '._domainkey.' + domain var keyData = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoesoDYpDnaUGZFoCElFKAkhbPoqBCfkmz3LqSfdAkye2DpoxlBx+qybWdYXe55mCWPeVUIk10Z/H9uriY4enbpmUM0t3mhgyrxuKwJtFg0YgQ0WGpMKecYjhYkt+pcHy7J11BrYh6lHx7mXf5SxuoOF1B6rG1MTzgBKDQqHsBvfz9xZSsNA5HW41EHu4dxRz/QLvzJYegLac8p6oU7l8O/yaVAse0DpgkVu+adfDV+flDq+nohyt2CJ+XHHdbIpE3cb01wp4Znz05zcYaTJd6WIQuis9sjGpS8sDEhY2gZkJVE2jvk1/mObTsyJuVuORapZnXO740owXe8Pvxq7uQIDAQAB' dns.resolveTxt( dkimDomain, ( error, records ) => { if( error ) return done( error ) records = records.map(( record ) => record.join( '' ) ) console.log( records ) records = records.map( DKIMKey.parse ) console.log( records ) assert.strictEqual( records[0].key.toString( 'base64' ), keyData ) assert.strictEqual( records[0].type, 'rsa' ) done() }) })
/** * Verifies if selected settings are valid (private key matches the publick key listed in DNS) * * @param {Function} callback Callback function with the result */ function verifyKeys(options, callback) { var domain = punycode.toASCII([options.keySelector, '_domainkey', options.domainName].join('.')); dns.resolveTxt(domain, function(err, result) { if (err) { return callback(err); } if (!result || !result.length) { return callback(new Error('Selector not found (%s)', domain)); } var data = {}; [].concat(result[0] || []).join('').split(/;/).forEach(function(row) { var key, val; row = row.split('='); key = (row.shift() || '').toString().trim(); val = (row.join('=') || '').toString().trim(); data[key] = val; }); if (!data.p) { return callback(new Error('DNS TXT record does not seem to be a DKIM value', domain)); } var pubKey = '-----BEGIN PUBLIC KEY-----\n' + data.p.replace(/.{78}/g, '$&\n') + '\n-----END PUBLIC KEY-----'; try { var sign = crypto.createSign('RSA-SHA256'); sign.update('nodemailer'); var signature = sign.sign(options.privateKey, 'hex'); var verifier = crypto.createVerify('RSA-SHA256'); verifier.update('nodemailer'); if (verifier.verify(pubKey, signature, 'hex')) { return callback(null, true); } else { return callback(new Error('Verification failed, keys do not match')); } } catch (E) { callback(E); } }); }
specify( '20171004 dialogflow.com', function( done ) { var selector = '20171004' var domain = 'dialogflow.com' var dkimDomain = selector + '._domainkey.' + domain var keyData = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzI5iaG0vxFScisTDRt9c/id87RBk8+3XK7WTvX8JhTuMwQGkrEsAVSRwj+RACDikGgNoWKO1ozBtGSSXRbwbkhytOWp2QJ75lAi1U/cNHFGZ5a3Vf7OjCB7/QiO4M0euPXZwfb5ya1OhxSCAReYHXkRzmkSjh5jRtPdhdXMJPkCw7Kcg3gQRkThPJ5FvAOYMlHUox4pXbp6H6VQnMvk8qahEhbxc+mv8zqgpBtwqndVE6BREOgku/GAXf8wcdm4Ns4I7XixTCmC40C9aePGL04cQiFt78hdsEsyWFe3uz0Rh5MTh1Z2m5Dk9FVHb384w1462ue61VVNNzl+LXASXiwIDAQAB' dns.resolveTxt( dkimDomain, ( error, records ) => { if( error ) return done( error ) records = records.map(( record ) => record.join( '' ) ) console.log( records ) records = records.map( DKIMKey.parse ) console.log( records ) assert.strictEqual( records[0].key.toString( 'base64' ), keyData ) assert.strictEqual( records[0].version, 'DKIM1' ) assert.strictEqual( records[0].type, 'rsa' ) done() }) })
dns.resolveMx(params[0], function (err, addresses) { message += '\nMX: '; message += err || JSON.stringify(addresses, null, 2).replace(/[\r\n]/g, ''); dns.resolveTxt(params[0], function (err, addresses) { message += '\nTxt: '; message += err || JSON.stringify(addresses, null, 2).replace(/[\r\n]/g, ''); dns.resolveSrv(params[0], function (err, addresses) { message += '\nSrv: '; message += err || JSON.stringify(addresses, null, 2).replace(/[\r\n]/g, ''); dns.resolveNs(params[0], function (err, addresses) { message += '\nNS: '; message += err || JSON.stringify(addresses, null, 2).replace(/[\r\n]/g, ''); dns.resolveCname(params[0], function (err, addresses) { message += '\nCName: '; message += err || JSON.stringify(addresses, null, 2).replace(/[\r\n]/g, ''); stanza.c('body').t(message); client.send(stanza); }); }); }); }); });
return new Promise((resolve, reject) => { dns.resolveTxt(`${owner}.${domain}`, (err, records) => { if (err && err.code === 'ENOTFOUND') { resolve([]) } else if (err) { reject(err) } else { resolve( records .map(record => { const [attribute, value] = record[0].split('=') return { attribute, value, method: attribute.split('-').pop(), uri: value, } }) .sort(compareAltConnections) ) } }) })
exports.get_dns_results = function (zone, ip, done) { const plugin = this; const query = ip.split('.').reverse().join('.') + '.' + zone; // plugin.logdebug(plugin, "query: " + query); // only run the callback once let calledDone = false; function doneOnce () { if (calledDone) return; calledDone = true; return done.apply(plugin, arguments); } const timer = setTimeout(() => { return doneOnce(new Error(zone + ' timeout'), zone, null); }, (plugin.cfg.main.timeout || 4) * 1000); dns.resolveTxt(query, function (err, addrs) { clearTimeout(timer); if (calledDone) return; if (err) { plugin.logerror(plugin, "error: " + err + ' running: ' + query); return doneOnce(err, zone); } if (!addrs || !addrs[0]) { return doneOnce(new Error('no results for ' + query), zone); } const first = addrs[0]; plugin.logdebug(plugin, zone + " answers: " + first); const result = plugin.get_result(zone, first); return doneOnce(null, zone, result); }) }
this.generate(addr.name, function(err, info) { if(!err) { var full = format(info.record, addr.domain); dns.resolveTxt(full, function(err, txts) { if(!err) { var result = txts[0][0], id = result.match(/(^|;)id=([a-zA-Z0-9]+)/)[2]; toxdns.decrypt(id, info.id, function(err, addr) { if(!err) { if(callback) { callback(undefined, addr); } } else if(callback) { callback(err); } }); } else if(callback) { callback(err); } }); } else if(callback) { callback(err); } });
dns.resolveSrv(srvAddress, (err, addresses) => { if (err) return callback(err); if (addresses.length === 0) { return callback(new MongoParseError('No addresses found at host')); } for (let i = 0; i < addresses.length; i++) { if (!matchesParentDomain(addresses[i].name, result.hostname, result.domainLength)) { return callback( new MongoParseError('Server record does not share hostname with parent URI') ); } } let base = result.auth ? `mongodb://${result.auth}@` : `mongodb://`; let connectionStrings = addresses.map( (address, i) => i === 0 ? `${base}${address.name}:${address.port}` : `${address.name}:${address.port}` ); let connectionString = `${connectionStrings.join(',')}/`; let connectionStringOptions = []; // Add the default database if needed if (result.path) { let defaultDb = result.path.slice(1); if (defaultDb.indexOf('?') !== -1) { defaultDb = defaultDb.slice(0, defaultDb.indexOf('?')); } connectionString += defaultDb; } // Default to SSL true if (!options.ssl && (!result.search || result.query['ssl'] == null)) { connectionStringOptions.push('ssl=true'); } // Keep original uri options if (result.search) { connectionStringOptions.push(result.search.replace('?', '')); } dns.resolveTxt(result.host, (err, record) => { if (err) { if (err.code !== 'ENODATA') { return callback(err); } record = null; } if (record) { if (record.length > 1) { return callback(new MongoParseError('Multiple text records not allowed')); } record = record[0]; record = record.length > 1 ? record.join('') : record[0]; if (record.indexOf('authSource') === -1 && record.indexOf('replicaSet') === -1) { return callback( new MongoParseError('Text record must only set `authSource` or `replicaSet`') ); } connectionStringOptions.push(record); } // Add any options to the connection string if (connectionStringOptions.length) { connectionString += `?${connectionStringOptions.join('&')}`; } parseConnectionString(connectionString, callback); }); });
client.addListener('message', function(from, to, message) { var params = message.split(' ').slice(1).join(' '); if (to == client.nick) { // Handling private messages to = from; } /**********************************************************[ !help ]**/ if (message.match(/^!help/)) { var help = "RobotIRC 0.1.2 supports the following commands:\n" + "!alexa => Get Alexa traffic rank for a domain or URL\n" + "!date => Display server local time\n" + "!expand => Expand a shortened URL\n" + "!headers => Display HTTP headers for queried URL\n" + "!resolve => Get A records (IPv4) and AAAA records (IPv6) for queried domain\n" + "!reverse => Get reverse (PTR) records from IPv4 or IPv6 addresses\n" + "!wikipedia => Query Wikipedia for an article summary\n"; client.say(to, help); } /*********************************************************[ !alexa ]**/ if (message.match(/^!alexa/)) { alexa(params, function(error, result) { if (!error && typeof result.rank != "undefined") { client.say(to, "Alexa Traffic Rank for " + result.idn.slice(0, -1) + ": " + result.rank); } }); } /**********************************************************[ !date ]**/ if (message.match(/^!date/)) { client.say(to, Date()); } /********************************************************[ !expand ]**/ if (message.match(/^!expand/)) { request({ method: "HEAD", url: params, followAllRedirects: true }, function(error, response) { if (!error && response.statusCode == 200) { client.say(to, response.request.href); } }); } /*******************************************************[ !headers ]**/ if (message.match(/^!headers/)) { request(params, function(error, response, body) { if (!error && response.statusCode == 200) { for (var item in response.headers) { client.say(to, item + ": " + response.headers[item] + "\n"); } } }); } /*******************************************************[ !resolve ]**/ if (message.match(/^!resolve/)) { dns.resolve4(params, function(error, addresses) { if (!error) { for (var item in addresses) { client.say(to, addresses[item] + "\n"); } } dns.resolve6(params, function(error, addresses) { if (!error) { for (var item in addresses) { client.say(to, addresses[item] + "\n"); } } }); }); } /*******************************************************[ !reverse ]**/ if (message.match(/^!reverse/)) { if (net.isIP(params)) { dns.reverse(params, function(error, domains) { if (!error) { client.say(to, domains[0]); } }); } } /*****************************************************[ !wikipedia ]**/ if (message.match(/^!wikipedia/)) { params.split(' ').join('_'); dns.resolveTxt(params + ".wp.dg.cx", function(error, txt) { if (!error) { client.say(to, txt[0]); } }); } });
SPF.prototype.check_host = function (ip, domain, mail_from, cb) { var self = this; domain = domain.toLowerCase(); if (mail_from) { mail_from = mail_from.toLowerCase(); } else { mail_from = 'postmaster@' + domain; } this.ipaddr = ipaddr.parse(ip); this.ip_ver = this.ipaddr.kind(); if (this.ip_ver === 'ipv6') { this.ip = this.ipaddr.toString(); } else { this.ip = ip; } this.domain = domain; this.mail_from = mail_from; this.log_debug('ip=' + ip + ' domain=' + domain + ' mail_from=' + mail_from); // Get the SPF record for domain dns.resolveTxt(domain, function (err, txt_rrs) { if (err) { self.log_debug('error looking up TXT record: ' + err.message); switch (err.code) { case 'ENOTFOUND': case 'ENODATA': case dns.NXDOMAIN: return cb(null, self.SPF_NONE); default: return cb(null, self.SPF_TEMPERROR); } } var spf_record; for (var i=0; i < txt_rrs.length; i++) { var match = /^(v=spf1(?:$|\s.+$))/i.exec(txt_rrs[i]); if (match) { if (!spf_record) { self.log_debug('found SPF record for domain ' + domain + ': ' + match[1]); spf_record = match[1].replace(/\s+/, ' ').toLowerCase(); } else { // We've already found an MX record self.log_debug('found additional SPF record for domain ' + domain + ': ' + match[1]); return cb(null, self.SPF_PERMERROR); } } else { self.log_debug('discarding TXT record: ' + txt_rrs[i]); } } if (!spf_record) { // No SPF record found? return cb(null, self.SPF_NONE); } // Store the SPF record we used in the object self.spf_record = spf_record; // Validate SPF record and build call chain var mech_array = []; var mod_array = []; var mech_regexp1 = /^([-+~?])?(all|a|mx|ptr)$/; var mech_regexp2 = /^([-+~?])?(a|mx|ptr|ip4|ip6|include|exists)((?::[^\/ ]+(?:\/\d+(?:\/\/\d+)?)?)|\/\d+(?:\/\/\d+)?)$/; var mod_regexp = /^([^ =]+)=([a-z0-9._-]+)$/; var split = spf_record.split(' '); for (var i=1; i<split.length; i++) { // Skip blanks if (!split[i]) continue; var match; if (match = (mech_regexp1.exec(split[i]) || mech_regexp2.exec(split[i]))) { // match[1] = qualifier // match[2] = mechanism // match[3] = optional args if (!match[1]) match[1] = '+'; self.log_debug('found mechanism: ' + match); // Validate IP addresses if (match[2] === 'ip4' || match[2] === 'ip6') { var ip_split = /^:([^\/ ]+)(?:\/([^ ]+))?$/.exec(match[3]); // Make sure the IP address is valid if(!ip_split || (ip_split && !ipaddr.isValid(ip_split[1]))) { self.log_debug('invalid IP address: ' + ip_split[1]); return cb(null, self.SPF_PERMERROR); } } else { // Validate macro strings if (match[3] && /%[^{%+-]/.exec(match[3])) { self.log_debug('invalid macro string'); return cb(null, self.SPF_PERMERROR); } if (match[3]) { // Expand macros match[3] = self.expand_macros(match[3]); } } var obj = {}; obj[match[2]] = [ match[1], match[3] ]; mech_array.push(obj); } else if (match = mod_regexp.exec(split[i])) { self.log_debug('found modifier: ' + match); // match[1] = modifier // match[2] = name // Make sure we have a method if (!self['mod_' + match[1]]) { self.log_debug('skipping unknown modifier: ' + match[1]); } else { var obj = {}; obj[match[1]] = match[2]; mod_array.push(obj); } } else { // Syntax error self.log_debug('syntax error: ' + split[i]); return cb(null, self.SPF_PERMERROR); } } self.log_debug('SPF record for \'' + self.domain + '\' validated OK'); // Set-up modifier run chain var mod_chain_caller = function (err, result) { // Throw any errors if (err) throw err; // Check limits if (self.count > self.LIMIT) { self.log_debug('lookup limit reached'); return cb(null, self.SPF_PERMERROR); } // Return any result that is not SPF_NONE if (result && result !== self.SPF_NONE) { return cb(err, result); } if (!mod_array.length) { return cb(null, self.SPF_NEUTRAL); } var next_in_chain = mod_array.shift(); var func = Object.keys(next_in_chain); var args = next_in_chain[func]; self.log_debug('running modifier: ' + func + ' args=' + args + ' domain=' + self.domain); self['mod_' + func](args, mod_chain_caller); } // Run all the mechanisms first var mech_chain_caller = function (err, result) { // Throw any errors if (err) throw err; // Check limits if (self.count > self.LIMIT) { self.log_debug('lookup limit reached'); return cb(null, self.SPF_PERMERROR); } // If we have a result other than SPF_NONE if (result && result !== self.SPF_NONE) { return cb(err, result); } // Return default if no more mechanisms to run if (!mech_array.length) { // Now run any modifiers if (mod_array.length) { return mod_chain_caller(); } else { return cb(null, self.SPF_NEUTRAL); } } var next_in_chain = mech_array.shift(); var func = Object.keys(next_in_chain); var args = next_in_chain[func]; self.log_debug('running mechanism: ' + func + ' args=' + args + ' domain=' + self.domain); self['mech_' + func](((args && args.length) ? args[0] : null), ((args && args.length) ? args[1] : null), mech_chain_caller); } // Start the chain mech_chain_caller(); }); }
DKIMObject.prototype.end = function () { if (this.run_cb) return; var bh = this.bh.digest('base64'); this.debug(this.identity + ':' + ' bodyhash=' + this.fields.bh + ' computed=' + bh); if (bh !== this.fields.bh) { return this.result('body hash did not verify', 'fail'); } // Now we canonicalize the specified headers for (var h=0; h<this.signed_headers.length; h++) { var header = this.signed_headers[h]; this.debug(this.identity + ': canonicalize header: ' + header); if (this.header_idx[header]) { // RFC 6376 section 5.4.2, read headers from bottom to top var this_header = this.header_idx[header].pop(); if (this_header) { // Skip this signature if dkim-signature is specified if (header === 'dkim-signature') { var h_md5 = md5(this_header); if (h_md5 === this.sig_md5) { this.debug(this.identity + ': skipped our own DKIM-Signature'); continue; } } if (this.headercanon === 'simple') { this.verifier.update(this_header); } else if (this.headercanon === 'relaxed') { var hc = this.header_canon_relaxed(this_header); this.verifier.update(hc); } } } } // Now add in our original DKIM-Signature header without the b= and trailing CRLF var our_sig = this.sig.replace(/b=([^;]+)/,'b='); if (this.headercanon === 'relaxed') { our_sig = this.header_canon_relaxed(our_sig); } our_sig = our_sig.replace(/\r\n$/,''); this.verifier.update(our_sig); // Do the DNS lookup to retrieve the public key var self = this; var timeout = false; var timer = setTimeout(function () { timeout = true; return self.result('DNS timeout', 'tempfail'); }, this.timeout * 1000); var lookup = this.fields.s + '._domainkey.' + this.fields.d; this.debug(this.identity + ': DNS lookup ' + lookup + ' (timeout=' + this.timeout + 's)'); dns.resolveTxt(lookup, function (err, res) { if (timeout) return; clearTimeout(timer); if (err) { switch (err.code) { case dns.NOTFOUND: case dns.NODATA: case dns.NXDOMAIN: return self.result('no key for signature', 'invalid'); default: self.debug(self.identity + ': DNS lookup error: ' + err.code); return self.result('key unavailable', 'tempfail'); } } if (!res) return self.result('no key for signature', 'invalid'); for (var r=0; r<res.length; r++) { var record = res[r]; // Node 0.11.x compatibility if (Array.isArray(record)) { record = record.join(''); } if (record.indexOf('p=') === -1) { self.debug(self.identity + ': ignoring TXT record: ' + record); continue; } self.debug(self.identity + ': got DNS record: ' + record); var rec = record.replace(/\r?\n/g, '').replace(/\s+/g,''); var split = rec.split(';'); for (var j=0; j<split.length; j++) { var split2 = split[j].split('='); if (split2[0]) self.dns_fields[split2[0]] = split2[1]; } // Validate if (!self.dns_fields.v || self.dns_fields.v !== 'DKIM1') { return self.result('invalid version', 'invalid'); } if (self.dns_fields.g) { if (self.dns_fields.g !== '*') { var s = self.dns_fields.g; // Escape any special regexp characters s = s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); // Make * a non-greedy match against anything except @ s = s.replace('\\*','[^@]*?'); var reg = new RegExp('^' + s + '@'); self.debug(self.identity + ': matching ' + self.dns_fields.g + ' against i=' + self.fields.i + ' regexp=' + reg.toString()); if (!reg.test(self.fields.i)) { return self.result('inapplicable key', 'invalid'); } } } else { return self.result('inapplicable key', 'invalid'); } if (self.dns_fields.h) { var hashes = self.dns_fields.h.split(':'); for (var k=0; k<hashes.length; k++) { var hash = hashes[k].trim(); if (self.fields.a.indexOf(hash) === -1) { return self.result('inappropriate hash algorithm', 'invalid'); } } } if (self.dns_fields.k) { if (self.fields.a.indexOf(self.dns_fields.k) === -1) { return self.result('inappropriate key type', 'invalid'); } } if (self.dns_fields.t) { var flags = self.dns_fields.t.split(':'); for (var f=0; f<flags.length; f++) { var flag = flags[f].trim(); if (flag === 'y') { // Test mode self.test_mode = true; } else if (flag === 's') { // 'i' and 'd' domain much match exactly var j = self.fields.i; j = j.substr(j.indexOf('@')+1, j.length); if (j !== self.fields.d) { return self.result('domain mismatch', 'invalid'); } } } } if (!self.dns_fields.p) return self.result('key revoked', 'invalid'); // crypto.verifier requires the key in PEM format self.public_key = '-----BEGIN PUBLIC KEY-----\r\n' + self.dns_fields.p.replace(/(.{1,76})/g, '$1\r\n') + '-----END PUBLIC KEY-----\r\n'; var verified; try { verified = self.verifier.verify(self.public_key, self.fields.b, 'base64'); self.debug(self.identity + ': verified=' + verified); } catch (e) { self.debug(self.identity + ': verification error: ' + e.message); return self.result('verification error', 'invalid'); } return self.result(null, ((verified) ? 'pass' : 'fail')); } // We didn't find a valid DKIM record for this signature return self.result('no key for signature', 'invalid'); }); };
dns.resolveSrv(srvAddress, function(err, addresses) { if (err) return callback(err); if (addresses.length === 0) { return callback(new Error('No addresses found at host')); } for (var i = 0; i < addresses.length; i++) { if (!matchesParentDomain(addresses[i].name, result.hostname, result.domainLength)) { return callback(new Error('srv record does not share hostname with parent uri')); } } var base = result.auth ? 'mongodb://' + result.auth + '@' : 'mongodb://'; var connectionStrings = addresses.map(function(address, i) { if (i === 0) return base + address.name + ':' + address.port; else return address.name + ':' + address.port; }); var connectionString = connectionStrings.join(',') + '/'; var connectionStringOptions = []; // Default to SSL true if (!options.ssl && !result.search) { connectionStringOptions.push('ssl=true'); } else if (!options.ssl && result.search && !result.search.match('ssl')) { connectionStringOptions.push('ssl=true'); } // Keep original uri options if (result.search) { connectionStringOptions.push(result.search.replace('?', '')); } dns.resolveTxt(result.host, function(err, record) { if (err && err.code !== 'ENODATA') return callback(err); if (err && err.code === 'ENODATA') record = null; if (record) { if (record.length > 1) { return callback(new Error('multiple text records not allowed')); } record = record[0]; if (record.length > 1) record = record.join(''); else record = record[0]; if (!record.includes('authSource') && !record.includes('replicaSet')) { return callback(new Error('text record must only set `authSource` or `replicaSet`')); } connectionStringOptions.push(record); } // Add any options to the connection string if (connectionStringOptions.length) { connectionString += '?' + connectionStringOptions.join('&'); } parseHandler(connectionString, options, callback); }); });