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,887 @@
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 crypto = require('crypto');
24
+ var events = require('events');
25
+ var stream = require('stream');
26
+ var tls = require('tls');
27
+ var util = require('util');
28
+
29
+ var Timer = process.binding('timer_wrap').Timer;
30
+ var Connection = null;
31
+ try {
32
+ Connection = process.binding('crypto').Connection;
33
+ } catch (e) {
34
+ throw new Error('node.js not compiled with openssl crypto support.');
35
+ }
36
+
37
+ var debug = util.debuglog('tls-legacy');
38
+
39
+ function SlabBuffer() {
40
+ this.create();
41
+ }
42
+
43
+
44
+ SlabBuffer.prototype.create = function create() {
45
+ this.isFull = false;
46
+ this.pool = new Buffer(tls.SLAB_BUFFER_SIZE);
47
+ this.offset = 0;
48
+ this.remaining = this.pool.length;
49
+ };
50
+
51
+
52
+ SlabBuffer.prototype.use = function use(context, fn, size) {
53
+ if (this.remaining === 0) {
54
+ this.isFull = true;
55
+ return 0;
56
+ }
57
+
58
+ var actualSize = this.remaining;
59
+
60
+ if (!util.isNull(size)) actualSize = Math.min(size, actualSize);
61
+
62
+ var bytes = fn.call(context, this.pool, this.offset, actualSize);
63
+ if (bytes > 0) {
64
+ this.offset += bytes;
65
+ this.remaining -= bytes;
66
+ }
67
+
68
+ assert(this.remaining >= 0);
69
+
70
+ return bytes;
71
+ };
72
+
73
+
74
+ var slabBuffer = null;
75
+
76
+
77
+ // Base class of both CleartextStream and EncryptedStream
78
+ function CryptoStream(pair, options) {
79
+ stream.Duplex.call(this, options);
80
+
81
+ this.pair = pair;
82
+ this._pending = null;
83
+ this._pendingEncoding = '';
84
+ this._pendingCallback = null;
85
+ this._doneFlag = false;
86
+ this._retryAfterPartial = false;
87
+ this._halfRead = false;
88
+ this._sslOutCb = null;
89
+ this._resumingSession = false;
90
+ this._reading = true;
91
+ this._destroyed = false;
92
+ this._ended = false;
93
+ this._finished = false;
94
+ this._opposite = null;
95
+
96
+ if (util.isNull(slabBuffer)) slabBuffer = new SlabBuffer();
97
+ this._buffer = slabBuffer;
98
+
99
+ this.once('finish', onCryptoStreamFinish);
100
+
101
+ // net.Socket calls .onend too
102
+ this.once('end', onCryptoStreamEnd);
103
+ }
104
+ util.inherits(CryptoStream, stream.Duplex);
105
+
106
+
107
+ function onCryptoStreamFinish() {
108
+ this._finished = true;
109
+
110
+ if (this === this.pair.cleartext) {
111
+ debug('cleartext.onfinish');
112
+ if (this.pair.ssl) {
113
+ // Generate close notify
114
+ // NOTE: first call checks if client has sent us shutdown,
115
+ // second call enqueues shutdown into the BIO.
116
+ if (this.pair.ssl.shutdown() !== 1) {
117
+ if (this.pair.ssl && this.pair.ssl.error)
118
+ return this.pair.error();
119
+
120
+ this.pair.ssl.shutdown();
121
+ }
122
+
123
+ if (this.pair.ssl && this.pair.ssl.error)
124
+ return this.pair.error();
125
+ }
126
+ } else {
127
+ debug('encrypted.onfinish');
128
+ }
129
+
130
+ // Try to read just to get sure that we won't miss EOF
131
+ if (this._opposite.readable) this._opposite.read(0);
132
+
133
+ if (this._opposite._ended) {
134
+ this._done();
135
+
136
+ // No half-close, sorry
137
+ if (this === this.pair.cleartext) this._opposite._done();
138
+ }
139
+ }
140
+
141
+
142
+ function onCryptoStreamEnd() {
143
+ this._ended = true;
144
+ if (this === this.pair.cleartext) {
145
+ debug('cleartext.onend');
146
+ } else {
147
+ debug('encrypted.onend');
148
+ }
149
+ }
150
+
151
+
152
+ // NOTE: Called once `this._opposite` is set.
153
+ CryptoStream.prototype.init = function init() {
154
+ var self = this;
155
+ this._opposite.on('sslOutEnd', function() {
156
+ if (self._sslOutCb) {
157
+ var cb = self._sslOutCb;
158
+ self._sslOutCb = null;
159
+ cb(null);
160
+ }
161
+ });
162
+ };
163
+
164
+
165
+ CryptoStream.prototype._write = function write(data, encoding, cb) {
166
+ assert(util.isNull(this._pending));
167
+
168
+ // Black-hole data
169
+ if (!this.pair.ssl) return cb(null);
170
+
171
+ // When resuming session don't accept any new data.
172
+ // And do not put too much data into openssl, before writing it from encrypted
173
+ // side.
174
+ //
175
+ // TODO(indutny): Remove magic number, use watermark based limits
176
+ if (!this._resumingSession &&
177
+ this._opposite._internallyPendingBytes() < 128 * 1024) {
178
+ // Write current buffer now
179
+ var written;
180
+ if (this === this.pair.cleartext) {
181
+ debug('cleartext.write called with %d bytes', data.length);
182
+ written = this.pair.ssl.clearIn(data, 0, data.length);
183
+ } else {
184
+ debug('encrypted.write called with %d bytes', data.length);
185
+ written = this.pair.ssl.encIn(data, 0, data.length);
186
+ }
187
+
188
+ // Handle and report errors
189
+ if (this.pair.ssl && this.pair.ssl.error) {
190
+ return cb(this.pair.error(true));
191
+ }
192
+
193
+ // Force SSL_read call to cycle some states/data inside OpenSSL
194
+ this.pair.cleartext.read(0);
195
+
196
+ // Cycle encrypted data
197
+ if (this.pair.encrypted._internallyPendingBytes())
198
+ this.pair.encrypted.read(0);
199
+
200
+ // Get NPN and Server name when ready
201
+ this.pair.maybeInitFinished();
202
+
203
+ // Whole buffer was written
204
+ if (written === data.length) {
205
+ if (this === this.pair.cleartext) {
206
+ debug('cleartext.write succeed with ' + written + ' bytes');
207
+ } else {
208
+ debug('encrypted.write succeed with ' + written + ' bytes');
209
+ }
210
+
211
+ // Invoke callback only when all data read from opposite stream
212
+ if (this._opposite._halfRead) {
213
+ assert(util.isNull(this._sslOutCb));
214
+ this._sslOutCb = cb;
215
+ } else {
216
+ cb(null);
217
+ }
218
+ return;
219
+ } else if (written !== 0 && written !== -1) {
220
+ assert(!this._retryAfterPartial);
221
+ this._retryAfterPartial = true;
222
+ this._write(data.slice(written), encoding, cb);
223
+ this._retryAfterPartial = false;
224
+ return;
225
+ }
226
+ } else {
227
+ debug('cleartext.write queue is full');
228
+
229
+ // Force SSL_read call to cycle some states/data inside OpenSSL
230
+ this.pair.cleartext.read(0);
231
+ }
232
+
233
+ // No write has happened
234
+ this._pending = data;
235
+ this._pendingEncoding = encoding;
236
+ this._pendingCallback = cb;
237
+
238
+ if (this === this.pair.cleartext) {
239
+ debug('cleartext.write queued with %d bytes', data.length);
240
+ } else {
241
+ debug('encrypted.write queued with %d bytes', data.length);
242
+ }
243
+ };
244
+
245
+
246
+ CryptoStream.prototype._writePending = function writePending() {
247
+ var data = this._pending,
248
+ encoding = this._pendingEncoding,
249
+ cb = this._pendingCallback;
250
+
251
+ this._pending = null;
252
+ this._pendingEncoding = '';
253
+ this._pendingCallback = null;
254
+ this._write(data, encoding, cb);
255
+ };
256
+
257
+
258
+ CryptoStream.prototype._read = function read(size) {
259
+ // XXX: EOF?!
260
+ if (!this.pair.ssl) return this.push(null);
261
+
262
+ // Wait for session to be resumed
263
+ // Mark that we're done reading, but don't provide data or EOF
264
+ if (this._resumingSession || !this._reading) return this.push('');
265
+
266
+ var out;
267
+ if (this === this.pair.cleartext) {
268
+ debug('cleartext.read called with %d bytes', size);
269
+ out = this.pair.ssl.clearOut;
270
+ } else {
271
+ debug('encrypted.read called with %d bytes', size);
272
+ out = this.pair.ssl.encOut;
273
+ }
274
+
275
+ var bytesRead = 0,
276
+ start = this._buffer.offset,
277
+ last = start;
278
+ do {
279
+ assert(last === this._buffer.offset);
280
+ var read = this._buffer.use(this.pair.ssl, out, size - bytesRead);
281
+ if (read > 0) {
282
+ bytesRead += read;
283
+ }
284
+ last = this._buffer.offset;
285
+
286
+ // Handle and report errors
287
+ if (this.pair.ssl && this.pair.ssl.error) {
288
+ this.pair.error();
289
+ break;
290
+ }
291
+ } while (read > 0 &&
292
+ !this._buffer.isFull &&
293
+ bytesRead < size &&
294
+ this.pair.ssl !== null);
295
+
296
+ // Get NPN and Server name when ready
297
+ this.pair.maybeInitFinished();
298
+
299
+ // Create new buffer if previous was filled up
300
+ var pool = this._buffer.pool;
301
+ if (this._buffer.isFull) this._buffer.create();
302
+
303
+ assert(bytesRead >= 0);
304
+
305
+ if (this === this.pair.cleartext) {
306
+ debug('cleartext.read succeed with %d bytes', bytesRead);
307
+ } else {
308
+ debug('encrypted.read succeed with %d bytes', bytesRead);
309
+ }
310
+
311
+ // Try writing pending data
312
+ if (!util.isNull(this._pending)) this._writePending();
313
+ if (!util.isNull(this._opposite._pending)) this._opposite._writePending();
314
+
315
+ if (bytesRead === 0) {
316
+ // EOF when cleartext has finished and we have nothing to read
317
+ if (this._opposite._finished && this._internallyPendingBytes() === 0 ||
318
+ this.pair.ssl && this.pair.ssl.receivedShutdown) {
319
+ // Perform graceful shutdown
320
+ this._done();
321
+
322
+ // No half-open, sorry!
323
+ if (this === this.pair.cleartext) {
324
+ this._opposite._done();
325
+
326
+ // EOF
327
+ this.push(null);
328
+ } else if (!this.pair.ssl || !this.pair.ssl.receivedShutdown) {
329
+ // EOF
330
+ this.push(null);
331
+ }
332
+ } else {
333
+ // Bail out
334
+ this.push('');
335
+ }
336
+ } else {
337
+ // Give them requested data
338
+ this.push(pool.slice(start, start + bytesRead));
339
+ }
340
+
341
+ // Let users know that we've some internal data to read
342
+ var halfRead = this._internallyPendingBytes() !== 0;
343
+
344
+ // Smart check to avoid invoking 'sslOutEnd' in the most of the cases
345
+ if (this._halfRead !== halfRead) {
346
+ this._halfRead = halfRead;
347
+
348
+ // Notify listeners about internal data end
349
+ if (!halfRead) {
350
+ if (this === this.pair.cleartext) {
351
+ debug('cleartext.sslOutEnd');
352
+ } else {
353
+ debug('encrypted.sslOutEnd');
354
+ }
355
+
356
+ this.emit('sslOutEnd');
357
+ }
358
+ }
359
+ };
360
+
361
+
362
+ CryptoStream.prototype.setTimeout = function(timeout, callback) {
363
+ if (this.socket) this.socket.setTimeout(timeout, callback);
364
+ };
365
+
366
+
367
+ CryptoStream.prototype.setNoDelay = function(noDelay) {
368
+ if (this.socket) this.socket.setNoDelay(noDelay);
369
+ };
370
+
371
+
372
+ CryptoStream.prototype.setKeepAlive = function(enable, initialDelay) {
373
+ if (this.socket) this.socket.setKeepAlive(enable, initialDelay);
374
+ };
375
+
376
+ CryptoStream.prototype.__defineGetter__('bytesWritten', function() {
377
+ return this.socket ? this.socket.bytesWritten : 0;
378
+ });
379
+
380
+ CryptoStream.prototype.getPeerCertificate = function() {
381
+ if (this.pair.ssl) {
382
+ var c = this.pair.ssl.getPeerCertificate();
383
+
384
+ if (c) {
385
+ if (c.issuer) c.issuer = tls.parseCertString(c.issuer);
386
+ if (c.subject) c.subject = tls.parseCertString(c.subject);
387
+ return c;
388
+ }
389
+ }
390
+
391
+ return null;
392
+ };
393
+
394
+ CryptoStream.prototype.getSession = function() {
395
+ if (this.pair.ssl) {
396
+ return this.pair.ssl.getSession();
397
+ }
398
+
399
+ return null;
400
+ };
401
+
402
+ CryptoStream.prototype.isSessionReused = function() {
403
+ if (this.pair.ssl) {
404
+ return this.pair.ssl.isSessionReused();
405
+ }
406
+
407
+ return null;
408
+ };
409
+
410
+ CryptoStream.prototype.getCipher = function(err) {
411
+ if (this.pair.ssl) {
412
+ return this.pair.ssl.getCurrentCipher();
413
+ } else {
414
+ return null;
415
+ }
416
+ };
417
+
418
+
419
+ CryptoStream.prototype.end = function(chunk, encoding) {
420
+ if (this === this.pair.cleartext) {
421
+ debug('cleartext.end');
422
+ } else {
423
+ debug('encrypted.end');
424
+ }
425
+
426
+ // Write pending data first
427
+ if (!util.isNull(this._pending)) this._writePending();
428
+
429
+ this.writable = false;
430
+
431
+ stream.Duplex.prototype.end.call(this, chunk, encoding);
432
+ };
433
+
434
+
435
+ CryptoStream.prototype.destroySoon = function(err) {
436
+ if (this === this.pair.cleartext) {
437
+ debug('cleartext.destroySoon');
438
+ } else {
439
+ debug('encrypted.destroySoon');
440
+ }
441
+
442
+ if (this.writable)
443
+ this.end();
444
+
445
+ if (this._writableState.finished && this._opposite._ended) {
446
+ this.destroy();
447
+ } else {
448
+ // Wait for both `finish` and `end` events to ensure that all data that
449
+ // was written on this side was read from the other side.
450
+ var self = this;
451
+ var waiting = 1;
452
+ function finish() {
453
+ if (--waiting === 0) self.destroy();
454
+ }
455
+ this._opposite.once('end', finish);
456
+ if (!this._finished) {
457
+ this.once('finish', finish);
458
+ ++waiting;
459
+ }
460
+ }
461
+ };
462
+
463
+
464
+ CryptoStream.prototype.destroy = function(err) {
465
+ if (this._destroyed) return;
466
+ this._destroyed = true;
467
+ this.readable = this.writable = false;
468
+
469
+ // Destroy both ends
470
+ if (this === this.pair.cleartext) {
471
+ debug('cleartext.destroy');
472
+ } else {
473
+ debug('encrypted.destroy');
474
+ }
475
+ this._opposite.destroy();
476
+
477
+ var self = this;
478
+ process.nextTick(function() {
479
+ // Force EOF
480
+ self.push(null);
481
+
482
+ // Emit 'close' event
483
+ self.emit('close', err ? true : false);
484
+ });
485
+ };
486
+
487
+
488
+ CryptoStream.prototype._done = function() {
489
+ this._doneFlag = true;
490
+
491
+ if (this === this.pair.encrypted && !this.pair._secureEstablished)
492
+ return this.pair.error();
493
+
494
+ if (this.pair.cleartext._doneFlag &&
495
+ this.pair.encrypted._doneFlag &&
496
+ !this.pair._doneFlag) {
497
+ // If both streams are done:
498
+ this.pair.destroy();
499
+ }
500
+ };
501
+
502
+
503
+ // readyState is deprecated. Don't use it.
504
+ Object.defineProperty(CryptoStream.prototype, 'readyState', {
505
+ get: function() {
506
+ if (this._connecting) {
507
+ return 'opening';
508
+ } else if (this.readable && this.writable) {
509
+ return 'open';
510
+ } else if (this.readable && !this.writable) {
511
+ return 'readOnly';
512
+ } else if (!this.readable && this.writable) {
513
+ return 'writeOnly';
514
+ } else {
515
+ return 'closed';
516
+ }
517
+ }
518
+ });
519
+
520
+
521
+ function CleartextStream(pair, options) {
522
+ CryptoStream.call(this, pair, options);
523
+
524
+ // This is a fake kludge to support how the http impl sits
525
+ // on top of net Sockets
526
+ var self = this;
527
+ this._handle = {
528
+ readStop: function() {
529
+ self._reading = false;
530
+ },
531
+ readStart: function() {
532
+ if (self._reading && self._readableState.length > 0) return;
533
+ self._reading = true;
534
+ self.read(0);
535
+ if (self._opposite.readable) self._opposite.read(0);
536
+ }
537
+ };
538
+ }
539
+ util.inherits(CleartextStream, CryptoStream);
540
+
541
+
542
+ CleartextStream.prototype._internallyPendingBytes = function() {
543
+ if (this.pair.ssl) {
544
+ return this.pair.ssl.clearPending();
545
+ } else {
546
+ return 0;
547
+ }
548
+ };
549
+
550
+
551
+ CleartextStream.prototype.address = function() {
552
+ return this.socket && this.socket.address();
553
+ };
554
+
555
+
556
+ CleartextStream.prototype.__defineGetter__('remoteAddress', function() {
557
+ return this.socket && this.socket.remoteAddress;
558
+ });
559
+
560
+
561
+ CleartextStream.prototype.__defineGetter__('remotePort', function() {
562
+ return this.socket && this.socket.remotePort;
563
+ });
564
+
565
+
566
+ CleartextStream.prototype.__defineGetter__('localAddress', function() {
567
+ return this.socket && this.socket.localAddress;
568
+ });
569
+
570
+
571
+ CleartextStream.prototype.__defineGetter__('localPort', function() {
572
+ return this.socket && this.socket.localPort;
573
+ });
574
+
575
+
576
+ function EncryptedStream(pair, options) {
577
+ CryptoStream.call(this, pair, options);
578
+ }
579
+ util.inherits(EncryptedStream, CryptoStream);
580
+
581
+
582
+ EncryptedStream.prototype._internallyPendingBytes = function() {
583
+ if (this.pair.ssl) {
584
+ return this.pair.ssl.encPending();
585
+ } else {
586
+ return 0;
587
+ }
588
+ };
589
+
590
+
591
+ function onhandshakestart() {
592
+ debug('onhandshakestart');
593
+
594
+ var self = this;
595
+ var ssl = self.ssl;
596
+ var now = Timer.now();
597
+
598
+ assert(now >= ssl.lastHandshakeTime);
599
+
600
+ if ((now - ssl.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
601
+ ssl.handshakes = 0;
602
+ }
603
+
604
+ var first = (ssl.lastHandshakeTime === 0);
605
+ ssl.lastHandshakeTime = now;
606
+ if (first) return;
607
+
608
+ if (++ssl.handshakes > tls.CLIENT_RENEG_LIMIT) {
609
+ // Defer the error event to the next tick. We're being called from OpenSSL's
610
+ // state machine and OpenSSL is not re-entrant. We cannot allow the user's
611
+ // callback to destroy the connection right now, it would crash and burn.
612
+ setImmediate(function() {
613
+ var err = new Error('TLS session renegotiation attack detected.');
614
+ if (self.cleartext) self.cleartext.emit('error', err);
615
+ });
616
+ }
617
+ }
618
+
619
+
620
+ function onhandshakedone() {
621
+ // for future use
622
+ debug('onhandshakedone');
623
+ }
624
+
625
+
626
+ function onclienthello(hello) {
627
+ var self = this,
628
+ once = false;
629
+
630
+ this._resumingSession = true;
631
+ function callback(err, session) {
632
+ if (once) return;
633
+ once = true;
634
+
635
+ if (err) return self.socket.destroy(err);
636
+
637
+ self.ssl.loadSession(session);
638
+ self.ssl.endParser();
639
+
640
+ // Cycle data
641
+ self._resumingSession = false;
642
+ self.cleartext.read(0);
643
+ self.encrypted.read(0);
644
+ }
645
+
646
+ if (hello.sessionId.length <= 0 ||
647
+ !this.server ||
648
+ !this.server.emit('resumeSession', hello.sessionId, callback)) {
649
+ callback(null, null);
650
+ }
651
+ }
652
+
653
+
654
+ function onnewsession(key, session) {
655
+ if (!this.server) return;
656
+ this.server.emit('newSession', key, session);
657
+ }
658
+
659
+
660
+ /**
661
+ * Provides a pair of streams to do encrypted communication.
662
+ */
663
+
664
+ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
665
+ options) {
666
+ if (!(this instanceof SecurePair)) {
667
+ return new SecurePair(credentials,
668
+ isServer,
669
+ requestCert,
670
+ rejectUnauthorized,
671
+ options);
672
+ }
673
+
674
+ var self = this;
675
+
676
+ options || (options = {});
677
+
678
+ events.EventEmitter.call(this);
679
+
680
+ this.server = options.server;
681
+ this._secureEstablished = false;
682
+ this._isServer = isServer ? true : false;
683
+ this._encWriteState = true;
684
+ this._clearWriteState = true;
685
+ this._doneFlag = false;
686
+ this._destroying = false;
687
+
688
+ if (!credentials) {
689
+ this.credentials = crypto.createCredentials();
690
+ } else {
691
+ this.credentials = credentials;
692
+ }
693
+
694
+ if (!this._isServer) {
695
+ // For clients, we will always have either a given ca list or be using
696
+ // default one
697
+ requestCert = true;
698
+ }
699
+
700
+ this._rejectUnauthorized = rejectUnauthorized ? true : false;
701
+ this._requestCert = requestCert ? true : false;
702
+
703
+ this.ssl = new Connection(this.credentials.context,
704
+ this._isServer ? true : false,
705
+ this._isServer ? this._requestCert :
706
+ options.servername,
707
+ this._rejectUnauthorized);
708
+
709
+ if (this._isServer) {
710
+ this.ssl.onhandshakestart = onhandshakestart.bind(this);
711
+ this.ssl.onhandshakedone = onhandshakedone.bind(this);
712
+ this.ssl.onclienthello = onclienthello.bind(this);
713
+ this.ssl.onnewsession = onnewsession.bind(this);
714
+ this.ssl.lastHandshakeTime = 0;
715
+ this.ssl.handshakes = 0;
716
+ }
717
+
718
+ if (process.features.tls_sni) {
719
+ if (this._isServer && options.SNICallback) {
720
+ this.ssl.setSNICallback(options.SNICallback);
721
+ }
722
+ this.servername = null;
723
+ }
724
+
725
+ if (process.features.tls_npn && options.NPNProtocols) {
726
+ this.ssl.setNPNProtocols(options.NPNProtocols);
727
+ this.npnProtocol = null;
728
+ }
729
+
730
+ /* Acts as a r/w stream to the cleartext side of the stream. */
731
+ this.cleartext = new CleartextStream(this, options.cleartext);
732
+
733
+ /* Acts as a r/w stream to the encrypted side of the stream. */
734
+ this.encrypted = new EncryptedStream(this, options.encrypted);
735
+
736
+ /* Let streams know about each other */
737
+ this.cleartext._opposite = this.encrypted;
738
+ this.encrypted._opposite = this.cleartext;
739
+ this.cleartext.init();
740
+ this.encrypted.init();
741
+
742
+ process.nextTick(function() {
743
+ /* The Connection may be destroyed by an abort call */
744
+ if (self.ssl) {
745
+ self.ssl.start();
746
+
747
+ /* In case of cipher suite failures - SSL_accept/SSL_connect may fail */
748
+ if (self.ssl && self.ssl.error)
749
+ self.error();
750
+ }
751
+ });
752
+ }
753
+
754
+ util.inherits(SecurePair, events.EventEmitter);
755
+
756
+
757
+ exports.createSecurePair = function(credentials,
758
+ isServer,
759
+ requestCert,
760
+ rejectUnauthorized) {
761
+ var pair = new SecurePair(credentials,
762
+ isServer,
763
+ requestCert,
764
+ rejectUnauthorized);
765
+ return pair;
766
+ };
767
+
768
+
769
+ SecurePair.prototype.maybeInitFinished = function() {
770
+ if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
771
+ if (process.features.tls_npn) {
772
+ this.npnProtocol = this.ssl.getNegotiatedProtocol();
773
+ }
774
+
775
+ if (process.features.tls_sni) {
776
+ this.servername = this.ssl.getServername();
777
+ }
778
+
779
+ this._secureEstablished = true;
780
+ debug('secure established');
781
+ this.emit('secure');
782
+ }
783
+ };
784
+
785
+
786
+ SecurePair.prototype.destroy = function() {
787
+ if (this._destroying) return;
788
+
789
+ if (!this._doneFlag) {
790
+ debug('SecurePair.destroy');
791
+ this._destroying = true;
792
+
793
+ // SecurePair should be destroyed only after it's streams
794
+ this.cleartext.destroy();
795
+ this.encrypted.destroy();
796
+
797
+ this._doneFlag = true;
798
+ this.ssl.error = null;
799
+ this.ssl.close();
800
+ this.ssl = null;
801
+ }
802
+ };
803
+
804
+
805
+ SecurePair.prototype.error = function(returnOnly) {
806
+ var err = this.ssl.error;
807
+ this.ssl.error = null;
808
+
809
+ if (!this._secureEstablished) {
810
+ // Emit ECONNRESET instead of zero return
811
+ if (!err || err.message === 'ZERO_RETURN') {
812
+ var connReset = new Error('socket hang up');
813
+ connReset.code = 'ECONNRESET';
814
+ connReset.sslError = err && err.message;
815
+
816
+ err = connReset;
817
+ }
818
+ this.destroy();
819
+ if (!returnOnly) this.emit('error', err);
820
+ } else if (this._isServer &&
821
+ this._rejectUnauthorized &&
822
+ /peer did not return a certificate/.test(err.message)) {
823
+ // Not really an error.
824
+ this.destroy();
825
+ } else {
826
+ if (!returnOnly) this.cleartext.emit('error', err);
827
+ }
828
+ return err;
829
+ };
830
+
831
+
832
+ exports.pipe = function pipe(pair, socket) {
833
+ pair.encrypted.pipe(socket);
834
+ socket.pipe(pair.encrypted);
835
+
836
+ pair.encrypted.on('close', function() {
837
+ process.nextTick(function() {
838
+ // Encrypted should be unpiped from socket to prevent possible
839
+ // write after destroy.
840
+ pair.encrypted.unpipe(socket);
841
+ socket.destroySoon();
842
+ });
843
+ });
844
+
845
+ pair.fd = socket.fd;
846
+ var cleartext = pair.cleartext;
847
+ cleartext.socket = socket;
848
+ cleartext.encrypted = pair.encrypted;
849
+ cleartext.authorized = false;
850
+
851
+ // cycle the data whenever the socket drains, so that
852
+ // we can pull some more into it. normally this would
853
+ // be handled by the fact that pipe() triggers read() calls
854
+ // on writable.drain, but CryptoStreams are a bit more
855
+ // complicated. Since the encrypted side actually gets
856
+ // its data from the cleartext side, we have to give it a
857
+ // light kick to get in motion again.
858
+ socket.on('drain', function() {
859
+ if (pair.encrypted._pending)
860
+ pair.encrypted._writePending();
861
+ if (pair.cleartext._pending)
862
+ pair.cleartext._writePending();
863
+ pair.encrypted.read(0);
864
+ pair.cleartext.read(0);
865
+ });
866
+
867
+ function onerror(e) {
868
+ if (cleartext._controlReleased) {
869
+ cleartext.emit('error', e);
870
+ }
871
+ }
872
+
873
+ function onclose() {
874
+ socket.removeListener('error', onerror);
875
+ socket.removeListener('timeout', ontimeout);
876
+ }
877
+
878
+ function ontimeout() {
879
+ cleartext.emit('timeout');
880
+ }
881
+
882
+ socket.on('error', onerror);
883
+ socket.on('close', onclose);
884
+ socket.on('timeout', ontimeout);
885
+
886
+ return cleartext;
887
+ };