Skip to content

Commit ca9c0c9

Browse files
committed
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: #25093 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]>
1 parent 455bcca commit ca9c0c9

File tree

4 files changed

+75
-30
lines changed

4 files changed

+75
-30
lines changed

src/env.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
183183
V(fingerprint_string, "fingerprint") \
184184
V(flags_string, "flags") \
185185
V(fragment_string, "fragment") \
186+
V(function_string, "function") \
186187
V(get_data_clone_error_string, "_getDataCloneError") \
187188
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
188189
V(gid_string, "gid") \
@@ -204,6 +205,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
204205
V(issuercert_string, "issuerCertificate") \
205206
V(kill_signal_string, "killSignal") \
206207
V(kind_string, "kind") \
208+
V(library_string, "library") \
207209
V(mac_string, "mac") \
208210
V(main_string, "main") \
209211
V(max_buffer_string, "maxBuffer") \
@@ -313,7 +315,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
313315
V(write_host_object_string, "_writeHostObject") \
314316
V(write_queue_size_string, "writeQueueSize") \
315317
V(x_forwarded_string, "x-forwarded-for") \
316-
V(zero_return_string, "ZERO_RETURN")
318+
V(zero_return_string, "ZERO_RETURN") \
317319

318320
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
319321
V(as_external, v8::External) \

src/tls_wrap.cc

+37-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ using v8::Exception;
4141
using v8::Function;
4242
using v8::FunctionCallbackInfo;
4343
using v8::FunctionTemplate;
44+
using v8::Isolate;
4445
using v8::Local;
4546
using v8::Object;
4647
using v8::ReadOnly;
@@ -347,15 +348,50 @@ Local<Value> TLSWrap::GetSSLError(int status, int* err, std::string* msg) {
347348
{
348349
CHECK(*err == SSL_ERROR_SSL || *err == SSL_ERROR_SYSCALL);
349350

351+
unsigned long ssl_err = ERR_peek_error(); // NOLINT(runtime/int)
350352
BIO* bio = BIO_new(BIO_s_mem());
351353
ERR_print_errors(bio);
352354

353355
BUF_MEM* mem;
354356
BIO_get_mem_ptr(bio, &mem);
355357

358+
Isolate* isolate = env()->isolate();
359+
Local<Context> context = isolate->GetCurrentContext();
360+
356361
Local<String> message =
357-
OneByteString(env()->isolate(), mem->data, mem->length);
362+
OneByteString(isolate, mem->data, mem->length);
358363
Local<Value> exception = Exception::Error(message);
364+
Local<Object> obj = exception->ToObject(context).ToLocalChecked();
365+
366+
const char* ls = ERR_lib_error_string(ssl_err);
367+
const char* fs = ERR_func_error_string(ssl_err);
368+
const char* rs = ERR_reason_error_string(ssl_err);
369+
370+
if (ls != nullptr)
371+
obj->Set(context, env()->library_string(),
372+
OneByteString(isolate, ls)).FromJust();
373+
if (fs != nullptr)
374+
obj->Set(context, env()->function_string(),
375+
OneByteString(isolate, fs)).FromJust();
376+
if (rs != nullptr) {
377+
obj->Set(context, env()->reason_string(),
378+
OneByteString(isolate, rs)).FromJust();
379+
380+
// SSL has no API to recover the error name from the number, so we
381+
// transform reason strings like "this error" to "ERR_SSL_THIS_ERROR",
382+
// which ends up being close to the original error macro name.
383+
std::string code(rs);
384+
385+
for (auto& c : code) {
386+
if (c == ' ')
387+
c = '_';
388+
else
389+
c = ::toupper(c);
390+
}
391+
obj->Set(context, env()->code_string(),
392+
OneByteString(isolate, ("ERR_SSL_" + code).c_str()))
393+
.FromJust();
394+
}
359395

360396
if (msg != nullptr)
361397
msg->assign(mem->data, mem->data + mem->length);

test/parallel/test-tls-alert-handling.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ if (!common.hasCrypto)
77
if (!common.opensslCli)
88
common.skip('node compiled without OpenSSL CLI');
99

10+
const assert = require('assert');
1011
const net = require('net');
1112
const tls = require('tls');
1213
const fixtures = require('../common/fixtures');
@@ -29,7 +30,11 @@ const opts = {
2930
const max_iter = 20;
3031
let iter = 0;
3132

32-
const errorHandler = common.mustCall(() => {
33+
const errorHandler = common.mustCall((err) => {
34+
assert.strictEqual(err.code, 'ERR_SSL_WRONG_VERSION_NUMBER');
35+
assert.strictEqual(err.library, 'SSL routines');
36+
assert.strictEqual(err.function, 'ssl3_get_record');
37+
assert.strictEqual(err.reason, 'wrong version number');
3338
errorReceived = true;
3439
if (canCloseServer())
3540
server.close();
@@ -76,5 +81,10 @@ function sendBADTLSRecord() {
7681
socket.write(BAD_RECORD);
7782
socket.end();
7883
}));
79-
client.on('error', common.mustCall());
84+
client.on('error', common.mustCall((err) => {
85+
assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION');
86+
assert.strictEqual(err.library, 'SSL routines');
87+
assert.strictEqual(err.function, 'ssl3_read_bytes');
88+
assert.strictEqual(err.reason, 'tlsv1 alert protocol version');
89+
}));
8090
}

test/parallel/test-tls-min-max-version.js

+23-26
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION;
1111

1212
function test(cmin, cmax, cprot, smin, smax, sprot, expect) {
13+
assert(expect);
1314
connect({
1415
client: {
1516
checkServerIdentity: (servername, cert) => { },
@@ -26,23 +27,18 @@ function test(cmin, cmax, cprot, smin, smax, sprot, expect) {
2627
secureProtocol: sprot,
2728
},
2829
}, common.mustCall((err, pair, cleanup) => {
29-
if (expect && expect.match(/^ERR/)) {
30-
assert.strictEqual(err.code, expect);
30+
if (err) {
31+
assert.strictEqual(err.code, expect, err + '.code !== ' + expect);
3132
return cleanup();
3233
}
3334

34-
if (expect) {
35-
assert.ifError(pair.server.err);
36-
assert.ifError(pair.client.err);
37-
assert(pair.server.conn);
38-
assert(pair.client.conn);
39-
assert.strictEqual(pair.client.conn.getProtocol(), expect);
40-
assert.strictEqual(pair.server.conn.getProtocol(), expect);
41-
return cleanup();
42-
}
43-
44-
assert(pair.server.err);
45-
assert(pair.client.err);
35+
assert.ifError(err);
36+
assert.ifError(pair.server.err);
37+
assert.ifError(pair.client.err);
38+
assert(pair.server.conn);
39+
assert(pair.client.conn);
40+
assert.strictEqual(pair.client.conn.getProtocol(), expect);
41+
assert.strictEqual(pair.server.conn.getProtocol(), expect);
4642
return cleanup();
4743
}));
4844
}
@@ -83,17 +79,18 @@ test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1');
8379
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
8480

8581
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
86-
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', null);
87-
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', null);
88-
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', null);
89-
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', null);
82+
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'ECONNRESET');
83+
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'ECONNRESET');
84+
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
85+
'ERR_SSL_VERSION_TOO_LOW');
86+
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
9087
}
9188

9289
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
9390
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
94-
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', null);
91+
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'ECONNRESET');
9592
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
96-
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', null);
93+
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
9794
}
9895

9996
if (DEFAULT_MIN_VERSION === 'TLSv1') {
@@ -111,18 +108,18 @@ test(U, U, 'TLSv1_method', U, U, 'TLSv1_method', 'TLSv1');
111108

112109
// The default default.
113110
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
114-
test(U, U, 'TLSv1_1_method', U, U, U, null);
115-
test(U, U, 'TLSv1_method', U, U, U, null);
116-
test(U, U, U, U, U, 'TLSv1_1_method', null);
117-
test(U, U, U, U, U, 'TLSv1_method', null);
111+
test(U, U, 'TLSv1_1_method', U, U, U, 'ECONNRESET');
112+
test(U, U, 'TLSv1_method', U, U, U, 'ECONNRESET');
113+
test(U, U, U, U, U, 'TLSv1_1_method', 'ERR_SSL_VERSION_TOO_LOW');
114+
test(U, U, U, U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
118115
}
119116

120117
// The default with --tls-v1.1.
121118
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
122119
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
123-
test(U, U, 'TLSv1_method', U, U, U, null);
120+
test(U, U, 'TLSv1_method', U, U, U, 'ECONNRESET');
124121
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
125-
test(U, U, U, U, U, 'TLSv1_method', null);
122+
test(U, U, U, U, U, 'TLSv1_method', 'ERR_SSL_VERSION_TOO_LOW');
126123
}
127124

128125
// The default with --tls-v1.0.

0 commit comments

Comments
 (0)