mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-20 21:31:28 +02:00
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.
This commit is contained in:
parent
8ac54cafcd
commit
2c7e05f80e
64
src/dns.c
64
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user