I was attempting to create a simple Server <=> Client test to see how this module works, and experiment a bit. I noticed something breaking it. If you run the server.js without SU(super user) permissions, you cannot bind the port(465). That's normal, I know that. When I run it as SU, it is able to bind the port, and I can run client.js. Things execute properly, cool(I've got that down then.)
I don't like working in SU space when I'm testing or developing. So I changed my var PORT to 9465, on both the server.js and the client.js files. Ran the same thing again and it doesn't connect, the client clearly reaches the servers socket, but after a few seconds it times out and fails. The server reports unexpected socket close, and the client claims "Greeting never received". I've tried getting more debug info out of this, but I can't figure out how to do that. Here's the real kicker, I then thought ok, lets try running it as SU again(keeping the ports at 9465). When doing that, again, the server saw the socket close unexpectedly, and the client complained exactly the same.
Is this my problem or possibly a bug? I've replicated the project on another machine and it fails there too(attached at end).
Env Versions:
Node Versions: v10.24.0 through v14.16.1
mailparser@3.2.0
smtp-server@3.9.0 AND 3.8.0
Source Code
Here's the server.js code:
const smtp_server = require("smtp-server");
const mailparser = require("mailparser");
const crypto = require("crypto");
const fs = require("fs");
var PORT = 465; //ANYTHING BUT THIS VALUE CAUSES ISSUES
function hash(data, algorithm = 'md5') {
// Algorithm depends on availability of OpenSSL on platform
// Another algorithms: 'sha1', 'md5', 'sha256', 'sha512' ...
let shasum = crypto.createHash(algorithm);
try {
shasum.update(data);
return shasum.digest('hex');
} catch (error) {
return 'calc fail';
}
}
/*OAuth2 authentication*/
function onOAuth(auth, session, callback) {
if(auth.method !== "XOAUTH2") {
// should never occur in this case as only XOAUTH2 is allowed
return callback(new Error("Expecting XOAUTH2"));
}
if(auth.username !== "abc" || auth.accessToken !== "def") {
return callback(null, {
data: {
status: "401",
schemes: "bearer mac",
scope: "my_smtp_access_scope_name"
}
});
}
callback(null, { user: 123 }); // where 123 is the user id or similar property
}
function sanitizeHtml(input) {
return input.replace(/[\<]/g,"[").replace(/[\>]/g,"]");
}
const server = new smtp_server.SMTPServer({
logger: console,
name: "localhost",
banner: "welcome?",
size: 1024*8, // allow messages up to 1kb*#
secure: true,
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
onConnect: function( session, callback ) {
console.error("onConnect", session, callback);
if(session.remoteAddress != "127.0.0.1") {
console.error("Error connection not from localhost");
return callback(new Error("No connections from localhost allowed"));
}
return callback(); // Accept the connection
},
//authMethods: ["PLAIN", "LOGIN", "XOAUTH2"], //defaults to [‘PLAIN’, ‘LOGIN’].
//authOptional: true,
onAuth(auth, session, callback) {
console.log("onAuth: User...", auth, session );
if (auth.username !== "abc" || auth.password !== "def") {
return callback(new Error("Invalid username or password"));
}
callback(null, { user: 123 }); // where 123 is the user id or similar property
},
onData(stream, session, callback) {
console.log("onData: Receiving Mail...", session );
mailparser.simpleParser(stream).then((parsed)=>{
let mail = {
to: parsed.to.text,
from: parsed.from.text,
subject: parsed.subject,
received: parsed.date,
body: parsed.text,
htmlBody: ( parsed.html ? parsed.text : "" )
};
if(parsed.html) {
try {
mail.htmlBody = sanitizeHtml(parsed.html);
} catch (error) {
mail.htmlBody = parsed.text;
}
} else {
mail.htmlBody = "";
}
console.log("email",mail);
});
},
onRcptTo(address, session, callback) {
console.log("onRcptTo", address, session);
// do not accept messages larger than 100 bytes to specific recipients
let expectedSize = Number(session.envelope.mailFrom.args.SIZE) || 0;
if (address.address === "almost-full@example.com" && expectedSize > 100) {
err = new Error("Insufficient channel storage: " + address.address);
err.responseCode = 452;
return callback(err);
}
callback();
},
onMailFrom(address, session, callback) {
console.log("onMailFrom: Received Mail from", address, session );
if(address.address !== "allowed@example.com") {
return callback( new Error("Only allowed@example.com is allowed to send mail") );
}
return callback(); // Accept the address
},
onClose(address, session, callback) {
console.log("onClose", address, session, callback);
}
});
server.on("error", ( err )=>{
console.error("Error", err.message);
});
server.on("close", ( session )=>{
console.error("close", session);
console.log("Client Disconnect", session);
});
server.on("connect", ( session )=>{
console.error("connect", session);
});
server.on("auth", ( auth, session, callback )=>{
console.error("auth", auth, session, callback);
});
server.on("mailfrom", ( address, session, callback )=>{
console.log("mailfrom", address, session, callback );
});
server.on("rcptto", ( address, session, callback )=>{
console.log("rcptto", address, session, callback );
});
server.on("data", ( stream, session, callback )=>{
console.log("data", stream, session, callback );
});
console.log("Listen", PORT);
server.listen(PORT,"127.0.0.1", function() {
console.log("Listening...", arguments);
});
This is the client.js code, sending an email to the server.js:
const nodemailer = require('nodemailer');
var PORT = 465; //ANYTHING BUT THIS VALUE CAUSES ISSUES
let transport = nodemailer.createTransport({
host: '127.0.0.1',
port: PORT,
auth: {
user: 'abc',
pass: 'def'
},
tls: {
rejectUnauthorized: false
}
});
const message = {
from: 'allowed@example.com', // Sender address
to: 'allowed@example.com', // List of recipients
subject: 'a test email', // Subject line
text: 'Text in an email body.' // Plain text body
};
transport.sendMail(message, function(err, info) {
if(err) {
console.error("Error:",err)
} else {
console.log("Sent message");
console.log(info);
}
});
Server Log Output(when running as SU and client connects to send)[AKA WORKING]:
Error: Greeting never received
at SMTPConnection._formatError (/node_modules/nodemailer/lib/smtp-connection/index.js:774:19)
at SMTPConnection._onError (/node_modules/nodemailer/lib/smtp-connection/index.js:760:20)
at Timeout.<anonymous> (/node_modules/nodemailer/lib/smtp-connection/index.js:694:22)
at listOnTimeout (internal/timers.js:554:17)
at processTimers (internal/timers.js:497:7) {
code: 'ETIMEDOUT',
command: 'CONN'
}
2nd Machine Test:
[debuguser@server ~]$ node server.js &
[1] 8460
[debuguser@server ~]$ Listen 9465
{ component: 'smtp-server',
tnx: 'listen',
host: '127.0.0.1',
port: 9465,
secure: true,
protocol: 'SMTP' } '%s%s Server listening on %s:%s' 'Secure ' 'SMTP' '127.0.0.1' 9465
Listening... [Arguments] {}
[debuguser@server ~]$
[debuguser@server ~]$
[debuguser@server ~]$ node client.js
Error: { Error: Greeting never received
at SMTPConnection._formatError (/home/debuguser/node_modules/nodemailer/lib/smtp-connection/index.js:774:19)
at SMTPConnection._onError (/home/debuguser/node_modules/nodemailer/lib/smtp-connection/index.js:760:20)
at Timeout._greetingTimeout.setTimeout (/home/debuguser/node_modules/nodemailer/lib/smtp-connection/index.js:694:22)
at ontimeout (timers.js:436:11)
at tryOnTimeout (timers.js:300:5)
at listOnTimeout (timers.js:263:5)
at Timer.processTimers (timers.js:223:10) code: 'ETIMEDOUT', command: 'CONN' }
Error Socket closed unexpectedly
[debuguser@server ~]$
I was attempting to create a simple Server <=> Client test to see how this module works, and experiment a bit. I noticed something breaking it. If you run the server.js without SU(super user) permissions, you cannot bind the port(465). That's normal, I know that. When I run it as SU, it is able to bind the port, and I can run client.js. Things execute properly, cool(I've got that down then.)
I don't like working in SU space when I'm testing or developing. So I changed my
var PORT
to 9465, on both the server.js and the client.js files. Ran the same thing again and it doesn't connect, the client clearly reaches the servers socket, but after a few seconds it times out and fails. The server reports unexpected socket close, and the client claims "Greeting never received". I've tried getting more debug info out of this, but I can't figure out how to do that. Here's the real kicker, I then thought ok, lets try running it as SU again(keeping the ports at 9465). When doing that, again, the server saw the socket close unexpectedly, and the client complained exactly the same.Is this my problem or possibly a bug? I've replicated the project on another machine and it fails there too(attached at end).
Env Versions:
Node Versions: v10.24.0 through v14.16.1
Source Code
Here's the server.js code:
This is the client.js code, sending an email to the server.js:
Server Log Output(when running as SU and client connects to send)[AKA WORKING]:
ERRORONOUS RUN
When I run the server with port 9465, 1465, or any other port not in restricted space. Here's the result in the log:
Client Log:
2nd Machine Test: