Skip to content

Commit b62e228

Browse files
richardlauaddaleax
authored andcommitted
report: add fallback for uv_getnameinfo() failures
Attempt to report the host and port in the case that uv_getnameinfo() fails. PR-URL: #26140 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 06d592c commit b62e228

File tree

2 files changed

+98
-61
lines changed

2 files changed

+98
-61
lines changed

src/node_report_utils.cc

+41-29
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,52 @@ using node::MallocedBuffer;
77

88
static constexpr auto null = JSONWriter::Null{};
99

10+
// Utility function to format socket information.
11+
static void ReportEndpoint(uv_handle_t* h,
12+
struct sockaddr* addr,
13+
const char* name,
14+
JSONWriter* writer) {
15+
if (addr == nullptr) {
16+
writer->json_keyvalue(name, null);
17+
return;
18+
}
19+
20+
uv_getnameinfo_t endpoint;
21+
char* host = nullptr;
22+
char hostbuf[INET6_ADDRSTRLEN];
23+
const int family = addr->sa_family;
24+
const int port = ntohs(family == AF_INET ?
25+
reinterpret_cast<sockaddr_in*>(addr)->sin_port :
26+
reinterpret_cast<sockaddr_in6*>(addr)->sin6_port);
27+
28+
if (uv_getnameinfo(h->loop, &endpoint, nullptr, addr, NI_NUMERICSERV) == 0) {
29+
host = endpoint.host;
30+
DCHECK_EQ(port, std::stoi(endpoint.service));
31+
} else {
32+
const void* src = family == AF_INET ?
33+
static_cast<void*>(
34+
&(reinterpret_cast<sockaddr_in*>(addr)->sin_addr)) :
35+
static_cast<void*>(
36+
&(reinterpret_cast<sockaddr_in6*>(addr)->sin6_addr));
37+
if (uv_inet_ntop(family, src, hostbuf, sizeof(hostbuf)) == 0) {
38+
host = hostbuf;
39+
}
40+
}
41+
writer->json_objectstart(name);
42+
if (host != nullptr) {
43+
writer->json_keyvalue("host", host);
44+
}
45+
writer->json_keyvalue("port", port);
46+
writer->json_objectend();
47+
}
48+
1049
// Utility function to format libuv socket information.
1150
static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
1251
struct sockaddr_storage addr_storage;
1352
struct sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
1453
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
1554
int addr_size = sizeof(addr_storage);
1655
int rc = -1;
17-
bool wrote_local_endpoint = false;
18-
bool wrote_remote_endpoint = false;
1956

2057
switch (h->type) {
2158
case UV_UDP:
@@ -27,38 +64,13 @@ static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
2764
default:
2865
break;
2966
}
30-
if (rc == 0) {
31-
// uv_getnameinfo will format host and port and handle IPv4/IPv6.
32-
uv_getnameinfo_t local;
33-
rc = uv_getnameinfo(h->loop, &local, nullptr, addr, NI_NUMERICSERV);
34-
35-
if (rc == 0) {
36-
writer->json_objectstart("localEndpoint");
37-
writer->json_keyvalue("host", local.host);
38-
writer->json_keyvalue("port", local.service);
39-
writer->json_objectend();
40-
wrote_local_endpoint = true;
41-
}
42-
}
43-
if (!wrote_local_endpoint) writer->json_keyvalue("localEndpoint", null);
67+
ReportEndpoint(h, rc == 0 ? addr : nullptr, "localEndpoint", writer);
4468

4569
if (h->type == UV_TCP) {
4670
// Get the remote end of the connection.
4771
rc = uv_tcp_getpeername(&handle->tcp, addr, &addr_size);
48-
if (rc == 0) {
49-
uv_getnameinfo_t remote;
50-
rc = uv_getnameinfo(h->loop, &remote, nullptr, addr, NI_NUMERICSERV);
51-
52-
if (rc == 0) {
53-
writer->json_objectstart("remoteEndpoint");
54-
writer->json_keyvalue("host", remote.host);
55-
writer->json_keyvalue("port", remote.service);
56-
writer->json_objectend();
57-
wrote_local_endpoint = true;
58-
}
59-
}
72+
ReportEndpoint(h, rc == 0 ? addr : nullptr, "remoteEndpoint", writer);
6073
}
61-
if (!wrote_remote_endpoint) writer->json_keyvalue("remoteEndpoint", null);
6274
}
6375

6476
// Utility function to format libuv path information.

test/node-report/test-api-uvhandles.js

+57-32
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ if (process.argv[2] === 'child') {
6060
const data = { pid: child_process.pid,
6161
tcp_address: server.address(),
6262
udp_address: udp_socket.address(),
63-
skip_fs_watch: (watcher === undefined ?
64-
'fs.watch() unavailable' :
65-
false) };
63+
skip_fs_watch: (watcher === undefined) };
6664
process.send(data);
6765
http.get({ port: server.address().port });
6866
});
@@ -74,6 +72,8 @@ if (process.argv[2] === 'child') {
7472
tmpdir.refresh();
7573
const options = { encoding: 'utf8', silent: true, cwd: tmpdir.path };
7674
const child = fork('--experimental-report', [__filename, 'child'], options);
75+
let child_data;
76+
child.on('message', (data) => { child_data = data; });
7777
let stderr = '';
7878
child.stderr.on('data', (chunk) => { stderr += chunk; });
7979
let stdout = '';
@@ -94,36 +94,61 @@ if (process.argv[2] === 'child') {
9494
assert.deepStrictEqual(reports, [], report_msg, reports);
9595

9696
const report = JSON.parse(stdout);
97-
let fs = 0;
98-
let poll = 0;
99-
let process = 0;
100-
let timer = 0;
101-
let pipe = 0;
102-
let tcp = 0;
103-
let udp = 0;
104-
const fs_msg = 'fs_event not found';
105-
const poll_msg = 'poll_event not found';
106-
const process_msg = 'process event not found';
107-
const timer_msg = 'timer event not found';
108-
const pipe_msg = 'pipe event not found';
109-
const tcp_msg = 'tcp event not found';
110-
const udp_msg = 'udp event not found';
111-
for (const entry in report.libuv) {
112-
if (report.libuv[entry].type === 'fs_event') fs = 1;
113-
else if (report.libuv[entry].type === 'fs_poll') poll = 1;
114-
else if (report.libuv[entry].type === 'process') process = 1;
115-
else if (report.libuv[entry].type === 'timer') timer = 1;
116-
else if (report.libuv[entry].type === 'pipe') pipe = 1;
117-
else if (report.libuv[entry].type === 'tcp') tcp = 1;
118-
else if (report.libuv[entry].type === 'udp') udp = 1;
97+
const prefix = common.isWindows ? '\\\\?\\' : '';
98+
const expected_filename = `${prefix}${__filename}`;
99+
const found_tcp = [];
100+
// Functions are named to aid debugging when they are not called.
101+
const validators = {
102+
fs_event: common.mustCall(function fs_event_validator(handle) {
103+
if (!child_data.skip_fs_watch) {
104+
assert.strictEqual(handle.filename, expected_filename);
105+
assert(handle.is_referenced);
106+
}
107+
}),
108+
fs_poll: common.mustCall(function fs_poll_validator(handle) {
109+
assert.strictEqual(handle.filename, expected_filename);
110+
assert(handle.is_referenced);
111+
}),
112+
pipe: common.mustCallAtLeast(function pipe_validator(handle) {
113+
assert(handle.is_referenced);
114+
}),
115+
process: common.mustCall(function process_validator(handle) {
116+
assert.strictEqual(handle.pid, child_data.pid);
117+
assert(handle.is_referenced);
118+
}),
119+
tcp: common.mustCall(function tcp_validator(handle) {
120+
// TCP handles. The report should contain three sockets:
121+
// 1. The server's listening socket.
122+
// 2. The inbound socket making the request.
123+
// 3. The outbound socket sending the response.
124+
const port = child_data.tcp_address.port;
125+
if (handle.localEndpoint.port === port) {
126+
if (handle.remoteEndpoint === null) {
127+
found_tcp.push('listening');
128+
} else {
129+
found_tcp.push('inbound');
130+
}
131+
} else if (handle.remoteEndpoint.port === port) {
132+
found_tcp.push('outbound');
133+
}
134+
assert(handle.is_referenced);
135+
}, 3),
136+
timer: common.mustCall(function timer_validator(handle) {
137+
assert(!handle.is_referenced);
138+
assert.strictEqual(handle.repeat, 0);
139+
}),
140+
udp: common.mustCall(function udp_validator(handle) {
141+
assert.strictEqual(handle.localEndpoint.port,
142+
child_data.udp_address.port);
143+
assert(handle.is_referenced);
144+
}),
145+
};
146+
for (const entry of report.libuv) {
147+
if (validators[entry.type]) validators[entry.type](entry);
148+
}
149+
for (const socket of ['listening', 'inbound', 'outbound']) {
150+
assert(found_tcp.includes(socket), `${socket} TCP socket was not found`);
119151
}
120-
assert.deepStrictEqual(fs, 1, fs_msg);
121-
assert.deepStrictEqual(poll, 1, poll_msg);
122-
assert.deepStrictEqual(process, 1, process_msg);
123-
assert.deepStrictEqual(timer, 1, timer_msg);
124-
assert.deepStrictEqual(pipe, 1, pipe_msg);
125-
assert.deepStrictEqual(tcp, 1, tcp_msg);
126-
assert.deepStrictEqual(udp, 1, udp_msg);
127152

128153
// Common report tests.
129154
helper.validateContent(stdout);

0 commit comments

Comments
 (0)