about summary refs log tree commit diff
path: root/resolv
diff options
context:
space:
mode:
Diffstat (limited to 'resolv')
-rw-r--r--resolv/res_send.c106
1 files changed, 83 insertions, 23 deletions
diff --git a/resolv/res_send.c b/resolv/res_send.c
index 2366f59cd2..44d8cb0fee 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -88,6 +88,7 @@ static const char rcsid[] = "$BINDId: res_send.c,v 8.38 2000/03/30 20:16:51 vixi
 #include <sys/ioctl.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <resolv.h>
 #include <signal.h>
@@ -965,12 +966,73 @@ send_dg(res_state statp,
 			return (0);
 		}
 #endif /* !CANNOT_CONNECT_DGRAM */
+		/* Make socket non-blocking.  */
+		int fl = __fcntl (EXT(statp).nssocks[ns], F_GETFL);
+		if  (fl != -1)
+			__fcntl (EXT(statp).nssocks[ns], F_SETFL,
+				 fl | O_NONBLOCK);
 		Dprint(statp->options & RES_DEBUG,
 		       (stdout, ";; new DG socket\n"))
 	}
 	s = EXT(statp).nssocks[ns];
+	/*
+	 * Compute time for the total operation.
+	 */
+	seconds = (statp->retrans << ns);
+	if (ns > 0)
+		seconds /= statp->nscount;
+	if (seconds <= 0)
+		seconds = 1;
+	evNowTime(&now);
+	evConsTime(&timeout, seconds, 0);
+	evAddTime(&finish, &now, &timeout);
+	int need_recompute = 0;
+ resend:
+#ifdef _LIBC
+        /* Convert struct timespec in milliseconds.  */
+	ptimeout = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
+
+	pfd[0].fd = s;
+	pfd[0].events = POLLOUT;
+	n = __poll (pfd, 1, 0);
+	if (__builtin_expect (n == 0, 0)) {
+		n = __poll (pfd, 1, ptimeout);
+		need_recompute = 1;
+	}
+#else
+	FD_ZERO(&dsmask);
+	FD_SET(s, &dsmask);
+	struct timeval zerotime = { 0, 0 };
+	n = pselect(s + 1, NULL, &dsmask, NULL, &zerotime, NULL);
+	if (n == 0) {
+		n = pselect(s + 1, NULL, &dsmask, NULL, &timeout, NULL);
+		need_recompute = 1;
+	}
+#endif
+	if (n == 0) {
+		Dprint(statp->options & RES_DEBUG, (stdout,
+						    ";; timeout sending\n"));
+		*gotsomewhere = 1;
+		return (0);
+	}
+	if (n < 0) {
+		if (errno == EINTR) {
+		recompute_resend:
+			evNowTime(&now);
+			if (evCmpTime(finish, now) > 0) {
+				evSubTime(&timeout, &finish, &now);
+				goto resend;
+			}
+		}
+		Perror(statp, stderr, "select", errno);
+		res_nclose(statp);
+		return (0);
+	}
+	__set_errno (0);
 #ifndef CANNOT_CONNECT_DGRAM
 	if (send(s, (char*)buf, buflen, 0) != buflen) {
+		if (errno == EINTR || errno == EAGAIN)
+			goto recompute_resend;
 		Perror(statp, stderr, "send", errno);
 		res_nclose(statp);
 		return (0);
@@ -984,6 +1046,8 @@ send_dg(res_state statp,
 	if (sendto(s, (char*)buf, buflen, 0,
 		   (struct sockaddr *)nsap, sizeof *nsap) != buflen)
 	{
+		if (errno == EINTR || errno == EAGAIN)
+			goto recompute_resend;
 		Aerror(statp, stderr, "sendto", errno,
 		       (struct sockaddr *) nsap);
 		res_nclose(statp);
@@ -991,46 +1055,38 @@ send_dg(res_state statp,
 	}
 #endif /* !CANNOT_CONNECT_DGRAM */
 
-	/*
-	 * Wait for reply.
-	 */
-	seconds = (statp->retrans << ns);
-	if (ns > 0)
-		seconds /= statp->nscount;
-	if (seconds <= 0)
-		seconds = 1;
-	evNowTime(&now);
-	evConsTime(&timeout, seconds, 0);
-	evAddTime(&finish, &now, &timeout);
  wait:
+	if (need_recompute) {
+		evNowTime(&now);
+		if (evCmpTime(finish, now) <= 0) {
+		err_return:
+			Perror(statp, stderr, "select", errno);
+			res_nclose(statp);
+			return (0);
+		}
+		evSubTime(&timeout, &finish, &now);
+	}
 #ifdef _LIBC
         /* Convert struct timespec in milliseconds.  */
 	ptimeout = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
 
-	pfd[0].fd = s;
 	pfd[0].events = POLLIN;
 	n = __poll (pfd, 1, ptimeout);
 #else
-	FD_ZERO(&dsmask);
-	FD_SET(s, &dsmask);
 	n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
 #endif
 	if (n == 0) {
-		Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
+		Dprint(statp->options & RES_DEBUG, (stdout,
+						    ";; timeout receiving\n"));
 		*gotsomewhere = 1;
 		return (0);
 	}
 	if (n < 0) {
 		if (errno == EINTR) {
-			evNowTime(&now);
-			if (evCmpTime(finish, now) > 0) {
-				evSubTime(&timeout, &finish, &now);
-				goto wait;
-			}
+			need_recompute = 1;
+			goto wait;
 		}
-		Perror(statp, stderr, "select", errno);
-		res_nclose(statp);
-		return (0);
+		goto err_return;
 	}
 	__set_errno (0);
 #ifdef _LIBC
@@ -1056,6 +1112,10 @@ send_dg(res_state statp,
 	resplen = recvfrom(s, (char*)ans, anssiz,0,
 			   (struct sockaddr *)&from, &fromlen);
 	if (resplen <= 0) {
+		if (errno == EINTR || errno == EAGAIN) {
+			need_recompute = 1;
+			goto wait;
+		}
 		Perror(statp, stderr, "recvfrom", errno);
 		res_nclose(statp);
 		return (0);