macgyver 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +20 -0
  5. data/README.md +45 -0
  6. data/Rakefile +1 -0
  7. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
  8. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
  9. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
  10. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
  11. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
  12. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
  13. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
  14. data/assets/MacGap.app/Contents/Info.plist +48 -0
  15. data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
  16. data/assets/MacGap.app/Contents/PkgInfo +1 -0
  17. data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
  18. data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
  19. data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
  20. data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
  21. data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
  22. data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
  23. data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
  24. data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
  25. data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
  26. data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
  27. data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
  28. data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
  29. data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
  30. data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
  31. data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
  32. data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
  33. data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
  34. data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
  35. data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
  36. data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
  37. data/assets/MacGap.app/Contents/Resources/console.js +108 -0
  38. data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
  39. data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
  40. data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
  41. data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
  42. data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
  43. data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
  44. data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
  45. data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
  46. data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
  47. data/assets/MacGap.app/Contents/Resources/events.js +312 -0
  48. data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
  49. data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
  50. data/assets/MacGap.app/Contents/Resources/http.js +119 -0
  51. data/assets/MacGap.app/Contents/Resources/https.js +134 -0
  52. data/assets/MacGap.app/Contents/Resources/module.js +529 -0
  53. data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
  54. data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
  55. data/assets/MacGap.app/Contents/Resources/os.js +64 -0
  56. data/assets/MacGap.app/Contents/Resources/path.js +517 -0
  57. data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
  58. data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
  59. data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
  60. data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
  61. data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
  62. data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
  63. data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
  64. data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
  65. data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
  66. data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
  67. data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
  68. data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
  69. data/assets/MacGap.app/Contents/Resources/url.js +693 -0
  70. data/assets/MacGap.app/Contents/Resources/util.js +688 -0
  71. data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
  72. data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
  73. data/assets/index.html +38 -0
  74. data/bin/macgyver +104 -0
  75. data/macgyver.gemspec +19 -0
  76. data/test/public/index.html +27 -0
  77. metadata +121 -0
@@ -0,0 +1,831 @@
1
+ // Copyright Joyent, Inc. and other Node contributors.
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a
4
+ // copy of this software and associated documentation files (the
5
+ // "Software"), to deal in the Software without restriction, including
6
+ // without limitation the rights to use, copy, modify, merge, publish,
7
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ // persons to whom the Software is furnished to do so, subject to the
9
+ // following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included
12
+ // in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ var assert = require('assert');
23
+ var constants = require('constants');
24
+ var crypto = require('crypto');
25
+ var net = require('net');
26
+ var tls = require('tls');
27
+ var util = require('util');
28
+
29
+ var Timer = process.binding('timer_wrap').Timer;
30
+ var tls_wrap = process.binding('tls_wrap');
31
+
32
+ // Lazy load
33
+ var tls_legacy;
34
+
35
+ var debug = util.debuglog('tls');
36
+
37
+ function onhandshakestart() {
38
+ debug('onhandshakestart');
39
+
40
+ var self = this;
41
+ var ssl = self.ssl;
42
+ var now = Timer.now();
43
+
44
+ assert(now >= ssl.lastHandshakeTime);
45
+
46
+ if ((now - ssl.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
47
+ ssl.handshakes = 0;
48
+ }
49
+
50
+ var first = (ssl.lastHandshakeTime === 0);
51
+ ssl.lastHandshakeTime = now;
52
+ if (first) return;
53
+
54
+ if (++ssl.handshakes > tls.CLIENT_RENEG_LIMIT) {
55
+ // Defer the error event to the next tick. We're being called from OpenSSL's
56
+ // state machine and OpenSSL is not re-entrant. We cannot allow the user's
57
+ // callback to destroy the connection right now, it would crash and burn.
58
+ setImmediate(function() {
59
+ var err = new Error('TLS session renegotiation attack detected.');
60
+ self._tlsError(err);
61
+ });
62
+ }
63
+ }
64
+
65
+
66
+ function onhandshakedone() {
67
+ // for future use
68
+ debug('onhandshakedone');
69
+ this._finishInit();
70
+ }
71
+
72
+
73
+ function onclienthello(hello) {
74
+ var self = this,
75
+ onceSession = false,
76
+ onceSNI = false;
77
+
78
+ function callback(err, session) {
79
+ if (onceSession)
80
+ return self.destroy(new Error('TLS session callback was called 2 times'));
81
+ onceSession = true;
82
+
83
+ if (err)
84
+ return self.destroy(err);
85
+
86
+ // NOTE: That we have disabled OpenSSL's internal session storage in
87
+ // `node_crypto.cc` and hence its safe to rely on getting servername only
88
+ // from clienthello or this place.
89
+ var ret = self.ssl.loadSession(session);
90
+
91
+ // Servername came from SSL session
92
+ // NOTE: TLS Session ticket doesn't include servername information
93
+ //
94
+ // Another note, From RFC3546:
95
+ //
96
+ // If, on the other hand, the older
97
+ // session is resumed, then the server MUST ignore extensions appearing
98
+ // in the client hello, and send a server hello containing no
99
+ // extensions; in this case the extension functionality negotiated
100
+ // during the original session initiation is applied to the resumed
101
+ // session.
102
+ //
103
+ // Therefore we should account session loading when dealing with servername
104
+ if (ret && ret.servername) {
105
+ self._SNICallback(ret.servername, onSNIResult);
106
+ } else if (hello.servername && self._SNICallback) {
107
+ self._SNICallback(hello.servername, onSNIResult);
108
+ } else {
109
+ self.ssl.endParser();
110
+ }
111
+ }
112
+
113
+ function onSNIResult(err, context) {
114
+ if (onceSNI)
115
+ return self.destroy(new Error('TLS SNI callback was called 2 times'));
116
+ onceSNI = true;
117
+
118
+ if (err)
119
+ return self.destroy(err);
120
+
121
+ if (context)
122
+ self.ssl.sni_context = context;
123
+
124
+ self.ssl.endParser();
125
+ }
126
+
127
+ if (hello.sessionId.length <= 0 ||
128
+ hello.tlsTicket ||
129
+ this.server &&
130
+ !this.server.emit('resumeSession', hello.sessionId, callback)) {
131
+ // Invoke SNI callback, since we've no session to resume
132
+ if (hello.servername && this._SNICallback)
133
+ this._SNICallback(hello.servername, onSNIResult);
134
+ else
135
+ this.ssl.endParser();
136
+ }
137
+ }
138
+
139
+
140
+ function onnewsession(key, session) {
141
+ if (this.server)
142
+ this.server.emit('newSession', key, session);
143
+ }
144
+
145
+
146
+ /**
147
+ * Provides a wrap of socket stream to do encrypted communication.
148
+ */
149
+
150
+ function TLSSocket(socket, options) {
151
+ // Disallow wrapping TLSSocket in TLSSocket
152
+ assert(!(socket instanceof TLSSocket));
153
+
154
+ net.Socket.call(this, socket && {
155
+ handle: socket._handle,
156
+ allowHalfOpen: socket.allowHalfOpen,
157
+ readable: socket.readable,
158
+ writable: socket.writable
159
+ });
160
+
161
+ // To prevent assertion in afterConnect()
162
+ if (socket)
163
+ this._connecting = socket._connecting;
164
+
165
+ this._tlsOptions = options;
166
+ this._secureEstablished = false;
167
+ this._controlReleased = false;
168
+ this._SNICallback = null;
169
+ this.ssl = null;
170
+ this.servername = null;
171
+ this.npnProtocol = null;
172
+ this.authorized = false;
173
+ this.authorizationError = null;
174
+
175
+ // Just a documented property to make secure sockets
176
+ // distinguishable from regular ones.
177
+ this.encrypted = true;
178
+
179
+ this.on('error', this._tlsError);
180
+
181
+ if (!this._handle) {
182
+ this.once('connect', function() {
183
+ this._init(null);
184
+ });
185
+ } else {
186
+ this._init(socket);
187
+ }
188
+ }
189
+ util.inherits(TLSSocket, net.Socket);
190
+ exports.TLSSocket = TLSSocket;
191
+
192
+ TLSSocket.prototype._init = function(socket) {
193
+ assert(this._handle);
194
+
195
+ // lib/net.js expect this value to be non-zero if write hasn't been flushed
196
+ // immediately
197
+ // TODO(indutny): rewise this solution, it might be 1 before handshake and
198
+ // represent real writeQueueSize during regular writes.
199
+ this._handle.writeQueueSize = 1;
200
+
201
+ var self = this;
202
+ var options = this._tlsOptions;
203
+
204
+ // Wrap socket's handle
205
+ var credentials = options.credentials || crypto.createCredentials();
206
+ this.ssl = tls_wrap.wrap(this._handle, credentials.context, options.isServer);
207
+ this.server = options.server || null;
208
+
209
+ // For clients, we will always have either a given ca list or be using
210
+ // default one
211
+ var requestCert = !!options.requestCert || !options.isServer,
212
+ rejectUnauthorized = !!options.rejectUnauthorized;
213
+
214
+ this._requestCert = requestCert;
215
+ this._rejectUnauthorized = rejectUnauthorized;
216
+ if (requestCert || rejectUnauthorized)
217
+ this.ssl.setVerifyMode(requestCert, rejectUnauthorized);
218
+
219
+ if (options.isServer) {
220
+ this.ssl.onhandshakestart = onhandshakestart.bind(this);
221
+ this.ssl.onhandshakedone = onhandshakedone.bind(this);
222
+ this.ssl.onclienthello = onclienthello.bind(this);
223
+ this.ssl.onnewsession = onnewsession.bind(this);
224
+ this.ssl.lastHandshakeTime = 0;
225
+ this.ssl.handshakes = 0;
226
+
227
+ if (this.server &&
228
+ (this.server.listeners('resumeSession').length > 0 ||
229
+ this.server.listeners('newSession').length > 0)) {
230
+ this.ssl.enableSessionCallbacks();
231
+ }
232
+ } else {
233
+ this.ssl.onhandshakestart = function() {};
234
+ this.ssl.onhandshakedone = this._finishInit.bind(this);
235
+ }
236
+
237
+ this.ssl.onerror = function(err) {
238
+ // Destroy socket if error happened before handshake's finish
239
+ if (!this._secureEstablished) {
240
+ self._tlsError(err);
241
+ self.destroy();
242
+ } else if (options.isServer &&
243
+ rejectUnauthorized &&
244
+ /peer did not return a certificate/.test(err.message)) {
245
+ // Ignore server's authorization errors
246
+ self.destroy();
247
+ } else {
248
+ // Throw error
249
+ self._tlsError(err);
250
+ }
251
+ };
252
+
253
+ // If custom SNICallback was given, or if
254
+ // there're SNI contexts to perform match against -
255
+ // set `.onsniselect` callback.
256
+ if (process.features.tls_sni &&
257
+ options.isServer &&
258
+ options.server &&
259
+ (options.SNICallback !== SNICallback ||
260
+ options.server._contexts.length)) {
261
+ assert(typeof options.SNICallback === 'function');
262
+ this._SNICallback = options.SNICallback;
263
+ this.ssl.enableHelloParser();
264
+ }
265
+
266
+ if (process.features.tls_npn && options.NPNProtocols)
267
+ this.ssl.setNPNProtocols(options.NPNProtocols);
268
+
269
+ if (options.handshakeTimeout > 0)
270
+ this.setTimeout(options.handshakeTimeout, this._handleTimeout);
271
+
272
+ // Socket already has some buffered data - emulate receiving it
273
+ if (socket && socket._readableState.length) {
274
+ var buf;
275
+ while ((buf = socket.read()) !== null)
276
+ this.ssl.receive(buf);
277
+ }
278
+ };
279
+
280
+ TLSSocket.prototype.renegotiate = function(options, callback) {
281
+ var requestCert = this._requestCert,
282
+ rejectUnauthorized = this._rejectUnauthorized;
283
+
284
+ if (typeof options.requestCert !== 'undefined')
285
+ requestCert = !!options.requestCert;
286
+ if (typeof options.rejectUnauthorized !== 'undefined')
287
+ rejectUnauthorized = !!options.rejectUnauthorized;
288
+
289
+ if (requestCert !== this._requestCert ||
290
+ rejectUnauthorized !== this._rejectUnauthorized) {
291
+ this.ssl.setVerifyMode(requestCert, rejectUnauthorized);
292
+ this._requestCert = requestCert;
293
+ this._rejectUnauthorized = rejectUnauthorized;
294
+ }
295
+ if (!this.ssl.renegotiate()) {
296
+ if (callback) {
297
+ process.nextTick(function() {
298
+ callback(new Error('Failed to renegotiate'));
299
+ });
300
+ }
301
+ return false;
302
+ }
303
+
304
+ // Ensure that we'll cycle through internal openssl's state
305
+ this.write('');
306
+
307
+ if (callback) {
308
+ this.once('secure', function() {
309
+ callback(null);
310
+ });
311
+ }
312
+
313
+ return true;
314
+ };
315
+
316
+ TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
317
+ return this.ssl.setMaxSendFragment(size) == 1;
318
+ };
319
+
320
+ TLSSocket.prototype._handleTimeout = function() {
321
+ this._tlsError(new Error('TLS handshake timeout'));
322
+ };
323
+
324
+ TLSSocket.prototype._tlsError = function(err) {
325
+ this.emit('_tlsError', err);
326
+ if (this._controlReleased)
327
+ this.emit('error', err);
328
+ };
329
+
330
+ TLSSocket.prototype._releaseControl = function() {
331
+ if (this._controlReleased)
332
+ return false;
333
+ this._controlReleased = true;
334
+ this.removeListener('error', this._tlsError);
335
+ return true;
336
+ };
337
+
338
+ TLSSocket.prototype._finishInit = function() {
339
+ if (process.features.tls_npn) {
340
+ this.npnProtocol = this.ssl.getNegotiatedProtocol();
341
+ }
342
+
343
+ if (process.features.tls_sni && this._tlsOptions.isServer) {
344
+ this.servername = this.ssl.getServername();
345
+ }
346
+
347
+ debug('secure established');
348
+ this._secureEstablished = true;
349
+ if (this._tlsOptions.handshakeTimeout > 0)
350
+ this.setTimeout(0, this._handleTimeout);
351
+ this.emit('secure');
352
+ };
353
+
354
+ TLSSocket.prototype._start = function() {
355
+ this.ssl.start();
356
+ };
357
+
358
+ TLSSocket.prototype.setServername = function(name) {
359
+ this.ssl.setServername(name);
360
+ };
361
+
362
+ TLSSocket.prototype.setSession = function(session) {
363
+ if (util.isString(session))
364
+ session = new Buffer(session, 'binary');
365
+ this.ssl.setSession(session);
366
+ };
367
+
368
+ TLSSocket.prototype.getPeerCertificate = function() {
369
+ if (this.ssl) {
370
+ var c = this.ssl.getPeerCertificate();
371
+
372
+ if (c) {
373
+ if (c.issuer) c.issuer = tls.parseCertString(c.issuer);
374
+ if (c.subject) c.subject = tls.parseCertString(c.subject);
375
+ return c;
376
+ }
377
+ }
378
+
379
+ return null;
380
+ };
381
+
382
+ TLSSocket.prototype.getSession = function() {
383
+ if (this.ssl) {
384
+ return this.ssl.getSession();
385
+ }
386
+
387
+ return null;
388
+ };
389
+
390
+ TLSSocket.prototype.isSessionReused = function() {
391
+ if (this.ssl) {
392
+ return this.ssl.isSessionReused();
393
+ }
394
+
395
+ return null;
396
+ };
397
+
398
+ TLSSocket.prototype.getCipher = function(err) {
399
+ if (this.ssl) {
400
+ return this.ssl.getCurrentCipher();
401
+ } else {
402
+ return null;
403
+ }
404
+ };
405
+
406
+ // TODO: support anonymous (nocert) and PSK
407
+
408
+
409
+ // AUTHENTICATION MODES
410
+ //
411
+ // There are several levels of authentication that TLS/SSL supports.
412
+ // Read more about this in "man SSL_set_verify".
413
+ //
414
+ // 1. The server sends a certificate to the client but does not request a
415
+ // cert from the client. This is common for most HTTPS servers. The browser
416
+ // can verify the identity of the server, but the server does not know who
417
+ // the client is. Authenticating the client is usually done over HTTP using
418
+ // login boxes and cookies and stuff.
419
+ //
420
+ // 2. The server sends a cert to the client and requests that the client
421
+ // also send it a cert. The client knows who the server is and the server is
422
+ // requesting the client also identify themselves. There are several
423
+ // outcomes:
424
+ //
425
+ // A) verifyError returns null meaning the client's certificate is signed
426
+ // by one of the server's CAs. The server know's the client idenity now
427
+ // and the client is authorized.
428
+ //
429
+ // B) For some reason the client's certificate is not acceptable -
430
+ // verifyError returns a string indicating the problem. The server can
431
+ // either (i) reject the client or (ii) allow the client to connect as an
432
+ // unauthorized connection.
433
+ //
434
+ // The mode is controlled by two boolean variables.
435
+ //
436
+ // requestCert
437
+ // If true the server requests a certificate from client connections. For
438
+ // the common HTTPS case, users will want this to be false, which is what
439
+ // it defaults to.
440
+ //
441
+ // rejectUnauthorized
442
+ // If true clients whose certificates are invalid for any reason will not
443
+ // be allowed to make connections. If false, they will simply be marked as
444
+ // unauthorized but secure communication will continue. By default this is
445
+ // true.
446
+ //
447
+ //
448
+ //
449
+ // Options:
450
+ // - requestCert. Send verify request. Default to false.
451
+ // - rejectUnauthorized. Boolean, default to true.
452
+ // - key. string.
453
+ // - cert: string.
454
+ // - ca: string or array of strings.
455
+ // - sessionTimeout: integer.
456
+ //
457
+ // emit 'secureConnection'
458
+ // function (tlsSocket) { }
459
+ //
460
+ // "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
461
+ // "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
462
+ // "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
463
+ // "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
464
+ // "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
465
+ // "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
466
+ // "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
467
+ // "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
468
+ // "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
469
+ // "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
470
+ // "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
471
+ // "CERT_REJECTED"
472
+ //
473
+ function Server(/* [options], listener */) {
474
+ var options, listener;
475
+ if (util.isObject(arguments[0])) {
476
+ options = arguments[0];
477
+ listener = arguments[1];
478
+ } else if (util.isFunction(arguments[0])) {
479
+ options = {};
480
+ listener = arguments[0];
481
+ }
482
+
483
+ if (!(this instanceof Server)) return new Server(options, listener);
484
+
485
+ this._contexts = [];
486
+
487
+ var self = this;
488
+
489
+ // Handle option defaults:
490
+ this.setOptions(options);
491
+
492
+ var sharedCreds = crypto.createCredentials({
493
+ pfx: self.pfx,
494
+ key: self.key,
495
+ passphrase: self.passphrase,
496
+ cert: self.cert,
497
+ ca: self.ca,
498
+ ciphers: self.ciphers || tls.DEFAULT_CIPHERS,
499
+ ecdhCurve: util.isUndefined(self.ecdhCurve) ?
500
+ tls.DEFAULT_ECDH_CURVE : self.ecdhCurve,
501
+ secureProtocol: self.secureProtocol,
502
+ secureOptions: self.secureOptions,
503
+ crl: self.crl,
504
+ sessionIdContext: self.sessionIdContext
505
+ });
506
+ this._sharedCreds = sharedCreds;
507
+
508
+ var timeout = options.handshakeTimeout || (120 * 1000);
509
+
510
+ if (!util.isNumber(timeout)) {
511
+ throw new TypeError('handshakeTimeout must be a number');
512
+ }
513
+
514
+ if (self.sessionTimeout) {
515
+ sharedCreds.context.setSessionTimeout(self.sessionTimeout);
516
+ }
517
+
518
+ if (self.ticketKeys) {
519
+ sharedCreds.context.setTicketKeys(self.ticketKeys);
520
+ }
521
+
522
+ // constructor call
523
+ net.Server.call(this, function(raw_socket) {
524
+ var socket = new TLSSocket(raw_socket, {
525
+ credentials: sharedCreds,
526
+ isServer: true,
527
+ server: self,
528
+ requestCert: self.requestCert,
529
+ rejectUnauthorized: self.rejectUnauthorized,
530
+ handshakeTimeout: timeout,
531
+ NPNProtocols: self.NPNProtocols,
532
+ SNICallback: options.SNICallback || SNICallback
533
+ });
534
+
535
+ socket.on('secure', function() {
536
+ if (socket._requestCert) {
537
+ var verifyError = socket.ssl.verifyError();
538
+ if (verifyError) {
539
+ socket.authorizationError = verifyError.code;
540
+
541
+ if (socket._rejectUnauthorized)
542
+ socket.destroy();
543
+ } else {
544
+ socket.authorized = true;
545
+ }
546
+ }
547
+
548
+ if (!socket.destroyed && socket._releaseControl())
549
+ self.emit('secureConnection', socket);
550
+ });
551
+
552
+ socket.on('_tlsError', function(err) {
553
+ if (!socket._controlReleased)
554
+ self.emit('clientError', err, socket);
555
+ });
556
+ });
557
+
558
+ if (listener) {
559
+ this.on('secureConnection', listener);
560
+ }
561
+ }
562
+
563
+ util.inherits(Server, net.Server);
564
+ exports.Server = Server;
565
+ exports.createServer = function(options, listener) {
566
+ return new Server(options, listener);
567
+ };
568
+
569
+
570
+ Server.prototype._getServerData = function() {
571
+ return {
572
+ ticketKeys: this._sharedCreds.context.getTicketKeys().toString('hex')
573
+ };
574
+ };
575
+
576
+
577
+ Server.prototype._setServerData = function(data) {
578
+ this._sharedCreds.context.setTicketKeys(new Buffer(data.ticketKeys, 'hex'));
579
+ };
580
+
581
+
582
+ Server.prototype.setOptions = function(options) {
583
+ if (util.isBoolean(options.requestCert)) {
584
+ this.requestCert = options.requestCert;
585
+ } else {
586
+ this.requestCert = false;
587
+ }
588
+
589
+ if (util.isBoolean(options.rejectUnauthorized)) {
590
+ this.rejectUnauthorized = options.rejectUnauthorized;
591
+ } else {
592
+ this.rejectUnauthorized = false;
593
+ }
594
+
595
+ if (options.pfx) this.pfx = options.pfx;
596
+ if (options.key) this.key = options.key;
597
+ if (options.passphrase) this.passphrase = options.passphrase;
598
+ if (options.cert) this.cert = options.cert;
599
+ if (options.ca) this.ca = options.ca;
600
+ if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
601
+ if (options.crl) this.crl = options.crl;
602
+ if (options.ciphers) this.ciphers = options.ciphers;
603
+ if (!util.isUndefined(options.ecdhCurve))
604
+ this.ecdhCurve = options.ecdhCurve;
605
+ if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
606
+ var secureOptions = options.secureOptions || 0;
607
+ if (options.honorCipherOrder) {
608
+ secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
609
+ }
610
+ if (secureOptions) this.secureOptions = secureOptions;
611
+ if (options.NPNProtocols) tls.convertNPNProtocols(options.NPNProtocols, this);
612
+ if (options.sessionIdContext) {
613
+ this.sessionIdContext = options.sessionIdContext;
614
+ } else {
615
+ this.sessionIdContext = crypto.createHash('md5')
616
+ .update(process.argv.join(' '))
617
+ .digest('hex');
618
+ }
619
+ };
620
+
621
+ // SNI Contexts High-Level API
622
+ Server.prototype.addContext = function(servername, credentials) {
623
+ if (!servername) {
624
+ throw 'Servername is required parameter for Server.addContext';
625
+ }
626
+
627
+ var re = new RegExp('^' +
628
+ servername.replace(/([\.^$+?\-\\[\]{}])/g, '\\$1')
629
+ .replace(/\*/g, '[^\.]*') +
630
+ '$');
631
+ this._contexts.push([re, crypto.createCredentials(credentials).context]);
632
+ };
633
+
634
+ function SNICallback(servername, callback) {
635
+ var ctx;
636
+
637
+ this.server._contexts.some(function(elem) {
638
+ if (!util.isNull(servername.match(elem[0]))) {
639
+ ctx = elem[1];
640
+ return true;
641
+ }
642
+ });
643
+
644
+ callback(null, ctx);
645
+ }
646
+
647
+
648
+ // Target API:
649
+ //
650
+ // var s = tls.connect({port: 8000, host: "google.com"}, function() {
651
+ // if (!s.authorized) {
652
+ // s.destroy();
653
+ // return;
654
+ // }
655
+ //
656
+ // // s.socket;
657
+ //
658
+ // s.end("hello world\n");
659
+ // });
660
+ //
661
+ //
662
+ function normalizeConnectArgs(listArgs) {
663
+ var args = net._normalizeConnectArgs(listArgs);
664
+ var options = args[0];
665
+ var cb = args[1];
666
+
667
+ if (util.isObject(listArgs[1])) {
668
+ options = util._extend(options, listArgs[1]);
669
+ } else if (util.isObject(listArgs[2])) {
670
+ options = util._extend(options, listArgs[2]);
671
+ }
672
+
673
+ return (cb) ? [options, cb] : [options];
674
+ }
675
+
676
+ function legacyConnect(hostname, options, NPN, credentials) {
677
+ assert(options.socket);
678
+ if (!tls_legacy)
679
+ tls_legacy = require('_tls_legacy');
680
+
681
+ var pair = tls_legacy.createSecurePair(credentials,
682
+ false,
683
+ true,
684
+ !!options.rejectUnauthorized,
685
+ {
686
+ NPNProtocols: NPN.NPNProtocols,
687
+ servername: hostname
688
+ });
689
+ tls_legacy.pipe(pair, options.socket);
690
+ pair.cleartext._controlReleased = true;
691
+ pair.on('error', function(err) {
692
+ pair.cleartext.emit('error', err);
693
+ });
694
+
695
+ return pair;
696
+ }
697
+
698
+ exports.connect = function(/* [port, host], options, cb */) {
699
+ var args = normalizeConnectArgs(arguments);
700
+ var options = args[0];
701
+ var cb = args[1];
702
+
703
+ var defaults = {
704
+ rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED
705
+ };
706
+ options = util._extend(defaults, options || {});
707
+
708
+ var hostname = options.servername ||
709
+ options.host ||
710
+ options.socket && options.socket._host,
711
+ NPN = {},
712
+ credentials = crypto.createCredentials(options);
713
+ tls.convertNPNProtocols(options.NPNProtocols, NPN);
714
+
715
+ // Wrapping TLS socket inside another TLS socket was requested -
716
+ // create legacy secure pair
717
+ var socket;
718
+ var legacy;
719
+ var result;
720
+ if (options.socket instanceof TLSSocket) {
721
+ debug('legacy connect');
722
+ legacy = true;
723
+ socket = legacyConnect(hostname, options, NPN, credentials);
724
+ result = socket.cleartext;
725
+ } else {
726
+ legacy = false;
727
+ socket = new TLSSocket(options.socket, {
728
+ credentials: credentials,
729
+ isServer: false,
730
+ requestCert: true,
731
+ rejectUnauthorized: options.rejectUnauthorized,
732
+ NPNProtocols: NPN.NPNProtocols
733
+ });
734
+ result = socket;
735
+ }
736
+
737
+ if (socket._handle && !socket._connecting) {
738
+ onHandle();
739
+ } else {
740
+ // Not even started connecting yet (or probably resolving dns address),
741
+ // catch socket errors and assign handle.
742
+ if (!legacy && options.socket) {
743
+ options.socket.once('connect', function() {
744
+ assert(options.socket._handle);
745
+ socket._handle = options.socket._handle;
746
+ socket._handle.owner = socket;
747
+ socket.emit('connect');
748
+ });
749
+ }
750
+ socket.once('connect', onHandle);
751
+ }
752
+
753
+ if (cb)
754
+ result.once('secureConnect', cb);
755
+
756
+ if (!options.socket) {
757
+ assert(!legacy);
758
+ var connect_opt;
759
+ if (options.path && !options.port) {
760
+ connect_opt = { path: options.path };
761
+ } else {
762
+ connect_opt = {
763
+ port: options.port,
764
+ host: options.host,
765
+ localAddress: options.localAddress
766
+ };
767
+ }
768
+ socket.connect(connect_opt);
769
+ }
770
+
771
+ return result;
772
+
773
+ function onHandle() {
774
+ if (!legacy)
775
+ socket._releaseControl();
776
+
777
+ if (options.session)
778
+ socket.setSession(options.session);
779
+
780
+ if (!legacy) {
781
+ if (options.servername)
782
+ socket.setServername(options.servername);
783
+
784
+ socket._start();
785
+ }
786
+ socket.on('secure', function() {
787
+ var verifyError = socket.ssl.verifyError();
788
+
789
+ // Verify that server's identity matches it's certificate's names
790
+ if (!verifyError) {
791
+ var cert = result.getPeerCertificate();
792
+ var validCert = tls.checkServerIdentity(hostname, cert);
793
+ if (!validCert) {
794
+ verifyError = new Error('Hostname/IP doesn\'t match certificate\'s ' +
795
+ 'altnames');
796
+ }
797
+ }
798
+
799
+ if (verifyError) {
800
+ result.authorized = false;
801
+ result.authorizationError = verifyError.code || verifyError.message;
802
+
803
+ if (options.rejectUnauthorized) {
804
+ result.emit('error', verifyError);
805
+ result.destroy();
806
+ return;
807
+ } else {
808
+ result.emit('secureConnect');
809
+ }
810
+ } else {
811
+ result.authorized = true;
812
+ result.emit('secureConnect');
813
+ }
814
+
815
+ // Uncork incoming data
816
+ result.removeListener('end', onHangUp);
817
+ });
818
+
819
+ function onHangUp() {
820
+ // NOTE: This logic is shared with _http_client.js
821
+ if (!socket._hadError) {
822
+ socket._hadError = true;
823
+ var error = new Error('socket hang up');
824
+ error.code = 'ECONNRESET';
825
+ socket.destroy();
826
+ socket.emit('error', error);
827
+ }
828
+ }
829
+ result.once('end', onHangUp);
830
+ }
831
+ };