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