int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) {
- HandleScope scope(node_isolate);
-
TLSCallbacks* p = static_cast<TLSCallbacks*>(arg);
Environment* env = p->env();
const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
- if (servername != NULL) {
- // Call the SNI callback and use its return value as context
- Local<Object> object = p->object();
- Local<Value> ctx = object->Get(env->sni_context_string());
+ if (servername == NULL)
+ return SSL_TLSEXT_ERR_OK;
- if (!ctx->IsObject())
- return SSL_TLSEXT_ERR_NOACK;
+ HandleScope scope(env->isolate());
+ // Call the SNI callback and use its return value as context
+ Local<Object> object = p->object();
+ Local<Value> ctx = object->Get(env->sni_context_string());
- p->sni_context_.Dispose();
- p->sni_context_.Reset(node_isolate, ctx);
+ // Not an object, probably undefined or null
+ if (!ctx->IsObject())
+ return SSL_TLSEXT_ERR_NOACK;
- SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
- InitNPN(sc, p);
- SSL_set_SSL_CTX(s, sc->ctx_);
+ Local<FunctionTemplate> cons = env->secure_context_constructor_template();
+ if (!cons->HasInstance(ctx)) {
+ // Failure: incorrect SNI context object
+ Local<Value> err = Exception::TypeError(env->sni_context_err_string());
+ p->MakeCallback(env->onerror_string(), 1, &err);
+ return SSL_TLSEXT_ERR_NOACK;
}
+ p->sni_context_.Dispose();
+ p->sni_context_.Reset(node_isolate, ctx);
+
+ SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
+ InitNPN(sc, p);
+ SSL_set_SSL_CTX(s, sc->ctx_);
return SSL_TLSEXT_ERR_OK;
}
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
// Just to test asynchronous callback
setTimeout(function() {
- if (credentials)
- callback(null, crypto.createCredentials(credentials).context);
- else
+ if (credentials) {
+ if (credentials.emptyRegression)
+ callback(null, {});
+ else
+ callback(null, crypto.createCredentials(credentials).context);
+ } else {
callback(null, null);
+ }
}, 100);
}
};
'b.example.com': {
key: loadPEM('agent3-key'),
cert: loadPEM('agent3-cert')
+ },
+ 'c.another.com': {
+ emptyRegression: true
}
};
ca: [loadPEM('ca1-cert')],
servername: 'c.wrong.com',
rejectUnauthorized: false
+}, {
+ port: serverPort,
+ key: loadPEM('agent3-key'),
+ cert: loadPEM('agent3-cert'),
+ ca: [loadPEM('ca1-cert')],
+ servername: 'c.another.com',
+ rejectUnauthorized: false
}];
var serverResults = [],
- clientResults = [];
+ clientResults = [],
+ serverErrors = [],
+ clientErrors = [],
+ serverError,
+ clientError;
var server = tls.createServer(serverOptions, function(c) {
serverResults.push(c.servername);
});
+server.on('clientError', function(err) {
+ serverResults.push(null);
+ serverError = err.message;
+});
+
server.listen(serverPort, startTest);
function startTest() {
- function connectClient(options, callback) {
+ function connectClient(i, callback) {
+ var options = clientsOptions[i];
+ clientError = null;
+ serverError = null;
+
var client = tls.connect(options, function() {
clientResults.push(
/Hostname\/IP doesn't/.test(client.authorizationError || ''));
client.destroy();
- callback();
+ next();
});
- };
- connectClient(clientsOptions[0], function() {
- connectClient(clientsOptions[1], function() {
- connectClient(clientsOptions[2], function() {
- server.close();
- });
+ client.on('error', function(err) {
+ clientResults.push(false);
+ clientError = err.message;
+ next();
});
+
+ function next() {
+ clientErrors.push(clientError);
+ serverErrors.push(serverError);
+
+ if (i === clientsOptions.length - 1)
+ callback();
+ else
+ connectClient(i + 1, callback);
+ }
+ };
+
+ connectClient(0, function() {
+ server.close();
});
}
process.on('exit', function() {
assert.deepEqual(serverResults, ['a.example.com', 'b.example.com',
- 'c.wrong.com']);
- assert.deepEqual(clientResults, [true, true, false]);
+ 'c.wrong.com', null]);
+ assert.deepEqual(clientResults, [true, true, false, false]);
+ assert.deepEqual(clientErrors, [null, null, null, "socket hang up"]);
+ assert.deepEqual(serverErrors, [null, null, null, "Invalid SNI context"]);
});