Avoid hanngs in DHCP ping code when system time goes backwards.
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 19a6428..5fb4281 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -1755,14 +1755,22 @@
better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */
- int fd;
+ /* There can be a problem using dnsmasq_time() to end the loop, since
+ it's not monotonic, and can go backwards if the system clock is
+ tweaked, leading to the code getting stuck in this loop and
+ ignoring DHCP requests. To fix this, we check to see if select returned
+ as a result of a timeout rather than a socket becoming available. We
+ only allow this to happen as many times as it takes to get to the wait time
+ in quarter-second chunks. This provides a fallback way to end loop. */
+
+ int fd, rc;
struct sockaddr_in saddr;
struct {
struct ip ip;
struct icmp icmp;
} packet;
unsigned short id = rand16();
- unsigned int i, j;
+ unsigned int i, j, timeout_count;
int gotreply = 0;
time_t start, now;
@@ -1794,8 +1802,8 @@
while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr))));
- for (now = start = dnsmasq_time();
- difftime(now, start) < (float)PING_WAIT;)
+ for (now = start = dnsmasq_time(), timeout_count = 0;
+ (difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);)
{
struct timeval tv;
fd_set rset, wset;
@@ -1820,11 +1828,15 @@
}
#endif
- if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
+ rc = select(maxfd+1, &rset, &wset, NULL, &tv);
+
+ if (rc < 0)
{
FD_ZERO(&rset);
FD_ZERO(&wset);
}
+ else if (rc == 0)
+ timeout_count++;
now = dnsmasq_time();