@devmehq/email-validator-js
Version:
Advanced Email Validation with DNS MX lookup and Mailbox Verification
101 lines (100 loc) • 3.91 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyMailboxSMTP = verifyMailboxSMTP;
const net_1 = __importDefault(require("net"));
/**
* @param {String} smtpReply A message from the SMTP server.
* @return {Boolean} True if over quota.
*/
function isOverQuota(smtpReply) {
return smtpReply && /(over quota)/gi.test(smtpReply);
}
/**
* @see https://support.google.com/a/answer/3221692?hl=en
* @see http://www.greenend.org.uk/rjk/tech/smtpreplies.html
* @param {String} smtpReply A response from the SMTP server.
* @return {boolean} True if the error is recognized as a mailbox missing error.
*/
function isInvalidMailboxError(smtpReply) {
return smtpReply && /^(510|511|513|550|551|553)/.test(smtpReply) && !/(junk|spam|openspf|spoofing|host|rbl.+blocked)/gi.test(smtpReply);
}
/**
* @see https://www.ietf.org/mail-archive/web/ietf-smtp/current/msg06344.html
* @param {String} smtpReply A message from the SMTP server.
* @return {Boolean} True if this is a multiline greet.
*/
function isMultilineGreet(smtpReply) {
return smtpReply && /^(250|220)-/.test(smtpReply);
}
async function verifyMailboxSMTP(params) {
// Port 587 → STARTTLS
// Port 465 → TLS
const { local, domain, mxRecords = [], timeout, debug, port = 25 } = params;
const mxRecord = mxRecords[0];
const log = debug ? console.debug : () => { };
if (!mxRecord) {
return false;
}
return new Promise((resolve) => {
log('[verifyMailboxSMTP] connecting to', mxRecord, port);
const socket = net_1.default.connect({
host: mxRecord,
port,
});
// eslint-disable-next-line prefer-const
let resTimeout;
let resolved;
const ret = (result) => {
if (resolved)
return;
if (!(socket === null || socket === void 0 ? void 0 : socket.destroyed)) {
log('[verifyMailboxSMTP] closing socket');
socket === null || socket === void 0 ? void 0 : socket.write('QUIT\r\n');
socket === null || socket === void 0 ? void 0 : socket.end();
}
clearTimeout(resTimeout);
resolve(result);
resolved = true;
};
const messages = [`HELO ${domain}`, `MAIL FROM: <${local}@${domain}>`, `RCPT TO: <${local}@${domain}>`];
log('[verifyMailboxSMTP] writing messages', messages);
socket.on('data', (data) => {
const dataString = String(data);
log('[verifyMailboxSMTP] got data', dataString);
if (isInvalidMailboxError(dataString))
return ret(false);
if (isOverQuota(dataString))
return ret(false);
if (!dataString.includes('220') && !dataString.includes('250'))
return ret(null);
if (isMultilineGreet(dataString))
return;
if (messages.length > 0) {
const message = messages.shift();
log('[verifyMailboxSMTP] writing message', message);
return socket.write(`${message}\r\n`);
}
ret(true);
});
socket.on('error', (err) => {
log('[verifyMailboxSMTP] error in socket', err);
ret(null);
});
socket.on('close', (err) => {
log('[verifyMailboxSMTP] close socket', err);
ret(null);
});
socket.on('timeout', () => {
log('[verifyMailboxSMTP] timeout socket');
ret(null);
});
resTimeout = setTimeout(() => {
log(`[verifyMailboxSMTP] timed out (${timeout} ms)`);
socket.destroy();
ret(null);
}, timeout);
});
}
;