mirror of https://github.com/lukechilds/node.git
9 changed files with 944 additions and 23 deletions
@ -0,0 +1 @@ |
|||
node_modules/ |
@ -0,0 +1,15 @@ |
|||
{ |
|||
"name": "ssl-options-tests", |
|||
"version": "1.0.0", |
|||
"description": "", |
|||
"main": "test.js", |
|||
"scripts": { |
|||
"test": "node test.js" |
|||
}, |
|||
"author": "", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"async": "^0.9.0", |
|||
"debug": "^2.1.0" |
|||
} |
|||
} |
@ -0,0 +1,729 @@ |
|||
var tls = require('tls'); |
|||
var fs = require('fs'); |
|||
var path = require('path'); |
|||
var fork = require('child_process').fork; |
|||
var assert = require('assert'); |
|||
var constants = require('constants'); |
|||
var os = require('os'); |
|||
|
|||
var async = require('async'); |
|||
var debug = require('debug')('test-node-ssl'); |
|||
|
|||
var common = require('../../common'); |
|||
|
|||
var SSL2_COMPATIBLE_CIPHERS = 'RC4-MD5'; |
|||
|
|||
var CMD_LINE_OPTIONS = [ null, "--enable-ssl2", "--enable-ssl3" ]; |
|||
|
|||
var SERVER_SSL_PROTOCOLS = [ |
|||
null, |
|||
'SSLv2_method', 'SSLv2_server_method', |
|||
'SSLv3_method', 'SSLv3_server_method', |
|||
'TLSv1_method', 'TLSv1_server_method', |
|||
'SSLv23_method','SSLv23_server_method' |
|||
]; |
|||
|
|||
var CLIENT_SSL_PROTOCOLS = [ |
|||
null, |
|||
'SSLv2_method', 'SSLv2_client_method', |
|||
'SSLv3_method', 'SSLv3_client_method', |
|||
'TLSv1_method', 'TLSv1_client_method', |
|||
'SSLv23_method','SSLv23_client_method' |
|||
]; |
|||
|
|||
var SECURE_OPTIONS = [ |
|||
null, |
|||
0, |
|||
constants.SSL_OP_NO_SSLv2, |
|||
constants.SSL_OP_NO_SSLv3, |
|||
constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 |
|||
]; |
|||
|
|||
function xtend(source) { |
|||
var clone = {}; |
|||
|
|||
for (var property in source) { |
|||
if (source.hasOwnProperty(property)) { |
|||
clone[property] = source[property]; |
|||
} |
|||
} |
|||
|
|||
return clone; |
|||
} |
|||
|
|||
function isAutoNegotiationProtocol(sslProtocol) { |
|||
assert(sslProtocol === null || typeof sslProtocol === 'string'); |
|||
|
|||
return sslProtocol == null || |
|||
sslProtocol === 'SSLv23_method' || |
|||
sslProtocol === 'SSLv23_client_method' || |
|||
sslProtocol === 'SSLv23_server_method'; |
|||
} |
|||
|
|||
function isSameSslProtocolVersion(serverSecureProtocol, clientSecureProtocol) { |
|||
assert(serverSecureProtocol === null || typeof serverSecureProtocol === 'string'); |
|||
assert(clientSecureProtocol === null || typeof clientSecureProtocol === 'string'); |
|||
|
|||
if (serverSecureProtocol === clientSecureProtocol) { |
|||
return true; |
|||
} |
|||
|
|||
var serverProtocolPrefix = ''; |
|||
if (serverSecureProtocol) |
|||
serverProtocolPrefix = serverSecureProtocol.split('_')[0]; |
|||
|
|||
var clientProtocolPrefix = ''; |
|||
if (clientSecureProtocol) |
|||
clientProtocolPrefix = clientSecureProtocol.split('_')[0]; |
|||
|
|||
if (serverProtocolPrefix === clientProtocolPrefix) { |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
function secureProtocolsCompatible(serverSecureProtocol, clientSecureProtocol) { |
|||
if (isAutoNegotiationProtocol(serverSecureProtocol) || |
|||
isAutoNegotiationProtocol(clientSecureProtocol)) { |
|||
return true; |
|||
} |
|||
|
|||
if (isSameSslProtocolVersion(serverSecureProtocol, |
|||
clientSecureProtocol)) { |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
function isSsl3Protocol(secureProtocol) { |
|||
assert(secureProtocol === null || typeof secureProtocol === 'string'); |
|||
|
|||
return secureProtocol === 'SSLv3_method' || |
|||
secureProtocol === 'SSLv3_client_method' || |
|||
secureProtocol === 'SSLv3_server_method'; |
|||
} |
|||
|
|||
function isSsl2Protocol(secureProtocol) { |
|||
assert(secureProtocol === null || typeof secureProtocol === 'string'); |
|||
|
|||
return secureProtocol === 'SSLv2_method' || |
|||
secureProtocol === 'SSLv2_client_method' || |
|||
secureProtocol === 'SSLv2_server_method'; |
|||
} |
|||
|
|||
function secureProtocolCompatibleWithSecureOptions(secureProtocol, secureOptions, cmdLineOption) { |
|||
if (secureOptions == null) { |
|||
if (isSsl2Protocol(secureProtocol) && |
|||
(!cmdLineOption || cmdLineOption.indexOf('--enable-ssl2') === -1)) { |
|||
return false; |
|||
} |
|||
|
|||
if (isSsl3Protocol(secureProtocol) && |
|||
(!cmdLineOption || cmdLineOption.indexOf('--enable-ssl3') === -1)) { |
|||
return false; |
|||
} |
|||
} else { |
|||
if (secureOptions & constants.SSL_OP_NO_SSLv2 && isSsl2Protocol(secureProtocol)) { |
|||
return false; |
|||
} |
|||
|
|||
if (secureOptions & constants.SSL_OP_NO_SSLv3 && isSsl3Protocol(secureProtocol)) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
function testSetupsCompatible(serverSetup, clientSetup) { |
|||
debug('Determing test result for:'); |
|||
debug(serverSetup); |
|||
debug(clientSetup); |
|||
|
|||
/* |
|||
* If the protocols specified by the client and server are |
|||
* not compatible (e.g SSLv2 vs SSLv3), then the test should fail. |
|||
*/ |
|||
if (!secureProtocolsCompatible(serverSetup.secureProtocol, |
|||
clientSetup.secureProtocol)) { |
|||
debug('secureProtocols not compatible! server secureProtocol: ' + |
|||
serverSetup.secureProtocol + ', client secureProtocol: ' + |
|||
clientSetup.secureProtocol); |
|||
return false; |
|||
} |
|||
|
|||
/* |
|||
* If the client's options are not compatible with the server's protocol, |
|||
* then the test should fail. Same if server's options are not compatible |
|||
* with the client's protocol. |
|||
*/ |
|||
if (!secureProtocolCompatibleWithSecureOptions(serverSetup.secureProtocol, |
|||
clientSetup.secureOptions, |
|||
clientSetup.cmdLine) || |
|||
!secureProtocolCompatibleWithSecureOptions(clientSetup.secureProtocol, |
|||
serverSetup.secureOptions, |
|||
serverSetup.cmdLine)) { |
|||
debug('Secure protocol not compatible with secure options!'); |
|||
return false; |
|||
} |
|||
|
|||
if (isSsl2Protocol(serverSetup.secureProtocol) || |
|||
isSsl2Protocol(clientSetup.secureProtocol)) { |
|||
|
|||
/* |
|||
* It seems that in order to be able to use SSLv2, at least the server |
|||
* *needs* to advertise at least one cipher compatible with it. |
|||
*/ |
|||
if (serverSetup.ciphers !== SSL2_COMPATIBLE_CIPHERS) { |
|||
return false; |
|||
} |
|||
|
|||
/* |
|||
* If only either one of the client or server specify SSLv2 as their |
|||
* protocol, then *both* of them *need* to advertise at least one cipher |
|||
* that is compatible with SSLv2. |
|||
*/ |
|||
if ((!isSsl2Protocol(serverSetup.secureProtocol) || !isSsl2Protocol(clientSetup.secureProtocol)) && |
|||
(clientSetup.ciphers !== SSL2_COMPATIBLE_CIPHERS || serverSetup.ciphers !== SSL2_COMPATIBLE_CIPHERS)) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
function sslSetupMakesSense(cmdLineOption, secureProtocol, secureOption) { |
|||
if (isSsl2Protocol(secureProtocol)) { |
|||
if (secureOption & constants.SSL_OP_NO_SSLv2 || |
|||
(secureOption == null && (!cmdLineOption || cmdLineOption.indexOf('--enable-ssl2') === -1))) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
if (isSsl3Protocol(secureProtocol)) { |
|||
if (secureOption & constants.SSL_OP_NO_SSLv3 || |
|||
(secureOption == null && (!cmdLineOption || cmdLineOption.indexOf('--enable-ssl3') === -1))) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
function createTestsSetups() { |
|||
|
|||
var serversSetup = []; |
|||
var clientsSetup = []; |
|||
|
|||
CMD_LINE_OPTIONS.forEach(function (cmdLineOption) { |
|||
SERVER_SSL_PROTOCOLS.forEach(function (serverSecureProtocol) { |
|||
SECURE_OPTIONS.forEach(function (secureOption) { |
|||
if (sslSetupMakesSense(cmdLineOption, |
|||
serverSecureProtocol, |
|||
secureOption)) { |
|||
var serverSetup = { |
|||
cmdLine: cmdLineOption, |
|||
secureProtocol: serverSecureProtocol, |
|||
secureOptions: secureOption |
|||
}; |
|||
|
|||
serversSetup.push(serverSetup); |
|||
|
|||
if (isSsl2Protocol(serverSecureProtocol)) { |
|||
var setupWithSsl2Ciphers = xtend(serverSetup); |
|||
setupWithSsl2Ciphers.ciphers = SSL2_COMPATIBLE_CIPHERS; |
|||
serversSetup.push(setupWithSsl2Ciphers); |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
CLIENT_SSL_PROTOCOLS.forEach(function (clientSecureProtocol) { |
|||
SECURE_OPTIONS.forEach(function (secureOption) { |
|||
if (sslSetupMakesSense(cmdLineOption, |
|||
clientSecureProtocol, |
|||
secureOption)) { |
|||
var clientSetup = { |
|||
cmdLine: cmdLineOption, |
|||
secureProtocol: clientSecureProtocol, |
|||
secureOptions: secureOption |
|||
}; |
|||
|
|||
clientsSetup.push(clientSetup); |
|||
|
|||
if (isSsl2Protocol(clientSecureProtocol)) { |
|||
var setupWithSsl2Ciphers = xtend(clientSetup); |
|||
setupWithSsl2Ciphers.ciphers = SSL2_COMPATIBLE_CIPHERS; |
|||
clientsSetup.push(setupWithSsl2Ciphers); |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
var testSetups = []; |
|||
var testId = 0; |
|||
serversSetup.forEach(function (serverSetup) { |
|||
clientsSetup.forEach(function (clientSetup) { |
|||
var testSetup = { |
|||
server: serverSetup, |
|||
client: clientSetup, |
|||
ID: testId++ |
|||
}; |
|||
|
|||
var successExpected = false; |
|||
if (testSetupsCompatible(serverSetup, clientSetup)) { |
|||
successExpected = true; |
|||
} |
|||
testSetup.successExpected = successExpected; |
|||
|
|||
testSetups.push(testSetup); |
|||
}); |
|||
}); |
|||
|
|||
return testSetups; |
|||
} |
|||
|
|||
function runServer(port, secureProtocol, secureOptions, ciphers) { |
|||
debug('Running server!'); |
|||
debug('port: ' + port); |
|||
debug('secureProtocol: ' + secureProtocol); |
|||
debug('secureOptions: ' + secureOptions); |
|||
debug('ciphers: ' + ciphers); |
|||
|
|||
var keyPath = path.join(common.fixturesDir, 'agent.key'); |
|||
var certPath = path.join(common.fixturesDir, 'agent.crt'); |
|||
|
|||
var key = fs.readFileSync(keyPath).toString(); |
|||
var cert = fs.readFileSync(certPath).toString(); |
|||
|
|||
var server = new tls.Server({ key: key, |
|||
cert: cert, |
|||
ca: [], |
|||
ciphers: ciphers, |
|||
secureProtocol: secureProtocol, |
|||
secureOptions: secureOptions |
|||
}); |
|||
|
|||
server.listen(port, function() { |
|||
process.on('message', function onChildMsg(msg) { |
|||
if (msg === 'close') { |
|||
server.close(); |
|||
process.exit(0); |
|||
} |
|||
}); |
|||
|
|||
process.send('server_listening'); |
|||
}); |
|||
|
|||
server.on('error', function onServerError(err) { |
|||
debug('Server error: ' + err); |
|||
process.exit(1); |
|||
}); |
|||
|
|||
server.on('clientError', function onClientError(err) { |
|||
debug('Client error on server: ' + err); |
|||
process.exit(1); |
|||
}); |
|||
} |
|||
|
|||
function runClient(port, secureProtocol, secureOptions, ciphers) { |
|||
debug('Running client!'); |
|||
debug('port: ' + port); |
|||
debug('secureProtocol: ' + secureProtocol); |
|||
debug('secureOptions: ' + secureOptions); |
|||
debug('ciphers: ' + ciphers); |
|||
|
|||
var con = tls.connect(port, |
|||
{ |
|||
rejectUnauthorized: false, |
|||
secureProtocol: secureProtocol, |
|||
secureOptions: secureOptions |
|||
}, |
|||
function() { |
|||
|
|||
// TODO jgilli: test that sslProtocolUsed is at least as "secure" as
|
|||
// "secureProtocol"
|
|||
/* |
|||
* var sslProtocolUsed = con.getVersion(); |
|||
* debug('Protocol used: ' + sslProtocolUsed); |
|||
*/ |
|||
|
|||
process.send('client_done'); |
|||
}); |
|||
|
|||
con.on('error', function(err) { |
|||
debug('Client could not connect:' + err); |
|||
process.exit(1); |
|||
}); |
|||
} |
|||
|
|||
function stringToSecureOptions(secureOptionsString) { |
|||
assert(typeof secureOptionsString === 'string'); |
|||
|
|||
var secureOptions; |
|||
|
|||
var optionStrings = secureOptionsString.split('|'); |
|||
optionStrings.forEach(function (option) { |
|||
if (option === 'SSL_OP_NO_SSLv2') { |
|||
secureOptions |= constants.SSL_OP_NO_SSLv2; |
|||
} |
|||
|
|||
if (option === 'SSL_OP_NO_SSLv3') { |
|||
secureOptions |= constants.SSL_OP_NO_SSLv3; |
|||
} |
|||
|
|||
if (option === '0') { |
|||
secureOptions = 0; |
|||
} |
|||
}); |
|||
|
|||
return secureOptions; |
|||
} |
|||
|
|||
function processTestCmdLineOptions(argv){ |
|||
var options = {}; |
|||
|
|||
argv.forEach(function (arg) { |
|||
var key; |
|||
var value; |
|||
|
|||
var keyValue = arg.split(':'); |
|||
var key = keyValue[0]; |
|||
|
|||
if (keyValue.length == 2 && keyValue[1].length > 0) { |
|||
value = keyValue[1]; |
|||
|
|||
if (key === 'secureOptions') { |
|||
value = stringToSecureOptions(value); |
|||
} |
|||
|
|||
if (key === 'port') { |
|||
value = +value; |
|||
} |
|||
} |
|||
|
|||
options[key] = value; |
|||
}); |
|||
|
|||
return options; |
|||
} |
|||
|
|||
function checkTestExitCode(testSetup, serverExitCode, clientExitCode) { |
|||
if (testSetup.successExpected) { |
|||
if (serverExitCode === 0 && clientExitCode === 0) { |
|||
debug('Test succeeded as expected!'); |
|||
return true; |
|||
} |
|||
} else { |
|||
if (serverExitCode !== 0 || clientExitCode !== 0) { |
|||
debug('Test failed as expected!'); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
function secureOptionsToString(secureOptions) { |
|||
var secureOptsString = ''; |
|||
|
|||
if (secureOptions & constants.SSL_OP_NO_SSLv2) { |
|||
secureOptsString += 'SSL_OP_NO_SSLv2'; |
|||
} |
|||
|
|||
if (secureOptions & constants.SSL_OP_NO_SSLv3) { |
|||
secureOptsString += '|SSL_OP_NO_SSLv3'; |
|||
} |
|||
|
|||
if (secureOptions === 0) { |
|||
secureOptsString = '0'; |
|||
} |
|||
|
|||
return secureOptsString; |
|||
} |
|||
|
|||
function forkTestProcess(processType, testSetup, port) { |
|||
var argv = [ processType ]; |
|||
|
|||
if (testSetup.secureProtocol) { |
|||
argv.push('secureProtocol:' + testSetup.secureProtocol); |
|||
} else { |
|||
argv.push('secureProtocol:'); |
|||
} |
|||
|
|||
argv.push('secureOptions:' + secureOptionsToString(testSetup.secureOptions)); |
|||
|
|||
if (testSetup.ciphers) { |
|||
argv.push('ciphers:' + testSetup.ciphers); |
|||
} else { |
|||
argv.push('ciphers:'); |
|||
} |
|||
|
|||
argv.push('port:' + port); |
|||
|
|||
var forkOptions; |
|||
if (testSetup.cmdLine) { |
|||
forkOptions = { |
|||
execArgv: [ testSetup.cmdLine ] |
|||
} |
|||
} |
|||
|
|||
return fork(process.argv[1], |
|||
argv, |
|||
forkOptions); |
|||
} |
|||
|
|||
function runTest(testSetup, testDone) { |
|||
var clientSetup = testSetup.client; |
|||
var serverSetup = testSetup.server; |
|||
|
|||
assert(clientSetup); |
|||
assert(serverSetup); |
|||
|
|||
debug('Starting new test on port: ' + testSetup.port); |
|||
|
|||
debug('client setup:'); |
|||
debug(clientSetup); |
|||
|
|||
debug('server setup:'); |
|||
debug(serverSetup); |
|||
|
|||
debug('Success expected:' + testSetup.successExpected); |
|||
|
|||
var serverExitCode; |
|||
|
|||
var clientStarted = false; |
|||
var clientExitCode; |
|||
|
|||
var serverChild = forkTestProcess('server', serverSetup, testSetup.port); |
|||
assert(serverChild); |
|||
|
|||
serverChild.on('message', function onServerMsg(msg) { |
|||
if (msg === 'server_listening') { |
|||
debug('Starting client!'); |
|||
clientStarted = true; |
|||
|
|||
var clientChild = forkTestProcess('client', clientSetup, testSetup.port); |
|||
assert(clientChild); |
|||
|
|||
clientChild.on('exit', function onClientExited(exitCode) { |
|||
debug('Client exited with code:' + exitCode); |
|||
|
|||
clientExitCode = exitCode; |
|||
if (serverExitCode != null) { |
|||
var err; |
|||
if (!checkTestExitCode(testSetup, serverExitCode, clientExitCode)) |
|||
err = new Error("Test failed!"); |
|||
|
|||
return testDone(err); |
|||
} else { |
|||
if (serverChild.connected) { |
|||
serverChild.send('close'); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
clientChild.on('message', function onClientMsg(msg) { |
|||
if (msg === 'client_done' && serverChild.connected) { |
|||
serverChild.send('close'); |
|||
} |
|||
}) |
|||
} |
|||
}); |
|||
|
|||
serverChild.on('exit', function onServerExited(exitCode) { |
|||
debug('Server exited with code:' + exitCode); |
|||
|
|||
serverExitCode = exitCode; |
|||
if (clientExitCode != null || !clientStarted) { |
|||
var err; |
|||
if (!checkTestExitCode(testSetup, serverExitCode, clientExitCode)) |
|||
err = new Error("Test failed!"); |
|||
|
|||
return testDone(err); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function usage() { |
|||
console.log('Usage: test-node-ssl [-j N] [--list-tests] [-s startIndex] ' + |
|||
'[-e endIndex] [-o outputFile]'); |
|||
process.exit(1); |
|||
} |
|||
|
|||
function processDriverCmdLineOptions(argv) { |
|||
var options = { |
|||
parallelTests: 1 |
|||
}; |
|||
|
|||
for (var i = 1; i < argv.length; ++i) { |
|||
if (argv[i] === '-j') { |
|||
|
|||
var nbParallelTests = +argv[i + 1]; |
|||
if (!nbParallelTests) { |
|||
usage(); |
|||
} else { |
|||
options.parallelTests = argv[++i]; |
|||
} |
|||
} |
|||
|
|||
if (argv[i] === '-s') { |
|||
var start = +argv[i + 1]; |
|||
if (!start) { |
|||
usage(); |
|||
} else { |
|||
options.start = argv[++i]; |
|||
} |
|||
} |
|||
|
|||
if (argv[i] === '-e') { |
|||
var end = +argv[i + 1]; |
|||
if (!end) { |
|||
usage(); |
|||
} else { |
|||
options.end = argv[++i]; |
|||
} |
|||
} |
|||
|
|||
if (argv[i] === '--list-tests') { |
|||
options.listTests = true; |
|||
} |
|||
|
|||
if (argv[i] === '-o') { |
|||
var outputFile = argv[i + 1]; |
|||
if (!outputFile) { |
|||
usage(); |
|||
} else { |
|||
options.outputFile = argv[++i]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return options; |
|||
} |
|||
|
|||
function outputTestResult(test, err, output) { |
|||
output.write(os.EOL); |
|||
output.write('Test:' + os.EOL); |
|||
output.write(JSON.stringify(test, null, " ")); |
|||
output.write(os.EOL); |
|||
output.write('Result:'); |
|||
output.write(err ? 'failure' : 'success'); |
|||
output.write(os.EOL); |
|||
} |
|||
|
|||
var agentType = process.argv[2]; |
|||
if (agentType === 'client' || agentType === 'server') { |
|||
var options = processTestCmdLineOptions(process.argv); |
|||
debug('secureProtocol: ' + options.secureProtocol); |
|||
debug('secureOptions: ' + options.secureOptions); |
|||
debug('ciphers:' + options.ciphers); |
|||
debug('port:' + options.port); |
|||
|
|||
if (agentType === 'client') { |
|||
runClient(options.port, |
|||
options.secureProtocol, |
|||
options.secureOptions, |
|||
options.ciphers); |
|||
} else if (agentType === 'server') { |
|||
runServer(options.port, |
|||
options.secureProtocol, |
|||
options.secureOptions, |
|||
options.ciphers); |
|||
} |
|||
} else { |
|||
var driverOptions = processDriverCmdLineOptions(process.argv); |
|||
debug('Tests driver options:'); |
|||
debug(driverOptions); |
|||
/* |
|||
* This is the tests driver process. |
|||
* |
|||
* It forks itself twice for each test. Each of the two forked processees are |
|||
* respectfully used as an SSL client and an SSL server. The client and |
|||
* server setup their SSL connection as generated by the "createTestsSetups" |
|||
* function. Once both processes have exited, the tests driver process |
|||
* compare both client and server exit codes with the expected test result |
|||
* of the test setup. If they match, the test is successful, otherwise it |
|||
* failed. |
|||
*/ |
|||
|
|||
var testSetups = createTestsSetups(); |
|||
|
|||
if (driverOptions.listTests) { |
|||
console.log(testSetups); |
|||
process.exit(0); |
|||
} |
|||
|
|||
var testOutput = process.stdout; |
|||
if (driverOptions.outputFile) { |
|||
testOutput = fs.createWriteStream(driverOptions.outputFile) |
|||
.on('error', function onError(err) { |
|||
console.error(err); |
|||
process.exit(1); |
|||
}); |
|||
} |
|||
|
|||
debug('Tests setups:'); |
|||
debug('Number of tests: ' + testSetups.length); |
|||
debug(JSON.stringify(testSetups, null, " ")); |
|||
debug(); |
|||
|
|||
var nbTestsStarted = 0; |
|||
|
|||
function runTests(tests, callback) { |
|||
var nbTests = tests.length; |
|||
if (nbTests === 0) { |
|||
return callback(); |
|||
} |
|||
var error; |
|||
var nbTestsDone = 0; |
|||
|
|||
debug('Starting new batch of tests...'); |
|||
|
|||
var port = common.PORT; |
|||
async.each(tests, function (test, testDone) { |
|||
test.port = port++; |
|||
|
|||
++nbTestsStarted; |
|||
debug('Starting test nb: ' + nbTestsStarted); |
|||
|
|||
runTest(test, function onTestDone(err) { |
|||
++nbTestsDone; |
|||
if (err && error === undefined) { |
|||
error = new Error('Test with ID ' + test.ID + ' failed: ' + err); |
|||
} |
|||
|
|||
outputTestResult(test, err, testOutput); |
|||
|
|||
if (nbTestsDone === nbTests) |
|||
return testDone(error); |
|||
return testDone(); |
|||
}); |
|||
|
|||
}, function testsDone(err, results) { |
|||
if (err) { |
|||
assert(false, |
|||
"At least one test in the most recent batch failed: " + err); |
|||
} |
|||
|
|||
return callback(err); |
|||
}); |
|||
} |
|||
|
|||
function runAllTests(allTests, allTestsDone) { |
|||
if (allTests.length === 0) { |
|||
return allTestsDone(); |
|||
} |
|||
|
|||
return runTests(allTests.splice(0, driverOptions.parallelTests), |
|||
runAllTests.bind(global, allTests, allTestsDone)); |
|||
} |
|||
|
|||
runAllTests(testSetups.slice(driverOptions.start, driverOptions.end), |
|||
function allDone(err) { |
|||
console.log('All tests done!'); |
|||
}); |
|||
} |
@ -0,0 +1,131 @@ |
|||
// 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.
|
|||
|
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var tls = require('tls'); |
|||
var fs = require('fs'); |
|||
var nconns = 0; |
|||
var SSL_Method = 'SSLv23_method'; |
|||
var localhost = '127.0.0.1'; |
|||
var opCipher = process.binding('constants').SSL_OP_CIPHER_SERVER_PREFERENCE; |
|||
|
|||
/* |
|||
* This test is to make sure we are preserving secureOptions that are passed |
|||
* to the server. |
|||
* |
|||
* Also that if honorCipherOrder is passed we are preserving that in the |
|||
* options. |
|||
* |
|||
* And that if we are passing in secureOptions no new options (aside from the |
|||
* honorCipherOrder case) are added to the secureOptions |
|||
*/ |
|||
|
|||
|
|||
process.on('exit', function() { |
|||
assert.equal(nconns, 6); |
|||
}); |
|||
|
|||
function test(honorCipherOrder, clientCipher, expectedCipher, secureOptions, cb) { |
|||
var soptions = { |
|||
secureProtocol: SSL_Method, |
|||
key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), |
|||
cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'), |
|||
ciphers: 'AES256-SHA:RC4-SHA:DES-CBC-SHA', |
|||
secureOptions: secureOptions, |
|||
honorCipherOrder: !!honorCipherOrder |
|||
}; |
|||
|
|||
var server = tls.createServer(soptions, function(cleartextStream) { |
|||
nconns++; |
|||
}); |
|||
|
|||
if (!!honorCipherOrder) { |
|||
assert.strictEqual(server.secureOptions & opCipher, opCipher, 'we should preserve cipher preference'); |
|||
} |
|||
|
|||
if (secureOptions) { |
|||
var expectedSecureOpts = secureOptions; |
|||
if (!!honorCipherOrder) expectedSecureOpts |= opCipher; |
|||
|
|||
assert.strictEqual(server.secureOptions & expectedSecureOpts, |
|||
expectedSecureOpts, 'we should preserve secureOptions'); |
|||
assert.strictEqual(server.secureOptions & ~expectedSecureOpts, |
|||
0, |
|||
'we should not add extra options'); |
|||
} |
|||
|
|||
server.listen(common.PORT, localhost, function() { |
|||
var coptions = { |
|||
rejectUnauthorized: false, |
|||
secureProtocol: SSL_Method |
|||
}; |
|||
if (clientCipher) { |
|||
coptions.ciphers = clientCipher; |
|||
} |
|||
var client = tls.connect(common.PORT, localhost, coptions, function() { |
|||
var cipher = client.getCipher(); |
|||
client.end(); |
|||
server.close(); |
|||
assert.equal(cipher.name, expectedCipher); |
|||
if (cb) cb(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
test1(); |
|||
|
|||
function test1() { |
|||
// Client has the preference of cipher suites by default
|
|||
test(false, 'DES-CBC-SHA:RC4-SHA:AES256-SHA','DES-CBC-SHA', 0, test2); |
|||
} |
|||
|
|||
function test2() { |
|||
// Server has the preference of cipher suites where AES256-SHA is in
|
|||
// the first.
|
|||
test(true, 'DES-CBC-SHA:RC4-SHA:AES256-SHA', 'AES256-SHA', 0, test3); |
|||
} |
|||
|
|||
function test3() { |
|||
// Server has the preference of cipher suites. RC4-SHA is given
|
|||
// higher priority over DES-CBC-SHA among client cipher suites.
|
|||
test(true, 'DES-CBC-SHA:RC4-SHA', 'RC4-SHA', 0, test4); |
|||
} |
|||
|
|||
function test4() { |
|||
// As client has only one cipher, server has no choice in regardless
|
|||
// of honorCipherOrder.
|
|||
test(true, 'DES-CBC-SHA', 'DES-CBC-SHA', 0, test5); |
|||
} |
|||
|
|||
function test5() { |
|||
test(false, |
|||
'DES-CBC-SHA', |
|||
'DES-CBC-SHA', |
|||
process.binding('constants').SSL_OP_SINGLE_DH_USE, test6); |
|||
} |
|||
|
|||
function test6() { |
|||
test(true, |
|||
'DES-CBC-SHA', |
|||
'DES-CBC-SHA', |
|||
process.binding('constants').SSL_OP_SINGLE_DH_USE); |
|||
} |
Loading…
Reference in new issue