@ -37,211 +37,228 @@ const tls_wrap = process.binding('tls_wrap');
const TCP = process . binding ( 'tcp_wrap' ) . TCP ;
const Pipe = process . binding ( 'pipe_wrap' ) . Pipe ;
const errors = require ( 'internal/errors' ) ;
const kConnectOptions = Symbol ( 'connect-options' ) ;
const kDisableRenegotiation = Symbol ( 'disable-renegotiation' ) ;
const kErrorEmitted = Symbol ( 'error-emitted' ) ;
const kHandshakeTimeout = Symbol ( 'handshake-timeout' ) ;
const kRes = Symbol ( 'res' ) ;
const kSNICallback = Symbol ( 'snicallback' ) ;
const noop = ( ) => { } ;
function onhandshakestart ( ) {
debug ( 'onhandshakestart' ) ;
var self = this ;
var ssl = self . _ handle ;
var now = Timer . now ( ) ;
const owner = this . owner ;
const now = Timer . now ( ) ;
assert ( now >= ssl . lastHandshakeTime ) ;
assert ( now >= this . lastHandshakeTime ) ;
if ( ( now - ssl . lastHandshakeTime ) >= tls . CLIENT_RENEG_WINDOW * 1000 ) {
ssl . handshakes = 0 ;
if ( ( now - this . lastHandshakeTime ) >= tls . CLIENT_RENEG_WINDOW * 1000 ) {
this . handshakes = 0 ;
}
var first = ( ssl . lastHandshakeTime === 0 ) ;
ssl . lastHandshakeTime = now ;
const first = ( this . lastHandshakeTime === 0 ) ;
this . lastHandshakeTime = now ;
if ( first ) return ;
if ( ++ ssl . handshakes > tls . CLIENT_RENEG_LIMIT ) {
if ( ++ this . handshakes > tls . CLIENT_RENEG_LIMIT ) {
// Defer the error event to the next tick. We're being called from OpenSSL's
// state machine and OpenSSL is not re-entrant. We cannot allow the user's
// callback to destroy the connection right now, it would crash and burn.
setImmediate ( function ( ) {
var err = new errors . Error ( 'ERR_TLS_SESSION_ATTACK' ) ;
self . _ emitTLSError ( err ) ;
} ) ;
setImmediate ( emitSessionAttackError , owner ) ;
}
if ( this [ kDisableRenegotiation ] && ssl . handshakes > 0 ) {
if ( owner [ kDisableRenegotiation ] && this . handshakes > 0 ) {
const err = new Error ( 'TLS session renegotiation disabled for this socket' ) ;
self . _ emitTLSError ( err ) ;
owner . _ emitTLSError ( err ) ;
}
}
function emitSessionAttackError ( socket ) {
socket . _ emitTLSError ( new errors . Error ( 'ERR_TLS_SESSION_ATTACK' ) ) ;
}
function onhandshakedone ( ) {
// for future use
debug ( 'onhandshakedone' ) ;
this . _ finishInit ( ) ;
const owner = this . owner ;
// `newSession` callback wasn't called yet
if ( owner . _ newSessionPending ) {
owner . _ securePending = true ;
return ;
}
owner . _ finishInit ( ) ;
}
function loadSession ( self , hello , cb ) {
function loadSession ( hello ) {
const owner = this . owner ;
var once = false ;
function onSession ( err , session ) {
if ( once )
return cb ( new errors . Error ( 'ERR_MULTIPLE_CALLBACK' ) ) ;
return owner . destroy ( new errors . Error ( 'ERR_MULTIPLE_CALLBACK' ) ) ;
once = true ;
if ( err )
return cb ( err ) ;
return owner . destroy ( err ) ;
if ( ! self . _ handle )
return cb ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
if ( owner . _ handle === null )
return owner . destroy ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
self . _ handle . loadSession ( session ) ;
cb ( null ) ;
owner . _ handle . loadSession ( session ) ;
owner . _ handle . endParser ( ) ;
}
if ( hello . sessionId . length <= 0 ||
hello . tlsTicket ||
self . server &&
! self . server . emit ( 'resumeSession' , hello . sessionId , onSession ) ) {
cb ( null ) ;
owner . server &&
! owner . server . emit ( 'resumeSession' , hello . sessionId , onSession ) ) {
owner . _ handle . endParser ( ) ;
}
}
function loadSNI ( self , servername , cb ) {
if ( ! servername || ! self . _ SNICallback )
return cb ( null ) ;
function loadSNI ( info ) {
const owner = this . owner ;
const servername = info . servername ;
if ( ! servername || ! owner . _ SNICallback )
return requestOCSP ( owner , info ) ;
var once = false ;
self . _ SNICallback ( servername , function ( err , context ) {
let once = false ;
owner . _ SNICallback ( servername , ( err , context ) => {
if ( once )
return cb ( new errors . Error ( 'ERR_MULTIPLE_CALLBACK' ) ) ;
return owner . destroy ( new errors . Error ( 'ERR_MULTIPLE_CALLBACK' ) ) ;
once = true ;
if ( err )
return cb ( err ) ;
return owner . destroy ( err ) ;
if ( ! self . _ handle )
return cb ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
if ( owner . _ handle === null )
return owner . destroy ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
// TODO(indutny): eventually disallow raw `SecureContext`
if ( context )
self . _ handle . sni_context = context . context || context ;
owner . _ handle . sni_context = context . context || context ;
cb ( null , self . _ handle . sni_context ) ;
requestOCSP ( owner , info ) ;
} ) ;
}
function requestOCSP ( self , hello , ctx , cb ) {
if ( ! hell o. OCSPRequest || ! self . server )
return cb ( null ) ;
function requestOCSP ( socket , info ) {
if ( ! inf o. OCSPRequest || ! socket . server )
return requestOCSPDone ( socket ) ;
if ( ! ctx )
ctx = self . server . _ sharedCreds ;
let ctx = socket . _ handle . sni_context ;
// TLS socket is using a `net.Server` instead of a tls.TLSServer.
// Some TLS properties like `server._sharedCreds` will not be present
if ( ! ctx )
return cb ( null ) ;
if ( ! ctx ) {
ctx = socket . server . _ sharedCreds ;
// TLS socket is using a `net.Server` instead of a tls.TLSServer.
// Some TLS properties like `server._sharedCreds` will not be present
if ( ! ctx )
return requestOCSPDone ( socket ) ;
}
// TODO(indutny): eventually disallow raw `SecureContext`
if ( ctx . context )
ctx = ctx . context ;
if ( self . server . listenerCount ( 'OCSPRequest' ) === 0 ) {
return cb ( null ) ;
} else {
self . server . emit ( 'OCSPRequest' ,
ctx . getCertificate ( ) ,
ctx . getIssuer ( ) ,
onOCSP ) ;
if ( socket . server . listenerCount ( 'OCSPRequest' ) === 0 ) {
return requestOCSPDone ( socket ) ;
}
var once = false ;
function onOCSP ( err , response ) {
let once = false ;
const onOCSP = ( err , response ) => {
if ( once )
return cb ( new errors . Error ( 'ERR_MULTIPLE_CALLBACK' ) ) ;
return socket . destroy ( new errors . Error ( 'ERR_MULTIPLE_CALLBACK' ) ) ;
once = true ;
if ( err )
return cb ( err ) ;
return socket . destroy ( err ) ;
if ( ! self . _ handle )
return cb ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
if ( socket . _ handle === null )
return socket . destroy ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
if ( response )
self . _ handle . setOCSPResponse ( response ) ;
cb ( null ) ;
}
}
function onclienthello ( hello ) {
var self = this ;
loadSession ( self , hello , function ( err ) {
if ( err )
return self . destroy ( err ) ;
socket . _ handle . setOCSPResponse ( response ) ;
requestOCSPDone ( socket ) ;
} ;
self . _ handle . endParser ( ) ;
} ) ;
socket . server . emit ( 'OCSPRequest' ,
ctx . getCertificate ( ) ,
ctx . getIssuer ( ) ,
onOCSP ) ;
}
function oncertcb ( info ) {
var self = this ;
var servername = info . servername ;
loadSNI ( self , servername , function ( err , ctx ) {
if ( err )
return self . destroy ( err ) ;
requestOCSP ( self , info , ctx , function ( err ) {
if ( err )
return self . destroy ( err ) ;
if ( ! self . _ handle )
return self . destroy ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
try {
self . _ handle . certCbDone ( ) ;
} catch ( e ) {
self . destroy ( e ) ;
}
} ) ;
} ) ;
function requestOCSPDone ( socket ) {
try {
socket . _ handle . certCbDone ( ) ;
} catch ( e ) {
socket . destroy ( e ) ;
}
}
function onnewsession ( key , session ) {
if ( ! this . server )
const owner = this . owner ;
if ( ! owner . server )
return ;
var self = this ;
var once = false ;
this . _ newSessionPending = true ;
if ( ! this . server . emit ( 'newSession' , key , session , done ) )
done ( ) ;
function done ( ) {
const done = ( ) => {
if ( once )
return ;
once = true ;
if ( ! self . _ handle )
return self . destroy ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
if ( owner . _ handle === null )
return owner . destroy ( new errors . Error ( 'ERR_SOCKET_CLOSED' ) ) ;
self . _ handle . newSessionDone ( ) ;
this . newSessionDone ( ) ;
self . _ newSessionPending = false ;
if ( self . _ securePending )
self . _ finishInit ( ) ;
self . _ securePending = false ;
}
owner . _ newSessionPending = false ;
if ( owner . _ securePending )
owner . _ finishInit ( ) ;
owner . _ securePending = false ;
} ;
owner . _ newSessionPending = true ;
if ( ! owner . server . emit ( 'newSession' , key , session , done ) )
done ( ) ;
}
function onocspresponse ( resp ) {
this . emit ( 'OCSPResponse' , resp ) ;
this . owner . emit ( 'OCSPResponse' , resp ) ;
}
function onerror ( err ) {
const owner = this . owner ;
if ( owner . _ writableState . errorEmitted )
return ;
// Destroy socket if error happened before handshake's finish
if ( ! owner . _ secureEstablished ) {
// When handshake fails control is not yet released,
// so self._tlsError will return null instead of actual error
owner . destroy ( err ) ;
} else if ( owner . _ tlsOptions . isServer &&
owner . _ rejectUnauthorized &&
/peer did not return a certificate/ . test ( err . message ) ) {
// Ignore server's authorization errors
owner . destroy ( ) ;
} else {
// Throw error
owner . _ emitTLSError ( err ) ;
}
owner . _ writableState . errorEmitted = true ;
}
function initRead ( tls , wrapped ) {
@ -278,6 +295,7 @@ function TLSSocket(socket, options) {
this . alpnProtocol = null ;
this . authorized = false ;
this . authorizationError = null ;
this [ kRes ] = null ;
// Wrap plain JS Stream into StreamWrap
var wrap ;
@ -369,7 +387,6 @@ TLSSocket.prototype.disableRenegotiation = function disableRenegotiation() {
} ;
TLSSocket . prototype . _ wrapHandle = function ( wrap ) {
var res ;
var handle ;
if ( wrap )
@ -382,33 +399,42 @@ TLSSocket.prototype._wrapHandle = function(wrap) {
}
// Wrap socket's handle
var context = options . secureContext ||
options . credentials ||
tls . createSecureContext ( options ) ;
res = tls_wrap . wrap ( handle . _ externalStream ,
context . context ,
! ! options . isServer ) ;
const context = options . secureContext ||
options . credentials ||
tls . createSecureContext ( options ) ;
const res = tls_wrap . wrap ( handle . _ externalStream ,
context . context ,
! ! options . isServer ) ;
res . _ parent = handle ;
res . _ parentWrap = wrap ;
res . _ secureContext = context ;
res . reading = handle . reading ;
this [ kRes ] = res ;
defineHandleReading ( this , handle ) ;
this . on ( 'close' , onSocketCloseDestroySSL ) ;
return res ;
} ;
// This eliminates a cyclic reference to TLSWrap
// Ref: https://github.com/nodejs/node/commit/f7620fb96d339f704932f9bb9a0dceb9952df2d4
function defineHandleReading ( socket , handle ) {
Object . defineProperty ( handle , 'reading' , {
get : function get ( ) {
return res . reading ;
get : ( ) => {
return socket [ kRes ] . reading ;
} ,
set : function set ( value ) {
res . reading = value ;
set : ( value ) => {
socket [ kRes ] . reading = value ;
}
} ) ;
}
this . on ( 'close' , function ( ) {
// Make sure we are not doing it on OpenSSL's stack
setImmediate ( destroySSL , this ) ;
res = null ;
} ) ;
return res ;
} ;
function onSocketCloseDestroySSL ( ) {
// Make sure we are not doing it on OpenSSL's stack
setImmediate ( destroySSL , this ) ;
this [ kRes ] = null ;
}
function destroySSL ( self ) {
self . _ destroySSL ( ) ;
@ -425,7 +451,6 @@ TLSSocket.prototype._destroySSL = function _destroySSL() {
} ;
TLSSocket . prototype . _ init = function ( socket , wrap ) {
var self = this ;
var options = this . _ tlsOptions ;
var ssl = this . _ handle ;
@ -448,11 +473,11 @@ TLSSocket.prototype._init = function(socket, wrap) {
ssl . setVerifyMode ( requestCert , rejectUnauthorized ) ;
if ( options . isServer ) {
ssl . onhandshakestart = ( ) => onhandshakestart . call ( this ) ;
ssl . onhandshakedone = ( ) => onhandshakedone . call ( this ) ;
ssl . onclienthello = ( hello ) => onclienthello . call ( this , hello ) ;
ssl . oncertcb = ( info ) => oncertcb . call ( this , info ) ;
ssl . onnewsession = ( key , session ) => onnewsession . call ( this , key , session ) ;
ssl . onhandshakestart = onhandshakestart ;
ssl . onhandshakedone = onhandshakedone ;
ssl . onclienthello = loadSession ;
ssl . oncertcb = loadSNI ;
ssl . onnewsession = onnewsession ;
ssl . lastHandshakeTime = 0 ;
ssl . handshakes = 0 ;
@ -465,35 +490,15 @@ TLSSocket.prototype._init = function(socket, wrap) {
ssl . enableCertCb ( ) ;
}
} else {
ssl . onhandshakestart = function ( ) { } ;
ssl . onhandshakedone = ( ) => this . _ finishInit ( ) ;
ssl . onocspresponse = ( resp ) => onocspresponse . call ( this , resp ) ;
ssl . onhandshakestart = noop ;
ssl . onhandshakedone = this . _ finishInit . bind ( this ) ;
ssl . onocspresponse = onocspresponse ;
if ( options . session )
ssl . setSession ( options . session ) ;
}
ssl . onerror = function ( err ) {
if ( self . _ writableState . errorEmitted )
return ;
// Destroy socket if error happened before handshake's finish
if ( ! self . _ secureEstablished ) {
// When handshake fails control is not yet released,
// so self._tlsError will return null instead of actual error
self . destroy ( err ) ;
} else if ( options . isServer &&
rejectUnauthorized &&
/peer did not return a certificate/ . test ( err . message ) ) {
// Ignore server's authorization errors
self . destroy ( ) ;
} else {
// Throw error
self . _ emitTLSError ( err ) ;
}
self . _ writableState . errorEmitted = true ;
} ;
ssl . onerror = onerror ;
// If custom SNICallback was given, or if
// there're SNI contexts to perform match against -
@ -526,17 +531,15 @@ TLSSocket.prototype._init = function(socket, wrap) {
// To prevent assertion in afterConnect() and properly kick off readStart
this . connecting = socket . connecting || ! socket . _ handle ;
socket . once ( 'connect' , function ( ) {
self . connecting = false ;
self . emit ( 'connect' ) ;
socket . once ( 'connect' , ( ) => {
this . connecting = false ;
this . emit ( 'connect' ) ;
} ) ;
}
// Assume `tls.connect()`
if ( wrap ) {
wrap . on ( 'error' , function ( err ) {
self . _ emitTLSError ( err ) ;
} ) ;
wrap . on ( 'error' , ( err ) => this . _ emitTLSError ( err ) ) ;
} else {
assert ( ! socket ) ;
this . connecting = true ;
@ -544,15 +547,15 @@ TLSSocket.prototype._init = function(socket, wrap) {
} ;
TLSSocket . prototype . renegotiate = function ( options , callback ) {
var requestCert = this . _ requestCert ;
var rejectUnauthorized = this . _ rejectUnauthorized ;
if ( this . destroyed )
return ;
if ( typeof options . requestCert !== 'undefined' )
let requestCert = this . _ requestCert ;
let rejectUnauthorized = this . _ rejectUnauthorized ;
if ( options . requestCert !== undefined )
requestCert = ! ! options . requestCert ;
if ( typeof options . rejectUnauthorized !== 'undefined' )
if ( options . rejectUnauthorized !== undefined )
rejectUnauthorized = ! ! options . rejectUnauthorized ;
if ( requestCert !== this . _ requestCert ||
@ -572,9 +575,7 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
this . write ( '' ) ;
if ( callback ) {
this . once ( 'secure' , function ( ) {
callback ( null ) ;
} ) ;
this . once ( 'secure' , ( ) => callback ( null ) ) ;
}
return true ;
@ -614,18 +615,12 @@ TLSSocket.prototype._releaseControl = function() {
} ;
TLSSocket . prototype . _ finishInit = function ( ) {
// `newSession` callback wasn't called yet
if ( this . _ newSessionPending ) {
this . _ securePending = true ;
return ;
}
if ( process . features . tls_npn ) {
this . npnProtocol = this . _ handle . getNegotiatedProtocol ( ) ;
}
if ( process . features . tls_alpn ) {
this . alpnProtocol = this . ssl . getALPNNegotiatedProtocol ( ) ;
this . alpnProtocol = this . _ handle . getALPNNegotiatedProtocol ( ) ;
}
if ( process . features . tls_sni && this . _ tlsOptions . isServer ) {
@ -641,9 +636,7 @@ TLSSocket.prototype._finishInit = function() {
TLSSocket . prototype . _ start = function ( ) {
if ( this . connecting ) {
this . once ( 'connect' , function ( ) {
this . _ start ( ) ;
} ) ;
this . once ( 'connect' , this . _ start ) ;
return ;
}
@ -717,6 +710,64 @@ TLSSocket.prototype.getProtocol = function() {
// TODO: support anonymous (nocert) and PSK
function onSocketSecure ( ) {
if ( this . _ requestCert ) {
const verifyError = this . _ handle . verifyError ( ) ;
if ( verifyError ) {
this . authorizationError = verifyError . code ;
if ( this . _ rejectUnauthorized )
this . destroy ( ) ;
} else {
this . authorized = true ;
}
}
if ( ! this . destroyed && this . _ releaseControl ( ) )
this . _ tlsOptions . server . emit ( 'secureConnection' , this ) ;
}
function onSocketTLSError ( err ) {
if ( ! this . _ controlReleased && ! this [ kErrorEmitted ] ) {
this [ kErrorEmitted ] = true ;
this . _ tlsOptions . server . emit ( 'tlsClientError' , err , this ) ;
}
}
function onSocketClose ( err ) {
// Closed because of error - no need to emit it twice
if ( err )
return ;
// Emit ECONNRESET
if ( ! this . _ controlReleased && ! this [ kErrorEmitted ] ) {
this [ kErrorEmitted ] = true ;
const connReset = new Error ( 'socket hang up' ) ;
connReset . code = 'ECONNRESET' ;
this . _ tlsOptions . server . emit ( 'tlsClientError' , connReset , this ) ;
}
}
function tlsConnectionListener ( rawSocket ) {
const socket = new TLSSocket ( rawSocket , {
secureContext : this . _ sharedCreds ,
isServer : true ,
server : this ,
requestCert : this . requestCert ,
rejectUnauthorized : this . rejectUnauthorized ,
handshakeTimeout : this [ kHandshakeTimeout ] ,
NPNProtocols : this . NPNProtocols ,
ALPNProtocols : this . ALPNProtocols ,
SNICallback : this [ kSNICallback ] || SNICallback
} ) ;
socket . on ( 'secure' , onSocketSecure ) ;
socket [ kErrorEmitted ] = false ;
socket . on ( 'close' , onSocketClose ) ;
socket . on ( '_tlsError' , onSocketTLSError ) ;
}
// AUTHENTICATION MODES
//
// There are several levels of authentication that TLS/SSL supports.
@ -797,95 +848,43 @@ function Server(options, listener) {
this . _ contexts = [ ] ;
var self = this ;
// Handle option defaults:
this . setOptions ( options ) ;
var sharedCreds = tls . createSecureContext ( {
pfx : self . pfx ,
key : self . key ,
passphrase : self . passphrase ,
cert : self . cert ,
ca : self . ca ,
ciphers : self . ciphers ,
ecdhCurve : self . ecdhCurve ,
dhparam : self . dhparam ,
secureProtocol : self . secureProtocol ,
secureOptions : self . secureOptions ,
honorCipherOrder : self . honorCipherOrder ,
crl : self . crl ,
sessionIdContext : self . sessionIdContext
pfx : this . pfx ,
key : this . key ,
passphrase : this . passphrase ,
cert : this . cert ,
ca : this . ca ,
ciphers : this . ciphers ,
ecdhCurve : this . ecdhCurve ,
dhparam : this . dhparam ,
secureProtocol : this . secureProtocol ,
secureOptions : this . secureOptions ,
honorCipherOrder : this . honorCipherOrder ,
crl : this . crl ,
sessionIdContext : this . sessionIdContext
} ) ;
this . _ sharedCreds = sharedCreds ;
var timeout = options . handshakeTimeout || ( 120 * 1000 ) ;
this [ kHandshakeTimeout ] = options . handshakeTimeout || ( 120 * 1000 ) ;
this [ kSNICallback ] = options . SNICallback ;
if ( typeof timeout !== 'number' ) {
if ( typeof this [ kHandshakeTimeout ] !== 'number' ) {
throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'timeout' , 'number' ) ;
}
if ( self . sessionTimeout ) {
sharedCreds . context . setSessionTimeout ( self . sessionTimeout ) ;
if ( this . sessionTimeout ) {
sharedCreds . context . setSessionTimeout ( this . sessionTimeout ) ;
}
if ( self . ticketKeys ) {
sharedCreds . context . setTicketKeys ( self . ticketKeys ) ;
if ( this . ticketKeys ) {
sharedCreds . context . setTicketKeys ( this . ticketKeys ) ;
}
// constructor call
net . Server . call ( this , function ( raw_socket ) {
var socket = new TLSSocket ( raw_socket , {
secureContext : sharedCreds ,
isServer : true ,
server : self ,
requestCert : self . requestCert ,
rejectUnauthorized : self . rejectUnauthorized ,
handshakeTimeout : timeout ,
NPNProtocols : self . NPNProtocols ,
ALPNProtocols : self . ALPNProtocols ,
SNICallback : options . SNICallback || SNICallback
} ) ;
socket . on ( 'secure' , function ( ) {
if ( socket . _ requestCert ) {
var verifyError = socket . _ handle . verifyError ( ) ;
if ( verifyError ) {
socket . authorizationError = verifyError . code ;
if ( socket . _ rejectUnauthorized )
socket . destroy ( ) ;
} else {
socket . authorized = true ;
}
}
if ( ! socket . destroyed && socket . _ releaseControl ( ) )
self . emit ( 'secureConnection' , socket ) ;
} ) ;
var errorEmitted = false ;
socket . on ( 'close' , function ( err ) {
// Closed because of error - no need to emit it twice
if ( err )
return ;
// Emit ECONNRESET
if ( ! socket . _ controlReleased && ! errorEmitted ) {
errorEmitted = true ;
var connReset = new Error ( 'socket hang up' ) ;
connReset . code = 'ECONNRESET' ;
self . emit ( 'tlsClientError' , connReset , socket ) ;
}
} ) ;
socket . on ( '_tlsError' , function ( err ) {
if ( ! socket . _ controlReleased && ! errorEmitted ) {
errorEmitted = true ;
self . emit ( 'tlsClientError' , err , socket ) ;
}
} ) ;
} ) ;
net . Server . call ( this , tlsConnectionListener ) ;
if ( listener ) {
this . on ( 'secureConnection' , listener ) ;
@ -971,16 +970,17 @@ Server.prototype.addContext = function(servername, context) {
} ;
function SNICallback ( servername , callback ) {
var ctx ;
const contexts = this . server . _ contexts ;
this . server . _ contexts . some ( function ( elem ) {
for ( var i = 0 ; i < contexts . length ; i ++ ) {
const elem = contexts [ i ] ;
if ( elem [ 0 ] . test ( servername ) ) {
ctx = elem [ 1 ] ;
return true ;
callback ( null , elem [ 1 ] ) ;
return ;
}
} ) ;
}
callback ( null , ctx ) ;
callback ( null , undefined ) ;
}
@ -1017,6 +1017,66 @@ function normalizeConnectArgs(listArgs) {
return ( cb ) ? [ options , cb ] : [ options ] ;
}
function onConnectSecure ( ) {
const options = this [ kConnectOptions ] ;
// Check the size of DHE parameter above minimum requirement
// specified in options.
const ekeyinfo = this . getEphemeralKeyInfo ( ) ;
if ( ekeyinfo . type === 'DH' && ekeyinfo . size < options . minDHSize ) {
const err = new errors . Error ( 'ERR_TLS_DH_PARAM_SIZE' , ekeyinfo . size ) ;
this . emit ( 'error' , err ) ;
this . destroy ( ) ;
return ;
}
let verifyError = this . _ handle . verifyError ( ) ;
// Verify that server's identity matches it's certificate's names
// Unless server has resumed our existing session
if ( ! verifyError && ! this . isSessionReused ( ) ) {
const hostname = options . servername ||
options . host ||
( options . socket && options . socket . _ host ) ||
'localhost' ;
const cert = this . getPeerCertificate ( ) ;
verifyError = options . checkServerIdentity ( hostname , cert ) ;
}
if ( verifyError ) {
this . authorized = false ;
this . authorizationError = verifyError . code || verifyError . message ;
if ( options . rejectUnauthorized ) {
this . destroy ( verifyError ) ;
return ;
} else {
this . emit ( 'secureConnect' ) ;
}
} else {
this . authorized = true ;
this . emit ( 'secureConnect' ) ;
}
// Uncork incoming data
this . removeListener ( 'end' , onConnectEnd ) ;
}
function onConnectEnd ( ) {
// NOTE: This logic is shared with _http_client.js
if ( ! this . _ hadError ) {
const options = this [ kConnectOptions ] ;
this . _ hadError = true ;
const error = new Error ( 'socket hang up' ) ;
error . code = 'ECONNRESET' ;
error . path = options . path ;
error . host = options . host ;
error . port = options . port ;
error . localAddress = options . localAddress ;
this . destroy ( error ) ;
}
}
exports . connect = function ( ... args /* [port,] [host,] [options,] [cb] */ ) {
args = normalizeConnectArgs ( args ) ;
var options = args [ 0 ] ;
@ -1040,10 +1100,6 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
'options.minDHSize is not a positive number: ' +
options . minDHSize ) ;
var hostname = options . servername ||
options . host ||
( options . socket && options . socket . _ host ) ||
'localhost' ;
const NPN = { } ;
const ALPN = { } ;
const context = options . secureContext || tls . createSecureContext ( options ) ;
@ -1062,6 +1118,8 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
requestOCSP : options . requestOCSP
} ) ;
socket [ kConnectOptions ] = options ;
if ( cb )
socket . once ( 'secureConnect' , cb ) ;
@ -1074,9 +1132,7 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
localAddress : options . localAddress ,
lookup : options . lookup
} ;
socket . connect ( connectOpt , function ( ) {
socket . _ start ( ) ;
} ) ;
socket . connect ( connectOpt , socket . _ start ) ;
}
socket . _ releaseControl ( ) ;
@ -1090,59 +1146,8 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
if ( options . socket )
socket . _ start ( ) ;
socket . on ( 'secure' , function ( ) {
// Check the size of DHE parameter above minimum requirement
// specified in options.
var ekeyinfo = socket . getEphemeralKeyInfo ( ) ;
if ( ekeyinfo . type === 'DH' && ekeyinfo . size < options . minDHSize ) {
var err = new errors . Error ( 'ERR_TLS_DH_PARAM_SIZE' , ekeyinfo . size ) ;
socket . emit ( 'error' , err ) ;
socket . destroy ( ) ;
return ;
}
var verifyError = socket . _ handle . verifyError ( ) ;
// Verify that server's identity matches it's certificate's names
// Unless server has resumed our existing session
if ( ! verifyError && ! socket . isSessionReused ( ) ) {
var cert = socket . getPeerCertificate ( ) ;
verifyError = options . checkServerIdentity ( hostname , cert ) ;
}
if ( verifyError ) {
socket . authorized = false ;
socket . authorizationError = verifyError . code || verifyError . message ;
if ( options . rejectUnauthorized ) {
socket . destroy ( verifyError ) ;
return ;
} else {
socket . emit ( 'secureConnect' ) ;
}
} else {
socket . authorized = true ;
socket . emit ( 'secureConnect' ) ;
}
// Uncork incoming data
socket . removeListener ( 'end' , onHangUp ) ;
} ) ;
function onHangUp ( ) {
// NOTE: This logic is shared with _http_client.js
if ( ! socket . _ hadError ) {
socket . _ hadError = true ;
var error = new Error ( 'socket hang up' ) ;
error . code = 'ECONNRESET' ;
error . path = options . path ;
error . host = options . host ;
error . port = options . port ;
error . localAddress = options . localAddress ;
socket . destroy ( error ) ;
}
}
socket . once ( 'end' , onHangUp ) ;
socket . on ( 'secure' , onConnectSecure ) ;
socket . once ( 'end' , onConnectEnd ) ;
return socket ;
} ;