function startDNSQueries(params){ let domain = params.email.split(/[@]/).splice(-1)[0].toLowerCase() logger.info("Resolving DNS... " + domain) dns.resolveMx(domain,(err,addresses) => { if (err || (typeof addresses === 'undefined')) { params.callback(err, { success: false, info: 'Domain not found', code: infoCodes.domainNotFound }); } else if (addresses && addresses.length <= 0) { params.callback(null, { success: false, info: 'No MX Records', code: infoCodes.noMxRecords }); } else{ params.addresses = addresses // Find the lowest priority mail server let priority = 10000, lowestPriorityIndex = 0 for (let i = 0 ; i < addresses.length ; i++) { if (addresses[i].priority < priority) { priority = addresses[i].priority lowestPriorityIndex = i logger.info('MX Records ' + JSON.stringify(addresses[i])) } } params.options.smtp = addresses[lowestPriorityIndex].exchange logger.info("Choosing " + params.options.smtp + " for connection") beginSMTPQueries(params) } }) }
dns.resolve4(params[0], function (err, addresses) { var message = 'A: '; message += err || JSON.stringify(addresses, null, 2).replace(/[\r\n]/g, ''); 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); }); }); }); }); }); });
dns.resolveNs(domain, function (err, addresses) { //if (err) throw err; if (!err) { //console.log('NS: ' + (addresses)); for (var x in addresses) { console.log(addresses[x]) } } dns.resolveMx(domain, function (err, addresses) { //if (err) throw err; if (!err) { addresses.forEach(function (a) { console.log(a); }); } // adds.forEach(function (a) { // dns.reverse(a, function (err, domains) { // if (err) { // console.log('reverse for ' + a + ' failed: ' + err.message); // } else { // console.log('reverse for ' + a + ': ' + domains); // //JSON.stringify(domains)); // } // }); // }); }); });
/** * connect to domain by Mx record */ function connectMx(domain, callback) { dns.resolveMx(domain, function(err, data) { if (err) return callback(err); data.sort(function(a, b) {return a.priority < b. priority}); logger.debug('mx resolved: ', data); if (!data || data.length == 0) return callback(new Error('can not resolve Mx of <' + domain + '>')); function tryConnect(i) { if (i >= data.length) return callback(new Error('can not connect to any SMTP server')); var sock = tcp.createConnection(25, data[i].exchange); sock.on('error', function(err) { logger.error('Error on connectMx for: ', data[i], err); tryConnect(++i); }); sock.on('connect', function() { logger.debug("MX connection created: ", data[i].exchange); sock.removeAllListeners('error'); callback(null, sock); }); }; tryConnect(0); }); }
prepareDns(DnsServer.testResolveMX(prio, name), function() { dns.resolveMx("vertx.io", function(err, records) { vassert.assertTrue("Unexpected error: " + err, err === null); vassert.assertTrue("Unexpected priority: " + records[0], prio == records[0].priority); vassert.assertTrue("Unexpected exchange: " + records[0], name === records[0].exchange); vassert.testComplete(); }); });
var validateViaDNS = function () { dnslib.resolveMx(domain, (function(err, addresses){ if(err || !addresses || !addresses.length){ this.server.emit(validationFailedEvent, email); return callback(new Error(err && err.SMTPResponse || dnsErrorMessage)); } validateViaLocal.call(this); }).bind(this)); };
DirectMailer.prototype._resolveMx = function(domain, callback){ // Do not try to resolve the domain name if it is an IP address if(net.isIP(domain)){ return callback(null, [{'priority': 10, 'exchange': domain}]); } dns.resolveMx(domain, callback); };
function domainVerify() { dns.resolveMx( self.name, this.parallel() ); dns.resolveCname( self.name, this.parallel() ); },
return new Promise(function (fulfill, reject) { dns.resolveMx(host, function (err, addresses) { if (err) { reject(err); } else if (addresses.length === 0) { reject(new Error('MX Record nonexistent')); } else { fulfill(); } }); })
DirectMailer.prototype._resolveMx = function(domain, callback) { domain = domain.replace(/[\[\]]/g, ''); // Do not try to resolve the domain name if it is an IP address if (net.isIP(domain)) { return callback(null, [{ 'priority': 0, 'exchange': domain }]); } dns.resolveMx(domain, function(err, list) { if (err) { if (err.code === 'ENODATA') { // fallback to A dns.resolve4(domain, function(err, list) { if (err) { if (err.code === 'ENODATA') { // fallback to AAAA dns.resolve6(domain, function(err, list) { if (err) { return callback(err); } // return the first resolved Ipv6 with priority 0 return callback(null, [].concat(list || []).map(function(entry) { return { 'priority': 0, 'exchange': entry }; }).slice(0, 1)); }); } else { callback(err); } return; } // return the first resolved Ipv4 with priority 0 return callback(null, [].concat(list || []).map(function(entry) { return { 'priority': 0, 'exchange': entry }; }).slice(0, 1)); }); } else { callback(err); } return; } callback(null, list); }); };
function getMX(cb){ dns.resolveMx("gmail.com",function(err,mx){ var highest; for(var i = 0; i < mx.length; i++){ if(!highest || highest.priority > mx[i].priority){ highest = mx[i]; } } cb(highest.exchange); }); }
TEST(function test_resolveMx_failure(done) { var req = dns.resolveMx('something.invalid', function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); assert.strictEqual(result, undefined); done(); }); checkWrap(req); });
function getexchange (domain, cb) { dns.resolveMx(domain, function (err, addresses) { if (err) return cb(err); var resolver = {} addresses.reverse(); for (var i=0;i<addresses.length;i++) { resolver[addresses[i].priority] = addresses[i].exchange; } var keys = Object.keys(resolver) keys.sort() cb(null, resolver[keys[0]]) }) }
var validateViaDNS = function () { try { dns.resolveMx(domain, (function (err, addresses) { if (err || !addresses || !addresses.length) { _this.emit(validationFailedEvent, email); return reject(new Error(dnsErrorMessage)); } validateViaLocal(); })); } catch (e) { return reject(e); } };
var validateViaDNS = function () { if(!self.server.options.disableDNSValidation) { dnslib.resolveMx(domain, function(err, addresses){ if(err || !addresses || !addresses.length){ self.server.emit(validationFailedEvent, email); return callback(new Error(dnsErrorMessage)); } return validateViaLocal(); }); } else { return validateViaLocal(); } };
namespace.run(function () { namespace.set('test', 707); t.equal(namespace.get('test'), 707, "state has been mutated"); dns.resolveMx('newrelic.com', function (err, addresses) { t.notOk(err, "lookup succeeded"); t.ok(addresses.length > 0, "some results were found"); t.equal(namespace.get('test'), 707, "mutated state has persisted to dns.resolveMx's callback"); t.end(); }); });
TEST(function test_resolveMx_failure(done) { dnsPromises.resolveMx(addresses.INVALID_HOST) .then(common.mustNotCall()) .catch(common.expectsError({ errno: 'ENOTFOUND' })); const req = dns.resolveMx(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); assert.strictEqual(result, undefined); done(); }); checkWrap(req); });
SMTPServerConnection.prototype._onCommandMAIL = function(mail){ var match, email, domain; if(!this.hostNameAppearsAs){ return this.client.send("503 5.5.1 Error: send HELO/EHLO first"); } if(this.server.options.requireAuthentication && !this.authentication.authenticated){ return this.client.send("530 5.5.1 Authentication Required"); } if(this.envelope.from){ return this.client.send("503 5.5.1 Error: nested MAIL command"); } if(!(match = mail.match(/^from\:\s*<([^@>]+\@([^@>]+))>$/i))){ return this.client.send("501 5.1.7 Bad sender address syntax"); } email = match[1] || ""; domain = (match[2] || "").toLowerCase(); dnslib.resolveMx(domain, (function(err, addresses){ if(err || !addresses || !addresses.length){ return this.client.send("450 4.1.8 <"+email+">: Sender address rejected: Domain not found"); } if(this.server.options.validateSender){ this.server.emit("validateSender", this.envelope, email, (function(err){ if(err){ return this.client.send("550 5.1.1 <"+email+">: Sender address rejected: User unknown in local sender table"); } // force domain part to be lowercase email = email.substr(0, email.length - domain.length) + domain; this.envelope.from = email; this.client.send("250 2.1.0 Ok"); }).bind(this)); }else{ // force domain part to be lowercase email = email.substr(0, email.length - domain.length) + domain; this.envelope.from = email; this.client.send("250 2.1.0 Ok"); } }).bind(this)); };
SMTPServerConnection.prototype._onCommandRCPT = function(mail){ var match, email, domain; if(!this.envelope.from){ return this.client.send("503 5.5.1 Error: need MAIL command"); } if(!(match = mail.match(/^to\:\s*<([^@>]+\@([^@>]+))>$/i))){ return this.client.send("501 5.1.7 Bad recipient address syntax"); } email = match[1] || ""; domain = (match[2] || "").toLowerCase(); dnslib.resolveMx(domain, (function(err, addresses){ if(err || !addresses || !addresses.length){ return this.client.send("450 4.1.8 <"+email+">: Recipient address rejected: Domain not found"); } if(this.server.options.validateRecipients){ this.server.emit("validateRecipient", this.envelope, email, (function(err){ if(err){ return this.client.send("550 5.1.1 <"+email+">: Recipient address rejected: User unknown in local recipient table"); } // force domain part to be lowercase email = email.substr(0, email.length - domain.length) + domain; // add to recipients list if(this.envelope.to.indexOf(email)<0){ this.envelope.to.push(email); } this.client.send("250 2.1.0 Ok"); }).bind(this)); }else{ // force domain part to be lowercase email = email.substr(0, email.length - domain.length) + domain; // add to recipients list if(this.envelope.to.indexOf(email)<0){ this.envelope.to.push(email); } this.client.send("250 2.1.0 Ok"); } }).bind(this)); };
var get_smtp_servers = function(recipient, cb) { var domain = recipient.replace(/.*@/, ''); dns.resolveMx(domain, function(err, addresses) { if (err) return cb(err); var hosts = []; var sorted = addresses.sort(function(a, b){ return (a.priority > b.priority) }); sorted.forEach(function(host){ hosts.push(host.exchange); }); cb(null, hosts); }); };
exports.hook_mail = function(next, connection, params) { var mail_from = params[0]; // Check for MAIL FROM without an @ first - ignore those here if (!mail_from.host) { return next(); } var called_next = 0; var timeout_id = 0; var plugin = this; var domain = mail_from.host; var config = this.config.get('mail_from.is_resolvable.ini'); var timeout = config.general && (config.general['timeout'] || 60); var timeout_msg = config.general && (config.general['timeout_msg'] || ''); // Just in case DNS never comes back (UDP), we should DENYSOFT. timeout_id = setTimeout(function () { plugin.loginfo('timed out when looking up ' + domain + '\'s MX record. Disconnecting.'); called_next++; return next(DENYSOFT, timeout_msg); }, timeout * 1000); dns.resolveMx(domain, function(err, addresses) { if (called_next) { // This happens when we've called next() from our plugin timeout // handler, but we eventually get a response from DNS. We do not // want to call next() again, so we just return. return; } if (err && err.code != dns.NXDOMAIN && err.code != 'ENOTFOUND') { plugin.logerror("DNS Error: " + err); clearTimeout(timeout_id); return next(DENYSOFT, "Temporary resolver error"); } if (addresses && addresses.length) { clearTimeout(timeout_id); return next(); } clearTimeout(timeout_id); return next(DENYSOFT, "No MX for your FROM address"); }); }
this.get_smtp_servers = function(callback){ var domain = this.recipient.replace(/.*@/, ''); // console.log(domain); dns.resolveMx(domain, function(err, addresses) { if (err) return callback(err); var hosts = []; var sorted = addresses.sort(function(a, b){ return (a.priority > b.priority); }); sorted.forEach(function(host){ hosts.push(host.exchange); }); callback(null, hosts); }); };
TEST(function test_resolveMx(done) { const req = dns.resolveMx('gmail.com', function(err, result) { assert.ifError(err); assert.ok(result.length > 0); for (let i = 0; i < result.length; i++) { const item = result[i]; assert.ok(item); assert.strictEqual(typeof item, 'object'); assert.ok(item.exchange); assert.strictEqual(typeof item.exchange, 'string'); assert.strictEqual(typeof item.priority, 'number'); } done(); }); checkWrap(req); });
TEST(function test_resolveMx(done) { var req = dns.resolveMx('gmail.com', function(err, result) { if (err) throw err; assert.ok(result.length > 0); for (var i = 0; i < result.length; i++) { var item = result[i]; assert.ok(item); assert.ok(typeof item === 'object'); assert.ok(item.exchange); assert.ok(typeof item.exchange === 'string'); assert.ok(typeof item.priority === 'number'); } done(); }); checkWrap(req); });
/* Resolve MX @domain {String} @callback {Function} :: callback(error, socket); */ /** * Resolve MX record * @param {String} domain Domain name. * @param {ResolveMxCallback} callback Callback. */ function resolveMx(domain, callback) { dns.resolveMx(domain, function(err, data) { if (err) { callback(err, data); return; } if (!data || data.length === 0) { callback(new Error(errors.resolve + domain)); return; } data.sort(function(a, b) { return a.priority < b. priority; }); function tryConnect(index) { if (index >= data.length) { callback(new Error(errors.connection)); return; } var sock = net.createConnection(25, data[index].exchange); sock.on('error', function(err) { tryConnect(++index); }); sock.on('connect', function() { sock.removeAllListeners('error'); callback(null, sock); }); } tryConnect(0); }); }
module.exports = function isGmail(email, callback) { if ((typeof email !== 'string') || !isEmail(email)) { process.nextTick(function () { callback(false); }); } else { var domain = email.split('@')[1]; if (~['gmail.com', 'google.com', 'googlemail.com'].indexOf(domain)) { process.nextTick(function () { callback(true); }); } else { dns.resolveMx(domain, function (err, result) { callback( err ? false : result.some(function (item) { return item.exchange.match(/google(mail)?\.com$/i); }) ); }); } } };
TEST(async function test_resolveMx(done) { function validateResult(result) { assert.ok(result.length > 0); for (const item of result) { assert.strictEqual(typeof item, 'object'); assert.ok(item.exchange); assert.strictEqual(typeof item.exchange, 'string'); assert.strictEqual(typeof item.priority, 'number'); } } validateResult(await dnsPromises.resolveMx(addresses.MX_HOST)); const req = dns.resolveMx(addresses.MX_HOST, function(err, result) { assert.ifError(err); validateResult(result); done(); }); checkWrap(req); });
"use strict";var net=require("net");var tls=require("tls");var events=require("events");var dns=require("dns");var fs=require("fs");var path=require("path");var CRLF="\r\n";var UNDEFINED="undefined";var errors={notvalid:"E-mail address is not valid",resolve:"Cannot resolve MX of ",connection:"Cannot connect to any SMTP server."};function Mailer(){this.debug=false;this.Message=Message;this.Mail=Message}Mailer.prototype.__proto__=Object.create(events.EventEmitter.prototype,{constructor:{value:Mailer,enumberable:false}});Mailer.prototype.create=function(subject,body){return new Message(subject,body)};function resolveMx(domain,callback){dns.resolveMx(domain,function(err,data){if(err){callback(err,data);return}if(!data||data.length===0){callback(new Error(errors.resolve+domain));return}data.sort(function(a,b){return a.priority<b.priority});function tryConnect(index){if(index>=data.length){callback(new Error(errors.connection));return}var sock=net.createConnection(25,data[index].exchange);sock.on("error",function(err){tryConnect(++index)});sock.on("connect",function(){callback(null,sock)})}tryConnect(0)})}function Message(subject,body){this.subject=subject||"";this.body=body||"";this.files=[];this.addressTo=[];this.addressReply=[];this.addressCC=[];this.addressBCC=[];this.addressFrom={name:"",address:""};this.callback=null;this.closed=false}Message.prototype.sender=function(address,name){return this.from(address,name)};Message.prototype.from=function(address,name){if(!address.isEmail())throw new Error(errors.notvalid);var self=this;self.addressFrom.name=name||"";self.addressFrom.address=address;return self};Message.prototype.to=function(address){if(!address.isEmail())throw new Error(errors.notvalid);var self=this;self.addressTo.push(address);return self};Message.prototype.cc=function(address){if(!address.isEmail())throw new Error(errors.notvalid);var self=this;self.addressCC.push(address);return self};Message.prototype.bcc=function(address){if(!address.isEmail())throw new Error(errors.notvalid);var self=this;self.addressBCC.push(address);return self};Message.prototype.reply=function(address){if(!address.isEmail())throw new Error(errors.notvalid);var self=this;self.addressReply.push(address);return self};Message.prototype.attachment=function(filename,name){var self=this;if(name===undefined)name=path.basename(filename);self.files.push({name:name,filename:filename,contentType:framework_utils.getContentType(path.extname(name))});return self};Message.prototype.attachmentInline=function(filename,name,contentId){var self=this;if(name===undefined)name=path.basename(filename);self.files.push({name:name,filename:filename,contentType:utils.getContentType(path.extname(name)),disposition:"inline",contentId:contentId});return self};Message.prototype.send=function(smtp,options,fnCallback){var self=this;smtp=smtp||null;if(typeof options==="function"){var tmp=fnCallback;fnCallback=options;options=tmp}self.callback=fnCallback;options=framework_utils.copy(options,{secure:false,port:25,user:"",password:"",timeout:1e4});if(smtp===null||smtp===""){smtp=getHostName(self.addressFrom.address);resolveMx(smtp,function(err,socket){if(err){mailer.emit("error",err,self);if(fnCallback)fnCallback(err);return}socket.on("error",function(err){mailer.emit("error",err,self);if(fnCallback)fnCallback(err)});self._send(socket,options)});return self}var socket;if(options.secure){var internal=framework_utils.copy(options);internal.host=smtp;socket=tls.connect(internal,function(){self._send(this,options)})}else socket=net.createConnection(options.port,smtp);socket.on("error",function(err){socket.destroy();self.closed=true;if(err.stack.indexOf("ECONNRESET")!==-1)mailer.emit("error",err,self)});socket.on("clientError",function(err){mailer.emit("error",err,self)});socket.on("connect",function(){if(!options.secure)self._send(socket,options)});return self};Message.prototype._send=function(socket,options){var self=this;var command="";var buffer=[];var message=[];var host=getHostName(self.addressFrom.address);var date=new Date;var boundary="--totaljs"+date.getTime();var isAuthenticated=false;var isAuthorization=false;var authType="";var auth=[];var err=null;var ending=null;mailer.emit("send",self);socket.setTimeout(options.timeout||5e3,function(){self.closed=true;mailer.emit("error",new Error(framework_utils.httpStatus(408)),self);if(socket!==null)socket.destroy();socket=null});socket.setEncoding("utf8");var write=function(line){if(self.closed)return;if(mailer.debug)console.log("SEND",line);socket.write(line+CRLF)};buffer.push("MAIL FROM: <"+self.addressFrom.address+">");message.push("Message-ID: <"+GUID()+"@WIN-"+s4()+">");message.push("MIME-Version: 1.0");message.push("From: "+(self.addressFrom.name.length>0?unicode_encode(self.addressFrom.name)+" <"+self.addressFrom.address+">":self.addressFrom.address));var length=self.addressTo.length;var builder="";if(length>0){for(var i=0;i<length;i++){var mail="<"+self.addressTo[i]+">";buffer.push("RCPT TO: "+mail);builder+=(builder!==""?", ":"")+mail}message.push("To: "+builder);builder=""}length=self.addressCC.length;if(length>0){for(var i=0;i<length;i++){var mail="<"+self.addressCC[i]+">";buffer.push("RCPT TO: "+mail);builder+=(builder!==""?", ":"")+mail}message.push("Cc: "+builder);builder=""}length=self.addressBCC.length;if(length>0){for(var i=0;i<length;i++)buffer.push("RCPT TO: <"+self.addressBCC[i]+">")}buffer.push("DATA");buffer.push("QUIT");buffer.push("");message.push("Date: "+date.toUTCString());message.push("Subject: "+unicode_encode(self.subject));length=self.addressReply.length;if(length>0){for(var i=0;i<length;i++)builder+=(builder!==""?", ":"")+"<"+self.addressReply[i]+">";message.push("Reply-To: "+builder);builder=""}message.push("Content-Type: multipart/mixed; boundary="+boundary);message.push("");message.push("--"+boundary);message.push("Content-Type: "+(self.body.indexOf("<")!==-1&&self.body.lastIndexOf(">")!==-1?"text/html":"text/plain")+"; charset=utf-8");message.push("Content-Transfer-Encoding: base64");message.push("");message.push(prepareBASE64(new Buffer(self.body.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n")).toString("base64")));length=self.files.length;if(mailer.debug){socket.on("end",function(){self.closed=true;if(socket)socket.destroy()})}socket.on("data",function(data){if(self.closed)return;var response=data.toString().split(CRLF);var length=response.length;for(var i=0;i<length;i++){var line=response[i];if(line==="")continue;if(socket)socket.emit("line",line)}});socket.on("line",function(line){line=line.toUpperCase();if(mailer.debug)console.log("<–––",line);var code=parseInt(line.match(/\d+/)[0],10);if(code===250&&!isAuthorization){if(line.indexOf("AUTH LOGIN PLAIN")!==-1||line.indexOf("AUTH PLAIN LOGIN")!==-1||options.user&&options.password){authType="plain";isAuthorization=true;if(line.indexOf("XOAUTH")===-1){auth.push("AUTH LOGIN");auth.push(new Buffer(options.user).toString("base64"));auth.push(new Buffer(options.password).toString("base64"))}else auth.push("AUTH PLAIN "+new Buffer("\x00"+options.user+"\x00"+options.password).toString("base64"))}}if(line.substring(3,4)==="-"){return}if(!isAuthenticated&&isAuthorization){isAuthenticated=true;code=334}switch(code){case 220:command=/\besmtp\b/i.test(line)?"EHLO":"HELO";write(command+" "+host);break;case 221:case 250:case 251:case 235:write(buffer.shift());if(buffer.length===0){mailer.emit("success",self);if(self.callback)self.callback(null);ending=setTimeout(function(){if(socket!==null)socket.destroy();socket=null},500)}break;case 334:var value=auth.shift();if(value===undefined){err=new Error("Forbidden.");mailer.emit("error",err,self);if(self.callback)self.callback(err);if(socket!==null)socket.destroy();socket=null;break}write(value);break;case 354:write(message.join(CRLF));if(self.files.length>0){message=null;self._writeAttachment(write,boundary,socket);return}write("--"+boundary+"--");write("");write(".");message=null;break;default:if(code<400)break;err=new Error(line);if(socket!==null)socket.destroy();socket=null;mailer.emit("error",err,self);if(self.callback)self.callback(err);break}})};Message.prototype._writeAttachment=function(write,boundary,socket){var self=this;var attachment=self.files.shift();if(attachment===undefined){write("--"+boundary+"--");write("");write(".");return}var name=attachment.name;var stream=fs.createReadStream(attachment.filename,{encoding:"base64"});var message=[];var ext=path.extname(attachment.filename);message.push("--"+boundary);if(attachment.contentId){message.push('Content-Disposition: inline; filename="'+name+'"');message.push("Content-ID: <"+attachment.contentId+">")}else{message.push('Content-Disposition: attachment; filename="'+name+'"')}message.push("Content-Type: application/octet-stream;");message.push("Content-Transfer-Encoding: base64");message.push(CRLF);write(message.join(CRLF));stream.on("data",function(buf){var length=buf.length;var count=0;var beg=0;while(count<length){count+=68;if(count>length)count=length;write(buf.slice(beg,count).toString("base64"));beg=count}});stream.on("end",function(){write(CRLF);self._writeAttachment(write,boundary,socket)});return self};function prepareBASE64(value){var index=0;var output="";var length=value.length;while(index<length){var max=index+68;if(max>length)max=length;output+=value.substring(index,max)+"\n";index=max}return output}function getHostName(address){return address.substring(address.indexOf("@")+1)}function s4(){return Math.floor((1+Math.random())*65536).toString(16).substring(1).toUpperCase()}function GUID(){return s4()+s4()+"-"+s4()+"-"+s4()+"-"+s4()+"-"+s4()+s4()+s4()}function unicode_encode(val){if(!val)return"";return"=?utf-8?B?"+new Buffer(val).toString("base64")+"?="}var mailer=new Mailer;module.exports=mailer;
exports.hook_mail = function (next, connection, params) { const plugin = this; const mail_from = params[0]; const txn = connection.transaction; const results = txn.results; // Check for MAIL FROM without an @ first - ignore those here if (!mail_from.host) { results.add(plugin, {skip: 'null host'}); return next(); } let called_next = 0; const domain = mail_from.host; const c = plugin.cfg.main; const timeout_id = setTimeout(() => { // DNS answer didn't return (UDP) connection.loginfo(plugin, 'timed out resolving MX for ' + domain); called_next++; if (txn) results.add(plugin, {err: 'timeout(' + domain + ')'}); next(DENYSOFT, 'Temporary resolver error (timeout)'); }, c.timeout * 1000); function mxDone (code, reply) { if (called_next) return; clearTimeout(timeout_id); called_next++; next(code, reply); } // IS: IPv6 compatible dns.resolveMx(domain, (err, addresses) => { if (!txn) return; if (err && plugin.mxErr(connection, domain, 'MX', err, mxDone)) return; if (!addresses || !addresses.length) { // Check for implicit MX 0 record return plugin.implicit_mx(connection, domain, mxDone); } // Verify that the MX records resolve to valid addresses let records = {}; let pending_queries = 0; function check_results () { if (pending_queries !== 0) return; records = Object.keys(records); if (records && records.length) { connection.logdebug(plugin, domain + ': ' + records); results.add(plugin, {pass: '******'}); return mxDone(); } results.add(plugin, {fail: 'has_fwd_dns'}); return mxDone(((c.reject_no_mx) ? DENY : DENYSOFT), 'MX without A/AAAA records'); } addresses.forEach(addr => { // Handle MX records that are IP addresses // This is invalid - but a lot of MTAs allow it. if (net_utils.get_ipany_re('^\\[','\\]$','').test(addr.exchange)) { connection.logwarn(plugin, domain + ': invalid MX ' + addr.exchange); if (c.allow_mx_ip) { records[addr.exchange] = 1; } return; } pending_queries++; net_utils.get_ips_by_host(addr.exchange, (err2, addresses2) => { pending_queries--; if (!txn) return; if (err2 && err2.length === 2) { results.add(plugin, {msg: err2[0].message}); connection.logdebug(plugin, domain + ': MX ' + addr.priority + ' ' + addr.exchange + ' => ' + err2[0].message); check_results(); return; } connection.logdebug(plugin, domain + ': MX ' + addr.priority + ' ' + addr.exchange + ' => ' + addresses2); for (let i=0; i < addresses2.length; i++) { // Ignore anything obviously bogus if (net.isIPv4(addresses2[i])){ if (plugin.re_bogus_ip.test(addresses2[i])) { connection.logdebug(plugin, addr.exchange + ': discarding ' + addresses2[i]); continue; } } if (net.isIPv6(addresses2[i])){ if (net_utils.ipv6_bogus(addresses2[i])) { connection.logdebug(plugin, addr.exchange + ': discarding ' + addresses2[i]); continue; } } records[addresses2[i]] = 1; } check_results(); }); }); // In case we don't run any queries check_results(); }); }
exports.lookup_mx = function lookup_mx (domain, cb) { var mxs = []; // Possible DNS errors // NODATA // FORMERR // BADRESP // NOTFOUND // BADNAME // TIMEOUT // CONNREFUSED // NOMEM // DESTRUCTION // NOTIMP // EREFUSED // SERVFAIL // default wrap_mx just returns our object with "priority" and "exchange" keys var wrap_mx = function (a) { return a }; var process_dns = function (err, addresses) { if (err) { if (err.code === 'ENODATA') { // Most likely this is a hostname with no MX record // Drop through and we'll get the A record instead. return 0; } cb(err); } else if (addresses && addresses.length) { for (var i=0,l=addresses.length; i < l; i++) { var mx = wrap_mx(addresses[i]); // hmail.logdebug("Got an MX from DNS: " + hmail.todo.domain + " => " + mx.priority + " " + mx.exchange); mxs.push(mx); } cb(null, mxs); } else { // return zero if we need to keep trying next option return 0; } return 1; }; dns.resolveMx(domain, function(err, addresses) { if (process_dns(err, addresses)) { return; } // if MX lookup failed, we lookup an A record. To do that we change // wrap_mx() to return same thing as resolveMx() does. wrap_mx = function (a) { return {priority:0,exchange:a} }; dns.resolve(domain, function(err, addresses) { if (process_dns(err, addresses)) { return; } var err = new Error("Found nowhere to deliver to"); err.code = 'NOMX'; cb(err); }); }); }