feat: Add XMPP dialog in STARTTLS routine (#6508)

This commit is contained in:
Shaan 2025-12-20 18:35:53 +06:00 committed by GitHub
parent 22a0ed6061
commit f3c76dbc6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 6 deletions

View File

@ -54,12 +54,32 @@ class TCPMonitorType extends MonitorType {
const preTLS = () =>
new Promise((resolve, reject) => {
let timeout;
let dialogTimeout;
let bannerTimeout;
socket_ = net.connect(monitor.port, monitor.hostname);
const onTimeout = () => {
log.debug(this.name, `[${monitor.name}] Pre-TLS connection timed out`);
reject("Connection timed out");
doReject("Connection timed out");
};
const onBannerTimeout = () => {
log.debug(this.name, `[${monitor.name}] Pre-TLS timed out waiting for banner`);
// No banner. Could be a XMPP server?
socket_.write(`<stream:stream to='${monitor.hostname}' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>`);
};
const doResolve = () => {
dialogTimeout && clearTimeout(dialogTimeout);
bannerTimeout && clearTimeout(bannerTimeout);
resolve({ socket: socket_ });
};
const doReject = (error) => {
dialogTimeout && clearTimeout(dialogTimeout);
bannerTimeout && clearTimeout(bannerTimeout);
socket_.end();
reject(error);
};
socket_.on("connect", () => {
@ -70,10 +90,10 @@ class TCPMonitorType extends MonitorType {
const response = data.toString();
const response_ = response.toLowerCase();
log.debug(this.name, `[${monitor.name}] Pre-TLS response: ${response}`);
clearTimeout(bannerTimeout);
switch (true) {
case response_.includes("start tls") || response_.includes("begin tls"):
timeout && clearTimeout(timeout);
resolve({ socket: socket_ });
doResolve();
break;
case response.startsWith("* OK") || response.match(/CAPABILITY.+STARTTLS/):
socket_.write("a001 STARTTLS\r\n");
@ -84,8 +104,16 @@ class TCPMonitorType extends MonitorType {
case response.includes("250-STARTTLS"):
socket_.write("STARTTLS\r\n");
break;
case response_.includes("<proceed"):
doResolve();
break;
case response_.includes("<starttls"):
socket_.write("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
break;
case response_.includes("<stream:stream") || response_.includes("</stream:stream>"):
break;
default:
reject(`Unexpected response: ${response}`);
doReject(`Unexpected response: ${response}`);
}
});
socket_.on("error", error => {
@ -93,7 +121,8 @@ class TCPMonitorType extends MonitorType {
reject(error);
});
socket_.setTimeout(1000 * TIMEOUT, onTimeout);
timeout = setTimeout(onTimeout, 1000 * TIMEOUT);
dialogTimeout = setTimeout(onTimeout, 1000 * TIMEOUT);
bannerTimeout = setTimeout(onBannerTimeout, 1000 * 1.5);
});
const reuseSocket = monitor.smtpSecurity === "starttls" ? await preTLS() : {};

View File

@ -185,4 +185,26 @@ describe("TCP Monitor", () => {
regex
);
});
test("XMPP server with valid certificate (STARTTLS)", async t => {
const tcpMonitor = new TCPMonitorType();
const monitor = {
hostname: "xmpp.earth",
port: 5222,
smtpSecurity: "starttls",
isEnabledExpiryNotification: () => true,
handleTlsInfo: async tlsInfo => {
return tlsInfo;
},
};
const heartbeat = {
msg: "",
status: PENDING,
};
await tcpMonitor.check(monitor, heartbeat, {});
assert.strictEqual(heartbeat.status, UP);
});
});