twiedenbein-thelounge
Version:
The self-hosted Web IRC client
140 lines (139 loc) • 6.27 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const net_1 = __importDefault(require("net"));
const chalk_1 = __importDefault(require("chalk"));
const helper_1 = __importDefault(require("./helper"));
const config_1 = __importDefault(require("./config"));
const log_1 = __importDefault(require("./log"));
class Identification {
connectionId;
connections;
oidentdFile;
constructor(startedCallback) {
this.connectionId = 0;
this.connections = new Map();
if (typeof config_1.default.values.oidentd === "string") {
this.oidentdFile = helper_1.default.expandHome(config_1.default.values.oidentd);
log_1.default.info(`Oidentd file: ${chalk_1.default.green(this.oidentdFile)}`);
this.refresh();
}
if (config_1.default.values.identd.enable) {
if (this.oidentdFile) {
log_1.default.warn("Using both identd and oidentd at the same time, this is most likely not intended.");
}
const server = net_1.default.createServer(this.serverConnection.bind(this));
server.on("error", (err) => {
startedCallback(this, err);
});
server.listen({
port: config_1.default.values.identd.port || 113,
host: config_1.default.values.bind,
}, () => {
const address = server.address();
if (typeof address === "string") {
log_1.default.info(`Identd server available on ${chalk_1.default.green(address)}`);
}
else if (address?.address) {
log_1.default.info(`Identd server available on ${chalk_1.default.green(address.address + ":" + address.port.toString())}`);
}
startedCallback(this);
});
}
else {
startedCallback(this);
}
}
serverConnection(socket) {
socket.on("error", (err) => log_1.default.error(`Identd socket error: ${err}`));
socket.setTimeout(5000, () => {
log_1.default.warn(`identd: no data received, closing connection to ${socket.remoteAddress || "undefined"}`);
socket.destroy();
});
socket.once("data", (data) => {
this.respondToIdent(socket, data);
socket.end();
});
}
respondToIdent(socket, buffer) {
if (!socket.remoteAddress) {
log_1.default.warn("identd: no remote address");
return;
}
const data = buffer.toString().split(",");
const lport = parseInt(data[0], 10) || 0;
const fport = parseInt(data[1], 10) || 0;
if (lport < 1 || fport < 1 || lport > 65535 || fport > 65535) {
log_1.default.warn(`identd: bogus request from ${socket.remoteAddress}`);
return;
}
log_1.default.debug(`identd: remote ${socket.remoteAddress} query ${lport}, ${fport}`);
for (const connection of this.connections.values()) {
// we only want to respond if all the ip,port tuples match, to avoid user enumeration
if (connection.socket.remotePort === fport &&
connection.socket.localPort === lport &&
socket.remoteAddress === connection.socket.remoteAddress &&
socket.localAddress === connection.socket.localAddress) {
const reply = `${lport}, ${fport} : USERID : TheLounge : ${connection.user}\r\n`;
log_1.default.debug(`identd: reply is ${reply.trimEnd()}`);
socket.write(reply);
return;
}
}
const reply = `${lport}, ${fport} : ERROR : NO-USER\r\n`;
log_1.default.debug(`identd: reply is ${reply.trimEnd()}`);
socket.write(reply);
}
addSocket(socket, user) {
const id = ++this.connectionId;
this.connections.set(id, { socket, user });
if (this.oidentdFile) {
this.refresh();
}
return id;
}
removeSocket(id) {
this.connections.delete(id);
if (this.oidentdFile) {
this.refresh();
}
}
refresh() {
let file = "# Warning: file generated by The Lounge: changes will be overwritten!\n";
this.connections.forEach((connection, id) => {
if (!connection.socket.remotePort || !connection.socket.localPort) {
// Race condition: this can happen when more than one socket gets disconnected at
// once, since we `refresh()` for each one being added/removed. This results
// in there possibly being one or more disconnected sockets remaining when we get here.
log_1.default.warn(`oidentd: socket has no remote or local port (id=${id}). See https://github.com/thelounge/thelounge/pull/4695.`);
return;
}
if (!connection.socket.remoteAddress) {
log_1.default.warn(`oidentd: socket has no remote address, will not respond to queries`);
return;
}
if (!connection.socket.localAddress) {
log_1.default.warn(`oidentd: socket has no local address, will not respond to queries`);
return;
}
// we only want to respond if all the ip,port tuples match, to avoid user enumeration
file +=
`to ${connection.socket.remoteAddress}` +
` fport ${connection.socket.remotePort}` +
` from ${connection.socket.localAddress}` +
` lport ${connection.socket.localPort}` +
` { reply "${connection.user}" }\n`;
});
if (this.oidentdFile) {
fs_1.default.writeFile(this.oidentdFile, file, { flag: "w+" }, function (err) {
if (err) {
log_1.default.error("Failed to update oidentd file!", err.message);
}
});
}
}
}
exports.default = Identification;