package: build with babel for ES2015 support
* Rename lib/ -> src/ * Add `postinstall` npm target for compiling src files to lib * Add `build-watch` npm target for development with babel --watch * Add `lib/` to .gitignore * Add `source-map-support` module for babel-generated sourcemaps
This commit is contained in:
parent
d042619b21
commit
0109a87e55
55 changed files with 9 additions and 3 deletions
258
src/io/ioserver.js
Normal file
258
src/io/ioserver.js
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
var sio = require("socket.io");
|
||||
var Logger = require("../logger");
|
||||
var db = require("../database");
|
||||
var User = require("../user");
|
||||
var Server = require("../server");
|
||||
var Config = require("../config");
|
||||
var cookieParser = require("cookie-parser")(Config.get("http.cookie-secret"));
|
||||
var $util = require("../utilities");
|
||||
var Flags = require("../flags");
|
||||
var Account = require("../account");
|
||||
var typecheck = require("json-typecheck");
|
||||
var net = require("net");
|
||||
var util = require("../utilities");
|
||||
var crypto = require("crypto");
|
||||
var isTorExit = require("../tor").isTorExit;
|
||||
var session = require("../session");
|
||||
|
||||
var CONNECT_RATE = {
|
||||
burst: 5,
|
||||
sustained: 0.1
|
||||
};
|
||||
|
||||
var ipThrottle = {};
|
||||
// Keep track of number of connections per IP
|
||||
var ipCount = {};
|
||||
|
||||
/**
|
||||
* Called before an incoming socket.io connection is accepted.
|
||||
*/
|
||||
function handleAuth(socket, accept) {
|
||||
var data = socket.request;
|
||||
|
||||
socket.user = false;
|
||||
if (data.headers.cookie) {
|
||||
cookieParser(data, null, function () {
|
||||
var auth = data.signedCookies.auth;
|
||||
if (!auth) {
|
||||
return accept(null, true);
|
||||
}
|
||||
|
||||
session.verifySession(auth, function (err, user) {
|
||||
if (!err) {
|
||||
socket.user = {
|
||||
name: user.name,
|
||||
global_rank: user.global_rank
|
||||
};
|
||||
}
|
||||
accept(null, true);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
accept(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
function throttleIP(sock) {
|
||||
var ip = sock._realip;
|
||||
|
||||
if (!(ip in ipThrottle)) {
|
||||
ipThrottle[ip] = $util.newRateLimiter();
|
||||
}
|
||||
|
||||
if (ipThrottle[ip].throttle(CONNECT_RATE)) {
|
||||
Logger.syslog.log("WARN: IP throttled: " + ip);
|
||||
sock.emit("kick", {
|
||||
reason: "Your IP address is connecting too quickly. Please "+
|
||||
"wait 10 seconds before joining again."
|
||||
});
|
||||
sock.disconnect();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function ipLimitReached(sock) {
|
||||
var ip = sock._realip;
|
||||
|
||||
sock.on("disconnect", function () {
|
||||
ipCount[ip]--;
|
||||
if (ipCount[ip] === 0) {
|
||||
/* Clear out unnecessary counters to save memory */
|
||||
delete ipCount[ip];
|
||||
}
|
||||
});
|
||||
|
||||
if (!(ip in ipCount)) {
|
||||
ipCount[ip] = 0;
|
||||
}
|
||||
|
||||
ipCount[ip]++;
|
||||
if (ipCount[ip] > Config.get("io.ip-connection-limit")) {
|
||||
sock.emit("kick", {
|
||||
reason: "Too many connections from your IP address"
|
||||
});
|
||||
sock.disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function addTypecheckedFunctions(sock) {
|
||||
sock.typecheckedOn = function (msg, template, cb) {
|
||||
sock.on(msg, function (data) {
|
||||
typecheck(data, template, function (err, data) {
|
||||
if (err) {
|
||||
sock.emit("errorMsg", {
|
||||
msg: "Unexpected error for message " + msg + ": " + err.message
|
||||
});
|
||||
} else {
|
||||
cb(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
sock.typecheckedOnce = function (msg, template, cb) {
|
||||
sock.once(msg, function (data) {
|
||||
typecheck(data, template, function (err, data) {
|
||||
if (err) {
|
||||
sock.emit("errorMsg", {
|
||||
msg: "Unexpected error for message " + msg + ": " + err.message
|
||||
});
|
||||
} else {
|
||||
cb(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a connection is accepted
|
||||
*/
|
||||
function handleConnection(sock) {
|
||||
var ip = sock.client.conn.remoteAddress;
|
||||
if (!ip) {
|
||||
sock.emit("kick", {
|
||||
reason: "Your IP address could not be determined from the socket connection. See https://github.com/Automattic/socket.io/issues/1737 for details"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ipLimitReached(sock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.syslog.log("Accepted socket from " + ip);
|
||||
|
||||
addTypecheckedFunctions(sock);
|
||||
|
||||
var user = new User(sock);
|
||||
if (sock.user) {
|
||||
user.setFlag(Flags.U_REGISTERED);
|
||||
user.clearFlag(Flags.U_READY);
|
||||
user.refreshAccount({ name: sock.user.name },
|
||||
function (err, account) {
|
||||
if (err) {
|
||||
user.clearFlag(Flags.U_REGISTERED);
|
||||
user.setFlag(Flags.U_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
user.socket.emit("login", {
|
||||
success: true,
|
||||
name: user.getName(),
|
||||
guest: false
|
||||
});
|
||||
db.recordVisit(ip, user.getName());
|
||||
user.socket.emit("rank", user.account.effectiveRank);
|
||||
user.setFlag(Flags.U_LOGGED_IN);
|
||||
user.emit("login", account);
|
||||
Logger.syslog.log(ip + " logged in as " + user.getName());
|
||||
user.setFlag(Flags.U_READY);
|
||||
});
|
||||
} else {
|
||||
user.socket.emit("rank", -1);
|
||||
user.setFlag(Flags.U_READY);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function (srv) {
|
||||
var bound = {};
|
||||
var io = sio.instance = sio();
|
||||
|
||||
io.use(handleAuth);
|
||||
io.on("connection", handleConnection);
|
||||
|
||||
Config.get("listen").forEach(function (bind) {
|
||||
if (!bind.io) {
|
||||
return;
|
||||
}
|
||||
var id = bind.ip + ":" + bind.port;
|
||||
if (id in bound) {
|
||||
Logger.syslog.log("[WARN] Ignoring duplicate listen address " + id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (id in srv.servers) {
|
||||
io.attach(srv.servers[id]);
|
||||
} else {
|
||||
var server = require("http").createServer().listen(bind.port, bind.ip);
|
||||
server.on("clientError", function (err, socket) {
|
||||
console.error("clientError on " + id + " - " + err);
|
||||
try {
|
||||
socket.destroy();
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
io.attach(server);
|
||||
}
|
||||
|
||||
bound[id] = null;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* Clean out old rate limiters */
|
||||
setInterval(function () {
|
||||
for (var ip in ipThrottle) {
|
||||
if (ipThrottle[ip].lastTime < Date.now() - 60 * 1000) {
|
||||
var obj = ipThrottle[ip];
|
||||
/* Not strictly necessary, but seems to help the GC out a bit */
|
||||
for (var key in obj) {
|
||||
delete obj[key];
|
||||
}
|
||||
delete ipThrottle[ip];
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.get("aggressive-gc") && global && global.gc) {
|
||||
global.gc();
|
||||
}
|
||||
}, 5 * 60 * 1000);
|
||||
Loading…
Add table
Add a link
Reference in a new issue