diff --git a/src/node_report.cc b/src/node_report.cc index cffb9dd1b655af..523e08c24e3f9a 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -202,7 +202,9 @@ static void WriteNodeReport(Isolate* isolate, writer.json_arraystart("libuv"); if (env != nullptr) { - uv_walk(env->event_loop(), WalkHandle, static_cast(&writer)); + uv_walk(env->event_loop(), + exclude_network ? WalkHandleNoNetwork : WalkHandleNetwork, + static_cast(&writer)); writer.json_start(); writer.json_keyvalue("type", "loop"); diff --git a/src/node_report.h b/src/node_report.h index 7a2e817ac82f6b..98be339ae90d8f 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -19,7 +19,8 @@ namespace node { namespace report { // Function declarations - utility functions in src/node_report_utils.cc -void WalkHandle(uv_handle_t* h, void* arg); +void WalkHandleNetwork(uv_handle_t* h, void* arg); +void WalkHandleNoNetwork(uv_handle_t* h, void* arg); template std::string ValueToHexString(T value) { diff --git a/src/node_report_utils.cc b/src/node_report_utils.cc index 516eac22dc63a2..d4eb52c1ed89c0 100644 --- a/src/node_report_utils.cc +++ b/src/node_report_utils.cc @@ -12,7 +12,8 @@ static constexpr auto null = JSONWriter::Null{}; static void ReportEndpoint(uv_handle_t* h, struct sockaddr* addr, const char* name, - JSONWriter* writer) { + JSONWriter* writer, + bool exclude_network) { if (addr == nullptr) { writer->json_keyvalue(name, null); return; @@ -20,35 +21,42 @@ static void ReportEndpoint(uv_handle_t* h, uv_getnameinfo_t endpoint; char* host = nullptr; - char hostbuf[INET6_ADDRSTRLEN]; const int family = addr->sa_family; const int port = ntohs(family == AF_INET ? reinterpret_cast(addr)->sin_port : reinterpret_cast(addr)->sin6_port); - if (uv_getnameinfo(h->loop, &endpoint, nullptr, addr, NI_NUMERICSERV) == 0) { + writer->json_objectstart(name); + if (!exclude_network && + uv_getnameinfo(h->loop, &endpoint, nullptr, addr, NI_NUMERICSERV) == 0) { host = endpoint.host; DCHECK_EQ(port, std::stoi(endpoint.service)); + writer->json_keyvalue("host", host); + } + + if (family == AF_INET) { + char ipbuf[INET_ADDRSTRLEN]; + if (uv_ip4_name( + reinterpret_cast(addr), ipbuf, sizeof(ipbuf)) == 0) { + writer->json_keyvalue("ip4", ipbuf); + if (host == nullptr) writer->json_keyvalue("host", ipbuf); + } } else { - const void* src = family == AF_INET ? - static_cast( - &(reinterpret_cast(addr)->sin_addr)) : - static_cast( - &(reinterpret_cast(addr)->sin6_addr)); - if (uv_inet_ntop(family, src, hostbuf, sizeof(hostbuf)) == 0) { - host = hostbuf; + char ipbuf[INET6_ADDRSTRLEN]; + if (uv_ip6_name( + reinterpret_cast(addr), ipbuf, sizeof(ipbuf)) == 0) { + writer->json_keyvalue("ip6", ipbuf); + if (host == nullptr) writer->json_keyvalue("host", ipbuf); } } - writer->json_objectstart(name); - if (host != nullptr) { - writer->json_keyvalue("host", host); - } writer->json_keyvalue("port", port); writer->json_objectend(); } // Utility function to format libuv socket information. -static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) { +static void ReportEndpoints(uv_handle_t* h, + JSONWriter* writer, + bool exclude_network) { struct sockaddr_storage addr_storage; struct sockaddr* addr = reinterpret_cast(&addr_storage); uv_any_handle* handle = reinterpret_cast(h); @@ -65,7 +73,8 @@ static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) { default: break; } - ReportEndpoint(h, rc == 0 ? addr : nullptr, "localEndpoint", writer); + ReportEndpoint( + h, rc == 0 ? addr : nullptr, "localEndpoint", writer, exclude_network); switch (h->type) { case UV_UDP: @@ -77,7 +86,8 @@ static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) { default: break; } - ReportEndpoint(h, rc == 0 ? addr : nullptr, "remoteEndpoint", writer); + ReportEndpoint( + h, rc == 0 ? addr : nullptr, "remoteEndpoint", writer, exclude_network); } // Utility function to format libuv pipe information. @@ -155,7 +165,7 @@ static void ReportPath(uv_handle_t* h, JSONWriter* writer) { } // Utility function to walk libuv handles. -void WalkHandle(uv_handle_t* h, void* arg) { +void WalkHandle(uv_handle_t* h, void* arg, bool exclude_network = false) { const char* type = uv_handle_type_name(h->type); JSONWriter* writer = static_cast(arg); uv_any_handle* handle = reinterpret_cast(h); @@ -177,7 +187,7 @@ void WalkHandle(uv_handle_t* h, void* arg) { break; case UV_TCP: case UV_UDP: - ReportEndpoints(h, writer); + ReportEndpoints(h, writer, exclude_network); break; case UV_NAMED_PIPE: ReportPipeEndpoints(h, writer); @@ -267,6 +277,11 @@ void WalkHandle(uv_handle_t* h, void* arg) { } writer->json_end(); } - +void WalkHandleNetwork(uv_handle_t* h, void* arg) { + WalkHandle(h, arg, false); +} +void WalkHandleNoNetwork(uv_handle_t* h, void* arg) { + WalkHandle(h, arg, true); +} } // namespace report } // namespace node diff --git a/test/report/test-report-exclude-network.js b/test/report/test-report-exclude-network.js index c5e50135482f1a..0e7afefae39652 100644 --- a/test/report/test-report-exclude-network.js +++ b/test/report/test-report-exclude-network.js @@ -1,5 +1,6 @@ 'use strict'; require('../common'); +const http = require('node:http'); const assert = require('node:assert'); const { spawnSync } = require('node:child_process'); const tmpdir = require('../common/tmpdir'); @@ -38,4 +39,53 @@ describe('report exclude network option', () => { const report = process.report.getReport(); assert.strictEqual(report.header.networkInterfaces, undefined); }); + + it('should not do DNS queries in libuv if exclude network', async () => { + const server = http.createServer(function(req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); + }); + const port = await new Promise((resolve) => server.listen(0, async () => { + await Promise.all([ + fetch('http://127.0.0.1:' + server.address().port), + fetch('http://[::1]:' + server.address().port), + ]); + resolve(server.address().port); + server.close(); + })); + process.report.excludeNetwork = false; + let report = process.report.getReport(); + let tcp = report.libuv.filter((uv) => uv.type === 'tcp' && uv.remoteEndpoint?.port === port); + assert.strictEqual(tcp.length, 2); + const findHandle = (host, local = true, ip4 = true) => { + return tcp.some( + ({ [local ? 'localEndpoint' : 'remoteEndpoint']: ep }) => + (ep[ip4 ? 'ip4' : 'ip6'] === (ip4 ? '127.0.0.1' : '::1') && ep.host === host), + ); + }; + try { + assert.ok(findHandle('localhost'), 'local localhost handle not found'); + assert.ok(findHandle('localhost', false), 'remote localhost handle not found'); + + assert.ok(findHandle('ip6-localhost', true, false), 'local ip6-localhost handle not found'); + assert.ok(findHandle('ip6-localhost', false, false), 'remote ip6-localhost handle not found'); + } catch (e) { + throw new Error(e.message + ' in ' + JSON.stringify(tcp, null, 2)); + } + + process.report.excludeNetwork = true; + report = process.report.getReport(); + tcp = report.libuv.filter((uv) => uv.type === 'tcp' && uv.remoteEndpoint?.port === port); + + try { + assert.strictEqual(tcp.length, 2); + assert.ok(findHandle('127.0.0.1'), 'local 127.0.0.1 handle not found'); + assert.ok(findHandle('127.0.0.1', false), 'remote 127.0.0.1 handle not found'); + + assert.ok(findHandle('::1', true, false), 'local ::1 handle not found'); + assert.ok(findHandle('::1', false, false), 'remote ::1 handle not found'); + } catch (e) { + throw new Error(e.message + ' in ' + JSON.stringify(tcp, null, 2)); + } + }); });