Blob Blame History Raw
From 9633d828e8166e47af733cbc6563ac93e5e06a30 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Wed, 9 Dec 2020 10:23:09 +0100
Subject: [PATCH] ping: add support for sub-second timeouts

Timeouts (-W) were previously silently rounded down to the next lower
integral number.  Subsecond values were rounded to zero which resulted in
infinite timeouts, therefore ping never exited if there were no responses
and timeouts below 1s.  This commit fixes this issue.

[Sami: I changed ping_strtod() to return double.  Claudius did updated
needed value by pointer reference, and had multiplication by 1000 in
wrapper function.  I think that made understanding the code unnecessarily
difficult, so implementation was slightly changed.]

Backported from upstream PATCH:
  918e824dc13a39e4d68fcd82fd2d248c9fba6bbd  Claudius Zingerli <gitmail@zeuz.ch>
  93dfb95d48977d151dbe94983e4998959e748aee  Rosen Penev <rosenp@gmail.com>
---
 ping.c | 89 ++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 58 insertions(+), 31 deletions(-)

diff --git a/ping.c b/ping.c
index d9a3f5d..33f7d45 100644
--- a/ping.c
+++ b/ping.c
@@ -57,6 +57,7 @@
 #ifndef WITHOUT_IFADDRS
 #include <ifaddrs.h>
 #endif
+#include <math.h>
 
 #ifndef ICMP_FILTER
 #define ICMP_FILTER	1
@@ -192,6 +193,38 @@ static void set_socket_option(socket_st *sock, int level, int optname, const voi
 	}
 }
 
+/* Much like stdtod(3, but will fails if str is not valid number. */
+static double ping_strtod(const char *str, const char *err_msg)
+{
+	double num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+#ifdef USE_IDN
+	setlocale(LC_ALL, "C");
+#endif
+	num = strtod(str, &end);
+#ifdef USE_IDN
+	setlocale(LC_ALL, "");
+#endif
+	if (errno || str == end || (end && *end))
+		goto err;
+	switch (fpclassify(num)) {
+	case FP_NORMAL:
+	case FP_ZERO:
+		break;
+	default:
+		errno = ERANGE;
+		goto err;
+	}
+	return num;
+err:
+	fprintf(stderr, "%s: %s", err_msg, str);
+	exit(2);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -298,30 +331,19 @@ main(int argc, char **argv)
 			options |= F_PTIMEOFDAY;
 			break;
 		case 'i':
-		{
-			double dbl;
-			char *ep;
-
-			errno = 0;
-#ifdef USE_IDN
-			setlocale(LC_ALL, "C");
-#endif
-			dbl = strtod(optarg, &ep);
-#ifdef USE_IDN
-			setlocale(LC_ALL, "");
-#endif
-
-			if (errno || *ep != '\0' ||
-				!finite(dbl) || dbl < 0.0 || dbl >= (double)INT_MAX / 1000 - 1.0) {
-				fprintf(stderr, "ping: bad timing interval\n");
-				exit(2);
-			}
-
-			interval = (int)(dbl * 1000);
-
-			options |= F_INTERVAL;
-			break;
-		}
+                {
+                        double optval;
+
+                        optval = ping_strtod(optarg, "bad timing interval");
+                        if (isgreater(optval, (double)INT_MAX / 1000)) {
+                                fprintf(stderr, "ping: bad timing interval\n");
+                                exit(2);
+                        }
+
+                        interval = (int)(optval * 1000);
+                        options |= F_INTERVAL;
+                }
+                        break;
 		case 'I':
 			/* IPv6 */
 			if (strchr(optarg, ':')) {
@@ -460,13 +482,18 @@ main(int argc, char **argv)
 			}
 			break;
 		case 'W':
-			lingertime = atoi(optarg);
-			if (lingertime < 0 || lingertime > INT_MAX/1000000) {
-				fprintf(stderr, "ping: bad linger time.\n");
-				exit(2);
-			}
-			lingertime *= 1000;
-			break;
+                {
+                        double optval;
+
+                        optval = ping_strtod(optarg, "bad linger time");
+                        if (isless(optval, 0.001) || isgreater(optval, (double)INT_MAX / 1000)) {
+                                fprintf(stderr, "ping: bad linger time.\n");
+                                exit(2);
+                        }
+                        /* lingertime will be converted to usec later */
+                        lingertime = (int)(optval * 1000);
+                }
+                        break;
 		default:
 			usage();
 			break;
-- 
2.29.2