Commit | Line | Data |
---|---|---|
d99e7823 | 1 | var engine = require('engine.io'), |
94b00b2d | 2 | WebsocketRpc = require('./websocketrpc.js'), |
d99e7823 D |
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'), | |
94b00b2d | 13 | winston = require('winston'), |
d99e7823 D |
14 | Client = require('./client.js').Client, |
15 | HttpHandler = require('./httphandler.js').HttpHandler, | |
0ca6adac D |
16 | rehash = require('./rehash.js'), |
17 | Stats = require('./stats.js'); | |
1286229a D |
18 | |
19 | ||
20 | ||
21 | rehash.on('rehashed', function (files) { | |
22 | Client = require('./client.js').Client; | |
2f1e8a71 | 23 | HttpHandler = require('./httphandler.js').HttpHandler; |
1286229a D |
24 | }); |
25 | ||
186531ed D |
26 | |
27 | // Instance of HttpHandler | |
28 | var http_handler; | |
29 | ||
a8bf3ea4 | 30 | |
4d9fba5a | 31 | var WebListener = module.exports = function (web_config) { |
a0bf87be | 32 | var hs, opts, |
a8bf3ea4 JA |
33 | that = this; |
34 | ||
71c81a3b | 35 | |
a8bf3ea4 | 36 | events.EventEmitter.call(this); |
b156e01a | 37 | |
8b0eb787 | 38 | http_handler = new HttpHandler(web_config); |
b156e01a | 39 | |
8b0eb787 | 40 | if (web_config.ssl) { |
a8bf3ea4 | 41 | opts = { |
562ecbe0 PV |
42 | key: fs.readFileSync(web_config.ssl_key), |
43 | cert: fs.readFileSync(web_config.ssl_cert) | |
a8bf3ea4 | 44 | }; |
186531ed | 45 | |
a8bf3ea4 | 46 | // Do we have an intermediate certificate? |
480552a2 D |
47 | if (typeof web_config.ssl_ca !== 'undefined') { |
48 | // An array of them? | |
49 | if (typeof web_config.ssl_ca.map !== 'undefined') { | |
50 | opts.ca = web_config.ssl_ca.map(function (f) { return fs.readFileSync(f); }); | |
186531ed | 51 | |
480552a2 D |
52 | } else { |
53 | opts.ca = fs.readFileSync(web_config.ssl_ca); | |
54 | } | |
55 | } | |
186531ed | 56 | |
15ab9e0e | 57 | hs = spdy.createServer(opts); |
8f2efe03 | 58 | |
e380bc9e D |
59 | hs.listen(web_config.port, web_config.address, function () { |
60 | that.emit('listening'); | |
61 | }); | |
a8bf3ea4 | 62 | } else { |
186531ed | 63 | |
a8bf3ea4 | 64 | // Start some plain-text server up |
15ab9e0e | 65 | hs = http.createServer(); |
186531ed | 66 | |
e380bc9e D |
67 | hs.listen(web_config.port, web_config.address, function () { |
68 | that.emit('listening'); | |
69 | }); | |
a8bf3ea4 | 70 | } |
d558508c JA |
71 | |
72 | hs.on('error', function (err) { | |
73 | that.emit('error', err); | |
b156e01a JA |
74 | }); |
75 | ||
15ab9e0e D |
76 | this.ws = new engine.Server(); |
77 | ||
78 | hs.on('upgrade', function(req, socket, head){ | |
fdec8078 D |
79 | // engine.io can sometimes "loose" the clients remote address. Keep note of it |
80 | req.meta = { | |
81 | remote_address: req.connection.remoteAddress | |
82 | }; | |
83 | ||
15ab9e0e D |
84 | that.ws.handleUpgrade(req, socket, head); |
85 | }); | |
86 | ||
87 | hs.on('request', function(req, res){ | |
ba317e87 D |
88 | var base_path = (global.config.http_base_path || ''), |
89 | transport_url; | |
90 | ||
91 | // Trim off any trailing slashes | |
92 | if (base_path.substr(base_path.length - 1) === '/') { | |
93 | base_path = base_path.substr(0, base_path.length - 1); | |
94 | } | |
95 | transport_url = base_path + '/transport'; | |
15ab9e0e | 96 | |
0ca6adac D |
97 | Stats.incr('http.request'); |
98 | ||
15ab9e0e D |
99 | // engine.io can sometimes "loose" the clients remote address. Keep note of it |
100 | req.meta = { | |
101 | remote_address: req.connection.remoteAddress | |
102 | }; | |
103 | ||
104 | // If the request is for our transport, pass it onto engine.io | |
105 | if (req.url.toLowerCase().indexOf(transport_url.toLowerCase()) === 0) { | |
106 | that.ws.handleRequest(req, res); | |
107 | } else { | |
108 | http_handler.serve(req, res); | |
109 | } | |
110 | ||
111 | ||
d99e7823 | 112 | }); |
d99e7823 D |
113 | |
114 | this.ws.on('connection', function(socket) { | |
0ca6adac D |
115 | Stats.incr('http.websocket'); |
116 | ||
d99e7823 D |
117 | initialiseSocket(socket, function(err, authorised) { |
118 | var client; | |
119 | ||
120 | if (!authorised) { | |
121 | socket.close(); | |
122 | return; | |
123 | } | |
124 | ||
bede49f6 | 125 | client = new Client(socket, {server_config: web_config}); |
d99e7823 D |
126 | client.on('dispose', function () { |
127 | that.emit('client_dispose', this); | |
128 | }); | |
129 | ||
130 | that.emit('connection', client); | |
a09bb5c3 D |
131 | |
132 | // Call any modules listening for new clients | |
133 | global.modules.emit('client created', {client: client}); | |
d99e7823 D |
134 | }); |
135 | }); | |
a8bf3ea4 JA |
136 | }; |
137 | util.inherits(WebListener, events.EventEmitter); | |
138 | ||
186531ed D |
139 | |
140 | ||
e7af0cbb JA |
141 | function rangeCheck(addr, range) { |
142 | var i, ranges, parts; | |
143 | ranges = (!_.isArray(range)) ? [range] : range; | |
144 | for (i = 0; i < ranges.length; i++) { | |
145 | parts = ranges[i].split('/'); | |
146 | if (ipaddr.process(addr).match(ipaddr.process(parts[0]), parts[1])) { | |
147 | return true; | |
148 | } | |
149 | } | |
150 | return false; | |
151 | } | |
152 | ||
a8bf3ea4 | 153 | |
15fefff7 D |
154 | /** |
155 | * Get the reverse DNS entry for this connection. | |
156 | * Used later on for webirc, etc functionality | |
157 | */ | |
d99e7823 D |
158 | function initialiseSocket(socket, callback) { |
159 | var request = socket.request, | |
15ab9e0e | 160 | address = request.meta.remote_address, |
39105e8e | 161 | revdns; |
d99e7823 D |
162 | |
163 | // Key/val data stored to the socket to be read later on | |
164 | // May also be synced to a redis DB to lookup clients | |
15ab9e0e | 165 | socket.meta = socket.request.meta; |
c6e3ed44 D |
166 | |
167 | // If a forwarded-for header is found, switch the source address | |
d99e7823 | 168 | if (request.headers[global.config.http_proxy_ip_header || 'x-forwarded-for']) { |
c6e3ed44 | 169 | // Check we're connecting from a whitelisted proxy |
e7af0cbb | 170 | if (!global.config.http_proxies || !rangeCheck(address, global.config.http_proxies)) { |
94b00b2d | 171 | winston.info('Unlisted proxy: %s', address); |
c6e3ed44 D |
172 | callback(null, false); |
173 | return; | |
174 | } | |
175 | ||
176 | // We're sent from a whitelisted proxy, replace the hosts | |
d99e7823 | 177 | address = request.headers[global.config.http_proxy_ip_header || 'x-forwarded-for']; |
c6e3ed44 D |
178 | } |
179 | ||
a0bf87be | 180 | socket.meta.real_address = address; |
b156e01a | 181 | |
13d7faa3 | 182 | // If enabled, don't go over the connection limit |
b737610b JA |
183 | if (global.config.max_client_conns && global.config.max_client_conns > 0) { |
184 | if (global.clients.numOnAddress(address) + 1 > global.config.max_client_conns) { | |
13d7faa3 D |
185 | return callback(null, false); |
186 | } | |
c36ed4eb | 187 | } |
be481108 D |
188 | |
189 | ||
190 | try { | |
191 | dns.reverse(address, function (err, domains) { | |
39105e8e D |
192 | if (!err && domains.length > 0) { |
193 | revdns = _.first(domains); | |
194 | } | |
195 | ||
196 | if (!revdns) { | |
197 | // No reverse DNS found, use the IP | |
a0bf87be | 198 | socket.meta.revdns = address; |
39105e8e D |
199 | callback(null, true); |
200 | ||
be481108 | 201 | } else { |
39105e8e D |
202 | // Make sure the reverse DNS matches the A record to use the hostname.. |
203 | dns.lookup(revdns, function (err, ip_address, family) { | |
204 | if (!err && ip_address == address) { | |
205 | // A record matches PTR, perfectly valid hostname | |
206 | socket.meta.revdns = revdns; | |
207 | } else { | |
208 | // A record does not match the PTR, invalid hostname | |
209 | socket.meta.revdns = address; | |
210 | } | |
211 | ||
212 | // We have all the info we need, proceed with the connection | |
213 | callback(null, true); | |
214 | }); | |
be481108 | 215 | } |
be481108 | 216 | }); |
39105e8e | 217 | |
be481108 | 218 | } catch (err) { |
a0bf87be | 219 | socket.meta.revdns = address; |
a8bf3ea4 | 220 | callback(null, true); |
be481108 | 221 | } |
15fefff7 | 222 | } |