Pick some unused port (so that connect will fail) and repeatedly attempt to connect using "localhost" hostname:
The number of open file descriptors will continue to climb with each attempt. Switching from 'localhost' -> '127.0.0.1' and all opened fd's are closed after connection failure.
$ strace -f node localhost.js 2>&1 | perl -nE 'if (/STREAM/ || /close/ || /connect/) { print $_; }'
...
~~~~~~ AF_INET6 ~~~~~~~
[pid 192620] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 18
[pid 192620] connect(18, {sa_family=AF_INET6, sin6_port=htons(1883), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)
~~~~ IPV4 socket opened, but not closed (see 26, 27, 28, ... with no corresponding close) ~~~~
[pid 192620] socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 26
[pid 192620] connect(26, {sa_family=AF_INET, sin_port=htons(1883), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
~~~~~ AF_INET6 fd closed ~~~~~~~
[pid 192620] close(18) = 0
[pid 192627] close(18) = 0
[pid 192627] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 18
[pid 192627] connect(18, {sa_family=AF_UNIX, sun_path="/run/systemd/resolve/io.systemd.Resolve"}, 42) = -1 ENOENT (No such file or directory)
[pid 192627] close(18) = 0
[pid 192627] close(18) = 0
[pid 192627] connect(18, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
[pid 192627] close(18) = 0
[pid 192627] connect(18, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
[pid 192627] close(18) = 0
[pid 192620] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 18
[pid 192620] connect(18, {sa_family=AF_INET6, sin6_port=htons(1883), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)
[pid 192620] socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 27
[pid 192620] connect(27, {sa_family=AF_INET, sin_port=htons(1883), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
[pid 192620] close(18) = 0
[pid 192628] close(18) = 0
[pid 192629] close(18) = 0
[pid 192629] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 18
[pid 192629] connect(18, {sa_family=AF_UNIX, sun_path="/run/systemd/resolve/io.systemd.Resolve"}, 42) = -1 ENOENT (No such file or directory)
[pid 192629] close(18) = 0
[pid 192629] close(18) = 0
[pid 192629] connect(18, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
[pid 192629] close(18) = 0
[pid 192629] connect(18, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
[pid 192629] close(18) = 0
[pid 192620] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 18
[pid 192620] connect(18, {sa_family=AF_INET6, sin6_port=htons(1883), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)
[pid 192620] socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 28
[pid 192620] connect(28, {sa_family=AF_INET, sin_port=htons(1883), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
[pid 192620] close(18) = 0
[pid 192630] close(18) = 0
[pid 192630] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 18
[pid 192630] connect(18, {sa_family=AF_UNIX, sun_path="/run/systemd/resolve/io.systemd.Resolve"}, 42) = -1 ENOENT (No such file or directory)
[pid 192630] close(18) = 0
[pid 192630] close(18) = 0
[pid 192630] connect(18, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
[pid 192630] close(18) = 0
[pid 192630] connect(18, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
[pid 192630] close(18) = 0
[pid 192620] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 18
[pid 192620] connect(18, {sa_family=AF_INET6, sin6_port=htons(1883), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)
[pid 192620] socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 29
[pid 192620] connect(29, {sa_family=AF_INET, sin_port=htons(1883), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
[pid 192620] close(18) = 0
[pid 192627] close(18) = 0
[pid 192627] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 18
[pid 192627] connect(18, {sa_family=AF_UNIX, sun_path="/run/systemd/resolve/io.systemd.Resolve"}, 42) = -1 ENOENT (No such file or directory)
[pid 192627] close(18) = 0
[pid 192627] close(18) = 0
[pid 192627] connect(18, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
[pid 192627] close(18) = 0
[pid 192627] connect(18, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
[pid 192627] close(18) = 0
[pid 192620] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 18
[pid 192620] connect(18, {sa_family=AF_INET6, sin6_port=htons(1883), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)
...
See above.
I'd expected unused sockfd's to be closed.
See above. For example that logs the lsof count, you should be able to use:
But I'm sure its not so simple. I'll poke around but if any net.js experts around here would be generous enough to offer an explanation I'd be grateful.
Version
v20.3.1
Platform
Linux stream 6.3.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 21 Jun 2023 20:46:20 +0000 x86_64 GNU/Linux
Subsystem
net
What steps will reproduce the bug?
Pick some unused port (so that connect will fail) and repeatedly attempt to connect using "localhost" hostname:
The number of open file descriptors will continue to climb with each attempt. Switching from
'localhost' -> '127.0.0.1'and all opened fd's are closed after connection failure.From strace, we see the
socket(...)syscalls for AF_INET6 (::1) and AF_INET (127.0.0,1), but only the AF_INET6 related descriptor is closed after connection failure.lsof -pwill show a similar result, with climbing file descriptors.How often does it reproduce? Is there a required condition?
See above.
What is the expected behavior? Why is that the expected behavior?
I'd expected unused sockfd's to be closed.
What do you see instead?
See above. For example that logs the
lsofcount, you should be able to use:Additional information
Issue is resolved on more recent
mainbranch 5da84a6 but I didn't see any commits that explicitly address this (I would be curious to see this if I missed it).My naive guess would be that:
afterConnectseems to destroy/close failed out fd's.node/lib/net.js
Lines 1569 to 1584 in 9869bdc
but
afterConnectMultipledoesn't:node/lib/net.js
Lines 1602 to 1622 in 9869bdc
But I'm sure its not so simple. I'll poke around but if any net.js experts around here would be generous enough to offer an explanation I'd be grateful.
Cheers!