@@ -9,7 +9,6 @@ var debugLevel = process.env['NODE_DEBUG'] ? 1 : 0;
99function debug ( ) {
1010 if ( debugLevel > 0 ) sys . error . apply ( this , arguments ) ;
1111}
12-
1312var binding = process . binding ( 'net' ) ;
1413
1514// Note about Buffer interface:
@@ -45,6 +44,14 @@ var EINPROGRESS = binding.EINPROGRESS;
4544var ENOENT = binding . ENOENT ;
4645var END_OF_FILE = 42 ;
4746
47+ // Do we have openssl crypto?
48+ try {
49+ var SecureContext = process . binding ( 'crypto' ) . SecureContext ;
50+ var SecureStream = process . binding ( 'crypto' ) . SecureStream ;
51+ var crypto = true ;
52+ } catch ( e ) {
53+ var crypto = false ;
54+ }
4855
4956// IDLE TIMEOUTS
5057//
@@ -246,6 +253,16 @@ function allocNewPool () {
246253 pool . used = 0 ;
247254}
248255
256+ var securePool = null ;
257+ function allocNewSecurePool ( ) {
258+ securePool = new Buffer ( 40 * 1024 ) ;
259+ }
260+ var emptyBuffer = null ;
261+ function allocEmptyBuffer ( ) {
262+ emptyBuffer = new Buffer ( 1 ) ;
263+ emptyBuffer . sent = 0 ;
264+ emptyBuffer . length = 0 ;
265+ }
249266
250267function _doFlush ( ) {
251268 var socket = this . socket ;
@@ -271,20 +288,46 @@ function initStream (self) {
271288
272289 //debug('pool.used ' + pool.used);
273290 var bytesRead ;
291+ var secureBytesRead ;
274292
275293 try {
276- bytesRead = read ( self . fd ,
294+ if ( self . secure ) {
295+ if ( ! securePool ) allocNewSecurePool ( ) ;
296+ secureBytesRead = read ( self . fd , securePool , 0 , securePool . length ) ;
297+ self . secureStream . readInject ( securePool , 0 , secureBytesRead ) ;
298+ bytesRead = self . secureStream . readExtract ( pool , pool . used , pool . length - pool . used ) ;
299+ if ( ! self . secureEstablished ) {
300+ if ( self . secureStream . isInitFinished ( ) ) {
301+ self . secureEstablished = true ;
302+ if ( self . _events && self . _events [ 'secure' ] ) self . emit ( 'secure' ) ;
303+ }
304+ }
305+ if ( secureBytesRead === null && ! self . server ) {
306+ // Client needs to write as part of handshake
307+ this . _writeWatcher . start ( ) ;
308+ }
309+ } else {
310+ bytesRead = read ( self . fd ,
277311 pool ,
278312 pool . used ,
279- pool . length - pool . used ) ;
313+ pool . length - pool . used ) ;
314+ }
280315 } catch ( e ) {
281- self . forceClose ( e ) ;
316+ if ( this . forceClose ) this . forceClose ( e ) ;
282317 return ;
283318 }
284319
285320 //debug('bytesRead ' + bytesRead + '\n');
286321
287- if ( bytesRead === 0 ) {
322+ if ( self . secure && bytesRead == 0 && secureBytesRead > 0 ) {
323+ // Deal with SSL handshake
324+ if ( self . server ) {
325+ self . _checkForSecureHandshake ( ) ;
326+ } else {
327+ if ( self . secureEstablised ) self . flush ( ) ;
328+ else self . _checkForSecureHandshake ( ) ;
329+ }
330+ } else if ( bytesRead === 0 ) {
288331 self . readable = false ;
289332 self . _readWatcher . stop ( ) ;
290333
@@ -341,10 +384,36 @@ function initStream (self) {
341384 self . writable = false ;
342385}
343386
387+ function Credentials ( method ) {
388+ if ( ! crypto ) {
389+ throw new Error ( 'node.js not compiled with openssl crypto support.' ) ;
390+ }
391+ this . context = new SecureContext ( ) ;
392+ if ( method ) this . context . init ( method ) ;
393+ else this . context . init ( ) ;
394+ }
395+
396+ exports . createCredentials = function ( cred ) {
397+ var c = new Credentials ( cred . method ) ;
398+ if ( cred . key ) c . context . setKey ( cred . key ) ;
399+ if ( cred . cert ) c . context . setCert ( cred . cert ) ;
400+ if ( cred . ca ) {
401+ if ( ( typeof ( cred . ca ) == 'object' ) && cred . ca . length ) {
402+ for ( var i = 0 ; i < cred . ca . length ; i ++ )
403+ c . context . addCACert ( cred . ca [ i ] ) ;
404+ } else {
405+ c . context . addCACert ( cred . ca ) ;
406+ }
407+ }
408+ return c ;
409+ }
410+ exports . Credentials = Credentials ;
411+
344412function Stream ( fd ) {
345413 events . EventEmitter . call ( this ) ;
346414
347415 this . fd = null ;
416+ this . secure = false ;
348417
349418 if ( parseInt ( fd ) >= 0 ) {
350419 this . open ( fd ) ;
@@ -353,6 +422,55 @@ function Stream (fd) {
353422sys . inherits ( Stream , events . EventEmitter ) ;
354423exports . Stream = Stream ;
355424
425+ Stream . prototype . setSecure = function ( credentials ) {
426+ if ( ! crypto ) {
427+ throw new Error ( 'node.js not compiled with openssl crypto support.' ) ;
428+ }
429+ this . secure = true ;
430+ this . secureEstablished = false ;
431+ // If no credentials given, create a new one for just this Stream
432+ if ( ! credentials ) {
433+ this . credentials = new Credentials ( ) ;
434+ } else {
435+ this . credentials = credentials ;
436+ }
437+ this . secureStream = new SecureStream ( this . credentials . context , this . server ?1 :0 ) ;
438+
439+ if ( ! this . server ) {
440+ // If client, trigger handshake
441+ this . _checkForSecureHandshake ( ) ;
442+ }
443+
444+
445+ }
446+
447+ Stream . prototype . verifyPeer = function ( ) {
448+ if ( ! this . secure ) {
449+ throw new Error ( 'Stream is not a secure stream.' ) ;
450+ }
451+ return this . secureStream . verifyPeer ( this . credentials . context ) ;
452+ }
453+
454+ Stream . prototype . _checkForSecureHandshake = function ( ) {
455+ // Do an empty write to see if we need to write out as part of handshake
456+ if ( ! emptyBuffer ) allocEmptyBuffer ( ) ;
457+ this . write ( emptyBuffer ) ;
458+ }
459+
460+ Stream . prototype . getPeerCertificate = function ( credentials ) {
461+ if ( ! this . secure ) {
462+ throw new Error ( 'Stream is not a secure stream.' ) ;
463+ }
464+ return this . secureStream . getPeerCertificate ( ) ;
465+ }
466+
467+ Stream . prototype . getCipher = function ( ) {
468+ if ( ! this . secure ) {
469+ throw new Error ( 'Stream is not a secure stream.' ) ;
470+ }
471+ return this . secureStream . getCurrentCipher ( ) ;
472+ }
473+
356474
357475Stream . prototype . open = function ( fd ) {
358476 initStream ( this ) ;
@@ -411,6 +529,15 @@ Stream.prototype.write = function (data, encoding) {
411529} ;
412530
413531
532+ Stream . prototype . _shutdownSecure = function ( ) {
533+ this . secureStream . shutdown ( ) ;
534+ if ( ! securePool ) allocNewSecurePool ( ) ;
535+ var secureLen = this . secureStream . writeExtract ( securePool , 0 , securePool . length ) ;
536+ try {
537+ var secureBytesWritten = write ( this . fd , securePool , 0 , secureLen ) ;
538+ } catch ( e ) { }
539+ }
540+
414541// Directly writes the data to socket.
415542//
416543// Steps:
@@ -420,14 +547,15 @@ Stream.prototype.write = function (data, encoding) {
420547// 3. Slice out remaining
421548// 4. Unshift remaining onto _writeQueue. Return false.
422549Stream . prototype . _writeOut = function ( data , encoding ) {
423- if ( ! this . writable ) throw new Error ( 'Stream is not writable' ) ;
550+ if ( ! this . writable ) {
551+ if ( this . secure ) return false ;
552+ else throw new Error ( 'Stream is not writable' ) ;
553+ }
424554
425555
426556 var buffer , off , len ;
427557 var bytesWritten , charsWritten ;
428-
429558 var queuedData = false ;
430-
431559 if ( typeof data != 'string' ) {
432560 // 'data' is a buffer, ignore 'encoding'
433561 buffer = data ;
@@ -458,7 +586,7 @@ Stream.prototype._writeOut = function (data, encoding) {
458586 assert ( charsWritten <= data . length ) ;
459587 }
460588
461- assert ( bytesWritten > 0 ) ;
589+ if ( encoding ) assert ( bytesWritten > 0 ) ;
462590
463591 buffer = pool ;
464592 len = bytesWritten ;
@@ -478,11 +606,26 @@ Stream.prototype._writeOut = function (data, encoding) {
478606 }
479607 }
480608
481-
482- // Send the buffer.
483-
484609 try {
485- bytesWritten = write ( this . fd , buffer , off , len ) ;
610+ if ( this . secure ) {
611+ if ( ! buffer ) return false ;
612+ bytesWritten = this . secureStream . writeInject ( buffer , off , len ) ;
613+ if ( ! securePool ) allocNewSecurePool ( ) ;
614+ var secureLen = this . secureStream . writeExtract ( securePool , 0 , securePool . length ) ;
615+ if ( secureLen == - 1 ) {
616+ // Check our read again for secure handshake
617+ this . _readWatcher . callback ( ) ;
618+ secureBytesWritten = 0 ;
619+ } else {
620+ var secureBytesWritten = write ( this . fd , securePool , 0 , secureLen ) ;
621+ }
622+ if ( ! this . secureEstablished && this . secureStream . isInitFinished ( ) ) {
623+ this . secureEstablished = true ;
624+ if ( this . _events && this . _events [ 'secure' ] ) this . emit ( 'secure' ) ;
625+ }
626+ } else {
627+ bytesWritten = write ( this . fd , buffer , off , len ) ;
628+ }
486629 } catch ( e ) {
487630 this . forceClose ( e ) ;
488631 return false ;
@@ -675,6 +818,10 @@ Stream.prototype.forceClose = function (exception) {
675818
676819 timeout . unenroll ( this ) ;
677820
821+ if ( this . secure ) {
822+ this . secureStream . close ( ) ;
823+ }
824+
678825 // FIXME Bug when this.fd == 0
679826 if ( typeof this . fd == 'number' ) {
680827 close ( this . fd ) ;
@@ -691,6 +838,9 @@ Stream.prototype._shutdown = function () {
691838 if ( this . writable ) {
692839 this . writable = false ;
693840
841+ if ( this . secure ) {
842+ this . _shutdownSecure ( ) ;
843+ }
694844 try {
695845 shutdown ( this . fd , 'write' )
696846 } catch ( e ) {
0 commit comments