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,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
+ };