commit
5d624fa49f
|
@ -115,8 +115,6 @@ max-channels-per-user: 5
|
||||||
max-accounts-per-ip: 5
|
max-accounts-per-ip: 5
|
||||||
# Minimum number of seconds between guest logins from the same IP
|
# Minimum number of seconds between guest logins from the same IP
|
||||||
guest-login-delay: 60
|
guest-login-delay: 60
|
||||||
# Block known Tor IP addresses
|
|
||||||
enable-tor-blocker: true
|
|
||||||
|
|
||||||
# Configure statistics tracking
|
# Configure statistics tracking
|
||||||
stats:
|
stats:
|
||||||
|
|
|
@ -17,7 +17,7 @@ var Config = require("./config");
|
||||||
var Server = require("./server");
|
var Server = require("./server");
|
||||||
|
|
||||||
function eventUsername(user) {
|
function eventUsername(user) {
|
||||||
return user.getName() + "@" + user.ip;
|
return user.getName() + "@" + user.realip;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAnnounce(user, data) {
|
function handleAnnounce(user, data) {
|
||||||
|
|
|
@ -330,10 +330,24 @@ Channel.prototype.acceptUser = function (user) {
|
||||||
user.autoAFK();
|
user.autoAFK();
|
||||||
user.socket.on("readChanLog", this.handleReadLog.bind(this, user));
|
user.socket.on("readChanLog", this.handleReadLog.bind(this, user));
|
||||||
|
|
||||||
Logger.syslog.log(user.ip + " joined " + this.name);
|
Logger.syslog.log(user.realip + " joined " + this.name);
|
||||||
this.logger.log("[login] Accepted connection from " + user.longip);
|
if (user.socket._isUsingTor) {
|
||||||
|
if (this.modules.options && this.modules.options.get("torbanned")) {
|
||||||
|
user.kick("This channel has banned connections from Tor.");
|
||||||
|
user.socket.disconnect(true);
|
||||||
|
this.logger.log("[login] Blocked connection from Tor exit at " +
|
||||||
|
user.displayip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log("[login] Accepted connection from Tor exit at " +
|
||||||
|
user.displayip);
|
||||||
|
} else {
|
||||||
|
this.logger.log("[login] Accepted connection from " + user.displayip);
|
||||||
|
}
|
||||||
|
|
||||||
if (user.is(Flags.U_LOGGED_IN)) {
|
if (user.is(Flags.U_LOGGED_IN)) {
|
||||||
this.logger.log("[login] " + user.longip + " authenticated as " + user.getName());
|
this.logger.log("[login] " + user.displayip + " authenticated as " + user.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -367,7 +381,7 @@ Channel.prototype.partUser = function (user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log("[login] " + user.longip + " (" + user.getName() + ") " +
|
this.logger.log("[login] " + user.displayip + " (" + user.getName() + ") " +
|
||||||
"disconnected.");
|
"disconnected.");
|
||||||
user.channel = null;
|
user.channel = null;
|
||||||
/* Should be unnecessary because partUser only occurs if the socket dies */
|
/* Should be unnecessary because partUser only occurs if the socket dies */
|
||||||
|
@ -412,7 +426,7 @@ Channel.prototype.packUserData = function (user) {
|
||||||
muted: user.is(Flags.U_MUTED),
|
muted: user.is(Flags.U_MUTED),
|
||||||
smuted: user.is(Flags.U_SMUTED),
|
smuted: user.is(Flags.U_SMUTED),
|
||||||
aliases: user.account.aliases,
|
aliases: user.account.aliases,
|
||||||
ip: util.maskIP(user.longip)
|
ip: user.displayip
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -425,7 +439,7 @@ Channel.prototype.packUserData = function (user) {
|
||||||
muted: user.is(Flags.U_MUTED),
|
muted: user.is(Flags.U_MUTED),
|
||||||
smuted: user.is(Flags.U_SMUTED),
|
smuted: user.is(Flags.U_SMUTED),
|
||||||
aliases: user.account.aliases,
|
aliases: user.account.aliases,
|
||||||
ip: user.ip
|
ip: user.realip
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -534,7 +548,7 @@ Channel.prototype.sendUserJoin = function (users, user) {
|
||||||
user.account.aliases.join(",") + ")", 2);
|
user.account.aliases.join(",") + ")", 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
Channel.prototype.readLog = function (shouldMaskIP, cb) {
|
Channel.prototype.readLog = function (cb) {
|
||||||
var maxLen = 102400;
|
var maxLen = 102400;
|
||||||
var file = this.logger.filename;
|
var file = this.logger.filename;
|
||||||
this.activeLock.lock();
|
this.activeLock.lock();
|
||||||
|
@ -558,16 +572,6 @@ Channel.prototype.readLog = function (shouldMaskIP, cb) {
|
||||||
buffer += data;
|
buffer += data;
|
||||||
});
|
});
|
||||||
read.on("end", function () {
|
read.on("end", function () {
|
||||||
if (shouldMaskIP) {
|
|
||||||
buffer = buffer.replace(
|
|
||||||
/(?:^|\s)(\d+\.\d+\.\d+)\.\d+/g,
|
|
||||||
"$1.x"
|
|
||||||
).replace(
|
|
||||||
/(?:^|\s)((?:[0-9a-f]+:){3}[0-9a-f]+):(?:[0-9a-f]+:){3}[0-9a-f]+/g,
|
|
||||||
"$1:x:x:x:x"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, buffer);
|
cb(null, buffer);
|
||||||
self.activeLock.release();
|
self.activeLock.release();
|
||||||
});
|
});
|
||||||
|
@ -589,7 +593,7 @@ Channel.prototype.handleReadLog = function (user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldMaskIP = user.account.globalRank < 255;
|
var shouldMaskIP = user.account.globalRank < 255;
|
||||||
this.readLog(shouldMaskIP, function (err, data) {
|
this.readLog(function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
user.socket.emit("readChanLog", {
|
user.socket.emit("readChanLog", {
|
||||||
success: false,
|
success: false,
|
||||||
|
|
|
@ -24,36 +24,48 @@ function KickBanModule(channel) {
|
||||||
|
|
||||||
KickBanModule.prototype = Object.create(ChannelModule.prototype);
|
KickBanModule.prototype = Object.create(ChannelModule.prototype);
|
||||||
|
|
||||||
|
function checkIPBan(cname, ip, cb) {
|
||||||
|
db.channels.isIPBanned(cname, ip, function (err, banned) {
|
||||||
|
if (err) {
|
||||||
|
cb(false);
|
||||||
|
} else {
|
||||||
|
cb(banned);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNameBan(cname, name, cb) {
|
||||||
|
db.channels.isNameBanned(cname, name, function (err, banned) {
|
||||||
|
if (err) {
|
||||||
|
cb(false);
|
||||||
|
} else {
|
||||||
|
cb(banned);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
KickBanModule.prototype.onUserPreJoin = function (user, data, cb) {
|
KickBanModule.prototype.onUserPreJoin = function (user, data, cb) {
|
||||||
if (!this.channel.is(Flags.C_REGISTERED)) {
|
if (!this.channel.is(Flags.C_REGISTERED)) {
|
||||||
return cb(null, ChannelModule.PASSTHROUGH);
|
return cb(null, ChannelModule.PASSTHROUGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cname = this.channel.name;
|
var cname = this.channel.name;
|
||||||
db.channels.isIPBanned(cname, user.longip, function (err, banned) {
|
checkIPBan(cname, user.realip, function (banned) {
|
||||||
if (err) {
|
if (banned) {
|
||||||
cb(null, ChannelModule.PASSTHROUGH);
|
cb(null, ChannelModule.DENY);
|
||||||
} else if (!banned) {
|
user.kick("Your IP address is banned from this channel.");
|
||||||
if (user.is(Flags.U_LOGGED_IN)) {
|
} else {
|
||||||
checkNameBan();
|
checkNameBan(cname, user.getName(), function (banned) {
|
||||||
|
if (banned) {
|
||||||
|
cb(null, ChannelModule.DENY);
|
||||||
|
user.kick("Your username is banned from this channel.");
|
||||||
} else {
|
} else {
|
||||||
cb(null, ChannelModule.PASSTHROUGH);
|
cb(null, ChannelModule.PASSTHROUGH);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
cb(null, ChannelModule.DENY);
|
|
||||||
user.kick("Your IP address is banned from this channel.");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkNameBan() {
|
|
||||||
db.channels.isNameBanned(cname, user.getName(), function (err, banned) {
|
|
||||||
if (err) {
|
|
||||||
cb(null, ChannelModule.PASSTHROUGH);
|
|
||||||
} else {
|
|
||||||
cb(null, banned ? ChannelModule.DENY : ChannelModule.PASSTHROUGH);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
KickBanModule.prototype.onUserPostJoin = function (user) {
|
KickBanModule.prototype.onUserPostJoin = function (user) {
|
||||||
|
@ -98,7 +110,7 @@ KickBanModule.prototype.sendBanlist = function (users) {
|
||||||
for (var i = 0; i < banlist.length; i++) {
|
for (var i = 0; i < banlist.length; i++) {
|
||||||
bans.push({
|
bans.push({
|
||||||
id: banlist[i].id,
|
id: banlist[i].id,
|
||||||
ip: banlist[i].ip === "*" ? "*" : util.maskIP(banlist[i].ip),
|
ip: banlist[i].ip === "*" ? "*" : util.cloakIP(banlist[i].ip),
|
||||||
name: banlist[i].name,
|
name: banlist[i].name,
|
||||||
reason: banlist[i].reason,
|
reason: banlist[i].reason,
|
||||||
bannedby: banlist[i].bannedby
|
bannedby: banlist[i].bannedby
|
||||||
|
@ -298,7 +310,7 @@ KickBanModule.prototype.banName = function (actor, name, reason, cb) {
|
||||||
KickBanModule.prototype.banIP = function (actor, ip, name, reason, cb) {
|
KickBanModule.prototype.banIP = function (actor, ip, name, reason, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
reason = reason.substring(0, 255);
|
reason = reason.substring(0, 255);
|
||||||
var masked = util.maskIP(ip);
|
var masked = util.cloakIP(ip);
|
||||||
|
|
||||||
var chan = this.channel;
|
var chan = this.channel;
|
||||||
var error = function (what) {
|
var error = function (what) {
|
||||||
|
@ -325,10 +337,11 @@ KickBanModule.prototype.banIP = function (actor, ip, name, reason, cb) {
|
||||||
|
|
||||||
return Q.nfcall(db.channels.ban, chan.name, ip, name, reason, actor.getName());
|
return Q.nfcall(db.channels.ban, chan.name, ip, name, reason, actor.getName());
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
chan.logger.log("[mod] " + actor.getName() + " banned " + ip + " (" + name + ")");
|
var cloaked = util.cloakIP(ip);
|
||||||
|
chan.logger.log("[mod] " + actor.getName() + " banned " + cloaked + " (" + name + ")");
|
||||||
if (chan.modules.chat) {
|
if (chan.modules.chat) {
|
||||||
chan.modules.chat.sendModMessage(actor.getName() + " banned " +
|
chan.modules.chat.sendModMessage(actor.getName() + " banned " +
|
||||||
util.maskIP(ip) + " (" + name + ")",
|
cloaked + " (" + name + ")",
|
||||||
chan.modules.permissions.permissions.ban);
|
chan.modules.permissions.permissions.ban);
|
||||||
}
|
}
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
|
@ -381,7 +394,7 @@ KickBanModule.prototype.kickBanTarget = function (name, ip) {
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
for (var i = 0; i < this.channel.users.length; i++) {
|
for (var i = 0; i < this.channel.users.length; i++) {
|
||||||
if (this.channel.users[i].getLowerName() === name ||
|
if (this.channel.users[i].getLowerName() === name ||
|
||||||
this.channel.users[i].longip === ip) {
|
this.channel.users[i].realip === ip) {
|
||||||
this.channel.users[i].kick("You're banned!");
|
this.channel.users[i].kick("You're banned!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ function OptionsModule(channel) {
|
||||||
show_public: false, // List the channel on the index page
|
show_public: false, // List the channel on the index page
|
||||||
enable_link_regex: true, // Use the built-in link filter
|
enable_link_regex: true, // Use the built-in link filter
|
||||||
password: false, // Channel password (false -> no password required for entry)
|
password: false, // Channel password (false -> no password required for entry)
|
||||||
allow_dupes: false // Allow duplicate videos on the playlist
|
allow_dupes: false, // Allow duplicate videos on the playlist
|
||||||
|
torbanned: false // Block connections from Tor exit nodes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +246,10 @@ OptionsModule.prototype.handleSetOptions = function (user, data) {
|
||||||
this.opts.allow_dupes = Boolean(data.allow_dupes);
|
this.opts.allow_dupes = Boolean(data.allow_dupes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("torbanned" in data && user.account.effectiveRank >= 3) {
|
||||||
|
this.opts.torbanned = Boolean(data.torbanned);
|
||||||
|
}
|
||||||
|
|
||||||
this.channel.logger.log("[mod] " + user.getName() + " updated channel options");
|
this.channel.logger.log("[mod] " + user.getName() + " updated channel options");
|
||||||
this.sendOpts(this.channel.users);
|
this.sendOpts(this.channel.users);
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,7 +67,7 @@ PollModule.prototype.onUserPostJoin = function (user) {
|
||||||
|
|
||||||
PollModule.prototype.onUserPart = function(user) {
|
PollModule.prototype.onUserPart = function(user) {
|
||||||
if (this.poll) {
|
if (this.poll) {
|
||||||
this.poll.unvote(user.ip);
|
this.poll.unvote(user.realip);
|
||||||
this.sendPollUpdate(this.channel.users);
|
this.sendPollUpdate(this.channel.users);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -142,7 +142,7 @@ PollModule.prototype.handleVote = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.poll) {
|
if (this.poll) {
|
||||||
this.poll.vote(user.ip, data.option);
|
this.poll.vote(user.realip, data.option);
|
||||||
this.sendPollUpdate(this.channel.users);
|
this.sendPollUpdate(this.channel.users);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@ VoteskipModule.prototype.onUserPart = function(user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unvote(user.ip);
|
this.unvote(user.realip);
|
||||||
this.update();
|
this.update();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ VoteskipModule.prototype.handleVoteskip = function (user) {
|
||||||
this.poll = new Poll("[server]", "voteskip", ["skip"], false);
|
this.poll = new Poll("[server]", "voteskip", ["skip"], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.poll.vote(user.ip, 0);
|
this.poll.vote(user.realip, 0);
|
||||||
|
|
||||||
var title = "";
|
var title = "";
|
||||||
if (this.channel.modules.playlist.current) {
|
if (this.channel.modules.playlist.current) {
|
||||||
|
|
|
@ -69,7 +69,6 @@ var defaults = {
|
||||||
"max-channels-per-user": 5,
|
"max-channels-per-user": 5,
|
||||||
"max-accounts-per-ip": 5,
|
"max-accounts-per-ip": 5,
|
||||||
"guest-login-delay": 60,
|
"guest-login-delay": 60,
|
||||||
"enable-tor-blocker": true,
|
|
||||||
stats: {
|
stats: {
|
||||||
interval: 3600000,
|
interval: 3600000,
|
||||||
"max-age": 86400000
|
"max-age": 86400000
|
||||||
|
|
|
@ -11,6 +11,8 @@ var Account = require("../account");
|
||||||
var typecheck = require("json-typecheck");
|
var typecheck = require("json-typecheck");
|
||||||
var net = require("net");
|
var net = require("net");
|
||||||
var util = require("../utilities");
|
var util = require("../utilities");
|
||||||
|
var crypto = require("crypto");
|
||||||
|
var isTorExit = require("../tor").isTorExit;
|
||||||
|
|
||||||
var CONNECT_RATE = {
|
var CONNECT_RATE = {
|
||||||
burst: 5,
|
burst: 5,
|
||||||
|
@ -43,27 +45,8 @@ function handleAuth(data, accept) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function throttleIP(sock) {
|
||||||
* Called after a connection is accepted
|
var ip = sock._realip;
|
||||||
*/
|
|
||||||
function handleConnection(sock) {
|
|
||||||
var ip = sock.handshake.address.address;
|
|
||||||
var longip = ip;
|
|
||||||
sock._ip = ip;
|
|
||||||
if (net.isIPv6(ip)) {
|
|
||||||
longip = util.expandIPv6(ip);
|
|
||||||
}
|
|
||||||
sock._longip = longip;
|
|
||||||
var srv = Server.getServer();
|
|
||||||
if (srv.torblocker && srv.torblocker.shouldBlockIP(ip)) {
|
|
||||||
sock.emit("kick", {
|
|
||||||
reason: "This server does not allow connections from Tor. "+
|
|
||||||
"Please log in with your regular internet connection."
|
|
||||||
});
|
|
||||||
Logger.syslog.log("Blocked Tor IP: " + ip);
|
|
||||||
sock.disconnect(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(ip in ipThrottle)) {
|
if (!(ip in ipThrottle)) {
|
||||||
ipThrottle[ip] = $util.newRateLimiter();
|
ipThrottle[ip] = $util.newRateLimiter();
|
||||||
|
@ -75,17 +58,15 @@ function handleConnection(sock) {
|
||||||
reason: "Your IP address is connecting too quickly. Please "+
|
reason: "Your IP address is connecting too quickly. Please "+
|
||||||
"wait 10 seconds before joining again."
|
"wait 10 seconds before joining again."
|
||||||
});
|
});
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for global ban on the IP
|
return false;
|
||||||
if (db.isGlobalIPBanned(ip)) {
|
|
||||||
Logger.syslog.log("Rejecting " + ip + " - global banned");
|
|
||||||
sock.emit("kick", { reason: "Your IP is globally banned." });
|
|
||||||
sock.disconnect(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ipLimitReached(sock) {
|
||||||
|
var ip = sock._realip;
|
||||||
|
|
||||||
sock.on("disconnect", function () {
|
sock.on("disconnect", function () {
|
||||||
ipCount[ip]--;
|
ipCount[ip]--;
|
||||||
if (ipCount[ip] === 0) {
|
if (ipCount[ip] === 0) {
|
||||||
|
@ -106,9 +87,9 @@ function handleConnection(sock) {
|
||||||
sock.disconnect(true);
|
sock.disconnect(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.syslog.log("Accepted socket from " + ip);
|
function addTypecheckedFunctions(sock) {
|
||||||
|
|
||||||
sock.typecheckedOn = function (msg, template, cb) {
|
sock.typecheckedOn = function (msg, template, cb) {
|
||||||
sock.on(msg, function (data) {
|
sock.on(msg, function (data) {
|
||||||
typecheck(data, template, function (err, data) {
|
typecheck(data, template, function (err, data) {
|
||||||
|
@ -136,6 +117,44 @@ function handleConnection(sock) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after a connection is accepted
|
||||||
|
*/
|
||||||
|
function handleConnection(sock) {
|
||||||
|
var ip = sock.handshake.address.address;
|
||||||
|
if (net.isIPv6(ip)) {
|
||||||
|
ip = util.expandIPv6(ip);
|
||||||
|
}
|
||||||
|
sock._realip = ip;
|
||||||
|
sock._displayip = $util.cloakIP(ip);
|
||||||
|
|
||||||
|
if (isTorExit(ip)) {
|
||||||
|
sock._isUsingTor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var srv = Server.getServer();
|
||||||
|
|
||||||
|
if (throttleIP(sock)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for global ban on the IP
|
||||||
|
if (db.isGlobalIPBanned(ip)) {
|
||||||
|
Logger.syslog.log("Rejecting " + ip + " - global banned");
|
||||||
|
sock.emit("kick", { reason: "Your IP is globally banned." });
|
||||||
|
sock.disconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipLimitReached(sock)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.syslog.log("Accepted socket from " + ip);
|
||||||
|
|
||||||
|
addTypecheckedFunctions(sock);
|
||||||
|
|
||||||
var user = new User(sock);
|
var user = new User(sock);
|
||||||
if (sock.handshake.user) {
|
if (sock.handshake.user) {
|
||||||
|
@ -148,6 +167,7 @@ function handleConnection(sock) {
|
||||||
user.setFlag(Flags.U_READY);
|
user.setFlag(Flags.U_READY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.socket.emit("login", {
|
user.socket.emit("login", {
|
||||||
success: true,
|
success: true,
|
||||||
name: user.getName(),
|
name: user.getName(),
|
||||||
|
|
|
@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const VERSION = "3.3.2";
|
const VERSION = "3.4.0";
|
||||||
var singleton = null;
|
var singleton = null;
|
||||||
var Config = require("./config");
|
var Config = require("./config");
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ var Server = function () {
|
||||||
self.announcement = null;
|
self.announcement = null;
|
||||||
self.httplog = null;
|
self.httplog = null;
|
||||||
self.infogetter = null;
|
self.infogetter = null;
|
||||||
self.torblocker = null;
|
|
||||||
self.servers = {};
|
self.servers = {};
|
||||||
self.ioServers = {};
|
self.ioServers = {};
|
||||||
|
|
||||||
|
@ -109,11 +108,6 @@ var Server = function () {
|
||||||
|
|
||||||
// background tasks init ----------------------------------------------
|
// background tasks init ----------------------------------------------
|
||||||
require("./bgtask")(self);
|
require("./bgtask")(self);
|
||||||
|
|
||||||
// tor blocker init ---------------------------------------------------
|
|
||||||
if (Config.get("enable-tor-blocker")) {
|
|
||||||
self.torblocker = require("./torblocker")();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype.getHTTPIP = function (req) {
|
Server.prototype.getHTTPIP = function (req) {
|
||||||
|
|
|
@ -24,8 +24,8 @@ function retrieveIPs(cb) {
|
||||||
|
|
||||||
var d = domain.create();
|
var d = domain.create();
|
||||||
d.on("error", function (err) {
|
d.on("error", function (err) {
|
||||||
if (err.trace)
|
if (err.stack)
|
||||||
Logger.errlog.log(err.trace());
|
Logger.errlog.log(err.stack);
|
||||||
else
|
else
|
||||||
Logger.errlog.log(err);
|
Logger.errlog.log(err);
|
||||||
});
|
});
|
||||||
|
@ -63,15 +63,7 @@ function getTorIPs(cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function () {
|
var _ipList = [];
|
||||||
var x = {
|
|
||||||
ipList: [],
|
|
||||||
shouldBlockIP: function (ip) {
|
|
||||||
return this.ipList.indexOf(ip) >= 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var init = function () {
|
|
||||||
getTorIPs(function (err, ips) {
|
getTorIPs(function (err, ips) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.errlog.log(err);
|
Logger.errlog.log(err);
|
||||||
|
@ -79,10 +71,9 @@ module.exports = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.syslog.log("Loaded Tor IP list");
|
Logger.syslog.log("Loaded Tor IP list");
|
||||||
x.ipList = ips;
|
_ipList = ips;
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
init();
|
exports.isTorExit = function (ip) {
|
||||||
return x;
|
return _ipList.indexOf(ip) >= 0;
|
||||||
};
|
};
|
33
lib/user.js
33
lib/user.js
|
@ -14,9 +14,10 @@ function User(socket) {
|
||||||
MakeEmitter(self);
|
MakeEmitter(self);
|
||||||
self.flags = 0;
|
self.flags = 0;
|
||||||
self.socket = socket;
|
self.socket = socket;
|
||||||
self.ip = socket._ip;
|
self.realip = socket._realip;
|
||||||
self.longip = socket._longip;
|
self.displayip = socket._displayip;
|
||||||
self.account = Account.default(self.longip);
|
self.hostmask = socket._hostmask;
|
||||||
|
self.account = Account.default(self.realip);
|
||||||
self.channel = null;
|
self.channel = null;
|
||||||
self.queueLimiter = util.newRateLimiter();
|
self.queueLimiter = util.newRateLimiter();
|
||||||
self.chatLimiter = util.newRateLimiter();
|
self.chatLimiter = util.newRateLimiter();
|
||||||
|
@ -65,7 +66,7 @@ function User(socket) {
|
||||||
self.kick("Attempted initACP from non privileged user. This incident " +
|
self.kick("Attempted initACP from non privileged user. This incident " +
|
||||||
"will be reported.");
|
"will be reported.");
|
||||||
Logger.eventlog.log("[acp] Attempted initACP from socket client " +
|
Logger.eventlog.log("[acp] Attempted initACP from socket client " +
|
||||||
self.getName() + "@" + self.ip);
|
self.getName() + "@" + self.realip);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -176,7 +177,7 @@ User.prototype.setAFK = function (afk) {
|
||||||
if (afk) {
|
if (afk) {
|
||||||
this.setFlag(Flags.U_AFK);
|
this.setFlag(Flags.U_AFK);
|
||||||
if (this.channel.modules.voteskip) {
|
if (this.channel.modules.voteskip) {
|
||||||
this.channel.modules.voteskip.unvote(this.ip);
|
this.channel.modules.voteskip.unvote(this.realip);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.clearFlag(Flags.U_AFK);
|
this.clearFlag(Flags.U_AFK);
|
||||||
|
@ -255,7 +256,7 @@ User.prototype.login = function (name, pw) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err === "Invalid username/password combination") {
|
if (err === "Invalid username/password combination") {
|
||||||
Logger.eventlog.log("[loginfail] Login failed (bad password): " + name
|
Logger.eventlog.log("[loginfail] Login failed (bad password): " + name
|
||||||
+ "@" + self.ip);
|
+ "@" + self.realip);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.socket.emit("login", {
|
self.socket.emit("login", {
|
||||||
|
@ -283,11 +284,11 @@ User.prototype.login = function (name, pw) {
|
||||||
success: true,
|
success: true,
|
||||||
name: user.name
|
name: user.name
|
||||||
});
|
});
|
||||||
db.recordVisit(self.longip, self.getName());
|
db.recordVisit(self.realip, self.getName());
|
||||||
self.socket.emit("rank", self.account.effectiveRank);
|
self.socket.emit("rank", self.account.effectiveRank);
|
||||||
Logger.syslog.log(self.ip + " logged in as " + user.name);
|
Logger.syslog.log(self.realip + " logged in as " + user.name);
|
||||||
if (self.inChannel()) {
|
if (self.inChannel()) {
|
||||||
self.channel.logger.log(self.longip + " logged in as " + user.name);
|
self.channel.logger.log(self.displayip + " logged in as " + user.name);
|
||||||
}
|
}
|
||||||
self.setFlag(Flags.U_LOGGED_IN);
|
self.setFlag(Flags.U_LOGGED_IN);
|
||||||
self.clearFlag(Flags.U_LOGGING_IN);
|
self.clearFlag(Flags.U_LOGGING_IN);
|
||||||
|
@ -300,8 +301,8 @@ var lastguestlogin = {};
|
||||||
User.prototype.guestLogin = function (name) {
|
User.prototype.guestLogin = function (name) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (self.ip in lastguestlogin) {
|
if (self.realip in lastguestlogin) {
|
||||||
var diff = (Date.now() - lastguestlogin[self.ip]) / 1000;
|
var diff = (Date.now() - lastguestlogin[self.realip]) / 1000;
|
||||||
if (diff < Config.get("guest-login-delay")) {
|
if (diff < Config.get("guest-login-delay")) {
|
||||||
self.socket.emit("login", {
|
self.socket.emit("login", {
|
||||||
success: false,
|
success: false,
|
||||||
|
@ -355,7 +356,7 @@ User.prototype.guestLogin = function (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login succeeded
|
// Login succeeded
|
||||||
lastguestlogin[self.ip] = Date.now();
|
lastguestlogin[self.realip] = Date.now();
|
||||||
|
|
||||||
var opts = { name: name };
|
var opts = { name: name };
|
||||||
if (self.inChannel()) {
|
if (self.inChannel()) {
|
||||||
|
@ -373,11 +374,11 @@ User.prototype.guestLogin = function (name) {
|
||||||
name: name,
|
name: name,
|
||||||
guest: true
|
guest: true
|
||||||
});
|
});
|
||||||
db.recordVisit(self.longip, self.getName());
|
db.recordVisit(self.realip, self.getName());
|
||||||
self.socket.emit("rank", 0);
|
self.socket.emit("rank", 0);
|
||||||
Logger.syslog.log(self.ip + " signed in as " + name);
|
Logger.syslog.log(self.realip + " signed in as " + name);
|
||||||
if (self.inChannel()) {
|
if (self.inChannel()) {
|
||||||
self.channel.logger.log(self.longip + " signed in as " + name);
|
self.channel.logger.log(self.displayip + " signed in as " + name);
|
||||||
}
|
}
|
||||||
self.setFlag(Flags.U_LOGGED_IN);
|
self.setFlag(Flags.U_LOGGED_IN);
|
||||||
self.emit("login", self.account);
|
self.emit("login", self.account);
|
||||||
|
@ -422,7 +423,7 @@ User.prototype.refreshAccount = function (opts, cb) {
|
||||||
opts.registered = this.is(Flags.U_REGISTERED);
|
opts.registered = this.is(Flags.U_REGISTERED);
|
||||||
var self = this;
|
var self = this;
|
||||||
var old = this.account;
|
var old = this.account;
|
||||||
Account.getAccount(name, this.longip, opts, function (err, account) {
|
Account.getAccount(name, this.realip, opts, function (err, account) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
/* Update account if anything changed in the meantime */
|
/* Update account if anything changed in the meantime */
|
||||||
for (var key in old) {
|
for (var key in old) {
|
||||||
|
|
|
@ -78,31 +78,6 @@
|
||||||
return salt.join('');
|
return salt.join('');
|
||||||
},
|
},
|
||||||
|
|
||||||
root.maskIP = function (ip) {
|
|
||||||
if (net.isIPv4(ip)) {
|
|
||||||
/* Full /32 IPv4 address */
|
|
||||||
return ip.replace(/^(\d+\.\d+\.\d+)\.\d+/, "$1.x");
|
|
||||||
} else if (net.isIPv4(ip + ".0")) {
|
|
||||||
/* /24 IPv4 range */
|
|
||||||
return ip + ".0/24";
|
|
||||||
} else if (net.isIPv4(ip + ".0.0")) {
|
|
||||||
/* /16 IPv4 widerange */
|
|
||||||
return ip + ".0.0/16";
|
|
||||||
} else if (net.isIPv6(ip)) {
|
|
||||||
/* /128 IPv6 address */
|
|
||||||
return ip.replace(/^((?:[0-9a-f]+:){3}[0-9a-f]+):(?:[0-9a-f]+:){3}[0-9a-f]+$/,
|
|
||||||
"$1:x:x:x:x");
|
|
||||||
} else if (net.isIPv6(ip + ":0:0:0:0")) {
|
|
||||||
/* /64 IPv6 range */
|
|
||||||
return ip + "::0/64";
|
|
||||||
} else if (net.isIPv6(ip + ":0:0:0:0:0")) {
|
|
||||||
/* /48 IPv6 widerange */
|
|
||||||
return ip + "::0/48";
|
|
||||||
} else {
|
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
root.getIPRange = function (ip) {
|
root.getIPRange = function (ip) {
|
||||||
if (net.isIPv6(ip)) {
|
if (net.isIPv6(ip)) {
|
||||||
return root.expandIPv6(ip)
|
return root.expandIPv6(ip)
|
||||||
|
@ -291,4 +266,53 @@
|
||||||
shasum.update(data);
|
shasum.update(data);
|
||||||
return shasum.digest("hex");
|
return shasum.digest("hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.cloakIP = function (ip) {
|
||||||
|
if (ip.match(/\d+\.\d+(\.\d+)?(\.\d+)?/)) {
|
||||||
|
return cloakIPv4(ip);
|
||||||
|
} else if (ip.match(/([0-9a-f]{1,4}\:){1,7}[0-9a-f]{1,4}/)) {
|
||||||
|
return cloakIPv6(ip);
|
||||||
|
} else {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iphash(data, len) {
|
||||||
|
var md5 = crypto.createHash("md5");
|
||||||
|
md5.update(data);
|
||||||
|
return md5.digest("base64").substring(0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloakIPv4(ip) {
|
||||||
|
var parts = ip.split(".");
|
||||||
|
var accumulator = "";
|
||||||
|
|
||||||
|
parts = parts.map(function (segment, i) {
|
||||||
|
if (i < 2) return segment;
|
||||||
|
|
||||||
|
var part = iphash(accumulator + segment + i, 3);
|
||||||
|
accumulator += segment;
|
||||||
|
return part;
|
||||||
|
});
|
||||||
|
|
||||||
|
while (parts.length < 4) parts.push("*");
|
||||||
|
return parts.join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloakIPv6(ip) {
|
||||||
|
var parts = ip.split(":");
|
||||||
|
parts.splice(4, 4);
|
||||||
|
var accumulator = "";
|
||||||
|
|
||||||
|
parts = parts.map(function (segment, i) {
|
||||||
|
if (i < 2) return segment;
|
||||||
|
|
||||||
|
var part = iphash(accumulator + segment + i, 4);
|
||||||
|
accumulator += segment;
|
||||||
|
return part;
|
||||||
|
});
|
||||||
|
|
||||||
|
while (parts.length < 4) parts.push("*");
|
||||||
|
return parts.join(":");
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"author": "Calvin Montgomery",
|
"author": "Calvin Montgomery",
|
||||||
"name": "CyTube",
|
"name": "CyTube",
|
||||||
"description": "Online media synchronizer and chat",
|
"description": "Online media synchronizer and chat",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "http://github.com/calzoneman/sync"
|
"url": "http://github.com/calzoneman/sync"
|
||||||
},
|
},
|
||||||
|
|
|
@ -75,6 +75,7 @@ mixin adminoptions
|
||||||
mixin textbox-auto("cs-externalcss", "External CSS", "Stylesheet URL")
|
mixin textbox-auto("cs-externalcss", "External CSS", "Stylesheet URL")
|
||||||
mixin textbox-auto("cs-externaljs", "External Javascript", "Script URL")
|
mixin textbox-auto("cs-externaljs", "External Javascript", "Script URL")
|
||||||
mixin rcheckbox-auto("cs-show_public", "List channel publicly")
|
mixin rcheckbox-auto("cs-show_public", "List channel publicly")
|
||||||
|
mixin rcheckbox-auto("cs-torbanned", "Block connections from Tor")
|
||||||
.form-group
|
.form-group
|
||||||
.col-sm-8.col-sm-offset-4
|
.col-sm-8.col-sm-offset-4
|
||||||
span.text-info Changes are automatically saved.
|
span.text-info Changes are automatically saved.
|
||||||
|
|
|
@ -864,6 +864,7 @@ function handleModPermissions() {
|
||||||
$("#cs-allow_voteskip").prop("checked", CHANNEL.opts.allow_voteskip);
|
$("#cs-allow_voteskip").prop("checked", CHANNEL.opts.allow_voteskip);
|
||||||
$("#cs-voteskip_ratio").val(CHANNEL.opts.voteskip_ratio);
|
$("#cs-voteskip_ratio").val(CHANNEL.opts.voteskip_ratio);
|
||||||
$("#cs-allow_dupes").val(CHANNEL.opts.allow_dupes);
|
$("#cs-allow_dupes").val(CHANNEL.opts.allow_dupes);
|
||||||
|
$("#cs-torbanned").val(CHANNEL.opts.torbanned);
|
||||||
(function() {
|
(function() {
|
||||||
if(typeof CHANNEL.opts.maxlength != "number") {
|
if(typeof CHANNEL.opts.maxlength != "number") {
|
||||||
$("#cs-maxlength").val("");
|
$("#cs-maxlength").val("");
|
||||||
|
|
Loading…
Reference in a new issue