diff --git a/defaults/config.js b/defaults/config.js index f57b40c7..5757a7a7 100644 --- a/defaults/config.js +++ b/defaults/config.js @@ -35,6 +35,15 @@ module.exports = { // bind: undefined, + // + // Sets whether the server is behind a reverse proxy and should honor the + // X-Forwarded-For header or not. + // + // @type boolean + // @default false + // + reverseProxy: false, + // // Set the default theme. // @@ -97,6 +106,25 @@ module.exports = { // lockNetwork: false, + // + // WEBIRC support + // + // If enabled, The Lounge will pass the connecting user's host and IP to the + // IRC server. Note that this requires to obtain a password from the IRC network + // The Lounge will be connecting to and generally involves a lot of trust from the + // network you are connecting to. + // + // Format (standard): {"irc.example.net": "hunter1", "irc.example.org": "passw0rd"} + // Format (function): + // {"irc.example.net": function(client, args, trusted) { + // // here, we return a webirc object fed directly to `irc-framework` + // return {password: "hunter1", address: args.ip, hostname: "webirc/"+args.hostname}; + // }} + // + // @type string | function(client, args):object(webirc) + // @default null + webirc: null, + // // Log settings // diff --git a/src/client.js b/src/client.js index f9ffa656..d8d6b676 100644 --- a/src/client.js +++ b/src/client.js @@ -129,6 +129,7 @@ Client.prototype.connect = function(args) { var client = this; var nick = args.nick || "lounge-user"; + var webirc = null; var network = new Network({ name: args.name || "", @@ -138,7 +139,9 @@ Client.prototype.connect = function(args) { password: args.password, username: args.username || nick.replace(/[^a-zA-Z0-9]/g, ""), realname: args.realname || "The Lounge User", - commands: args.commands + commands: args.commands, + ip: args.ip, + hostname: args.hostname, }); client.networks.push(network); @@ -169,6 +172,26 @@ Client.prototype.connect = function(args) { return; } + if (config.webirc !== null && network.host in config.webirc) { + args.ip = args.ip || (client.config && client.config.ip) || client.ip; + args.hostname = args.hostname || (client.config && client.config.hostname) || client.hostname || args.ip; + + if (args.ip) { + if (config.webirc[network.host] instanceof Function) { + webirc = config.webirc[network.host](client, args); + } else { + webirc = { + password: config.webirc[network.host], + address: args.ip, + hostname: args.hostname + }; + } + } else { + log.warn("Cannot find a valid WEBIRC configuration for " + nick + + "!" + network.username + "@" + network.host); + } + } + network.irc = new ircFramework.Client(); network.irc.requestCap([ "echo-message", @@ -185,6 +208,7 @@ Client.prototype.connect = function(args) { localAddress: config.bind, rejectUnauthorized: false, auto_reconnect: false, // TODO: Enable auto reconnection + webirc: webirc, }); network.irc.on("registered", function() { diff --git a/src/models/network.js b/src/models/network.js index 1df03704..bba9dc66 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -16,6 +16,8 @@ function Network(attr) { username: "", realname: "", channels: [], + ip: null, + hostname: null, id: id++, irc: null, serverOptions: { @@ -45,7 +47,9 @@ Network.prototype.export = function() { "password", "username", "realname", - "commands" + "commands", + "ip", + "hostname" ]); network.nick = (this.irc && this.irc.user.nick) || ""; network.join = _.map( diff --git a/src/server.js b/src/server.js index 6249a4c0..e9ec1c0c 100644 --- a/src/server.js +++ b/src/server.js @@ -6,6 +6,7 @@ var ClientManager = require("./clientManager"); var express = require("express"); var fs = require("fs"); var io = require("socket.io"); +var dns = require("dns"); var Helper = require("./helper"); var config = {}; @@ -19,8 +20,6 @@ module.exports = function(options) { .use(index) .use(express.static("client")); - app.enable("trust proxy"); - var server = null; var https = config.https || {}; var protocol = https.enable ? "https" : "http"; @@ -73,6 +72,14 @@ module.exports = function(options) { } }; +function getClientIp(req) { + if (!config.reverseProxy) { + return req.connection.remoteAddress; + } else { + return req.headers["x-forwarded-for"] || req.connection.remoteAddress; + } +} + function index(req, res, next) { if (req.url.split("?")[0] !== "/") { return next(); @@ -110,6 +117,9 @@ function init(socket, client, token) { socket.on( "conn", function(data) { + // prevent people from overriding webirc settings + data.ip = null; + data.hostname = null; client.connect(data); } ); @@ -185,6 +195,20 @@ function init(socket, client, token) { } } +function reverseDnsLookup(socket, client, token) { + client.ip = getClientIp(socket.request); + + dns.reverse(client.ip, function(err, host) { + if (!err && host.length) { + client.hostname = host[0]; + } else { + client.hostname = client.ip; + } + + init(socket, client, token); + }); +} + function auth(data) { var socket = this; if (config.public) { @@ -194,7 +218,11 @@ function auth(data) { manager.clients = _.without(manager.clients, client); client.quit(); }); - init(socket, client); + if (config.webirc) { + reverseDnsLookup(socket, client); + } else { + init(socket, client); + } } else { var success = false; _.each(manager.clients, function(client) { @@ -212,7 +240,11 @@ function auth(data) { if (data.remember || data.token) { token = client.token; } - init(socket, client, token); + if (config.webirc !== null && !client.config["ip"]) { + reverseDnsLookup(socket, client, token); + } else { + init(socket, client, token); + } return false; } }); diff --git a/test/models/network.js b/test/models/network.js index dcd9d92b..000c2cef 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -24,6 +24,8 @@ describe("Network", function() { commands: [], nick: "", join: "#thelounge,&foobar", + ip: null, + hostname: null }); }); });