If a TCP listening socket is externally destroyed (e.g., via ss -K,
or a process using NETLINK_SOCK_DIAG/SOCK_DESTROY), accept()
permanently returns -1 with errno == EINVAL because the socket is
no longer in TCP_LISTEN state. Since poll() keeps reporting the
stale fd as readable, the main loop spins calling
do_tcp_connection() -> accept() indefinitely, consuming 100% CPU.
Distinguish transient errors (EAGAIN, ECONNABORTED, EMFILE, ENFILE,
ENOMEM, ENOBUFS) from fatal ones. On transient errors just return
and retry on the next poll cycle. On fatal errors close the tcpfd
and mark it -1 so poll() no longer selects it.
In --bind-dynamic mode the listener will be automatically rebuilt
on the next address change event via newaddress().
Signed-off-by: Yuefu Zhou <yuefu16.zhou@gmail.com>
When dnsmasq forks a child to handle a TCP connection, the child
inherits copies of all listening sockets. These are never used but
keep the underlying sockets alive in the kernel. If a network
interface is removed and re-added while a child is running, the
parent's attempt to re-bind fails with EADDRINUSE because the child
still holds a reference.
Close all listener fds (UDP and TCP) in the child immediately after
fork in both do_tcp_connection() and swap_to_tcp().
[Original patch extended by Simon Kelley to include the TFTP listening
socket, and to extend the existing race-protection scheme for the
netlink socket to the listening sockets. Any bugs are my responsibility.]