Skip to content

Commit

Permalink
report: fix network queries in getReport libuv with exclude-network
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofandel committed Oct 31, 2024
1 parent f67e45e commit 3da21d4
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 22 deletions.
4 changes: 3 additions & 1 deletion src/node_report.cc
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,9 @@ static void WriteNodeReport(Isolate* isolate,

writer.json_arraystart("libuv");
if (env != nullptr) {
uv_walk(env->event_loop(), WalkHandle, static_cast<void*>(&writer));
uv_walk(env->event_loop(),
exclude_network ? WalkHandleNoNetwork : WalkHandleNetwork,
static_cast<void*>(&writer));

writer.json_start();
writer.json_keyvalue("type", "loop");
Expand Down
3 changes: 2 additions & 1 deletion src/node_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename T>
std::string ValueToHexString(T value) {
Expand Down
55 changes: 35 additions & 20 deletions src/node_report_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,51 @@ 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;
}

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<sockaddr_in*>(addr)->sin_port :
reinterpret_cast<sockaddr_in6*>(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<sockaddr_in*>(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<void*>(
&(reinterpret_cast<sockaddr_in*>(addr)->sin_addr)) :
static_cast<void*>(
&(reinterpret_cast<sockaddr_in6*>(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<sockaddr_in6*>(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<sockaddr*>(&addr_storage);
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
Expand All @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -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<JSONWriter*>(arg);
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
Expand All @@ -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);
Expand Down Expand Up @@ -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
51 changes: 51 additions & 0 deletions test/report/test-report-exclude-network.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -38,4 +39,54 @@ 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') &&
(Array.isArray(host) ? host.includes(ep.host) : 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(['localhost', 'ip6-localhost'], true, false), 'local ip6-localhost handle not found');
assert.ok(findHandle(['localhost', '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));
}
});
});

0 comments on commit 3da21d4

Please sign in to comment.