parent
9dd979228d
commit
9010f5fbab
@ -23,12 +23,10 @@ var tls = require('tls');
|
|||||||
var http = require('http');
|
var http = require('http');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
|
|
||||||
var NPN_ENABLED = process.binding('constants').NPN_ENABLED;
|
|
||||||
|
|
||||||
function Server(opts, requestListener) {
|
function Server(opts, requestListener) {
|
||||||
if (!(this instanceof Server)) return new Server(opts, requestListener);
|
if (!(this instanceof Server)) return new Server(opts, requestListener);
|
||||||
|
|
||||||
if (NPN_ENABLED && !opts.NPNProtocols) {
|
if (process.features.tls_npn && !opts.NPNProtocols) {
|
||||||
opts.NPNProtocols = ['http/1.1', 'http/1.0'];
|
opts.NPNProtocols = ['http/1.1', 'http/1.0'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +62,7 @@ Agent.prototype.defaultPort = 443;
|
|||||||
|
|
||||||
|
|
||||||
Agent.prototype._getConnection = function(options, cb) {
|
Agent.prototype._getConnection = function(options, cb) {
|
||||||
if (NPN_ENABLED && !this.options.NPNProtocols) {
|
if (process.features.tls_npn && !this.options.NPNProtocols) {
|
||||||
this.options.NPNProtocols = ['http/1.1', 'http/1.0'];
|
this.options.NPNProtocols = ['http/1.1', 'http/1.0'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +23,10 @@ var tls = require('tls');
|
|||||||
var http = require('http');
|
var http = require('http');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
|
|
||||||
var NPN_ENABLED = process.binding('constants').NPN_ENABLED;
|
|
||||||
|
|
||||||
function Server(opts, requestListener) {
|
function Server(opts, requestListener) {
|
||||||
if (!(this instanceof Server)) return new Server(opts, requestListener);
|
if (!(this instanceof Server)) return new Server(opts, requestListener);
|
||||||
|
|
||||||
if (NPN_ENABLED && !opts.NPNProtocols) {
|
if (process.features.tls_npn && !opts.NPNProtocols) {
|
||||||
opts.NPNProtocols = ['http/1.1', 'http/1.0'];
|
opts.NPNProtocols = ['http/1.1', 'http/1.0'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
lib/tls.js
74
lib/tls.js
@ -27,8 +27,6 @@ var stream = require('stream');
|
|||||||
var END_OF_FILE = 42;
|
var END_OF_FILE = 42;
|
||||||
var assert = require('assert').ok;
|
var assert = require('assert').ok;
|
||||||
|
|
||||||
var NPN_ENABLED = process.binding('constants').NPN_ENABLED;
|
|
||||||
|
|
||||||
var debug;
|
var debug;
|
||||||
if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
|
if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
|
||||||
debug = function(a) { console.error('TLS:', a); };
|
debug = function(a) { console.error('TLS:', a); };
|
||||||
@ -40,7 +38,6 @@ if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
|
|||||||
var Connection = null;
|
var Connection = null;
|
||||||
try {
|
try {
|
||||||
Connection = process.binding('crypto').Connection;
|
Connection = process.binding('crypto').Connection;
|
||||||
exports.NPN_ENABLED = NPN_ENABLED;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('node.js not compiled with openssl crypto support.');
|
throw new Error('node.js not compiled with openssl crypto support.');
|
||||||
}
|
}
|
||||||
@ -478,17 +475,19 @@ EncryptedStream.prototype._pusher = function(pool, offset, length) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
|
function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
|
||||||
NPNProtocols) {
|
options) {
|
||||||
if (!(this instanceof SecurePair)) {
|
if (!(this instanceof SecurePair)) {
|
||||||
return new SecurePair(credentials,
|
return new SecurePair(credentials,
|
||||||
isServer,
|
isServer,
|
||||||
requestCert,
|
requestCert,
|
||||||
rejectUnauthorized,
|
rejectUnauthorized,
|
||||||
NPNProtocols);
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
options || (options = {});
|
||||||
|
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
this._secureEstablished = false;
|
this._secureEstablished = false;
|
||||||
@ -514,11 +513,19 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
|
|||||||
this._requestCert = requestCert ? true : false;
|
this._requestCert = requestCert ? true : false;
|
||||||
|
|
||||||
this.ssl = new Connection(this.credentials.context,
|
this.ssl = new Connection(this.credentials.context,
|
||||||
this._isServer ? true : false, this._requestCert,
|
this._isServer ? true : false,
|
||||||
|
this._isServer ? this._requestCert : options.servername,
|
||||||
this._rejectUnauthorized);
|
this._rejectUnauthorized);
|
||||||
|
|
||||||
if (NPN_ENABLED && NPNProtocols) {
|
if (process.features.tls_sni) {
|
||||||
this.ssl.setNPNProtocols(NPNProtocols);
|
if (this._isServer && options.SNICallback) {
|
||||||
|
this.ssl.setSNICallback(options.SNICallback);
|
||||||
|
}
|
||||||
|
this.servername = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.features.tls_npn && options.NPNProtocols) {
|
||||||
|
this.ssl.setNPNProtocols(options.NPNProtocols);
|
||||||
this.npnProtocol = null;
|
this.npnProtocol = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,9 +636,14 @@ SecurePair.prototype.cycle = function(depth) {
|
|||||||
|
|
||||||
SecurePair.prototype.maybeInitFinished = function() {
|
SecurePair.prototype.maybeInitFinished = function() {
|
||||||
if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
|
if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
|
||||||
if (NPN_ENABLED) {
|
if (process.features.tls_npn) {
|
||||||
this.npnProtocol = this.ssl.getNegotiatedProtocol();
|
this.npnProtocol = this.ssl.getNegotiatedProtocol();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.features.tls_sni) {
|
||||||
|
this.servername = this.ssl.getServername();
|
||||||
|
}
|
||||||
|
|
||||||
this._secureEstablished = true;
|
this._secureEstablished = true;
|
||||||
debug('secure established');
|
debug('secure established');
|
||||||
this.emit('secure');
|
this.emit('secure');
|
||||||
@ -789,7 +801,10 @@ function Server(/* [options], listener */) {
|
|||||||
true,
|
true,
|
||||||
self.requestCert,
|
self.requestCert,
|
||||||
self.rejectUnauthorized,
|
self.rejectUnauthorized,
|
||||||
self.NPNProtocols);
|
{
|
||||||
|
NPNProtocols: self.NPNProtocols,
|
||||||
|
SNICallback: self.SNICallback
|
||||||
|
});
|
||||||
|
|
||||||
var cleartext = pipe(pair, socket);
|
var cleartext = pipe(pair, socket);
|
||||||
cleartext._controlReleased = false;
|
cleartext._controlReleased = false;
|
||||||
@ -797,6 +812,8 @@ function Server(/* [options], listener */) {
|
|||||||
pair.on('secure', function() {
|
pair.on('secure', function() {
|
||||||
pair.cleartext.authorized = false;
|
pair.cleartext.authorized = false;
|
||||||
pair.cleartext.npnProtocol = pair.npnProtocol;
|
pair.cleartext.npnProtocol = pair.npnProtocol;
|
||||||
|
pair.cleartext.servername = pair.servername;
|
||||||
|
|
||||||
if (!self.requestCert) {
|
if (!self.requestCert) {
|
||||||
cleartext._controlReleased = true;
|
cleartext._controlReleased = true;
|
||||||
self.emit('secureConnection', pair.cleartext, pair.encrypted);
|
self.emit('secureConnection', pair.cleartext, pair.encrypted);
|
||||||
@ -858,6 +875,38 @@ Server.prototype.setOptions = function(options) {
|
|||||||
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
|
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
|
||||||
if (options.secureOptions) this.secureOptions = options.secureOptions;
|
if (options.secureOptions) this.secureOptions = options.secureOptions;
|
||||||
if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
|
if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
|
||||||
|
if (options.SNICallback) {
|
||||||
|
this.SNICallback = options.SNICallback;
|
||||||
|
} else {
|
||||||
|
this.SNICallback = this.SNICallback.bind(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// SNI Contexts High-Level API
|
||||||
|
Server.prototype._contexts = [];
|
||||||
|
Server.prototype.addContext = function(servername, credentials) {
|
||||||
|
if (!servername) {
|
||||||
|
throw 'Servername is required parameter for Server.addContext';
|
||||||
|
}
|
||||||
|
|
||||||
|
var re = new RegExp('^' +
|
||||||
|
servername.replace(/([\.^$+?\-\\[\]{}])/g, '\\$1')
|
||||||
|
.replace(/\*/g, '.*') +
|
||||||
|
'$');
|
||||||
|
this._contexts.push([re, crypto.createCredentials(credentials).context]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype.SNICallback = function(servername) {
|
||||||
|
var ctx;
|
||||||
|
|
||||||
|
this._contexts.some(function(elem) {
|
||||||
|
if (servername.match(elem[0]) !== null) {
|
||||||
|
ctx = elem[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -902,7 +951,10 @@ exports.connect = function(port /* host, options, cb */) {
|
|||||||
|
|
||||||
convertNPNProtocols(options.NPNProtocols, this);
|
convertNPNProtocols(options.NPNProtocols, this);
|
||||||
var pair = new SecurePair(sslcontext, false, true, false,
|
var pair = new SecurePair(sslcontext, false, true, false,
|
||||||
this.NPNProtocols);
|
{
|
||||||
|
NPNProtocols: this.NPNProtocols,
|
||||||
|
servername: options.servername
|
||||||
|
});
|
||||||
|
|
||||||
var cleartext = pipe(pair, socket);
|
var cleartext = pipe(pair, socket);
|
||||||
|
|
||||||
|
14
src/node.cc
14
src/node.cc
@ -142,6 +142,18 @@ static bool use_uv = true;
|
|||||||
// disabled by default for now
|
// disabled by default for now
|
||||||
static bool use_http2 = false;
|
static bool use_http2 = false;
|
||||||
|
|
||||||
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
|
static bool use_npn = true;
|
||||||
|
#else
|
||||||
|
static bool use_npn = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
static bool use_sni = true;
|
||||||
|
#else
|
||||||
|
static bool use_sni = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Buffer for getpwnam_r(), getgrpam_r() and other misc callers; keep this
|
// Buffer for getpwnam_r(), getgrpam_r() and other misc callers; keep this
|
||||||
// scoped at file-level rather than method-level to avoid excess stack usage.
|
// scoped at file-level rather than method-level to avoid excess stack usage.
|
||||||
static char getbuf[PATH_MAX + 1];
|
static char getbuf[PATH_MAX + 1];
|
||||||
@ -2031,6 +2043,8 @@ static Handle<Object> GetFeatures() {
|
|||||||
obj->Set(String::NewSymbol("uv"), Boolean::New(use_uv));
|
obj->Set(String::NewSymbol("uv"), Boolean::New(use_uv));
|
||||||
obj->Set(String::NewSymbol("http2"), Boolean::New(use_http2));
|
obj->Set(String::NewSymbol("http2"), Boolean::New(use_http2));
|
||||||
obj->Set(String::NewSymbol("ipv6"), True()); // TODO ping libuv
|
obj->Set(String::NewSymbol("ipv6"), True()); // TODO ping libuv
|
||||||
|
obj->Set(String::NewSymbol("tls_npn"), Boolean::New(use_npn));
|
||||||
|
obj->Set(String::NewSymbol("tls_sni"), Boolean::New(use_sni));
|
||||||
obj->Set(String::NewSymbol("tls"),
|
obj->Set(String::NewSymbol("tls"),
|
||||||
Boolean::New(get_builtin_module("crypto") != NULL));
|
Boolean::New(get_builtin_module("crypto") != NULL));
|
||||||
|
|
||||||
|
@ -61,11 +61,14 @@ static Persistent<String> name_symbol;
|
|||||||
static Persistent<String> version_symbol;
|
static Persistent<String> version_symbol;
|
||||||
static Persistent<String> ext_key_usage_symbol;
|
static Persistent<String> ext_key_usage_symbol;
|
||||||
|
|
||||||
|
static Persistent<FunctionTemplate> secure_context_constructor;
|
||||||
|
|
||||||
void SecureContext::Initialize(Handle<Object> target) {
|
void SecureContext::Initialize(Handle<Object> target) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
Local<FunctionTemplate> t = FunctionTemplate::New(SecureContext::New);
|
Local<FunctionTemplate> t = FunctionTemplate::New(SecureContext::New);
|
||||||
|
secure_context_constructor = Persistent<FunctionTemplate>::New(t);
|
||||||
|
|
||||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
t->SetClassName(String::NewSymbol("SecureContext"));
|
t->SetClassName(String::NewSymbol("SecureContext"));
|
||||||
|
|
||||||
@ -585,6 +588,12 @@ void Connection::Initialize(Handle<Object> target) {
|
|||||||
NODE_SET_PROTOTYPE_METHOD(t, "setNPNProtocols", Connection::SetNPNProtocols);
|
NODE_SET_PROTOTYPE_METHOD(t, "setNPNProtocols", Connection::SetNPNProtocols);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "getServername", Connection::GetServername);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "setSNICallback", Connection::SetSNICallback);
|
||||||
|
#endif
|
||||||
|
|
||||||
target->Set(String::NewSymbol("Connection"), t->GetFunction());
|
target->Set(String::NewSymbol("Connection"), t->GetFunction());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,6 +713,56 @@ int Connection::SelectNextProtoCallback_(SSL *s,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Connection *p = static_cast<Connection*> SSL_get_app_data(s);
|
||||||
|
|
||||||
|
const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
|
||||||
|
|
||||||
|
if (servername) {
|
||||||
|
if (!p->servername_.IsEmpty()) {
|
||||||
|
p->servername_.Dispose();
|
||||||
|
}
|
||||||
|
p->servername_ = Persistent<String>::New(String::New(servername));
|
||||||
|
|
||||||
|
// Call sniCallback_ and use it's return value as context
|
||||||
|
if (!p->sniCallback_.IsEmpty()) {
|
||||||
|
if (!p->sniContext_.IsEmpty()) {
|
||||||
|
p->sniContext_.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get callback init args
|
||||||
|
Local<Value> argv[1] = {*p->servername_};
|
||||||
|
Local<Function> callback = *p->sniCallback_;
|
||||||
|
|
||||||
|
TryCatch try_catch;
|
||||||
|
|
||||||
|
// Call it
|
||||||
|
Local<Value> ret = callback->Call(Context::GetCurrent()->Global(),
|
||||||
|
1,
|
||||||
|
argv);
|
||||||
|
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
FatalException(try_catch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ret is SecureContext
|
||||||
|
if (secure_context_constructor->HasInstance(ret)) {
|
||||||
|
p->sniContext_ = Persistent<Value>::New(ret);
|
||||||
|
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(
|
||||||
|
Local<Object>::Cast(ret));
|
||||||
|
SSL_set_SSL_CTX(s, sc->ctx_);
|
||||||
|
} else {
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Handle<Value> Connection::New(const Arguments& args) {
|
Handle<Value> Connection::New(const Arguments& args) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
@ -724,8 +783,9 @@ Handle<Value> Connection::New(const Arguments& args) {
|
|||||||
p->bio_read_ = BIO_new(BIO_s_mem());
|
p->bio_read_ = BIO_new(BIO_s_mem());
|
||||||
p->bio_write_ = BIO_new(BIO_s_mem());
|
p->bio_write_ = BIO_new(BIO_s_mem());
|
||||||
|
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
|
||||||
SSL_set_app_data(p->ssl_, p);
|
SSL_set_app_data(p->ssl_, p);
|
||||||
|
|
||||||
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
if (is_server) {
|
if (is_server) {
|
||||||
// Server should advertise NPN protocols
|
// Server should advertise NPN protocols
|
||||||
SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
|
SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
|
||||||
@ -740,6 +800,15 @@ Handle<Value> Connection::New(const Arguments& args) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
if (is_server) {
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_);
|
||||||
|
} else {
|
||||||
|
String::Utf8Value servername(args[2]->ToString());
|
||||||
|
SSL_set_tlsext_host_name(p->ssl_, *servername);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SSL_set_bio(p->ssl_, p->bio_read_, p->bio_write_);
|
SSL_set_bio(p->ssl_, p->bio_read_, p->bio_write_);
|
||||||
|
|
||||||
#ifdef SSL_MODE_RELEASE_BUFFERS
|
#ifdef SSL_MODE_RELEASE_BUFFERS
|
||||||
@ -1333,6 +1402,39 @@ Handle<Value> Connection::SetNPNProtocols(const Arguments& args) {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
Handle<Value> Connection::GetServername(const Arguments& args) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Connection *ss = Connection::Unwrap(args);
|
||||||
|
|
||||||
|
if (ss->is_server_ && !ss->servername_.IsEmpty()) {
|
||||||
|
return ss->servername_;
|
||||||
|
} else {
|
||||||
|
return False();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value> Connection::SetSNICallback(const Arguments& args) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Connection *ss = Connection::Unwrap(args);
|
||||||
|
|
||||||
|
if (args.Length() < 1 || !args[0]->IsFunction()) {
|
||||||
|
return ThrowException(Exception::Error(String::New(
|
||||||
|
"Must give a Function as first argument")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release old handle
|
||||||
|
if (!ss->sniCallback_.IsEmpty()) {
|
||||||
|
ss->sniCallback_.Dispose();
|
||||||
|
}
|
||||||
|
ss->sniCallback_ = Persistent<Function>::New(
|
||||||
|
Local<Function>::Cast(args[0]));
|
||||||
|
|
||||||
|
return True();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void HexEncode(unsigned char *md_value,
|
static void HexEncode(unsigned char *md_value,
|
||||||
int md_len,
|
int md_len,
|
||||||
|
@ -104,6 +104,12 @@ class Connection : ObjectWrap {
|
|||||||
v8::Persistent<v8::Value> selectedNPNProto_;
|
v8::Persistent<v8::Value> selectedNPNProto_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
v8::Persistent<v8::Function> sniCallback_;
|
||||||
|
v8::Persistent<v8::Value> sniContext_;
|
||||||
|
v8::Persistent<v8::String> servername_;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static v8::Handle<v8::Value> New(const v8::Arguments& args);
|
static v8::Handle<v8::Value> New(const v8::Arguments& args);
|
||||||
static v8::Handle<v8::Value> EncIn(const v8::Arguments& args);
|
static v8::Handle<v8::Value> EncIn(const v8::Arguments& args);
|
||||||
@ -135,6 +141,13 @@ class Connection : ObjectWrap {
|
|||||||
unsigned int inlen, void *arg);
|
unsigned int inlen, void *arg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
// SNI
|
||||||
|
static v8::Handle<v8::Value> GetServername(const v8::Arguments& args);
|
||||||
|
static v8::Handle<v8::Value> SetSNICallback(const v8::Arguments& args);
|
||||||
|
static int SelectSNIContextCallback_(SSL *s, int *ad, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
int HandleBIOError(BIO *bio, const char* func, int rv);
|
int HandleBIOError(BIO *bio, const char* func, int rv);
|
||||||
int HandleSSLError(const char* func, int rv);
|
int HandleSSLError(const char* func, int rv);
|
||||||
|
|
||||||
@ -162,6 +175,12 @@ class Connection : ObjectWrap {
|
|||||||
if (!npnProtos_.IsEmpty()) npnProtos_.Dispose();
|
if (!npnProtos_.IsEmpty()) npnProtos_.Dispose();
|
||||||
if (!selectedNPNProto_.IsEmpty()) selectedNPNProto_.Dispose();
|
if (!selectedNPNProto_.IsEmpty()) selectedNPNProto_.Dispose();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
||||||
|
if (!sniCallback_.IsEmpty()) sniCallback_.Dispose();
|
||||||
|
if (!sniContext_.IsEmpty()) sniContext_.Dispose();
|
||||||
|
if (!servername_.IsEmpty()) servername_.Dispose();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -19,9 +19,7 @@
|
|||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
var NPN_ENABLED = process.binding('constants').NPN_ENABLED;
|
if (!process.features.tls_npn) {
|
||||||
|
|
||||||
if (!process.versions.openssl || !NPN_ENABLED) {
|
|
||||||
console.error("Skipping because node compiled without OpenSSL or " +
|
console.error("Skipping because node compiled without OpenSSL or " +
|
||||||
"with old OpenSSL version.");
|
"with old OpenSSL version.");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
112
test/simple/test-tls-sni-server-client.js
Normal file
112
test/simple/test-tls-sni-server-client.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||||
|
// following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included
|
||||||
|
// in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
if (!process.features.tls_sni) {
|
||||||
|
console.error("Skipping because node compiled without OpenSSL or " +
|
||||||
|
"with old OpenSSL version.");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var common = require('../common'),
|
||||||
|
assert = require('assert'),
|
||||||
|
fs = require('fs'),
|
||||||
|
tls = require('tls');
|
||||||
|
|
||||||
|
function filenamePEM(n) {
|
||||||
|
return require('path').join(common.fixturesDir, 'keys', n + ".pem");
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPEM(n) {
|
||||||
|
return fs.readFileSync(filenamePEM(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverOptions = {
|
||||||
|
key: loadPEM('agent2-key'),
|
||||||
|
cert: loadPEM('agent2-cert')
|
||||||
|
};
|
||||||
|
|
||||||
|
var SNIContexts = {
|
||||||
|
'a.example.com': {
|
||||||
|
key: loadPEM('agent1-key'),
|
||||||
|
cert: loadPEM('agent1-cert')
|
||||||
|
},
|
||||||
|
'asterisk.test.com': {
|
||||||
|
key: loadPEM('agent3-key'),
|
||||||
|
cert: loadPEM('agent3-cert')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var clientsOptions = [{
|
||||||
|
key: loadPEM('agent1-key'),
|
||||||
|
cert: loadPEM('agent1-cert'),
|
||||||
|
ca: [loadPEM('ca1-cert')],
|
||||||
|
servername: 'a.example.com'
|
||||||
|
},{
|
||||||
|
key: loadPEM('agent2-key'),
|
||||||
|
cert: loadPEM('agent2-cert'),
|
||||||
|
ca: [loadPEM('ca2-cert')],
|
||||||
|
servername: 'b.test.com'
|
||||||
|
},{
|
||||||
|
key: loadPEM('agent3-key'),
|
||||||
|
cert: loadPEM('agent3-cert'),
|
||||||
|
ca: [loadPEM('ca1-cert')],
|
||||||
|
servername: 'c.wrong.com'
|
||||||
|
}];
|
||||||
|
|
||||||
|
var serverPort = common.PORT;
|
||||||
|
|
||||||
|
var serverResults = [],
|
||||||
|
clientResults = [];
|
||||||
|
|
||||||
|
var server = tls.createServer(serverOptions, function(c) {
|
||||||
|
serverResults.push(c.servername);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.addContext('a.example.com', SNIContexts['a.example.com']);
|
||||||
|
server.addContext('*.test.com', SNIContexts['asterisk.test.com']);
|
||||||
|
|
||||||
|
server.listen(serverPort, startTest);
|
||||||
|
|
||||||
|
function startTest() {
|
||||||
|
function connectClient(options, callback) {
|
||||||
|
var client = tls.connect(serverPort, 'localhost', options, function() {
|
||||||
|
clientResults.push(client.authorized);
|
||||||
|
client.destroy();
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
connectClient(clientsOptions[0], function() {
|
||||||
|
connectClient(clientsOptions[1], function() {
|
||||||
|
connectClient(clientsOptions[2], function() {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on('exit', function() {
|
||||||
|
assert.deepEqual(serverResults, ['a.example.com', 'b.test.com',
|
||||||
|
'c.wrong.com']);
|
||||||
|
assert.deepEqual(clientResults, [true, true, false]);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user