Merge remote-tracking branch 'origin/development' into M2ys4U-server-time
[KiwiIRC.git] / server / weblistener.js
CommitLineData
d99e7823
D
1var engine = require('engine.io'),
2 WebsocketRpc = require('./websocketrpc.js');
3 events = require('events'),
4 http = require('http'),
5 https = require('https'),
6 util = require('util'),
7 fs = require('fs'),
8 dns = require('dns'),
9 url = require('url'),
10 _ = require('lodash'),
11 spdy = require('spdy'),
12 ipaddr = require('ipaddr.js'),
13 Client = require('./client.js').Client,
14 HttpHandler = require('./httphandler.js').HttpHandler,
15 rehash = require('./rehash.js');
1286229a
D
16
17
18
19rehash.on('rehashed', function (files) {
20 Client = require('./client.js').Client;
2f1e8a71 21 HttpHandler = require('./httphandler.js').HttpHandler;
1286229a
D
22});
23
186531ed
D
24
25// Instance of HttpHandler
26var http_handler;
27
a8bf3ea4 28
4d9fba5a 29var WebListener = module.exports = function (web_config) {
a0bf87be 30 var hs, opts,
a8bf3ea4
JA
31 that = this;
32
71c81a3b 33
a8bf3ea4 34 events.EventEmitter.call(this);
b156e01a 35
8b0eb787 36 http_handler = new HttpHandler(web_config);
b156e01a 37
8b0eb787 38 if (web_config.ssl) {
a8bf3ea4 39 opts = {
562ecbe0
PV
40 key: fs.readFileSync(web_config.ssl_key),
41 cert: fs.readFileSync(web_config.ssl_cert)
a8bf3ea4 42 };
186531ed 43
a8bf3ea4 44 // Do we have an intermediate certificate?
480552a2
D
45 if (typeof web_config.ssl_ca !== 'undefined') {
46 // An array of them?
47 if (typeof web_config.ssl_ca.map !== 'undefined') {
48 opts.ca = web_config.ssl_ca.map(function (f) { return fs.readFileSync(f); });
186531ed 49
480552a2
D
50 } else {
51 opts.ca = fs.readFileSync(web_config.ssl_ca);
52 }
53 }
186531ed 54
8f2efe03
JA
55 hs = spdy.createServer(opts, handleHttpRequest);
56
e380bc9e
D
57 hs.listen(web_config.port, web_config.address, function () {
58 that.emit('listening');
59 });
a8bf3ea4 60 } else {
186531ed 61
a8bf3ea4 62 // Start some plain-text server up
186531ed
D
63 hs = http.createServer(handleHttpRequest);
64
e380bc9e
D
65 hs.listen(web_config.port, web_config.address, function () {
66 that.emit('listening');
67 });
a8bf3ea4 68 }
d558508c
JA
69
70 hs.on('error', function (err) {
71 that.emit('error', err);
b156e01a
JA
72 });
73
d99e7823
D
74 this.ws = engine.attach(hs, {
75 transports: ['websocket', 'polling', 'flashsocket'],
76 path: (global.config.http_base_path || '') + '/transport'
77 });
d99e7823
D
78
79 this.ws.on('connection', function(socket) {
d99e7823
D
80 initialiseSocket(socket, function(err, authorised) {
81 var client;
82
83 if (!authorised) {
84 socket.close();
85 return;
86 }
87
88 client = new Client(socket);
89 client.on('dispose', function () {
90 that.emit('client_dispose', this);
91 });
92
93 that.emit('connection', client);
a09bb5c3
D
94
95 // Call any modules listening for new clients
96 global.modules.emit('client created', {client: client});
d99e7823
D
97 });
98 });
a8bf3ea4
JA
99};
100util.inherits(WebListener, events.EventEmitter);
101
186531ed
D
102
103
104function handleHttpRequest(request, response) {
0fe7c000 105 http_handler.serve(request, response);
186531ed
D
106}
107
e7af0cbb
JA
108function rangeCheck(addr, range) {
109 var i, ranges, parts;
110 ranges = (!_.isArray(range)) ? [range] : range;
111 for (i = 0; i < ranges.length; i++) {
112 parts = ranges[i].split('/');
113 if (ipaddr.process(addr).match(ipaddr.process(parts[0]), parts[1])) {
114 return true;
115 }
116 }
117 return false;
118}
119
a8bf3ea4 120
15fefff7
D
121/**
122 * Get the reverse DNS entry for this connection.
123 * Used later on for webirc, etc functionality
124 */
d99e7823
D
125function initialiseSocket(socket, callback) {
126 var request = socket.request,
127 address = request.connection.remoteAddress;
128
129 // Key/val data stored to the socket to be read later on
130 // May also be synced to a redis DB to lookup clients
a0bf87be 131 socket.meta = {};
c6e3ed44
D
132
133 // If a forwarded-for header is found, switch the source address
d99e7823 134 if (request.headers[global.config.http_proxy_ip_header || 'x-forwarded-for']) {
c6e3ed44 135 // Check we're connecting from a whitelisted proxy
e7af0cbb 136 if (!global.config.http_proxies || !rangeCheck(address, global.config.http_proxies)) {
c6e3ed44
D
137 console.log('Unlisted proxy:', address);
138 callback(null, false);
139 return;
140 }
141
142 // We're sent from a whitelisted proxy, replace the hosts
d99e7823 143 address = request.headers[global.config.http_proxy_ip_header || 'x-forwarded-for'];
c6e3ed44
D
144 }
145
a0bf87be 146 socket.meta.real_address = address;
b156e01a 147
13d7faa3 148 // If enabled, don't go over the connection limit
b737610b
JA
149 if (global.config.max_client_conns && global.config.max_client_conns > 0) {
150 if (global.clients.numOnAddress(address) + 1 > global.config.max_client_conns) {
13d7faa3
D
151 return callback(null, false);
152 }
c36ed4eb 153 }
be481108
D
154
155
156 try {
157 dns.reverse(address, function (err, domains) {
158 if (err || domains.length === 0) {
a0bf87be 159 socket.meta.revdns = address;
be481108 160 } else {
a0bf87be 161 socket.meta.revdns = _.first(domains) || address;
be481108 162 }
b156e01a 163
be481108
D
164 // All is well, authorise the connection
165 callback(null, true);
166 });
167 } catch (err) {
a0bf87be 168 socket.meta.revdns = address;
a8bf3ea4 169 callback(null, true);
be481108 170 }
15fefff7 171}