mirror of https://github.com/calzoneman/sync.git
Add IP cloaking; make tor bans channel specific
This commit is contained in:
parent
ecca806a58
commit
8fddbc3e6e
|
@ -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);
|
|
||||||
} else if (!banned) {
|
|
||||||
if (user.is(Flags.U_LOGGED_IN)) {
|
|
||||||
checkNameBan();
|
|
||||||
} else {
|
|
||||||
cb(null, ChannelModule.PASSTHROUGH);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cb(null, ChannelModule.DENY);
|
cb(null, ChannelModule.DENY);
|
||||||
user.kick("Your IP address is banned from this channel.");
|
user.kick("Your IP address is banned from this channel.");
|
||||||
|
} else {
|
||||||
|
checkNameBan(cname, user.getName(), function (banned) {
|
||||||
|
if (banned) {
|
||||||
|
cb(null, ChannelModule.DENY);
|
||||||
|
user.kick("Your username is banned from this channel.");
|
||||||
|
} else {
|
||||||
|
cb(null, ChannelModule.PASSTHROUGH);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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
|
||||||
|
@ -381,7 +393,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,16 +58,14 @@ 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." });
|
function ipLimitReached(sock) {
|
||||||
sock.disconnect(true);
|
var ip = sock._realip;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sock.on("disconnect", function () {
|
sock.on("disconnect", function () {
|
||||||
ipCount[ip]--;
|
ipCount[ip]--;
|
||||||
|
@ -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(ip)) {
|
||||||
|
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(),
|
||||||
|
|
|
@ -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,26 +63,17 @@ function getTorIPs(cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function () {
|
var _ipList = [];
|
||||||
var x = {
|
getTorIPs(function (err, ips) {
|
||||||
ipList: [],
|
if (err) {
|
||||||
shouldBlockIP: function (ip) {
|
Logger.errlog.log(err);
|
||||||
return this.ipList.indexOf(ip) >= 0;
|
return;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
var init = function () {
|
Logger.syslog.log("Loaded Tor IP list");
|
||||||
getTorIPs(function (err, ips) {
|
_ipList = ips;
|
||||||
if (err) {
|
});
|
||||||
Logger.errlog.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.syslog.log("Loaded Tor IP list");
|
exports.isTorExit = function (ip) {
|
||||||
x.ipList = ips;
|
return this._ipList.indexOf(ip) >= 0;
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
init();
|
|
||||||
return x;
|
|
||||||
};
|
};
|
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,48 @@
|
||||||
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(ip, segment, len) {
|
||||||
|
var md5 = crypto.createHash("md5");
|
||||||
|
md5.update(ip);
|
||||||
|
md5.update(segment);
|
||||||
|
return md5.digest("base64").substring(0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloakIPv4(ip) {
|
||||||
|
var parts = ip.split(".");
|
||||||
|
|
||||||
|
parts = parts.map(function (segment, i) {
|
||||||
|
if (i < 2) return segment;
|
||||||
|
|
||||||
|
return iphash(ip, segment + i, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
while (parts.length < 4) parts.push("*");
|
||||||
|
return parts.join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloakIPv6(ip) {
|
||||||
|
var parts = ip.split(":");
|
||||||
|
parts.splice(4, 4);
|
||||||
|
|
||||||
|
parts = parts.map(function (segment, i) {
|
||||||
|
if (i < 2) return segment;
|
||||||
|
|
||||||
|
return iphash(ip, segment + i, 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
while (parts.length < 4) parts.push("*");
|
||||||
|
return parts.join(":");
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -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 New Issue