Socket
Socket
Sign inDemoInstall

nodemailer

Package Overview
Dependencies
Maintainers
1
Versions
271
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nodemailer - npm Package Compare versions

Comparing version 4.7.0 to 5.0.0

5

CHANGELOG.md
# CHANGELOG
## 5.0.0 2018-12-28
- Start using dns.resolve() instead of dns.lookup() for resolving SMTP hostnames. Might be breaking change on some environments so upgrade with care
- Show more logs for renewing OAuth2 tokens, previously it was not possible to see what actually failed
## 4.7.0 2018-11-19

@@ -4,0 +9,0 @@

122

lib/shared/index.js

@@ -9,3 +9,123 @@ /* eslint no-console: 0 */

const fetch = require('../fetch');
const dns = require('dns');
const net = require('net');
const DNS_TTL = 5 * 60 * 1000;
const resolver = (family, hostname, options, callback) => {
dns['resolve' + family](hostname, options, (err, addresses) => {
if (err) {
switch (err.code) {
case dns.NODATA:
case dns.NOTFOUND:
case dns.NOTIMP:
return callback(null, []);
}
return callback(err);
}
return callback(null, Array.isArray(addresses) ? addresses : [].concat(addresses || []));
});
};
const dnsCache = (module.exports.dnsCache = new Map());
module.exports.resolveHostname = (options, callback) => {
options = options || {};
if (!options.host || net.isIP(options.host)) {
// nothing to do here
let value = {
host: options.host,
servername: options.servername || false
};
return callback(null, value);
}
let cached;
if (dnsCache.has(options.host)) {
cached = dnsCache.get(options.host);
if (!cached.expires || cached.expires >= Date.now()) {
return callback(null, {
host: cached.value.host,
servername: cached.value.servername,
_cached: true
});
}
}
resolver(4, options.host, {}, (err, addresses) => {
if (err) {
if (cached) {
// ignore error, use expired value
return callback(null, cached.value);
}
return callback(err);
}
if (addresses && addresses.length) {
let value = {
host: addresses[0] || options.host,
servername: options.servername || options.host
};
dnsCache.set(options.host, {
value,
expires: Date.now() + DNS_TTL
});
return callback(null, value);
}
resolver(6, options.host, {}, (err, addresses) => {
if (err) {
if (cached) {
// ignore error, use expired value
return callback(null, cached.value);
}
return callback(err);
}
if (addresses && addresses.length) {
let value = {
host: addresses[0] || options.host,
servername: options.servername || options.host
};
dnsCache.set(options.host, {
value,
expires: Date.now() + DNS_TTL
});
return callback(null, value);
}
try {
dns.lookup(options.host, {}, (err, address) => {
if (err) {
if (cached) {
// ignore error, use expired value
return callback(null, cached.value);
}
return callback(err);
}
if (!address && cached) {
// nothing was found, fallback to cached value
return callback(null, cached.value);
}
let value = {
host: address || options.host,
servername: options.servername || options.host
};
dnsCache.set(options.host, {
value,
expires: Date.now() + DNS_TTL
});
return callback(null, value);
});
} catch (err) {
if (cached) {
// ignore error, use expired value
return callback(null, cached.value);
}
return callback(err);
}
});
});
};
/**

@@ -150,3 +270,3 @@ * Parses connection url to a structured configuration object

/**
* Wrapper for creating a callback than either resolves or rejects a promise
* Wrapper for creating a callback that either resolves or rejects a promise
* based on input

@@ -153,0 +273,0 @@ *

152

lib/smtp-connection/index.js

@@ -206,2 +206,12 @@ 'use strict';

let setupConnectionHandlers = () => {
this._connectionTimeout = setTimeout(() => {
this._onError('Connection timeout', 'ETIMEDOUT', false, 'CONN');
}, this.options.connectionTimeout || CONNECTION_TIMEOUT);
this._socket.on('error', err => {
this._onError(err, 'ECONNECTION', false, 'CONN');
});
};
if (this.options.connection) {

@@ -223,17 +233,41 @@ // connection is already opened

}
return;
} else if (this.options.socket) {
// socket object is set up but not yet connected
this._socket = this.options.socket;
try {
this._socket.connect(
this.port,
this.host,
() => {
this._socket.setKeepAlive(true);
this._onConnect();
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved._cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved._cached ? 'hit' : 'miss'
);
Object.keys(resolved).forEach(key => {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
);
} catch (E) {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
try {
this._socket.connect(
this.port,
this.host,
() => {
this._socket.setKeepAlive(true);
this._onConnect();
}
);
setupConnectionHandlers();
} catch (E) {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
} else if (this.secureConnection) {

@@ -246,37 +280,73 @@ // connect using tls

}
try {
this._socket = tls.connect(
this.port,
this.host,
opts,
() => {
this._socket.setKeepAlive(true);
this._onConnect();
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved._cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved._cached ? 'hit' : 'miss'
);
Object.keys(resolved).forEach(key => {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
);
} catch (E) {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
try {
this._socket = tls.connect(
opts,
() => {
this._socket.setKeepAlive(true);
this._onConnect();
}
);
setupConnectionHandlers();
} catch (E) {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
} else {
// connect using plaintext
try {
this._socket = net.connect(
opts,
() => {
this._socket.setKeepAlive(true);
this._onConnect();
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved._cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved._cached ? 'hit' : 'miss'
);
Object.keys(resolved).forEach(key => {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
);
} catch (E) {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
try {
this._socket = net.connect(
opts,
() => {
this._socket.setKeepAlive(true);
this._onConnect();
}
);
setupConnectionHandlers();
} catch (E) {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
}
this._connectionTimeout = setTimeout(() => {
this._onError('Connection timeout', 'ETIMEDOUT', false, 'CONN');
}, this.options.connectionTimeout || CONNECTION_TIMEOUT);
this._socket.on('error', err => {
this._onError(err, 'ECONNECTION', false, 'CONN');
});
}

@@ -283,0 +353,0 @@

@@ -154,6 +154,7 @@ 'use strict';

let urlOptions;
let loggedUrlOptions;
if (this.options.serviceClient) {
// service account - https://developers.google.com/identity/protocols/OAuth2ServiceAccount
let iat = Math.floor(Date.now() / 1000); // unix time
let token = this.jwtSignRS256({
let tokenData = {
iss: this.options.serviceClient,

@@ -165,3 +166,4 @@ scope: this.options.scope || 'https://mail.google.com/',

exp: iat + this.options.serviceRequestTimeout
});
};
let token = this.jwtSignRS256(tokenData);

@@ -172,2 +174,7 @@ urlOptions = {

};
loggedUrlOptions = {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: tokenData
};
} else {

@@ -185,2 +192,9 @@ if (!this.options.refreshToken) {

};
loggedUrlOptions = {
client_id: this.options.clientId || '',
client_secret: (this.options.clientSecret || '').substr(0, 6) + '...',
refresh_token: (this.options.refreshToken || '').substr(0, 6) + '...',
grant_type: 'refresh_token'
};
}

@@ -190,4 +204,15 @@

urlOptions[key] = this.options.customParams[key];
loggedUrlOptions[key] = this.options.customParams[key];
});
this.logger.debug(
{
tnx: 'OAUTH2',
user: this.options.user,
action: 'generate'
},
'Requesting token using: %s',
JSON.stringify(loggedUrlOptions)
);
this.postRequest(this.options.accessUrl, urlOptions, this.options, (error, body) => {

@@ -207,5 +232,33 @@ let data;

if (!data || typeof data !== 'object') {
this.logger.debug(
{
tnx: 'OAUTH2',
user: this.options.user,
action: 'post'
},
'Response: %s',
(body || '').toString()
);
return callback(new Error('Invalid authentication response'));
}
let logData = {};
Object.keys(data).forEach(key => {
if (key !== 'access_token') {
logData[key] = data[key];
} else {
logData[key] = (data[key] || '').toString().substr(0, 6) + '...';
}
});
this.logger.debug(
{
tnx: 'OAUTH2',
user: this.options.user,
action: 'post'
},
'Response: %s',
JSON.stringify(logData)
);
if (data.error) {

@@ -256,3 +309,4 @@ return callback(new Error(data.error));

headers: params.customHeaders,
body: payload
body: payload,
allowErrorResponse: true
});

@@ -259,0 +313,0 @@

{
"name": "nodemailer",
"version": "4.7.0",
"version": "5.0.0",
"description": "Easy as cake e-mail sending from your Node.js applications",

@@ -5,0 +5,0 @@ "main": "lib/nodemailer.js",

# Nodemailer
[![Backers on Open Collective](https://opencollective.com/nodemailer/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nodemailer/sponsors/badge.svg)](#sponsors)
[![Backers on Open Collective](https://opencollective.com/nodemailer/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nodemailer/sponsors/badge.svg)](#sponsors)
[![Nodemailer](https://raw.githubusercontent.com/nodemailer/nodemailer/master/assets/nm_logo_200x136.png)](https://nodemailer.com/about/)

@@ -12,2 +13,6 @@

## Why version bump to 5?
Nodemailer changed from `dns.lookup()` to `dns.resolve` for resolving SMTP hostnames which might be backwards incompatible and thus the version bump. Nodemailer tries first `resolve4()` and if no match is found then `resolve6()` and finally reverts back to `lookup()`. Additionally found DNS results are cached (for 5 minutes). This should make it easier to manage high performance clients that send a lot of messages in parallel.
## Having an issue?

@@ -39,4 +44,2 @@

## Contributors

@@ -47,3 +50,2 @@

## Backers

@@ -55,3 +57,2 @@

## Sponsors

@@ -72,4 +73,2 @@

### License

@@ -79,5 +78,4 @@

--------------------------------------------------------------------------------
---
The Nodemailer logo was designed by [Sven Kristjansen](https://www.behance.net/kristjansen).

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc