summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudius Zingerli <gitmail@zeuz.ch>2018-02-18 21:01:04 +0100
committerSami Kerola <kerolasa@iki.fi>2018-10-21 14:40:30 +0100
commit918e824dc13a39e4d68fcd82fd2d248c9fba6bbd (patch)
treea02b9e73dd12c9e19d3bfaddc4286c10022c4787
parent55e1bc80f75a1dacc6f02da24a33543402bb3f93 (diff)
downloadiputils-918e824dc13a39e4d68fcd82fd2d248c9fba6bbd.tar.gz
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.] Reviewed-by: Sami Kerola <kerolasa@iki.fi> Reference: https://github.com/iputils/iputils/pull/122
-rw-r--r--ping.c68
1 files changed, 48 insertions, 20 deletions
diff --git a/ping.c b/ping.c
index 48dc031..2d3adc0 100644
--- a/ping.c
+++ b/ping.c
@@ -55,6 +55,7 @@
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <ifaddrs.h>
+#include <math.h>
#ifndef ICMP_FILTER
#define ICMP_FILTER 1
@@ -182,6 +183,40 @@ static void set_socket_option(socket_st *sock, int level, int optname, const voi
error(2, errno, "setsockopt");
}
+/* 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:
+ if (errno == ERANGE)
+ error(2, errno, "%s: %s", err_msg, str);
+ error(2, 0, "%s: %s", err_msg, str);
+ return num;
+}
+
int
main(int argc, char **argv)
{
@@ -275,27 +310,15 @@ main(int argc, char **argv)
break;
case 'i':
{
- double dbl;
- char *ep;
+ double optval;
- 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)
+ optval = ping_strtod(optarg, "bad timing interval");
+ if (isgreater(optval, (double)(INT_MAX / 1000)))
error(2, 0, "bad timing interval: %s", optarg);
-
- interval = (int)(dbl * 1000);
-
+ interval = (int)(optval * 1000);
options |= F_INTERVAL;
- break;
}
+ break;
case 'I':
/* IPv6 */
if (strchr(optarg, ':')) {
@@ -411,10 +434,15 @@ main(int argc, char **argv)
error(2, 0, "bad wait time: %s", optarg);
break;
case 'W':
- lingertime = atoi(optarg);
- if (lingertime <= 0 || lingertime > INT_MAX / 1000000) {
+ {
+ double optval;
+
+ optval = ping_strtod(optarg, "bad linger time");
+ if (isless(optval, 0.001) || isgreater(optval, (double)(INT_MAX / 1000)))
error(2, 0, "bad linger time: %s", optarg);
- lingertime *= 1000;
+ /* lingertime will be converted to usec later */
+ lingertime = (int)(optval * 1000);
+ }
break;
default:
usage();