From 46b1bcc51d16f007c9337c456c495597b01f695a Mon Sep 17 00:00:00 2001 From: Zhou Yuefu Date: Tue, 7 Jul 2026 11:13:44 +0800 Subject: [PATCH] Close inherited listening sockets in forked TCP child processes 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(). --- src/dnsmasq.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/dnsmasq.c b/src/dnsmasq.c index adaa692..9af3e09 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -2015,7 +2015,8 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot) pid_t p; union mysockaddr tcp_addr; socklen_t tcp_len = sizeof(union mysockaddr); - struct server *s; + struct server *s; + struct listener *l; int flags, auth_dns = 0; struct in_addr netmask; int pipefd[2]; @@ -2186,6 +2187,16 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot) alarm(CHILD_LIFETIME); close(pipefd[0]); /* close read end in child. */ daemon->pipe_to_parent = pipefd[1]; + + /* Close inherited listening sockets in the child process. + These are not needed here and holding them prevents the + parent from re-binding if an interface is removed and + re-added (the child's copy causes EADDRINUSE). */ + for (l = daemon->listeners; l; l = l->next) + { + if (l->fd != -1) close(l->fd); + if (l->tcpfd != -1) close(l->tcpfd); + } } /* The connected socket inherits non-blocking @@ -2232,6 +2243,7 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount) { struct server *s; + struct listener *l; if (!option_bool(OPT_DEBUG)) { @@ -2304,6 +2316,13 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header daemon->forward_to_tcp = forward; daemon->header_to_tcp = header; daemon->plen_to_tcp = *plen; + + /* Close inherited listening sockets in the child process. */ + for (l = daemon->listeners; l; l = l->next) + { + if (l->fd != -1) close(l->fd); + if (l->tcpfd != -1) close(l->tcpfd); + } } } -- 2.25.1