macgyver 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ };