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