From 2c7e05f80e3b4bacad84eb213b9db3bc5d8963dc Mon Sep 17 00:00:00 2001 From: Valentine Krasnobaeva Date: Wed, 13 Aug 2025 15:48:47 +0200 Subject: [PATCH] MEDIUM: dns: don't call connect to dest socket for AF_INET* When we perform connect call for a datagram socket, used to send DNS requests, we set for it the default destination address to some given nameserver. Then we simply use send(), as the destination address is already set. In some usecases described in GitHub issues #3001 and #2654, this approach becames inefficient, nameservers change its IP addresses dynamically, this triggers DNS resolution errors. To fix this, let's perform the bind() on the wildcard address for the datagram AF_INET* client socket. Like this we will allocate a port for it. Then let's use sendto() instead of send(). If the nameserver is local and is listening on the UNIX domain socket, we continue to use the existed approach (connect() and then send()). This fixes issues #3001 and #2654. This may be backported in all stable versions. --- src/dns.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/dns.c b/src/dns.c index 054f90a0c..e02eb1bdc 100644 --- a/src/dns.c +++ b/src/dns.c @@ -65,12 +65,52 @@ static int dns_connect_nameserver(struct dns_nameserver *ns) ns->counters->pid, ns->id); return -1; } - if (connect(fd, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to)) == -1) { - send_log(NULL, LOG_WARNING, - "DNS : section '%s': can't connect socket for nameserver '%s'.\n", - ns->counters->id, ns->id); - close(fd); - return -1; + + switch (proto->fam->sock_domain) { + case AF_INET: { + struct sockaddr_in address = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { .s_addr = INADDR_ANY } + }; + if (bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + send_log(NULL, LOG_WARNING, + "DNS : section '%s': can't bind socket for nameserver '%s' on 0.0.0.0:0.\n", + ns->counters->pid, ns->id); + return -1; + } + break; + } + case AF_INET6: { + struct sockaddr_in6 address6 = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = in6addr_any, + .sin6_flowinfo = 0, + .sin6_scope_id = 0 + }; + if (bind(fd, (struct sockaddr *)&address6, sizeof(address6)) < 0) { + send_log(NULL, LOG_WARNING, + "DNS : section '%s': can't bind socket for nameserver '%s' on :::0.\n", + ns->counters->pid, ns->id); + return -1; + } + break; + } + case AF_UNIX: + /* if IPC is used via local domain sockets, we don't expect that + * the path to DNS server socket can change dynamically. + */ + if (connect(fd, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to)) == -1) { + send_log(NULL, LOG_WARNING, + "DNS : section '%s': can't connect socket for nameserver '%s'.\n", + ns->counters->id, ns->id); + close(fd); + return -1; + } + break; + default: + BUG_ON(1, "DNS: Unsupported address family."); } /* Make the socket non blocking */ @@ -106,7 +146,17 @@ int dns_send_nameserver(struct dns_nameserver *ns, void *buf, size_t len) fd = dgram->t.sock.fd; } - ret = send(fd, buf, len, 0); + if (dgram->addr.to.ss_family == AF_UNIX) { + /* we do connect for AF_UNIX sockets and from the man + * sendto: "If sendto() is used on a connection-mode + * (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments + * dest_addr and addrlen are ignored (and the error + * EISCONN may be returned when they are not NULL and 0)..." + */ + ret = send(fd, buf, len, 0); + } else + ret = sendto(fd, buf, len, 0, (struct sockaddr*)&dgram->addr.to, get_addr_len(&dgram->addr.to)); + if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { struct ist myist;