src: add .code and SSL specific error properties
SSL errors have a long structured message, but lacked the standard .code property which can be used for stable comparisons. Add a `code` property, as well as the 3 string components of an SSL error: `reason`, `library`, and `function`. PR-URL: https://github.com/nodejs/node/pull/25093 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
This commit is contained in:
parent
455bcca005
commit
ca9c0c90c2
@ -183,6 +183,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
|||||||
V(fingerprint_string, "fingerprint") \
|
V(fingerprint_string, "fingerprint") \
|
||||||
V(flags_string, "flags") \
|
V(flags_string, "flags") \
|
||||||
V(fragment_string, "fragment") \
|
V(fragment_string, "fragment") \
|
||||||
|
V(function_string, "function") \
|
||||||
V(get_data_clone_error_string, "_getDataCloneError") \
|
V(get_data_clone_error_string, "_getDataCloneError") \
|
||||||
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
|
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
|
||||||
V(gid_string, "gid") \
|
V(gid_string, "gid") \
|
||||||
@ -204,6 +205,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
|||||||
V(issuercert_string, "issuerCertificate") \
|
V(issuercert_string, "issuerCertificate") \
|
||||||
V(kill_signal_string, "killSignal") \
|
V(kill_signal_string, "killSignal") \
|
||||||
V(kind_string, "kind") \
|
V(kind_string, "kind") \
|
||||||
|
V(library_string, "library") \
|
||||||
V(mac_string, "mac") \
|
V(mac_string, "mac") \
|
||||||
V(main_string, "main") \
|
V(main_string, "main") \
|
||||||
V(max_buffer_string, "maxBuffer") \
|
V(max_buffer_string, "maxBuffer") \
|
||||||
@ -313,7 +315,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
|||||||
V(write_host_object_string, "_writeHostObject") \
|
V(write_host_object_string, "_writeHostObject") \
|
||||||
V(write_queue_size_string, "writeQueueSize") \
|
V(write_queue_size_string, "writeQueueSize") \
|
||||||
V(x_forwarded_string, "x-forwarded-for") \
|
V(x_forwarded_string, "x-forwarded-for") \
|
||||||
V(zero_return_string, "ZERO_RETURN")
|
V(zero_return_string, "ZERO_RETURN") \
|
||||||
|
|
||||||
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
|
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
|
||||||
V(as_external, v8::External) \
|
V(as_external, v8::External) \
|
||||||
|
@ -41,6 +41,7 @@ using v8::Exception;
|
|||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::FunctionCallbackInfo;
|
using v8::FunctionCallbackInfo;
|
||||||
using v8::FunctionTemplate;
|
using v8::FunctionTemplate;
|
||||||
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::ReadOnly;
|
using v8::ReadOnly;
|
||||||
@ -347,15 +348,50 @@ Local<Value> TLSWrap::GetSSLError(int status, int* err, std::string* msg) {
|
|||||||
{
|
{
|
||||||
CHECK(*err == SSL_ERROR_SSL || *err == SSL_ERROR_SYSCALL);
|
CHECK(*err == SSL_ERROR_SSL || *err == SSL_ERROR_SYSCALL);
|
||||||
|
|
||||||
|
unsigned long ssl_err = ERR_peek_error(); // NOLINT(runtime/int)
|
||||||
BIO* bio = BIO_new(BIO_s_mem());
|
BIO* bio = BIO_new(BIO_s_mem());
|
||||||
ERR_print_errors(bio);
|
ERR_print_errors(bio);
|
||||||
|
|
||||||
BUF_MEM* mem;
|
BUF_MEM* mem;
|
||||||
BIO_get_mem_ptr(bio, &mem);
|
BIO_get_mem_ptr(bio, &mem);
|
||||||
|
|
||||||
|
Isolate* isolate = env()->isolate();
|
||||||
|
Local<Context> context = isolate->GetCurrentContext();
|
||||||
|
|
||||||
Local<String> message =
|
Local<String> message =
|
||||||
OneByteString(env()->isolate(), mem->data, mem->length);
|
OneByteString(isolate, mem->data, mem->length);
|
||||||
Local<Value> exception = Exception::Error(message);
|
Local<Value> exception = Exception::Error(message);
|
||||||
|
Local<Object> obj = exception->ToObject(context).ToLocalChecked();
|
||||||
|
|
||||||
|
const char* ls = ERR_lib_error_string(ssl_err);
|
||||||
|
const char* fs = ERR_func_error_string(ssl_err);
|
||||||
|
const char* rs = ERR_reason_error_string(ssl_err);
|
||||||
|
|
||||||
|
if (ls != nullptr)
|
||||||
|
obj->Set(context, env()->library_string(),
|
||||||
|
OneByteString(isolate, ls)).FromJust();
|
||||||
|
if (fs != nullptr)
|
||||||
|
obj->Set(context, env()->function_string(),
|
||||||
|
OneByteString(isolate, fs)).FromJust();
|
||||||
|
if (rs != nullptr) {
|
||||||
|
obj->Set(context, env()->reason_string(),
|
||||||
|
OneByteString(isolate, rs)).FromJust();
|
||||||
|
|
||||||
|
// SSL has no API to recover the error name from the number, so we
|
||||||
|
// transform reason strings like "this error" to "ERR_SSL_THIS_ERROR",
|
||||||
|
// which ends up being close to the original error macro name.
|
||||||
|
std::string code(rs);
|
||||||
|
|
||||||
|
for (auto& c : code) {
|
||||||
|
if (c == ' ')
|
||||||
|
c = '_';
|
||||||
|
else
|
||||||
|
c = ::toupper(c);
|
||||||
|
}
|
||||||
|
obj->Set(context, env()->code_string(),
|
||||||
|
OneByteString(isolate, ("ERR_SSL_" + code).c_str()))
|
||||||
|
.FromJust();
|
||||||
|
}
|
||||||
|
|
||||||
if (msg != nullptr)
|
if (msg != nullptr)
|
||||||
msg->assign(mem->data, mem->data + mem->length);
|
msg->assign(mem->data, mem->data + mem->length);
|
||||||
|
@ -7,6 +7,7 @@ if (!common.hasCrypto)
|
|||||||
if (!common.opensslCli)
|
if (!common.opensslCli)
|
||||||
common.skip('node compiled without OpenSSL CLI');
|
common.skip('node compiled without OpenSSL CLI');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const tls = require('tls');
|
const tls = require('tls');
|
||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
@ -29,7 +30,11 @@ const opts = {
|
|||||||
const max_iter = 20;
|
const max_iter = 20;
|
||||||
let iter = 0;
|
let iter = 0;
|
||||||
|
|
||||||
const errorHandler = common.mustCall(() => {
|
const errorHandler = common.mustCall((err) => {
|
||||||
|
assert.strictEqual(err.code, 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||||
|
assert.strictEqual(err.library, 'SSL routines');
|
||||||
|
assert.strictEqual(err.function, 'ssl3_get_record');
|
||||||
|
assert.strictEqual(err.reason, 'wrong version number');
|
||||||
errorReceived = true;
|
errorReceived = true;
|
||||||
if (canCloseServer())
|
if (canCloseServer())
|
||||||
server.close();
|
server.close();
|
||||||
@ -76,5 +81,10 @@ function sendBADTLSRecord() {
|
|||||||
socket.write(BAD_RECORD);
|
socket.write(BAD_RECORD);
|
||||||
socket.end();
|
socket.end();
|
||||||
}));
|
}));
|
||||||
client.on('error', common.mustCall());
|
client.on('error', common.mustCall((err) => {
|
||||||
|
assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION');
|
||||||
|
assert.strictEqual(err.library, 'SSL routines');
|
||||||
|
assert.strictEqual(err.function, 'ssl3_read_bytes');
|
||||||
|
assert.strictEqual(err.reason, 'tlsv1 alert protocol version');
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ const {
|
|||||||
const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION;
|
const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION;
|
||||||
|
|
||||||
function test(cmin, cmax, cprot, smin, smax, sprot, expect) {
|
function test(cmin, cmax, cprot, smin, smax, sprot, expect) {
|
||||||
|
assert(expect);
|
||||||
connect({
|
connect({
|
||||||
client: {
|
client: {
|
||||||
checkServerIdentity: (servername, cert) => { },
|
checkServerIdentity: (servername, cert) => { },
|
||||||
@ -26,23 +27,18 @@ function test(cmin, cmax, cprot, smin, smax, sprot, expect) {
|
|||||||
secureProtocol: sprot,
|
secureProtocol: sprot,
|
||||||
},
|
},
|
||||||
}, common.mustCall((err, pair, cleanup) => {
|
}, common.mustCall((err, pair, cleanup) => {
|
||||||
if (expect && expect.match(/^ERR/)) {
|
if (err) {
|
||||||
assert.strictEqual(err.code, expect);
|
assert.strictEqual(err.code, expect, err + '.code !== ' + expect);
|
||||||
return cleanup();
|
return cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expect) {
|
assert.ifError(err);
|
||||||
assert.ifError(pair.server.err);
|
assert.ifError(pair.server.err);
|
||||||
assert.ifError(pair.client.err);
|
assert.ifError(pair.client.err);
|
||||||
assert(pair.server.conn);
|
assert(pair.server.conn);
|
||||||
assert(pair.client.conn);
|
assert(pair.client.conn);
|
||||||
assert.strictEqual(pair.client.conn.getProtocol(), expect);
|
assert.strictEqual(pair.client.conn.getProtocol(), expect);
|
||||||
assert.strictEqual(pair.server.conn.getProtocol(), expect);
|
assert.strictEqual(pair.server.conn.getProtocol(), expect);
|
||||||
return cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(pair.server.err);
|
|
||||||
assert(pair.client.err);
|
|
||||||
return cleanup();
|
return cleanup();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -83,17 +79,18 @@ test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1');
|
|||||||
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
|
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
|
||||||
|
|
||||||
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
||||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', null);
|
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'ECONNRESET');
|
||||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', null);
|
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'ECONNRESET');
|
||||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', null);
|
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
|
||||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', null);
|
'ERR_SSL_VERSION_TOO_LOW');
|
||||||
|
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
||||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
|
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
|
||||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', null);
|
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'ECONNRESET');
|
||||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
|
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', null);
|
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEFAULT_MIN_VERSION === 'TLSv1') {
|
if (DEFAULT_MIN_VERSION === 'TLSv1') {
|
||||||
@ -111,18 +108,18 @@ test(U, U, 'TLSv1_method', U, U, 'TLSv1_method', 'TLSv1');
|
|||||||
|
|
||||||
// The default default.
|
// The default default.
|
||||||
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
||||||
test(U, U, 'TLSv1_1_method', U, U, U, null);
|
test(U, U, 'TLSv1_1_method', U, U, U, 'ECONNRESET');
|
||||||
test(U, U, 'TLSv1_method', U, U, U, null);
|
test(U, U, 'TLSv1_method', U, U, U, 'ECONNRESET');
|
||||||
test(U, U, U, U, U, 'TLSv1_1_method', null);
|
test(U, U, U, U, U, 'TLSv1_1_method', 'ERR_SSL_VERSION_TOO_LOW');
|
||||||
test(U, U, U, U, U, 'TLSv1_method', null);
|
test(U, U, U, U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default with --tls-v1.1.
|
// The default with --tls-v1.1.
|
||||||
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
||||||
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
|
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
|
||||||
test(U, U, 'TLSv1_method', U, U, U, null);
|
test(U, U, 'TLSv1_method', U, U, U, 'ECONNRESET');
|
||||||
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
|
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||||
test(U, U, U, U, U, 'TLSv1_method', null);
|
test(U, U, U, U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default with --tls-v1.0.
|
// The default with --tls-v1.0.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user