throttle some stuff for kvirc tls, fix hostnames
This commit is contained in:
@@ -100,7 +100,7 @@ class WTVIRC {
|
|||||||
this.supported_user_modes = "BRZciorswxz";
|
this.supported_user_modes = "BRZciorswxz";
|
||||||
this.supported_prefixes = ["ohv", "@%+"];
|
this.supported_prefixes = ["ohv", "@%+"];
|
||||||
this.supported_client_caps = ['chghost', 'away-notify', 'echo-message', 'invite-notify', 'multi-prefix', 'userhost-in-names', 'account-notify', 'extended-join'];
|
this.supported_client_caps = ['chghost', 'away-notify', 'echo-message', 'invite-notify', 'multi-prefix', 'userhost-in-names', 'account-notify', 'extended-join'];
|
||||||
this.supported_capabilities = ['TBURST', 'EOB', 'IE', 'EX'];
|
this.supported_server_caps = ['TBURST', 'EOB', 'IE', 'EX'];
|
||||||
this.caps = [
|
this.caps = [
|
||||||
`AWAYLEN=${this.awaylen} CASEMAPPING=rfc1459 BOT=B CHANMODES=${this.supported_channel_modes} CHANNELLEN=${this.channellen} CHANTYPES=${this.channelprefixes.join('')} PREFIX=(${this.supported_prefixes[0]})${this.supported_prefixes[1]} USERMODES=${this.supported_user_modes} MAXLIST=b:${this.maxbans},e:${this.maxexcept},i:${this.maxinvite},k:${this.maxkeylen},l:${this.maxlimit}`,
|
`AWAYLEN=${this.awaylen} CASEMAPPING=rfc1459 BOT=B CHANMODES=${this.supported_channel_modes} CHANNELLEN=${this.channellen} CHANTYPES=${this.channelprefixes.join('')} PREFIX=(${this.supported_prefixes[0]})${this.supported_prefixes[1]} USERMODES=${this.supported_user_modes} MAXLIST=b:${this.maxbans},e:${this.maxexcept},i:${this.maxinvite},k:${this.maxkeylen},l:${this.maxlimit}`,
|
||||||
`CHARSET=ascii MODES=3 EXCEPTS=e INVEX=I NETWORK=${this.network} CHANLIMIT=${this.channelprefixes.join('')}:${this.channellimit} NICKLEN=${this.nicklen} TOPICLEN=${this.topiclen} KICKLEN=${this.kicklen}`
|
`CHARSET=ascii MODES=3 EXCEPTS=e INVEX=I NETWORK=${this.network} CHANLIMIT=${this.channelprefixes.join('')}:${this.channellimit} NICKLEN=${this.nicklen} TOPICLEN=${this.topiclen} KICKLEN=${this.kicklen}`
|
||||||
@@ -123,7 +123,7 @@ class WTVIRC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.server_start_time = this.getDate();
|
this.server_start_time = this.getDate();
|
||||||
this.server = net.createServer(async (socket) => {
|
this.server = net.createServer(async socket => {
|
||||||
// Detect SSL handshake and wrap socket if needed
|
// Detect SSL handshake and wrap socket if needed
|
||||||
socket.once('data', async firstChunk => {
|
socket.once('data', async firstChunk => {
|
||||||
this.totalConnections++;
|
this.totalConnections++;
|
||||||
@@ -158,6 +158,9 @@ class WTVIRC {
|
|||||||
socket.push(firstChunk);
|
socket.push(firstChunk);
|
||||||
|
|
||||||
secureSocket.on('error', (err) => {
|
secureSocket.on('error', (err) => {
|
||||||
|
if (this.debug) {
|
||||||
|
console.error('Secure socket error:', err);
|
||||||
|
}
|
||||||
this.terminateSession(secureSocket, true);
|
this.terminateSession(secureSocket, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -239,7 +242,7 @@ class WTVIRC {
|
|||||||
socket.upgrading_to_tls = false;
|
socket.upgrading_to_tls = false;
|
||||||
socket.client_version = '';
|
socket.client_version = '';
|
||||||
socket.client_caps = [];
|
socket.client_caps = [];
|
||||||
socket.host = this.filterHostname(socket, socket.remoteAddress);
|
this.filterHostname(socket, socket.remoteAddress);
|
||||||
socket.timestamp = this.getDate();
|
socket.timestamp = this.getDate();
|
||||||
socket.secure = false;
|
socket.secure = false;
|
||||||
socket.uniqueId = `${this.serverId}${this.generateUniqueId(socket)}`;
|
socket.uniqueId = `${this.serverId}${this.generateUniqueId(socket)}`;
|
||||||
@@ -328,7 +331,7 @@ class WTVIRC {
|
|||||||
}
|
}
|
||||||
var output_reply = [];
|
var output_reply = [];
|
||||||
for (const cap of capabilities) {
|
for (const cap of capabilities) {
|
||||||
if (this.supported_capabilities.includes(cap)) {
|
if (this.supported_server_caps.includes(cap)) {
|
||||||
output_reply.push(cap);
|
output_reply.push(cap);
|
||||||
} else {
|
} else {
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
@@ -567,7 +570,7 @@ class WTVIRC {
|
|||||||
var oldNick = this.findUserByUniqueId(parts[1]);
|
var oldNick = this.findUserByUniqueId(parts[1]);
|
||||||
var newNick = parts[3];
|
var newNick = parts[3];
|
||||||
var targetSocket = this.findSocketByUniqueId(parts[1]);
|
var targetSocket = this.findSocketByUniqueId(parts[1]);
|
||||||
this.broadcastUser(oldNick, `:${targetSocket.nickname}!${targetSocket.username}@${targetSocket.host} NICK :${newNick}\r\n`);
|
this.broadcastUser(oldNick, `:${oldNick}!${this.usernames.get(oldNick)}@${targetSocket.host} NICK :${newNick}\r\n`);
|
||||||
this.processNickChange(targetSocket, newNick);
|
this.processNickChange(targetSocket, newNick);
|
||||||
this.broadcastToAllServers(line, socket);
|
this.broadcastToAllServers(line, socket);
|
||||||
break;
|
break;
|
||||||
@@ -1108,7 +1111,7 @@ class WTVIRC {
|
|||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
const userSocket = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s) === user);
|
const userSocket = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s) === user);
|
||||||
if (userSocket && userSocket.uniqueId !== sourceUniqueId) {
|
if (userSocket && userSocket.uniqueId !== sourceUniqueId) {
|
||||||
await this.sendThrottled(userSocket, [`:${nickname}!${sourceUsername}@${sourceSocket.host} ${srvCommand} ${targetUniqueId} :${message}`], 30);
|
await this.sendThrottled(userSocket, [`:${nickname}!${sourceUsername}@${sourceSocket.host} ${srvCommand} ${targetUniqueId} :${message}\r\n`], 30);
|
||||||
this.broadcastToAllServers(`:${sourceUniqueId} ${srvCommand} ${targetUniqueId} :${message}\r\n`, socket);
|
this.broadcastToAllServers(`:${sourceUniqueId} ${srvCommand} ${targetUniqueId} :${message}\r\n`, socket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1129,7 +1132,7 @@ class WTVIRC {
|
|||||||
if (this.clientIsWebTV(targetSocket)) {
|
if (this.clientIsWebTV(targetSocket)) {
|
||||||
srvCommand = 'PRIVMSG';
|
srvCommand = 'PRIVMSG';
|
||||||
}
|
}
|
||||||
await this.sendThrottled(targetSocket, [`:${nickname}!${sourceUsername}@${sourceSocket.host} ${srvCommand} ${targetNickname} :${message}`], 30);
|
await this.sendThrottled(targetSocket, [`:${nickname}!${sourceUsername}@${sourceSocket.host} ${srvCommand} ${targetNickname} :${message}\r\n`], 30);
|
||||||
this.broadcastToAllServers(`:${sourceUniqueId} ${srvCommand} ${targetUniqueId} :${message}\r\n`, socket);
|
this.broadcastToAllServers(`:${sourceUniqueId} ${srvCommand} ${targetUniqueId} :${message}\r\n`, socket);
|
||||||
break;
|
break;
|
||||||
case "WHOIS":
|
case "WHOIS":
|
||||||
@@ -1157,9 +1160,10 @@ class WTVIRC {
|
|||||||
whoisNick = whoisSocket.nickname;
|
whoisNick = whoisSocket.nickname;
|
||||||
const whois_username = this.usernames.get(whoisNick);
|
const whois_username = this.usernames.get(whoisNick);
|
||||||
var userinfo = this.userinfo.get(whoisNick) || whoisSocket.userinfo || '';
|
var userinfo = this.userinfo.get(whoisNick) || whoisSocket.userinfo || '';
|
||||||
socket.write(`:${this.serverId} 311 ${targetUniqueId} ${whoisNick} ${whois_username} ${whoisSocket.host} * :${userinfo}\r\n`);
|
var output_lines = [];
|
||||||
|
output_lines.push(`:${this.serverId} 311 ${targetUniqueId} ${whoisNick} ${whois_username} ${whoisSocket.host} * :${userinfo}\r\n`);
|
||||||
if (this.awaymsgs.has(whoisNick)) {
|
if (this.awaymsgs.has(whoisNick)) {
|
||||||
socket.write(`:${this.serverId} 301 ${targetUniqueId} ${whoisNick} :${this.awaymsgs.get(whoisNick)}\r\n`);
|
output_lines.push(`:${this.serverId} 301 ${targetUniqueId} ${whoisNick} :${this.awaymsgs.get(whoisNick)}\r\n`);
|
||||||
}
|
}
|
||||||
const userChannels = [];
|
const userChannels = [];
|
||||||
for (const [ch, users] of this.channels.entries()) {
|
for (const [ch, users] of this.channels.entries()) {
|
||||||
@@ -1179,24 +1183,25 @@ class WTVIRC {
|
|||||||
userChannels.push(prefix + ch);
|
userChannels.push(prefix + ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
socket.write(`:${this.serverId} 312 ${targetUniqueId} ${whoisNick} :${this.servername} :minisrv-${this.minisrv_config.version}\r\n`);
|
output_lines.push(`:${this.serverId} 312 ${targetUniqueId} ${whoisNick} :${this.servername} :minisrv-${this.minisrv_config.version}\r\n`);
|
||||||
if (this.isIRCOp(whoisNick)) {
|
if (this.isIRCOp(whoisNick)) {
|
||||||
socket.write(`:${this.serverId} 313 ${targetUniqueId} ${whoisNick} :is an IRC operator\r\n`);
|
output_lines.push(`:${this.serverId} 313 ${targetUniqueId} ${whoisNick} :is an IRC operator\r\n`);
|
||||||
}
|
}
|
||||||
if (usermodes && this.getUserModes(whoisNick).includes('s')) {
|
if (usermodes && this.getUserModes(whoisNick).includes('s')) {
|
||||||
socket.write(`:${this.serverId} 671 ${targetUniqueId} ${whoisNick} :is using a secure connection\r\n`);
|
output_lines.push(`:${this.serverId} 671 ${targetUniqueId} ${whoisNick} :is using a secure connection\r\n`);
|
||||||
}
|
}
|
||||||
if (usermodes && this.getUserModes(whoisNick).includes('r')) {
|
if (usermodes && this.getUserModes(whoisNick).includes('r')) {
|
||||||
socket.write(`:${this.serverId} 307 ${targetUniqueId} ${whoisNick} :is a registered nick\r\n`);
|
output_lines.push(`:${this.serverId} 307 ${targetUniqueId} ${whoisNick} :is a registered nick\r\n`);
|
||||||
}
|
}
|
||||||
var now = this.getDate();
|
var now = this.getDate();
|
||||||
var userTimestamp = this.usertimestamps.get(whoisNick) || now;
|
var userTimestamp = this.usertimestamps.get(whoisNick) || now;
|
||||||
var idleTime = now - userTimestamp;
|
var idleTime = now - userTimestamp;
|
||||||
socket.write(`:${this.serverId} 317 ${targetUniqueId} ${whoisNick} ${idleTime} ${this.usersignontimestamps.get(whoisNick) || 0} :seconds idle, signon time\r\n`);
|
output_lines.push(`:${this.serverId} 317 ${targetUniqueId} ${whoisNick} ${idleTime} ${this.usersignontimestamps.get(whoisNick) || 0} :seconds idle, signon time\r\n`);
|
||||||
if (userChannels.length > 0) {
|
if (userChannels.length > 0) {
|
||||||
socket.write(`:${this.serverId} 319 ${targetUniqueId} ${whoisNick} :${userChannels.join(' ')}\r\n`);
|
output_lines.push(`:${this.serverId} 319 ${targetUniqueId} ${whoisNick} :${userChannels.join(' ')}\r\n`);
|
||||||
}
|
}
|
||||||
socket.write(`:${this.serverId} 318 ${targetUniqueId} ${whoisNick} :End of /WHOIS list\r\n`);
|
output_lines.push(`:${this.serverId} 318 ${targetUniqueId} ${whoisNick} :End of /WHOIS list\r\n`);
|
||||||
|
await this.sendThrottled(socket, output_lines);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "SVSJOIN":
|
case "SVSJOIN":
|
||||||
@@ -1343,7 +1348,6 @@ class WTVIRC {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
socket.removeAllListeners('error');
|
socket.removeAllListeners('error');
|
||||||
secureSocket.setEncoding('ascii');
|
|
||||||
secureSocket.registered = socket.registered;
|
secureSocket.registered = socket.registered;
|
||||||
secureSocket.nickname = socket.nickname
|
secureSocket.nickname = socket.nickname
|
||||||
secureSocket.username = socket.username
|
secureSocket.username = socket.username
|
||||||
@@ -1382,7 +1386,7 @@ class WTVIRC {
|
|||||||
// Ensure data is a string
|
// Ensure data is a string
|
||||||
if (typeof data !== 'string') {
|
if (typeof data !== 'string') {
|
||||||
if (Buffer.isBuffer(data)) {
|
if (Buffer.isBuffer(data)) {
|
||||||
data = data.toString('utf8');
|
data = data.toString('ascii');
|
||||||
} else if (data && typeof data.toString === 'function') {
|
} else if (data && typeof data.toString === 'function') {
|
||||||
data = data.toString();
|
data = data.toString();
|
||||||
} else {
|
} else {
|
||||||
@@ -2171,11 +2175,13 @@ class WTVIRC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var users = this.getUsersInChannel(ch);
|
var users = this.getUsersInChannel(ch);
|
||||||
|
var output_lines = [];
|
||||||
|
var prefixRegex = new RegExp(`^[${this.supported_prefixes[1]}]`);
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
users.sort((a, b) => {
|
users.sort((a, b) => {
|
||||||
// Remove any prefixes for comparison
|
// Remove any prefixes for comparison
|
||||||
const cleanA = a.replace(/^[@%+]/, '');
|
const cleanA = a.replace(prefixRegex, '');
|
||||||
const cleanB = b.replace(/^[@%+]/, '');
|
const cleanB = b.replace(prefixRegex, '');
|
||||||
// Get privilege for each user
|
// Get privilege for each user
|
||||||
const ops = this.channelops.get(ch) || new Set();
|
const ops = this.channelops.get(ch) || new Set();
|
||||||
const halfops = this.channelhalfops.get(ch) || new Set();
|
const halfops = this.channelhalfops.get(ch) || new Set();
|
||||||
@@ -2194,17 +2200,18 @@ class WTVIRC {
|
|||||||
});
|
});
|
||||||
if (socket.client_caps.includes('userhost-in-names')) {
|
if (socket.client_caps.includes('userhost-in-names')) {
|
||||||
const userHosts = users.map(user => {
|
const userHosts = users.map(user => {
|
||||||
var nick = this.findUser(user.replace(/^[@%+]/, ''));
|
var nick = this.findUser(user.replace(prefixRegex, ''));
|
||||||
var username = this.usernames.get(nick) || 'unknown';
|
var username = this.usernames.get(nick) || 'unknown';
|
||||||
var host = this.hostnames.get(nick) || 'unknown';
|
var host = this.hostnames.get(nick) || 'unknown';
|
||||||
return `${user}!${username}@${host}`;
|
return `${user}!${username}@${host}`;
|
||||||
});
|
});
|
||||||
socket.write(`:${this.servername} 353 ${socket.nickname} = ${ch} :${userHosts.join(' ')}\r\n`);
|
output_lines.push(`:${this.servername} 353 ${socket.nickname} = ${ch} :${userHosts.join(' ')}\r\n`);
|
||||||
} else {
|
} else {
|
||||||
socket.write(`:${this.servername} 353 ${socket.nickname} = ${ch} :${users.join(' ')}\r\n`);
|
output_lines.push(`:${this.servername} 353 ${socket.nickname} = ${ch} :${users.join(' ')}\r\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 366 ${socket.nickname} ${ch} :End of /NAMES list\r\n`);
|
output_lines.push(`:${this.servername} 366 ${socket.nickname} ${ch} :End of /NAMES list\r\n`);
|
||||||
|
this.sendThrottled(socket, output_lines);
|
||||||
if (this.isReservedChannel(ch)) {
|
if (this.isReservedChannel(ch)) {
|
||||||
if (this.checkIfReservedChannelOp(socket, ch)) {
|
if (this.checkIfReservedChannelOp(socket, ch)) {
|
||||||
if (!this.channelops.has(ch) || this.channelops.get(ch) === true) {
|
if (!this.channelops.has(ch) || this.channelops.get(ch) === true) {
|
||||||
@@ -2235,20 +2242,23 @@ class WTVIRC {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var users = this.getUsersInChannel(channel);
|
var users = this.getUsersInChannel(channel);
|
||||||
|
var output_lines = [];
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
if (socket.client_caps.includes('userhost-in-names')) {
|
if (socket.client_caps.includes('userhost-in-names')) {
|
||||||
const userHosts = users.map(user => {
|
const userHosts = users.map(user => {
|
||||||
var nick = this.findUser(user.replace(/^[@%+]/, ''));
|
var prefixRegex = new RegExp(`^[${this.supported_prefixes[1]}]`);
|
||||||
|
var nick = this.findUser(user.replace(prefixRegex, ''));
|
||||||
var username = this.usernames.get(nick) || 'unknown';
|
var username = this.usernames.get(nick) || 'unknown';
|
||||||
var host = this.hostnames.get(nick) || 'unknown';
|
var host = this.hostnames.get(nick) || 'unknown';
|
||||||
return `${user}!${username}@${host}`;
|
return `${user}!${username}@${host}`;
|
||||||
});
|
});
|
||||||
socket.write(`:${this.servername} 353 ${socket.nickname} = ${ch} :${userHosts.join(' ')}\r\n`);
|
output_lines.push(`:${this.servername} 353 ${socket.nickname} = ${ch} :${userHosts.join(' ')}\r\n`);
|
||||||
} else {
|
} else {
|
||||||
socket.write(`:${this.servername} 353 ${socket.nickname} = ${ch} :${users.join(' ')}\r\n`);
|
output_lines.push(`:${this.servername} 353 ${socket.nickname} = ${ch} :${users.join(' ')}\r\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 366 ${socket.nickname} ${channel} :End of /NAMES list\r\n`);
|
output_lines.push(`:${this.servername} 366 ${socket.nickname} ${channel} :End of /NAMES list\r\n`);
|
||||||
|
this.sendThrottled(socket, output_lines);
|
||||||
break;
|
break;
|
||||||
case 'PART':
|
case 'PART':
|
||||||
if (!socket.registered) {
|
if (!socket.registered) {
|
||||||
@@ -2457,6 +2467,7 @@ class WTVIRC {
|
|||||||
socket.write(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
socket.write(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
||||||
} else {
|
} else {
|
||||||
// WHO for nickname
|
// WHO for nickname
|
||||||
|
var output_lines = [];
|
||||||
if (target.includes('*') || target.includes('?')) {
|
if (target.includes('*') || target.includes('?')) {
|
||||||
// Wildcard mask search for nicknames
|
// Wildcard mask search for nicknames
|
||||||
const maskRegex = new RegExp('^' + target.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', 'i');
|
const maskRegex = new RegExp('^' + target.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', 'i');
|
||||||
@@ -2467,13 +2478,14 @@ class WTVIRC {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
found = true;
|
found = true;
|
||||||
socket.write(`:${this.servername} 352 ${socket.nickname} * ${nick} ${sock.host} ${this.servername} ${nick} ${(this.awaymsgs.has(nick)) ? 'G' : 'H'}${(sock.secure) ? 'z' : ''} :0 ${sock.userinfo}\r\n`);
|
output_lines.push(`:${this.servername} 352 ${socket.nickname} * ${nick} ${sock.host} ${this.servername} ${nick} ${(this.awaymsgs.has(nick)) ? 'G' : 'H'}${(sock.secure) ? 'z' : ''} :0 ${sock.userinfo}\r\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
socket.write(`:${this.servername} 401 ${socket.nickname} ${target} :No such nick/channel\r\n`);
|
output_lines.push(`:${this.servername} 401 ${socket.nickname} ${target} :No such nick/channel\r\n`);
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
output_lines.push(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
||||||
|
this.sendThrottled(socket, output_lines);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
var whoisSocket = Array.from(this.nicknames.keys()).find(
|
var whoisSocket = Array.from(this.nicknames.keys()).find(
|
||||||
@@ -2482,14 +2494,15 @@ class WTVIRC {
|
|||||||
if (whoisSocket) {
|
if (whoisSocket) {
|
||||||
if (this.getUserModes(whoisSocket.nickname).includes('s')) {
|
if (this.getUserModes(whoisSocket.nickname).includes('s')) {
|
||||||
// Skip invisible users
|
// Skip invisible users
|
||||||
socket.write(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
output_lines.push(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 352 ${socket.nickname} * ${whoisSocket.nickname} ${whoisSocket.host} ${this.servername} ${whoisSocket.nickname} ${(this.awaymsgs.has(target)) ? 'G' : 'H'}${(whoisSocket.secure) ? 'z' : ''} :0 ${whoisSocket.userinfo}\r\n`);
|
output_lines.push(`:${this.servername} 352 ${socket.nickname} * ${whoisSocket.nickname} ${whoisSocket.host} ${this.servername} ${whoisSocket.nickname} ${(this.awaymsgs.has(target)) ? 'G' : 'H'}${(whoisSocket.secure) ? 'z' : ''} :0 ${whoisSocket.userinfo}\r\n`);
|
||||||
} else {
|
} else {
|
||||||
socket.write(`:${this.servername} 401 ${socket.nickname} ${target} :No such nick/channel\r\n`);
|
output_lines.push(`:${this.servername} 401 ${socket.nickname} ${target} :No such nick/channel\r\n`);
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
output_lines.push(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`);
|
||||||
|
this.sendThrottled(socket, output_lines);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3350,50 +3363,6 @@ class WTVIRC {
|
|||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHostname(socket) {
|
|
||||||
// Resolve the hostname for a socket, using reverse DNS lookup
|
|
||||||
// Requires a callback
|
|
||||||
if (socket && socket.remoteAddress) {
|
|
||||||
try {
|
|
||||||
var hostname = socket.remoteAddress
|
|
||||||
hostname = await new Promise((resolve) => {
|
|
||||||
// dns.reverse does not support a native timeout option, so we implement our own timeout
|
|
||||||
let timeoutHandle;
|
|
||||||
const timeoutPromise = new Promise((resolveTimeout) => {
|
|
||||||
timeoutHandle = setTimeout(() => {
|
|
||||||
if (!socket.hostname_resolved) {
|
|
||||||
socket.hostname_resolved = true;
|
|
||||||
socket.write(`:${this.servername} NOTICE AUTH :*** Could not resolve your hostname: Timeout; using your IP address (${socket.remoteAddress}) instead.\r\n`);
|
|
||||||
}
|
|
||||||
resolveTimeout(socket.remoteAddress);
|
|
||||||
}, 5000); // 5 seconds timeout
|
|
||||||
});
|
|
||||||
dns.reverse(socket.remoteAddress, (err, hostnames) => {
|
|
||||||
if (!err && hostnames && hostnames.length > 0) {
|
|
||||||
socket.hostname_resolved = true;
|
|
||||||
socket.write(`:${this.servername} NOTICE AUTH :*** Hostname found: ${hostnames[0]}\r\n`);
|
|
||||||
resolve(hostnames[0]);
|
|
||||||
} else {
|
|
||||||
if (!err) {
|
|
||||||
err = 'Domain name not found';
|
|
||||||
}
|
|
||||||
socket.hostname_resolved = true;
|
|
||||||
socket.write(`:${this.servername} NOTICE AUTH :*** Could not resolve your hostname: ${err}; using your IP address (${socket.remoteAddress}) instead.\r\n`);
|
|
||||||
resolve(socket.remoteAddress);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
if (this.debug) {
|
|
||||||
console.error(`Error resolving hostname for ${socket.remoteAddress}:`, e);
|
|
||||||
}
|
|
||||||
socket.hostname_resolved = true;
|
|
||||||
socket.write(`:${this.servername} NOTICE AUTH :*** Could not resolve your hostname: ${e}; using your IP address (${socket.remoteAddress}) instead.\r\n`);
|
|
||||||
hostname = socket.remoteAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filterHostname(socket, hostname) {
|
filterHostname(socket, hostname) {
|
||||||
// Masks the user's hostname or IP for privacy
|
// Masks the user's hostname or IP for privacy
|
||||||
const username = this.nicknames.get(socket);
|
const username = this.nicknames.get(socket);
|
||||||
@@ -4275,9 +4244,9 @@ class WTVIRC {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendThrottled(socket, lines, delayMs = 30) {
|
async sendThrottled(socket, lines, delayMs = 10) {
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
socket.write(line + '\r\n');
|
socket.write(line);
|
||||||
await new Promise(res => setTimeout(res, delayMs));
|
await new Promise(res => setTimeout(res, delayMs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4401,6 +4370,38 @@ class WTVIRC {
|
|||||||
this.usermodes.set(nickname, modes);
|
this.usermodes.set(nickname, modes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getHostname(socket) {
|
||||||
|
// Resolve the hostname for a socket, using reverse DNS lookup
|
||||||
|
if (socket && socket.remoteAddress) {
|
||||||
|
try {
|
||||||
|
var hostname = socket.remoteAddress
|
||||||
|
hostname = await new Promise((resolve) => {
|
||||||
|
dns.reverse(socket.remoteAddress, (err, hostnames) => {
|
||||||
|
if (!err && hostnames && hostnames.length > 0) {
|
||||||
|
socket.hostname_resolved = true;
|
||||||
|
socket.write(`:${this.servername} NOTICE AUTH :*** Hostname found: ${hostnames[0]}\r\n`);
|
||||||
|
resolve(hostnames[0]);
|
||||||
|
} else {
|
||||||
|
if (!err) {
|
||||||
|
err = 'Domain name not found';
|
||||||
|
}
|
||||||
|
socket.hostname_resolved = true;
|
||||||
|
socket.write(`:${this.servername} NOTICE AUTH :*** Could not resolve your hostname: ${err}; using your IP address (${socket.remoteAddress}) instead.\r\n`);
|
||||||
|
resolve(socket.remoteAddress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (this.debug) {
|
||||||
|
console.error(`Error resolving hostname for ${socket.remoteAddress}:`, e);
|
||||||
|
}
|
||||||
|
socket.hostname_resolved = true;
|
||||||
|
socket.write(`:${this.servername} NOTICE AUTH :*** Could not resolve your hostname: ${e}; using your IP address (${socket.remoteAddress}) instead.\r\n`);
|
||||||
|
}
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async doInitialHandshake(socket) {
|
async doInitialHandshake(socket) {
|
||||||
// Perform the initial handshake with the client
|
// Perform the initial handshake with the client
|
||||||
// We pause the socket to prevent sending data before the hostname is resolved
|
// We pause the socket to prevent sending data before the hostname is resolved
|
||||||
@@ -4427,11 +4428,11 @@ class WTVIRC {
|
|||||||
this.addUserUniqueId(nickname, socket.uniqueId);
|
this.addUserUniqueId(nickname, socket.uniqueId);
|
||||||
this.hostnames.set(nickname, socket.host);
|
this.hostnames.set(nickname, socket.host);
|
||||||
this.realhosts.set(nickname, socket.realhost);
|
this.realhosts.set(nickname, socket.realhost);
|
||||||
|
var output_lines = []
|
||||||
socket.write(`:${this.servername} NOTICE AUTH :Welcome to \x02${this.network}\x0F\r\n`);
|
output_lines.push(`:${this.servername} NOTICE AUTH :Welcome to \x02${this.network}\x0F\r\n`);
|
||||||
socket.write(`:${this.servername} 001 ${nickname} :Welcome to the IRC server, ${nickname}\r\n`);
|
output_lines.push(`:${this.servername} 001 ${nickname} :Welcome to the IRC server, ${nickname}\r\n`);
|
||||||
socket.write(`:${this.servername} 002 ${nickname} :Your host is ${this.servername}, running version zefIRCd v${this.version}\r\n`);
|
output_lines.push(`:${this.servername} 002 ${nickname} :Your host is ${this.servername}, running version zefIRCd v${this.version}\r\n`);
|
||||||
socket.write(`:${this.servername} 003 ${nickname} :This server is ready to accept commands\r\n`);
|
output_lines.push(`:${this.servername} 003 ${nickname} :This server is ready to accept commands\r\n`);
|
||||||
// Sort supported_channel_modes and supported_user_modes alphabetically with capitals first
|
// Sort supported_channel_modes and supported_user_modes alphabetically with capitals first
|
||||||
function sortModesAlphaCapsFirst(modes) {
|
function sortModesAlphaCapsFirst(modes) {
|
||||||
// Remove commas, split to chars, sort, then re-insert commas if needed
|
// Remove commas, split to chars, sort, then re-insert commas if needed
|
||||||
@@ -4469,11 +4470,11 @@ class WTVIRC {
|
|||||||
var channelModes = this.supported_channel_modes.split(',').join('') + this.supported_prefixes[0];
|
var channelModes = this.supported_channel_modes.split(',').join('') + this.supported_prefixes[0];
|
||||||
var sortedChannelModes = sortModesAlphaCapsFirst(channelModes).replace(/,/g, '');
|
var sortedChannelModes = sortModesAlphaCapsFirst(channelModes).replace(/,/g, '');
|
||||||
var sortedUserModes = sortModesAlphaCapsFirst(this.supported_user_modes);
|
var sortedUserModes = sortModesAlphaCapsFirst(this.supported_user_modes);
|
||||||
socket.write(`:${this.servername} 004 ${nickname} ${this.servername} zefIRCd-${this.version} ${sortedUserModes} ${sortedChannelModes} ${sortedModesWithParams}\r\n`);
|
output_lines.push(`:${this.servername} 004 ${nickname} ${this.servername} zefIRCd-${this.version} ${sortedUserModes} ${sortedChannelModes} ${sortedModesWithParams}\r\n`);
|
||||||
for (const caps of this.caps) {
|
for (const caps of this.caps) {
|
||||||
socket.write(`:${this.servername} 005 ${caps}\r\n`);
|
output_lines.push(`:${this.servername} 005 ${caps}\r\n`);
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 042 ${nickname} ${socket.uniqueId} :your unique ID\r\n`);
|
output_lines.push(`:${this.servername} 042 ${nickname} ${socket.uniqueId} :your unique ID\r\n`);
|
||||||
|
|
||||||
this.doMOTD(nickname, socket);
|
this.doMOTD(nickname, socket);
|
||||||
|
|
||||||
@@ -4490,22 +4491,22 @@ class WTVIRC {
|
|||||||
return modes.includes('o');
|
return modes.includes('o');
|
||||||
});
|
});
|
||||||
const serverCount = this.servers.size + 1; // Include this server
|
const serverCount = this.servers.size + 1; // Include this server
|
||||||
socket.write(`:${this.servername} 251 ${nickname} :There are ${visibleClients.length} visible users and ${invisibleClients.length} invisible users on this server\r\n`);
|
output_lines.push(`:${this.servername} 251 ${nickname} :There are ${visibleClients.length} visible users and ${invisibleClients.length} invisible users on this server\r\n`);
|
||||||
if (operClients.length > 0) {
|
if (operClients.length > 0) {
|
||||||
socket.write(`:${this.servername} 252 ${nickname} ${operClients.length} :operator(s) online\r\n`);
|
output_lines.push(`:${this.servername} 252 ${nickname} ${operClients.length} :operator(s) online\r\n`);
|
||||||
}
|
}
|
||||||
if (this.channels.size > 0) {
|
if (this.channels.size > 0) {
|
||||||
socket.write(`:${this.servername} 253 ${nickname} ${this.channels.size} :channels formed\r\n`);
|
output_lines.push(`:${this.servername} 253 ${nickname} ${this.channels.size} :channels formed\r\n`);
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 255 ${nickname} :I have ${this.clients.length} clients and ${serverCount} servers\r\n`);
|
output_lines.push(`:${this.servername} 255 ${nickname} :I have ${this.clients.length} clients and ${serverCount} servers\r\n`);
|
||||||
socket.write(`:${this.servername} 265 ${nickname} :Current Local Users: ${this.clients.length} Max: ${this.clientpeak}\r\n`);
|
output_lines.push(`:${this.servername} 265 ${nickname} :Current Local Users: ${this.clients.length} Max: ${this.clientpeak}\r\n`);
|
||||||
const globalUsers = this.countGlobalUsers();
|
const globalUsers = this.countGlobalUsers();
|
||||||
this.globalpeak = Math.max(this.globalpeak, this.countGlobalUsers());
|
this.globalpeak = Math.max(this.globalpeak, this.countGlobalUsers());
|
||||||
var totalSockets = this.clients.length + this.servers.size;
|
var totalSockets = this.clients.length + this.servers.size;
|
||||||
this.socketpeak = Math.max(this.socketpeak, totalSockets);
|
this.socketpeak = Math.max(this.socketpeak, totalSockets);
|
||||||
|
|
||||||
socket.write(`:${this.servername} 266 ${nickname} :Current Global Users: ${globalUsers} Max: ${this.globalpeak}\r\n`);
|
output_lines.push(`:${this.servername} 266 ${nickname} :Current Global Users: ${globalUsers} Max: ${this.globalpeak}\r\n`);
|
||||||
socket.write(`:${this.servername} 250 ${nickname} :Highest connection count: ${this.socketpeak} (${this.clientpeak} clients) (${this.totalConnections} connections received)\r\n`);
|
output_lines.push(`:${this.servername} 250 ${nickname} :Highest connection count: ${this.socketpeak} (${this.clientpeak} clients) (${this.totalConnections} connections received)\r\n`);
|
||||||
var usermodes = this.usermodes.get(nickname);
|
var usermodes = this.usermodes.get(nickname);
|
||||||
if (!usermodes || usermodes === true) {
|
if (!usermodes || usermodes === true) {
|
||||||
usermodes = [];
|
usermodes = [];
|
||||||
@@ -4521,8 +4522,16 @@ class WTVIRC {
|
|||||||
if (usermodes) {
|
if (usermodes) {
|
||||||
this.usermodes.set(nickname, usermodes);
|
this.usermodes.set(nickname, usermodes);
|
||||||
}
|
}
|
||||||
socket.write(`:${this.servername} 221 ${nickname} :+${this.usermodes.get(nickname).join('')}\r\n`);
|
if (usermodes.includes('x')) {
|
||||||
socket.write(`:${this.servername} PRIVMSG ${socket.nickname} :\x01VERSION\x01\r\n`);
|
socket.host = this.filterHostname(socket, socket.realhost);
|
||||||
|
if (socket.client_caps && socket.client_caps.includes('CHGHOST')) {
|
||||||
|
output_lines.push(`:${socket.nickname}!${socket.username}@${socket.host} CHGHOST ${socket.username} ${socket.host}\r\n`);
|
||||||
|
}
|
||||||
|
output_lines.push(`:${this.servername} 396 ${socket.nickname} ${socket.host} :is now your visible host\r\n`);
|
||||||
|
}
|
||||||
|
output_lines.push(`:${this.servername} 221 ${nickname} :+${this.usermodes.get(nickname).join('')}\r\n`);
|
||||||
|
output_lines.push(`:${this.servername} PRIVMSG ${socket.nickname} :\x01VERSION\x01\r\n`);
|
||||||
|
await this.sendThrottled(socket, output_lines);
|
||||||
this.broadcastConnection(socket);
|
this.broadcastConnection(socket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user