• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

schweikert / fping / 24923727977

25 Apr 2026 05:35AM UTC coverage: 88.0% (-0.09%) from 88.091%
24923727977

Pull #463

github

web-flow
Merge 9c87f385f into a468978b3
Pull Request #463: New option --oiface for outgoing interface

53 of 63 new or added lines in 3 files covered. (84.13%)

1 existing line in 1 file now uncovered.

1672 of 1900 relevant lines covered (88.0%)

344.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.83
/src/fping.c
1
/*
2
 * fping: fast-ping, file-ping, favorite-ping, funky-ping
3
 *
4
 *   Ping a list of target hosts in a round robin fashion.
5
 *   A better ping overall.
6
 *
7
 * fping website:  http://www.fping.org
8
 *
9
 * Current maintainer of fping: David Schweikert
10
 * Please send suggestions and patches to: david@schweikert.ch
11
 *
12
 *
13
 * Original author:  Roland Schemers  <schemers@stanford.edu>
14
 * IPv6 Support:     Jeroen Massar    <jeroen@unfix.org / jeroen@ipng.nl>
15
 * Improved main loop: David Schweikert <david@schweikert.ch>
16
 * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
17
 * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
18
 *
19
 *
20
 * Redistribution and use in source and binary forms are permitted
21
 * provided that the above copyright notice and this paragraph are
22
 * duplicated in all such forms and that any documentation,
23
 * advertising materials, and other materials related to such
24
 * distribution and use acknowledge that the software was developed
25
 * by Stanford University.  The name of the University may not be used
26
 * to endorse or promote products derived from this software without
27
 * specific prior written permission.
28
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31
 */
32

33
#ifdef __cplusplus
34
extern "C" {
35
#endif /* __cplusplus */
36

37
#include "config.h"
38
#include "fping.h"
39
#include "flags.h"
40
#include "options.h"
41
#include "optparse.h"
42

43
#include <errno.h>
44
#include <inttypes.h>
45
#include <limits.h>
46
#include <signal.h>
47
#include <stdarg.h>
48
#include <stdint.h>
49
#include <stdio.h>
50
#include <time.h>
51

52
#include "seqmap.h"
53
#include "output.h"
54
#include "stats.h"
55

56
#ifdef HAVE_UNISTD_H
57
#include <unistd.h>
58
#endif /* HAVE_UNISTD_H */
59

60
#ifdef HAVE_STDLIB_H
61
#include <stdlib.h>
62
#endif /* HAVE_STDLIB_H */
63

64
#include <stddef.h>
65
#include <string.h>
66

67
#include <sys/socket.h>
68
#include <sys/time.h>
69
#include <sys/types.h>
70

71
#if HAVE_SYS_FILE_H
72
#include <sys/file.h>
73
#endif /* HAVE_SYS_FILE_H */
74

75
#ifdef IPV6
76
#include <netinet/icmp6.h>
77
#include <netinet/ip6.h>
78
#endif
79
#include <netinet/in_systm.h>
80

81
#include <netinet/ip.h>
82
#include <netinet/ip_icmp.h>
83

84
#include <arpa/inet.h>
85
#include <ctype.h>
86
#include <netdb.h>
87

88
#include <sys/select.h>
89

90
/*** compatibility ***/
91

92
/* Mac OS X's getaddrinfo() does not fail if we use an invalid combination,
93
 * e.g. AF_INET6 with "127.0.0.1". If we pass AI_UNUSABLE to flags, it behaves
94
 * like other platforms. But AI_UNUSABLE isn't available on other platforms,
95
 * and we can safely use 0 for flags instead.
96
 */
97
#ifndef AI_UNUSABLE
98
#define AI_UNUSABLE 0
99
#endif
100

101
/* MSG_TRUNC available on Linux kernel 2.2+, makes recvmsg return the full
102
 * length of the raw packet received, even if the buffer is smaller */
103
#ifndef MSG_TRUNC
104
#define MSG_TRUNC 0
105
#define RECV_BUFSIZE 4096
106
#else
107
#define RECV_BUFSIZE 128
108
#endif
109

110
/*** externals ***/
111

112
extern char *optarg;
113
extern int optind, opterr;
114
#ifndef h_errno
115
extern int h_errno;
116
#endif
117

118
#ifdef __cplusplus
119
}
120
#endif /* __cplusplus */
121

122
/*** Constants ***/
123

124
/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
125
 * see github PR #217
126
 * The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
127
 */
128
#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
129
#define CLOCKID CLOCK_REALTIME
130
#endif
131

132
#if !defined(CLOCKID)
133
#if defined(CLOCK_MONOTONIC)
134
#define CLOCKID CLOCK_MONOTONIC
135
#else
136
#define CLOCKID CLOCK_REALTIME
137
#endif
138
#endif
139

140
/*** Ping packet defines ***/
141

142
#define MAX_IP_PACKET 65535 /* (theoretical) max IPv4 packet size */
143
#define SIZE_IP_HDR 20 /* min IPv4 header size */
144
#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
145
#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
146

147
#define MAX_GENERATE 131072 /* maximum number of hosts that -g can generate */
148
#define MAX_TARGET_NAME_LEN 255 /* maximum target name length read from file */
149

150
/* ICMP Timestamp has a fixed payload size of 12 bytes */
151
#define ICMP_TIMESTAMP_DATA_SIZE 12
152

153
#ifdef FPING_SAFE_LIMITS
154
#define MIN_INTERVAL_MS 1 /* in millisec */
155
#define MIN_PERHOST_INTERVAL_MS 10 /* in millisec */
156
#else
157
#define MIN_INTERVAL_MS 0
158
/* Set a very low limit for the per-host interval, even if safe limits are
159
 * disabled, so that the memory allocation of the event storage is not
160
 * unreasonably high. 0.001 ms would mean in theory at least 592 mbps of data
161
 * sent to a single host, which probably doesn't make sense in any scenario. */
162
#define MIN_PERHOST_INTERVAL_MS 0.001
163
#endif
164

165
/* response time array flags */
166
#define RESP_WAITING -1
167
#define RESP_UNUSED -2
168
#define RESP_ERROR -3
169
#define RESP_TIMEOUT -4
170

171
/* debugging flags */
172
#if defined(DEBUG) || defined(_DEBUG)
173
#define DBG_TRACE 1
174
#define DBG_SENT_TIMES 2
175
#define DBG_RANDOM_LOSE_FEW 4
176
#define DBG_RANDOM_LOSE_MANY 8
177
#define DBG_PRINT_PER_SYSTEM 16
178
#define DBG_REPORT_ALL_RTTS 32
179
#endif /* DEBUG || _DEBUG */
180

181
/* Long names for ICMP packet types */
182
#define ICMP_TYPE_STR_MAX 18
183
char *icmp_type_str[19] = {
184
    "ICMP Echo Reply", /* 0 */
185
    "",
186
    "",
187
    "ICMP Unreachable", /* 3 */
188
    "ICMP Source Quench", /* 4 */
189
    "ICMP Redirect", /* 5 */
190
    "",
191
    "",
192
    "ICMP Echo", /* 8 */
193
    "",
194
    "",
195
    "ICMP Time Exceeded", /* 11 */
196
    "ICMP Parameter Problem", /* 12 */
197
    "ICMP Timestamp Request", /* 13 */
198
    "ICMP Timestamp Reply", /* 14 */
199
    "ICMP Information Request", /* 15 */
200
    "ICMP Information Reply", /* 16 */
201
    "ICMP Mask Request", /* 17 */
202
    "ICMP Mask Reply" /* 18 */
203
};
204

205
char *icmp_unreach_str[16] = {
206
    "ICMP Network Unreachable", /* 0 */
207
    "ICMP Host Unreachable", /* 1 */
208
    "ICMP Protocol Unreachable", /* 2 */
209
    "ICMP Port Unreachable", /* 3 */
210
    "ICMP Unreachable (Fragmentation Needed)", /* 4 */
211
    "ICMP Unreachable (Source Route Failed)", /* 5 */
212
    "ICMP Unreachable (Destination Network Unknown)", /* 6 */
213
    "ICMP Unreachable (Destination Host Unknown)", /* 7 */
214
    "ICMP Unreachable (Source Host Isolated)", /* 8 */
215
    "ICMP Unreachable (Communication with Network Prohibited)", /* 9 */
216
    "ICMP Unreachable (Communication with Host Prohibited)", /* 10 */
217
    "ICMP Unreachable (Network Unreachable For Type Of Service)", /* 11 */
218
    "ICMP Unreachable (Host Unreachable For Type Of Service)", /* 12 */
219
    "ICMP Unreachable (Communication Administratively Prohibited)", /* 13 */
220
    "ICMP Unreachable (Host Precedence Violation)", /* 14 */
221
    "ICMP Unreachable (Precedence cutoff in effect)" /* 15 */
222
};
223

224
#define ICMP_UNREACH_MAXTYPE 15
225

226
#ifdef IPV6
227
/* Long names for ICMPv6 unreachable codes */
228
#define ICMP6_UNREACH_MAXCODE 9
229
char *icmp6_unreach_str[ICMP6_UNREACH_MAXCODE + 1] = {
230
    "No route to destination", /* 0 */
231
    "Communication with destination administratively prohibited", /* 1 */
232
    "Beyond scope of source address", /* 2 */
233
    "Address unreachable", /* 3 */
234
    "Port unreachable", /* 4 */
235
    "Source address failed ingress/egress policy", /* 5 */
236
    "Reject route to destination", /* 6 */
237
    "Error in Source Routing Header", /* 7 */
238
    "Headers too long", /* 8 */
239
    "Error in P-Route", /* 9 */
240
};
241

242
/* Long names for ICMPv6 time exceeded codes */
243
#define ICMP6_TIME_EXCEEDED_MAXCODE 1
244
char *icmp6_time_exceeded_str[ICMP6_TIME_EXCEEDED_MAXCODE + 1] = {
245
    "Hop limit exceeded in transit", /* 0 */
246
    "Fragment reassembly time exceeded", /* 1 */
247
};
248

249
/* Long names for ICMPv6 parameter problem codes */
250
#define ICMP6_PARAM_PROB_MAXCODE 10
251
char *icmp6_param_prob_str[ICMP6_PARAM_PROB_MAXCODE + 1] = {
252
    "Erroneous header field encountered", /* 0 */
253
    "Unrecognized Next Header type encountered", /* 1 */
254
    "Unrecognized IPv6 option encountered", /* 2 */
255
    "IPv6 First Fragment has incomplete IPv6 Header Chain", /* 3 */
256
    "SR Upper-layer Header Error", /* 4 */
257
    "Unrecognized Next Header type encountered by intermediate node", /* 5 */
258
    "Extension header too big", /* 6 */
259
    "Extension header chain too long", /* 7 */
260
    "Too many extension headers", /* 8 */
261
    "Too many options in extension header", /* 9 */
262
    "Option too big", /* 10 */
263
};
264
#endif
265

266
IP_HEADER_RESULT default_ip_header_result() {
3,130✔
267
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
3,130✔
268
}
269

270
int event_storage_count;
271

272
/*** globals ***/
273

274
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
275

276
/* we keep two separate queues: a ping queue, for when the next ping should be
277
 * sent, and a timeout queue. the reason for having two separate queues is that
278
 * the ping period and the timeout value are different, so if we put them in
279
 * the same event queue, we would need to scan many more entries when inserting
280
 * into the sorted list.
281
 */
282
struct event_queue event_queue_ping;
283
struct event_queue event_queue_timeout;
284

285
char *prog;
286
int ident4 = 0; /* our icmp identity field */
287
int ident6 = 0;
288
const int sock_opt_on = 1; /* to activate a socket option */
289
int socket4 = -1;
290
int socktype4 = -1;
291
int using_sock_dgram4 = 0;
292
#ifndef IPV6
293
int hints_ai_family = AF_INET;
294
#else
295
int socket6 = -1;
296
int socktype6 = -1;
297
int hints_ai_family = AF_UNSPEC;
298
#endif
299

300
volatile sig_atomic_t status_snapshot = 0;
301
volatile sig_atomic_t finish_requested = 0;
302

303
unsigned int debugging = 0;
304

305
unsigned int trials;
306
int64_t report_interval = 0;
307
int src_addr_set = 0;
308
struct in_addr src_addr;
309
#ifdef IPV6
310
int src_addr6_set = 0;
311
struct in6_addr src_addr6;
312
#endif
313

314
/* global stats */
315
int64_t max_reply = 0;
316
int64_t min_reply = 0;
317
int64_t total_replies = 0;
318
int64_t sum_replies = 0;
319
int max_hostname_len = 0;
320
int num_hosts = 0; /* total number of hosts */
321
int num_alive = 0, /* total number alive */
322
    num_unreachable = 0, /* total number unreachable */
323
    num_noaddress = 0; /* total number of addresses not found */
324
int num_timeout = 0, /* number of times select timed out */
325
    num_pingsent = 0, /* total pings sent */
326
    num_pingreceived = 0, /* total pings received */
327
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
328

329
struct timespec current_time; /* current time (pseudo) */
330
int64_t current_time_ns;
331
int64_t start_time;
332
int64_t end_time;
333
int64_t last_send_time; /* time last ping was sent */
334
int64_t next_report_time; /* time next -Q report is expected */
335

336

337
#if defined(DEBUG) || defined(_DEBUG)
338
int opt_debug_randomly_lose_on, opt_debug_trace_on, opt_debug_print_per_system_on;
339
int lose_factor;
340
#endif /* DEBUG || _DEBUG */
341

342
unsigned int fwmark = 0;
343

344
char *filename = NULL; /* file containing hosts to ping */
345

346
/************************************************************
347

348
  Function: p_setsockopt
349

350
*************************************************************
351

352
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
353

354
  Description:
355

356
  Elevates privileges to p_uid when required, calls
357
  setsockopt, and drops privileges back.
358

359
************************************************************/
360

361
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
362
    const void *optval, socklen_t optlen)
363
{
364
    const uid_t saved_uid = geteuid();
22✔
365
    int res;
366

367
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
368
        perror("cannot elevate privileges for setsockopt");
×
369
    }
370

371
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
372

373
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
374
        perror("fatal error: could not drop privileges after setsockopt");
×
375
        /* continuing would be a security hole */
376
        exit(4);
×
377
    }
378

379
    return res;
22✔
380
}
381

382
unsigned long strtoul_strict(const char *arg, int base)
454✔
383
{
384
    char *endptr;
385
    unsigned long val;
386

387
    while (isspace(*arg))
480✔
388
        arg++;
26✔
389

390
    if (arg[0] == '-')
454✔
391
        usage(1);
52✔
392

393
    errno = 0;
418✔
394
    val = strtoul(arg, &endptr, base);
418✔
395
    if (errno != 0 || arg == endptr || *endptr != '\0')
418✔
396
        usage(1);
100✔
397

398
    return val;
344✔
399
}
400

401
double strtod_strict(const char *arg)
290✔
402
{
403
    char *endptr;
404
    double val;
405

406
    errno = 0;
290✔
407
    val = strtod(arg, &endptr);
290✔
408
    if (errno != 0 || arg == endptr || *endptr != '\0' || val < 0)
290✔
409
        usage(1);
102✔
410

411
    return val;
222✔
412
}
413

414
/************************************************************
415

416
  Function: main
417

418
*************************************************************
419

420
  Inputs:  int argc, char** argv
421

422
  Description:
423

424
  Main program entry point
425

426
************************************************************/
427

428
int main(int argc, char **argv)
972✔
429
{
430
/* Debug: CPU Performance */
431
#if defined(DEBUG) || defined(_DEBUG)
432
    clock_t perf_cpu_start, perf_cpu_end;
433
    double perf_cpu_time_used;
434
    perf_cpu_start = clock();
435
#endif /* DEBUG || _DEBUG */
436

437
    int c;
438
    char *endptr;
439
    const uid_t suid = geteuid();
972✔
440
    int tos = 0;
972✔
441
    struct optparse optparse_state;
442
#ifdef USE_SIGACTION
443
    struct sigaction act;
444
#endif
445

446
    /* pre-parse -h/--help, so that we also can output help information
447
     * without trying to open the socket, which might fail */
448
    prog = argv[0];
972✔
449
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
972✔
450
        usage(0);
3✔
451
    }
1✔
452

453
    socket4 = open_ping_socket_ipv4(&socktype4);
970✔
454
#ifdef __linux__
455
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
456
     * structure is missing in the message.
457
     */
458
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
642✔
459
#endif
460

461
#ifdef IPV6
462
    socket6 = open_ping_socket_ipv6(&socktype6);
970✔
463
    /* if called (sym-linked) via 'fping6', imply '-6'
464
     * for backward compatibility */
465
    if (strstr(prog, "fping6")) {
970✔
466
        hints_ai_family = AF_INET6;
2✔
467
    }
468
#endif
469

470
    memset(&src_addr, 0, sizeof(src_addr));
970✔
471
#ifdef IPV6
472
    memset(&src_addr6, 0, sizeof(src_addr6));
970✔
473
#endif
474

475
    if (!suid && suid != getuid()) {
970✔
476
        /* *temporarily* drop privileges */
477
        if (seteuid(getuid()) == -1)
869✔
478
            perror("cannot setuid");
×
479
    }
239✔
480

481
    optparse_init(&optparse_state, argv);
970✔
482
    ident4 = ident6 = htons(getpid() & 0xFFFF);
970✔
483
    opt_verbose_on = 1;
970✔
484
    opt_backoff_on = 1;
970✔
485
    opterr = 1;
970✔
486

487
    /* get command line options */
488

489
    struct optparse_long longopts[] = {
970✔
490
        { "ipv4", '4', OPTPARSE_NONE },
491
        { "ipv6", '6', OPTPARSE_NONE },
492
        { "alive", 'a', OPTPARSE_NONE },
493
        { "addr", 'A', OPTPARSE_NONE },
494
        { "size", 'b', OPTPARSE_REQUIRED },
495
        { "backoff", 'B', OPTPARSE_REQUIRED },
496
        { "count", 'c', OPTPARSE_REQUIRED },
497
        { "vcount", 'C', OPTPARSE_REQUIRED },
498
        { "rdns", 'd', OPTPARSE_NONE },
499
        { "timestamp", 'D', OPTPARSE_NONE },
500
        { "timestamp-format", 0, OPTPARSE_REQUIRED },
501
        { "elapsed", 'e', OPTPARSE_NONE },
502
        { "file", 'f', OPTPARSE_REQUIRED },
503
        { "generate", 'g', OPTPARSE_NONE },
504
        { "help", 'h', OPTPARSE_NONE },
505
        { "ttl", 'H', OPTPARSE_REQUIRED },
506
        { "interval", 'i', OPTPARSE_REQUIRED },
507
        { "iface", 'I', OPTPARSE_REQUIRED },
508
        { "oiface", 0, OPTPARSE_REQUIRED },
509
        { "json", 'J', OPTPARSE_NONE },
510
        { "icmp-timestamp", 0, OPTPARSE_NONE },
511
#ifdef SO_MARK
512
        { "fwmark", 'k', OPTPARSE_REQUIRED },
513
#endif
514
        { "loop", 'l', OPTPARSE_NONE },
515
        { "all", 'm', OPTPARSE_NONE },
516
        { "dontfrag", 'M', OPTPARSE_NONE },
517
        { "name", 'n', OPTPARSE_NONE },
518
        { "netdata", 'N', OPTPARSE_NONE },
519
        { "outage", 'o', OPTPARSE_NONE },
520
        { "tos", 'O', OPTPARSE_REQUIRED },
521
        { "period", 'p', OPTPARSE_REQUIRED },
522
        { "quiet", 'q', OPTPARSE_NONE },
523
        { "squiet", 'Q', OPTPARSE_REQUIRED },
524
        { "retry", 'r', OPTPARSE_REQUIRED },
525
        { "random", 'R', OPTPARSE_NONE },
526
        { "stats", 's', OPTPARSE_NONE },
527
        { "src", 'S', OPTPARSE_REQUIRED },
528
        { "timeout", 't', OPTPARSE_REQUIRED },
529
        { NULL, 'T', OPTPARSE_REQUIRED },
530
        { "unreach", 'u', OPTPARSE_NONE },
531
        { "version", 'v', OPTPARSE_NONE },
532
        { "reachable", 'x', OPTPARSE_REQUIRED },
533
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
534
        { "check-source", 0, OPTPARSE_NONE },
535
        { "print-tos", 0, OPTPARSE_NONE },
536
        { "print-ttl", 0, OPTPARSE_NONE },
537
        { "seqmap-timeout", 0, OPTPARSE_REQUIRED },
538
#if defined(DEBUG) || defined(_DEBUG)
539
        { NULL, 'z', OPTPARSE_REQUIRED },
540
#endif
541
        { 0, 0, 0 }
542
    };
543

544
    double opt_val_double;
545
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
2,203✔
546
        switch (c) {
1,454✔
547
        case 0:
120✔
548
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
157✔
549
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
550
                  opt_timestamp_format = 1;
6✔
551
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
552
                  opt_timestamp_format = 2;
6✔
553
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
554
                  opt_timestamp_format = 3;
6✔
555
                }else{
2✔
556
                  usage(1);
6✔
557
                }
558
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
141✔
559
                opt_check_source_on = 1;
9✔
560
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
125✔
561
#ifdef IPV6
562
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
563
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
564
                    exit(1);
2✔
565
                }
566
                hints_ai_family = AF_INET;
27✔
567
#endif
568
                opt_icmp_request_typ = 13;
27✔
569
                opt_ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
570
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
98✔
571
                opt_print_tos_on = 1;
35✔
572
#if defined(HAVE_IP_RECVTOS)
573
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
35✔
574
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
575
                        perror("setsockopt IP_RECVTOS");
×
576
                    }
577
                }
578
#endif
579
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
580
                if (socket6 >= 0) {
35✔
581
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
35✔
582
                        perror("setsockopt IPV6_RECVTCLASS");
×
583
                    }
584
                }
9✔
585
#endif
586
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
69✔
587
                opt_print_ttl_on = 1;
29✔
588
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
589
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
590
                        perror("setsockopt IP_RECVTTL");
×
591
                    }
592
                }
593
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
594
                if (socket6 >= 0) {
29✔
595
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
596
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
597
                    }
598
                }
7✔
599
#endif
600
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
38✔
601
                opt_seqmap_timeout = strtod_strict(optparse_state.optarg) * 1000000;
27✔
602
            } else if (strstr(optparse_state.optlongname, "oiface") != NULL) {
13✔
603
                opt_oiface_on = 1;
4✔
604
#ifdef IP_PKTINFO
605
                if (socket4 >= 0) {
4✔
606
                    socket_set_outgoing_iface_ipv4(socket4, optparse_state.optarg);
4✔
607
                }
608
#ifdef IPV6
609
                if (socket6 >= 0) {
4✔
610
                    socket_set_outgoing_iface_ipv6(optparse_state.optarg);
4✔
611
                }
612
#endif
613
#else
614
                fprintf(stderr, "%s: --oiface is not supported on this platform (IP_PKTINFO unavailable)\n", prog);
615
                exit(3);
616
#endif
617
            } else {
618
                usage(1);
×
619
            }
620
            break;
137✔
621
        case '4':
32✔
622
#ifdef IPV6
623
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
624
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
625
                exit(1);
3✔
626
            }
627
            hints_ai_family = AF_INET;
40✔
628
#endif
629
            break;
40✔
630
        case '6':
30✔
631
#ifdef IPV6
632
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
33✔
633
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
634
                exit(1);
5✔
635
            }
636
            hints_ai_family = AF_INET6;
28✔
637
#else
638
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
639
            exit(1);
640
#endif
641
            break;
28✔
642
        case 'M':
2✔
643
#ifdef IP_MTU_DISCOVER
644
            if (socket4 >= 0) {
2✔
645
                int val = IP_PMTUDISC_DO;
2✔
646
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
647
                    perror("setsockopt IP_MTU_DISCOVER");
648
                }
649
            }
650
#ifdef IPV6
651
            if (socket6 >= 0) {
2✔
652
                int val = IPV6_PMTUDISC_DO;
2✔
653
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
654
                    perror("setsockopt IPV6_MTU_DISCOVER");
655
                }
656
            }
657
#endif
658
#else
659
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
660
            exit(1);
661
#endif
662
            break;
2✔
663

664
        case 't':
56✔
665
            opt_timeout = strtod_strict(optparse_state.optarg) * 1000000;
66✔
666
            opt_timeout_on = 1;
52✔
667
            break;
52✔
668

669
        case 'r':
44✔
670
            opt_retry = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
51✔
671
            break;
41✔
672

673
        case 'i':
38✔
674
            opt_interval = strtod_strict(optparse_state.optarg) * 1000000;
48✔
675
            break;
34✔
676

677
        case 'p':
90✔
678
            opt_perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
123✔
679
            break;
109✔
680

681
        case 'c':
102✔
682
            opt_count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
139✔
683
            if (!opt_count)
129✔
684
                usage(1);
3✔
685

686
            opt_count_on = 1;
127✔
687
            break;
127✔
688

689
        case 'C':
42✔
690
            opt_count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
60✔
691
            if (!opt_count)
50✔
692
                usage(1);
3✔
693

694
            opt_count_on = 1;
48✔
695
            opt_report_all_rtts_on = 1;
48✔
696
            break;
48✔
697

698
        case 'b':
20✔
699
            opt_ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
30✔
700
            opt_size_on = 1;
20✔
701
            break;
20✔
702

703
        case 'h':
2✔
704
            usage(0);
3✔
705
            break;
1✔
706

707
        case 'q':
26✔
708
            opt_verbose_on = 0;
38✔
709
            opt_quiet_on = 1;
38✔
710
            break;
38✔
711

712
        case 'Q':
34✔
713
            opt_verbose_on = 0;
43✔
714
            opt_quiet_on = 1;
43✔
715
            errno = 0;
43✔
716
            opt_val_double = strtod(optparse_state.optarg, &endptr);
43✔
717
            if (errno != 0 || optparse_state.optarg == endptr || (*endptr != '\0' && *endptr != ','))
43✔
718
                usage(1);
12✔
719
            if (opt_val_double < 0) {
35✔
720
                usage(1);
9✔
721
            }
3✔
722
            report_interval = opt_val_double * 1e9;
29✔
723

724
            /* recognize keyword(s) after number, ignore everything else */
725
            {
726
                char *comma = strchr(optparse_state.optarg, ',');
29✔
727
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
29✔
728
                    opt_cumulative_stats_on = 1;
4✔
729
                }
730
            }
731

732
            break;
29✔
733

734
        case 'e':
6✔
735
            opt_elapsed_on = 1;
8✔
736
            break;
8✔
737

738
        case 'm':
739
            opt_multif_on = 1;
×
740
            break;
×
741

742
        case 'N':
2✔
743
            opt_print_netdata_on = 1;
3✔
744
            break;
3✔
745

746
        case 'n':
4✔
747
            opt_name_on = 1;
6✔
748
            if (opt_rdns_on) {
6✔
749
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
750
                exit(1);
3✔
751
            }
752
            break;
3✔
753

754
        case 'd':
6✔
755
            opt_rdns_on = 1;
9✔
756
            if (opt_name_on) {
9✔
757
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
758
                exit(1);
3✔
759
            }
760
            break;
6✔
761

762
        case 'A':
2✔
763
            opt_addr_on = 1;
3✔
764
            break;
3✔
765

766
        case 'B':
18✔
767
            opt_backoff = strtod_strict(optparse_state.optarg);
26✔
768
            break;
14✔
769

770
        case 's':
8✔
771
            opt_stats_on = 1;
12✔
772
            break;
12✔
773

774
        case 'D':
22✔
775
            opt_timestamp_on = 1;
33✔
776
            break;
33✔
777

778
        case 'R':
4✔
779
            opt_random_data_on = 1;
5✔
780
            break;
5✔
781

782
        case 'l':
12✔
783
            opt_loop_on = 1;
17✔
784
            opt_backoff_on = 0;
17✔
785
            break;
17✔
786

787
        case 'u':
8✔
788
            opt_unreachable_on = 1;
12✔
789
            break;
12✔
790

791
        case 'a':
18✔
792
            opt_alive_on = 1;
26✔
793
            break;
26✔
794

795
        case 'H':
16✔
796
            opt_ttl = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
797
            if (!opt_ttl)
14✔
798
                usage(1);
3✔
799
            break;
12✔
800

801
#if defined(DEBUG) || defined(_DEBUG)
802
        case 'z':
803
            debugging = (unsigned int)strtoul_strict(optparse_state.optarg, 0);
804
            break;
805
#endif /* DEBUG || _DEBUG */
806

807
        case 'v':
4✔
808
            printf("%s: Version %s\n", prog, VERSION);
6✔
809
            exit(0);
6✔
810

811
        case 'x':
16✔
812
            opt_min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
813
            if (!opt_min_reachable)
14✔
814
                usage(1);
3✔
815
            break;
12✔
816

817
        case 'X':
14✔
818
            opt_min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
21✔
819
            if (!opt_min_reachable)
11✔
820
                usage(1);
3✔
821
            opt_fast_reachable_on = 1;
9✔
822
            break;
9✔
823

824
        case 'f':
18✔
825
            filename = optparse_state.optarg;
27✔
826
            break;
27✔
827
#ifdef SO_MARK
828
        case 'k':
18✔
829
            fwmark = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
18✔
830
            if (!fwmark)
8✔
831
                usage(1);
2✔
832

833
            if (socket4 >= 0)
6✔
834
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
835
                    perror("fwmark ipv4");
4✔
836

837
#ifdef IPV6
838
            if (socket6 >= 0)
6✔
839
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
840
                    perror("fwmark ipv6");
4✔
841
#endif
842

843
            break;
6✔
844
#endif
845

846
        case 'g':
106✔
847
            /* use IP list generation */
848
            /* mutually exclusive with using file input or command line targets */
849
            opt_generate_on = 1;
129✔
850
            break;
129✔
851

852
        case 'S':
10✔
853
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
854
                src_addr_set = 1;
6✔
855
                break;
6✔
856
            }
857
#ifdef IPV6
858
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
859
                src_addr6_set = 1;
4✔
860
                break;
4✔
861
            }
862
#endif
863
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
864
            exit(1);
3✔
865

866
        case 'I':
6✔
867
            opt_bindiface_on = 1;
6✔
868
#ifdef SO_BINDTODEVICE
869
            if (socket4 >= 0) {
6✔
870
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
871
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
872
                    exit(1);
2✔
873
                }
874
            }
875
#ifdef IPV6
876
            if (socket6 >= 0) {
4✔
877
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
878
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
×
879
                    exit(1);
×
880
                }
881
            }
882
#endif
883
#else
884
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
885
            exit(3);
886
            ;
887
#endif
888
            break;
4✔
889

890
        case 'J':
42✔
891
            opt_print_json_on = 1;
62✔
892
            break;
62✔
893

894
        case 'T':
2✔
895
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
896
            break;
3✔
897

898
        case 'O':
16✔
899
            {
900
                unsigned long val = strtoul_strict(optparse_state.optarg, 0);
24✔
901
                if (val > 255)
16✔
902
                    usage(1);
×
903
                tos = (int)val;
16✔
904
            }
905
            if (socket4 >= 0) {
16✔
906
                if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
12✔
907
                    perror("setting type of service octet IP_TOS");
×
908
                }
909
            }
4✔
910
#if defined(IPV6) && defined(IPV6_TCLASS)
911
            if (socket6 >= 0) {
16✔
912
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
12✔
913
                    perror("setting type of service octet IPV6_TCLASS");
×
914
                }
915
            }
4✔
916
#endif
917
            break;
16✔
918

919
        case 'o':
6✔
920
            opt_outage_on = 1;
7✔
921
            break;
7✔
922

923
        case '?':
4✔
924
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
925
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
926
            exit(1);
6✔
927
            break;
928
        }
929
    }
930

931
    /* permanently drop privileges */
932
    if (suid != getuid() && setuid(getuid())) {
573✔
933
        perror("fatal: failed to permanently drop privileges");
×
934
        /* continuing would be a security hole */
935
        exit(4);
×
936
    }
937

938
    /* validate various option settings */
939

940
#ifndef IPV6
941
    if (socket4 < 0) {
942
        crash_and_burn("can't create socket (must run as root?)");
943
    }
944
#else
945
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
573✔
946
        crash_and_burn("can't create socket (must run as root?)");
×
947
    }
948
#endif
949

950
    if (opt_ttl > 255) {
571✔
951
        fprintf(stderr, "%s: ttl %u out of range\n", prog, opt_ttl);
3✔
952
        exit(1);
3✔
953
    }
954

955
    if (opt_unreachable_on && opt_alive_on) {
568✔
956
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
957
        exit(1);
3✔
958
    }
959

960
    if (opt_oiface_on && opt_bindiface_on) {
565✔
NEW
961
        fprintf(stderr, "%s: specify only --oiface or -I, --iface\n", prog);
×
NEW
962
        exit(1);
×
963
    }
964

965
    if (opt_count_on && opt_loop_on) {
565✔
966
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
967
        exit(1);
3✔
968
    }
969

970
    if (opt_print_json_on && !opt_count_on && !opt_loop_on) {
562✔
971
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
972
        exit(1);
3✔
973
    }
974

975
    if (opt_interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
559✔
976
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
977
        exit(1);
3✔
978
    }
979

980
    if (opt_perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
556✔
981
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
982
        exit(1);
3✔
983
    }
984

985
    if (opt_ping_data_size > MAX_PING_DATA) {
553✔
986
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
987
            prog, opt_ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
988
        exit(1);
3✔
989
    }
990

991
    if ((opt_backoff > MAX_BACKOFF_FACTOR) || (opt_backoff < MIN_BACKOFF_FACTOR)) {
550✔
992
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
993
            prog, opt_backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
994
        exit(1);
6✔
995
    }
996

997
    if (opt_icmp_request_typ == 13 && opt_size_on != 0) {
544✔
998
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
999
        exit(1);
9✔
1000
    }
1001

1002
    if (opt_count_on) {
535✔
1003
        if (opt_verbose_on)
157✔
1004
            opt_per_recv_on = 1;
99✔
1005

1006
        opt_alive_on = opt_unreachable_on = opt_verbose_on = 0;
157✔
1007
    }
41✔
1008

1009
    if (opt_loop_on) {
535✔
1010
        if (!report_interval)
14✔
1011
            opt_per_recv_on = 1;
12✔
1012

1013
        opt_alive_on = opt_unreachable_on = opt_verbose_on = 0;
14✔
1014
    }
4✔
1015

1016
    if (opt_alive_on || opt_unreachable_on || opt_min_reachable)
535✔
1017
        opt_verbose_on = 0;
37✔
1018

1019
    trials = (opt_count > opt_retry + 1) ? opt_count : opt_retry + 1;
551✔
1020

1021
    /* auto-tune default timeout for count/loop modes
1022
     * see also github #32 */
1023
    if (opt_loop_on || opt_count_on) {
551✔
1024
        if (!opt_timeout_on) {
171✔
1025
            opt_timeout = opt_perhost_interval;
159✔
1026
            if (opt_timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
159✔
1027
                opt_timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1028
            }
1029
        }
43✔
1030
    }
45✔
1031

1032
#if defined(DEBUG) || defined(_DEBUG)
1033
    if (debugging & DBG_TRACE)
1034
        opt_debug_trace_on = 1;
1035

1036
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1037
        opt_debug_randomly_lose_on = 1;
1038
        lose_factor = 1; /* ie, 1/4 */
1039
    }
1040

1041
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1042
        opt_debug_randomly_lose_on = 1;
1043
        lose_factor = 5; /* ie, 3/4 */
1044
    }
1045

1046
    if (debugging & DBG_PRINT_PER_SYSTEM)
1047
        opt_debug_print_per_system_on = 1;
1048

1049
    if ((debugging & DBG_REPORT_ALL_RTTS) && !opt_loop_on)
1050
        opt_report_all_rtts_on = 1;
1051

1052
    if (opt_debug_trace_on) {
1053
        fprintf(stderr, "%s:\n  opt_count: %u, opt_retry: %u, opt_interval: %.0f ms\n",
1054
            prog, opt_count, opt_retry, opt_interval / 1e6);
1055
        fprintf(stderr, "  opt_perhost_interval: %.0f ms, opt_timeout: %.0f\n",
1056
            opt_perhost_interval / 1e6, opt_timeout / 1e6);
1057
        fprintf(stderr, "  opt_seqmap_timeout: %.0f\n", opt_seqmap_timeout / 1e6);
1058
        fprintf(stderr, "  opt_ping_data_size = %u, trials = %u\n",
1059
            opt_ping_data_size, trials);
1060

1061
        if (opt_verbose_on)
1062
            fprintf(stderr, "  opt_verbose_on set\n");
1063
        if (opt_multif_on)
1064
            fprintf(stderr, "  opt_multif_on set\n");
1065
        if (opt_name_on)
1066
            fprintf(stderr, "  opt_name_on set\n");
1067
        if (opt_addr_on)
1068
            fprintf(stderr, "  opt_addr_on set\n");
1069
        if (opt_stats_on)
1070
            fprintf(stderr, "  opt_stats_on set\n");
1071
        if (opt_unreachable_on)
1072
            fprintf(stderr, "  opt_unreachable_on set\n");
1073
        if (opt_alive_on)
1074
            fprintf(stderr, "  opt_alive_on set\n");
1075
        if (opt_elapsed_on)
1076
            fprintf(stderr, "  opt_elapsed_on set\n");
1077
        if (opt_version_on)
1078
            fprintf(stderr, "  opt_version_on set\n");
1079
        if (opt_count_on)
1080
            fprintf(stderr, "  opt_count_on set\n");
1081
        if (opt_loop_on)
1082
            fprintf(stderr, "  opt_loop_on set\n");
1083
        if (opt_backoff_on)
1084
            fprintf(stderr, "  opt_backoff_on set\n");
1085
        if (opt_per_recv_on)
1086
            fprintf(stderr, "  opt_per_recv_on set\n");
1087
        if (opt_report_all_rtts_on)
1088
            fprintf(stderr, "  opt_report_all_rtts_on set\n");
1089
        if (opt_debug_randomly_lose_on)
1090
            fprintf(stderr, "  opt_debug_randomly_lose_on set\n");
1091
        if (opt_debug_print_per_system_on)
1092
            fprintf(stderr, "  opt_debug_print_per_system_on set\n");
1093
        if (opt_outage_on)
1094
            fprintf(stderr, "  opt_outage_on set\n");
1095
        if (opt_print_netdata_on)
1096
            fprintf(stderr, "  opt_print_netdata_on set\n");
1097
        if (opt_print_json_on)
1098
            fprintf(stderr, "  opt_print_json_on set\n");
1099
    }
1100
#endif /* DEBUG || _DEBUG */
1101

1102
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1103
    if (opt_ttl > 0) {
551✔
1104
        if (socket4 >= 0) {
3✔
1105
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &opt_ttl, sizeof(opt_ttl))) {
3✔
1106
                perror("setting time to live");
×
1107
            }
1108
        }
1✔
1109
#ifdef IPV6
1110
        if (socket6 >= 0) {
3✔
1111
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &opt_ttl, sizeof(opt_ttl))) {
3✔
1112
                perror("setting time to live");
×
1113
            }
1114
        }
1✔
1115
#endif
1116
    }
1✔
1117

1118
#if HAVE_SO_TIMESTAMPNS
1119
    {
1120
        int opt = 1;
406✔
1121
        if (socket4 >= 0) {
406✔
1122
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
406✔
1123
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1124
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1125
                }
1126
            }
1127
        }
1128
#ifdef IPV6
1129
        if (socket6 >= 0) {
406✔
1130
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
406✔
1131
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1132
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1133
                }
1134
            }
1135
        }
1136
#endif
1137
    }
1138
#endif
1139

1140
    update_current_time();
551✔
1141
    start_time = current_time_ns;
551✔
1142

1143
    /* handle host names supplied on command line or in a file */
1144
    /* if the opt_generate_on is on, then generate the IP list */
1145

1146
    argv = &argv[optparse_state.optind];
551✔
1147
    argc -= optparse_state.optind;
551✔
1148

1149
    /* calculate how many ping can be in-flight per host */
1150
    if (opt_count_on) {
551✔
1151
        event_storage_count = opt_count;
172✔
1152
    }
56✔
1153
    else if (opt_loop_on) {
379✔
1154
        if (opt_perhost_interval > opt_timeout) {
14✔
1155
            event_storage_count = 1;
×
1156
        }
1157
        else {
1158
            event_storage_count = 1 + opt_timeout / opt_perhost_interval;
14✔
1159
        }
1160
    }
4✔
1161
    else {
1162
        event_storage_count = 1;
365✔
1163
    }
1164

1165
    /* file and generate are mutually exclusive */
1166
    /* file and command line are mutually exclusive */
1167
    /* generate requires command line parameters beyond the switches */
1168
    if ((*argv && filename) || (filename && opt_generate_on) || (opt_generate_on && !*argv))
551✔
1169
        usage(1);
19✔
1170

1171
    /* if no conditions are specified, then assume input from stdin */
1172
    if (!*argv && !filename && !opt_generate_on)
533✔
1173
        filename = "-";
69✔
1174

1175
    if (*argv && !opt_generate_on) {
533✔
1176
        while (*argv) {
689✔
1177
            add_name(*argv);
375✔
1178
            ++argv;
375✔
1179
        }
1180
    }
76✔
1181
    else if (filename) {
219✔
1182
        FILE *ping_file;
1183
        char line[MAX_TARGET_NAME_LEN + 1];
1184
        char host[MAX_TARGET_NAME_LEN + 1];
1185
        char scratch[MAX_TARGET_NAME_LEN + 1];
1186
        int skip, non_empty;
1187

1188
        if (strcmp(filename, "-") == 0)
96✔
1189
            ping_file = fdopen(0, "r");
72✔
1190
        else
1191
            ping_file = fopen(filename, "r");
24✔
1192

1193
        if (!ping_file)
96✔
1194
            errno_crash_and_burn("fopen");
3✔
1195

1196
        /*
1197
         * Read the first word of every non-comment line, skip everything else.
1198
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1199
         * character is a '#' are interpreted as comments and ignored.)
1200
        */
1201
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1202
            skip = non_empty = 0;
309✔
1203

1204
            /* skip over a prefix of the line where sscanf finds nothing */
1205
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1206
                continue;
99✔
1207
            }
1208

1209
            /* the first word of the line can indicate a comment line */
1210
            if (host[0] == '#') {
210✔
1211
                skip = 1; /* skip remainder of line */
42✔
1212
            } else {
14✔
1213
                non_empty = 1; /* we have something to add as a target name */
168✔
1214
                /*
1215
                 * We have found the start of a word.
1216
                 * This part of the line may contain all of the first word.
1217
                 */
1218
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1219
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1220
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1221
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1222
                        skip = 1; /* a second word starts in this part */
6✔
1223
                    }
2✔
1224
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1225
                        skip = 1; /* the first word ends in this part */
42✔
1226
                    }
14✔
1227
                }
34✔
1228
            }
1229
            /* read remainder of this input line */
1230
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1231
                if (skip) {
150✔
1232
                    continue; /* skip rest of data in this input line */
102✔
1233
                }
1234
                if (isspace(line[0])) {
48✔
1235
                    skip = 1; /* first word ended in previous part */
6✔
1236
                    continue;
6✔
1237
                }
1238
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1239
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1240
                    continue;
×
1241
                }
1242
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1243
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1244
                    exit(1);
24✔
1245
                }
1246
                /* append remainder of word started in previous line part */
1247
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1248
                /*
1249
                 * Since the "host" buffer is the same size as the "line"
1250
                 * buffer, a target name that fits into the "host" buffer
1251
                 * cannot use more than two consecutive line parts.
1252
                 * A target name that uses two consecutive line parts
1253
                 * and fits into the "host" buffer must end before the
1254
                 * end of the second "line" buffer.  Thus the rest of
1255
                 * the line can be skipped.
1256
                 */
1257
                skip = 1;
18✔
1258
            }
1259

1260
            if (non_empty)
186✔
1261
                add_name(host);
144✔
1262
        }
1263

1264
        fclose(ping_file);
70✔
1265
    }
24✔
1266
    else if (*argv && opt_generate_on) {
123✔
1267
        if (argc == 1) {
123✔
1268
            /* one target: we expect a cidr range (n.n.n.n/m) */
1269
            add_cidr(argv[0]);
68✔
1270
        }
14✔
1271
        else if (argc == 2) {
55✔
1272
            add_range(argv[0], argv[1]);
52✔
1273
        }
6✔
1274
        else {
1275
            usage(1);
3✔
1276
        }
1277
    }
21✔
1278
    else {
1279
        usage(1);
×
1280
    }
1281

1282
    if (!num_hosts) {
447✔
1283
        exit(num_noaddress ? 2 : 1);
36✔
1284
    }
1285

1286
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
411✔
1287
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1288
    }
2✔
1289
#ifdef IPV6
1290
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
409✔
1291
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1292
    }
1293
#endif
1294

1295
    /* allocate and initialize array to map host nr to host_entry */
1296
    {
1297
        struct event *cursor = event_queue_ping.first;
407✔
1298
        int i = 0;
407✔
1299
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
407✔
1300
        if (!table)
407✔
1301
            crash_and_burn("Can't malloc array of hosts");
×
1302
        /* initialize table of hosts. we know that we have ping events scheduled
1303
         * for each of them */
1304
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
1,001✔
1305
            table[i] = cursor->host;
594✔
1306
            cursor->host->i = i;
594✔
1307
            i++;
594✔
1308
        }
156✔
1309
    }
1310

1311
    init_ping_buffer_ipv4(opt_ping_data_size);
407✔
1312
#ifdef IPV6
1313
    init_ping_buffer_ipv6(opt_ping_data_size);
407✔
1314
#endif
1315

1316
#ifdef USE_SIGACTION
1317
    memset(&act, 0, sizeof(act));
407✔
1318
    act.sa_handler = signal_handler;
407✔
1319
    sigemptyset(&act.sa_mask);
407✔
1320
    sigaddset(&act.sa_mask, SIGINT);
407✔
1321
    sigaddset(&act.sa_mask, SIGQUIT);
407✔
1322
    act.sa_flags = SA_RESTART;
407✔
1323
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
407✔
1324
        crash_and_burn("failure to set signal handler");
30✔
1325
    }
30✔
1326
#else
1327
    signal(SIGINT, signal_handler);
1328
    signal(SIGQUIT, signal_handler);
1329
#endif
1330
    setlinebuf(stdout);
377✔
1331

1332
    // Last time we updated current_time_ns was before adding the hosts and
1333
    // possibly doing DNS resolution, which means that it isn't accurate
1334
    // anymore.
1335
    update_current_time();
377✔
1336
    if (report_interval) {
377✔
1337
        next_report_time = current_time_ns + report_interval;
22✔
1338
    }
2✔
1339

1340
    last_send_time = 0;
377✔
1341

1342
    seqmap_init(opt_seqmap_timeout);
377✔
1343

1344
    /* main loop */
1345
    main_loop();
377✔
1346

1347
/* Debug: CPU Performance */
1348
#if defined(DEBUG) || defined(_DEBUG)
1349
    perf_cpu_end = clock();
1350
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1351
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1352
#endif /* DEBUG || _DEBUG */
1353

1354
    finish();
377✔
1355

1356
    return 0;
81✔
1357
}
1358

1359
static inline int64_t timespec_ns(struct timespec *a)
6,767✔
1360
{
1361
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,767✔
1362
}
1363

1364
#if HAVE_SO_TIMESTAMPNS
1365
/* convert a struct timeval to nanoseconds */
1366
static inline int64_t timeval_ns(struct timeval *a)
1367
{
1368
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1369
}
1370
#endif /* HAVE_SO_TIMESTAMPNS */
1371

1372
void add_cidr(char *addr)
63✔
1373
{
1374
    char *addr_end;
1375
    char *mask_str;
1376
    unsigned long mask;
1377
    int ret;
1378
    struct addrinfo addr_hints;
1379
    struct addrinfo *addr_res;
1380
    unsigned long net_addr;
1381
#ifdef IPV6
1382
    uint64_t net_upper, net_lower;
1383
    char *scope_str;
1384
#endif /* IPV6 */
1385

1386
    /* Split address from mask */
1387
    addr_end = strrchr(addr, '/');
63✔
1388
    if (addr_end == NULL) {
63✔
1389
        usage(1);
3✔
1390
    }
1✔
1391
    mask_str = addr_end + 1;
61✔
1392

1393
#ifdef IPV6
1394
    /* IPv6 addresses can have a scope */
1395
    scope_str = strchr(addr, '%');
61✔
1396
    if (scope_str && mask_str < scope_str) {
61✔
1397
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1398
        exit(1);
2✔
1399
    }
1400
#endif /*IPV6 */
1401

1402
    *addr_end = '\0';
59✔
1403
    mask = strtoul_strict(mask_str, 10);
59✔
1404

1405
    /* parse address */
1406
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1407
    addr_hints.ai_family = hints_ai_family;
53✔
1408
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1409
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1410
    if (ret) {
53✔
1411
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1412
        exit(1);
7✔
1413
    }
1414
    if (addr_res->ai_family == AF_INET) {
46✔
1415
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1416
        freeaddrinfo(addr_res);
24✔
1417
        add_cidr_ipv4(net_addr, mask);
24✔
1418
#ifdef IPV6
1419
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1420
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1421
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1422
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1423
        freeaddrinfo(addr_res);
22✔
1424
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1425
#endif /* IPV6 */
1426
    } else {
1427
        freeaddrinfo(addr_res);
×
1428
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1429
        exit(1);
×
1430
    }
1431
}
30✔
1432

1433
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1434
{
1435
    unsigned long bitmask;
1436
    unsigned long net_last;
1437

1438
    /* check mask */
1439
    if (mask < 1 || mask > 32) {
24✔
1440
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1441
        exit(1);
6✔
1442
    }
1443

1444
    /* convert mask integer from 1 to 32 to a bitmask */
1445
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1446

1447
    /* calculate network range */
1448
    net_addr &= bitmask;
18✔
1449
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1450

1451
    /* exclude network and broadcast address for regular prefixes */
1452
    if (mask < 31) {
18✔
1453
        net_last--;
12✔
1454
        net_addr++;
12✔
1455
    }
4✔
1456

1457
    /* add all hosts in that network (net_addr and net_last inclusive) */
1458
    add_addr_range_ipv4(net_addr, net_last);
18✔
1459
}
16✔
1460

1461
#ifdef IPV6
1462
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1463
{
1464
    uint64_t bitmask_lower;
1465
    uint64_t last_lower;
1466

1467
    /* check mask -- 2^63 addresses should suffice for now */
1468
    if (mask < 65 || mask > 128) {
23✔
1469
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1470
        exit(1);
7✔
1471
    }
1472

1473
    /* convert mask integer from 65 to 128 to the lower part of a bitmask */
1474
    bitmask_lower = ((uint64_t)-1) << (128 - mask);
16✔
1475

1476
    /* calculate network range */
1477
    net_lower &= bitmask_lower;
16✔
1478
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1479

1480
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1481
}
12✔
1482
#endif /* IPV6 */
1483

1484
void add_range(char *start, char *end)
52✔
1485
{
1486
    struct addrinfo addr_hints;
1487
    struct addrinfo *addr_res;
1488
    unsigned long start_long;
1489
    unsigned long end_long;
1490
    int ret;
1491
#ifdef IPV6
1492
    uint64_t start_upper, start_lower;
1493
    uint64_t end_upper, end_lower;
1494
    char *start_scope_str, *end_scope_str;
1495

1496
    /*
1497
     * The compiler does not know that setting the address family hint to
1498
     * ensure that start and end are from the same address family also
1499
     * ensures that either start_long and end_long are initialized and used,
1500
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1501
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1502
     * supported to suppress compiler warnings.
1503
     */
1504
    start_long = -1;
52✔
1505
    end_long = 0;
52✔
1506
    start_upper = start_lower = -1;
52✔
1507
    end_upper = end_lower = 0;
52✔
1508
    start_scope_str = end_scope_str = NULL;
52✔
1509
#endif /* IPV6 */
1510

1511
    /* parse start address */
1512
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1513
    addr_hints.ai_family = hints_ai_family;
52✔
1514
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1515
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1516
    if (ret) {
52✔
1517
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1518
        exit(1);
9✔
1519
    }
1520
    /* start and end must be from the same address family */
1521
    hints_ai_family = addr_res->ai_family;
43✔
1522
    if (addr_res->ai_family == AF_INET) {
43✔
1523
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1524
        freeaddrinfo(addr_res);
17✔
1525
#ifdef IPV6
1526
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1527
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1528
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1529
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1530
        freeaddrinfo(addr_res);
26✔
1531
#endif /* IPV6 */
1532
    } else {
1533
        freeaddrinfo(addr_res);
×
1534
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1535
        exit(1);
×
1536
    }
1537

1538
#ifdef IPV6
1539
    /* IPv6 addresses can have a scope */
1540
    if (hints_ai_family == AF_INET6) {
43✔
1541
        start_scope_str = strchr(start, '%');
26✔
1542
        end_scope_str = strchr(end, '%');
26✔
1543
        if ((!start_scope_str && end_scope_str) ||
26✔
1544
            (start_scope_str && !end_scope_str) ||
24✔
1545
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1546
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1547
                exit(1);
6✔
1548
        }
1549
    }
1550
#endif
1551

1552
    /* parse end address */
1553
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1554
    addr_hints.ai_family = hints_ai_family;
37✔
1555
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1556
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1557
    if (ret) {
37✔
1558
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1559
        exit(1);
7✔
1560
    }
1561
    if (addr_res->ai_family == AF_INET) {
30✔
1562
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1563
        freeaddrinfo(addr_res);
12✔
1564
        add_addr_range_ipv4(start_long, end_long);
12✔
1565
#ifdef IPV6
1566
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1567
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1568
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1569
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1570
        freeaddrinfo(addr_res);
18✔
1571
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1572
#endif /* IPV6 */
1573
    } else {
1574
        freeaddrinfo(addr_res);
×
1575
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1576
        exit(1);
×
1577
    }
1578
}
24✔
1579

1580
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1581
{
1582
    /* check if generator limit is exceeded */
1583
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1584
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1585
        exit(1);
6✔
1586
    }
1587

1588
    /* generate */
1589
    for (; start_long <= end_long; start_long++) {
93✔
1590
        struct in_addr in_addr_tmp;
1591
        char buffer[20];
1592
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1593
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1594
        add_name(buffer);
69✔
1595
    }
23✔
1596
}
24✔
1597

1598
#ifdef IPV6
1599
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1600
{
1601
    int i;
1602
    uint64_t ret = 0;
134✔
1603
    for (i = 0; i < 8; i++) {
1,206✔
1604
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1605
    }
16✔
1606
    return ret;
134✔
1607
}
1608

1609
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1610
{
1611
    int i;
1612
    for (i = 0; i < 8; i++) {
720✔
1613
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1614
    }
1615
}
80✔
1616

1617
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1618
                         uint64_t end_upper, uint64_t end_lower,
1619
                         const char *scope_str)
1620
{
1621
    struct in6_addr in6_addr_tmp;
1622
    char buffer[100];
1623

1624
    /* prevent generating too many addresses */
1625
    if ((start_upper + 1 < end_upper) ||
34✔
1626
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1627
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1628
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1629
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1630
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1631
        exit(1);
8✔
1632
    }
1633

1634
    while ((start_upper < end_upper) ||
66✔
1635
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1636
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1637
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1638
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1639
        if (scope_str) {
40✔
1640
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1641
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1642
                exit(1);
×
1643
            }
1644
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1645
        }
1646
        add_name(buffer);
40✔
1647
        start_lower++;
40✔
1648
        if (start_lower == 0) {
40✔
1649
            start_upper++;
2✔
1650
        }
1651
    }
1652
}
26✔
1653
#endif /* IPv6 */
1654

1655
void main_loop()
392✔
1656
{
1657
    int64_t lt;
1658
    int64_t wait_time_ns;
1659
    struct event *event;
1660
    struct host_entry *h;
1661

1662
    while (event_queue_ping.first || event_queue_timeout.first) {
2,130✔
1663
        dbg_printf("%s", "# main_loop\n");
1664

1665
        /* timeout event ? */
1666
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,769✔
1667
            event = ev_dequeue(&event_queue_timeout);
101✔
1668
            h = event->host;
101✔
1669

1670
            dbg_printf("%s [%d]: timeout event\n", h->host, event->ping_index);
1671

1672
            stats_add(h, event->ping_index, 0, -1);
101✔
1673

1674
            if (opt_per_recv_on) {
101✔
1675
                print_timeout(h, event->ping_index);
25✔
1676
            }
7✔
1677

1678
            /* do we need to send a retry? */
1679
            if (!opt_loop_on && !opt_count_on) {
101✔
1680
                if (h->num_sent < opt_retry + 1) {
60✔
1681
                    if (opt_backoff_on) {
35✔
1682
                        h->timeout *= opt_backoff;
35✔
1683
                    }
9✔
1684
                    send_ping(h, event->ping_index);
35✔
1685
                }
9✔
1686
            }
14✔
1687

1688
            /* note: we process first timeout events, because we might need to
1689
             * wait to process ping events, while we for sure never need to
1690
             * wait for timeout events.
1691
             */
1692
            continue;
101✔
1693
        }
1694

1695
        /* ping event ? */
1696
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,930✔
1697
            /* Make sure that we don't ping more than once every "interval" */
1698
            lt = current_time_ns - last_send_time;
1,254✔
1699
            if (lt < opt_interval)
1,254✔
1700
                goto wait_for_reply;
250✔
1701

1702
            /* Dequeue the event */
1703
            event = ev_dequeue(&event_queue_ping);
1,004✔
1704
            h = event->host;
1,004✔
1705

1706
            dbg_printf("%s [%d]: ping event\n", h->host, event->ping_index);
1707

1708
            /* Send the ping */
1709
            send_ping(h, event->ping_index);
1,004✔
1710

1711
            /* Loop and count mode: schedule next ping */
1712
            if (opt_loop_on || (opt_count_on && event->ping_index + 1 < opt_count)) {
1,004✔
1713
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + opt_perhost_interval);
439✔
1714
            }
115✔
1715
        }
262✔
1716

1717
    wait_for_reply:
739✔
1718

1719
        /* When is the next ping next event? */
1720
        wait_time_ns = -1;
1,668✔
1721
        if (event_queue_ping.first) {
1,668✔
1722
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,222✔
1723
            if (wait_time_ns < 0)
1,222✔
1724
                wait_time_ns = 0;
530✔
1725
            /* make sure that we wait enough, so that the inter-ping delay is
1726
             * bigger than 'interval' */
1727
            if (wait_time_ns < opt_interval) {
1,222✔
1728
                lt = current_time_ns - last_send_time;
532✔
1729
                if (lt < opt_interval) {
532✔
1730
                    wait_time_ns = opt_interval - lt;
532✔
1731
                }
176✔
1732
            }
176✔
1733

1734
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1735
        }
332✔
1736

1737
        /* When is the next timeout event? */
1738
        if (event_queue_timeout.first) {
1,668✔
1739
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,054✔
1740
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,054✔
1741
                wait_time_ns = wait_time_timeout;
442✔
1742
                if (wait_time_ns < 0) {
442✔
UNCOV
1743
                    wait_time_ns = 0;
×
1744
                }
1745
            }
109✔
1746

1747
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1748
        }
279✔
1749

1750
        /* When is the next report due? */
1751
        if (report_interval && (opt_loop_on || opt_count_on)) {
1,668✔
1752
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1753
            if (wait_time_next_report < wait_time_ns) {
214✔
1754
                wait_time_ns = wait_time_next_report;
50✔
1755
                if (wait_time_ns < 0) {
50✔
1756
                    wait_time_ns = 0;
×
1757
                }
1758
            }
2✔
1759

1760
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1761
        }
6✔
1762

1763
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1764
        if (wait_time_ns == -1) {
1,668✔
1765
            break;
14✔
1766
        }
1767

1768
        /* end of loop was requested by interrupt signal handler */
1769
        if (finish_requested) {
1,654✔
1770
            break;
17✔
1771
        }
1772

1773
        /* Receive replies */
1774
        /* (this is what sleeps during each loop iteration) */
1775
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1776
        if (wait_for_reply(wait_time_ns)) {
1,637✔
1777
            while (wait_for_reply(0))
1,493✔
1778
                ; /* process other replies in the queue */
1779
        }
251✔
1780

1781
        update_current_time();
1,637✔
1782

1783
        if (status_snapshot) {
1,637✔
1784
            status_snapshot = 0;
3✔
1785
            if (opt_print_json_on)
3✔
1786
                print_per_system_splits_json();
×
1787
            else
1788
                print_per_system_splits();
3✔
1789
        }
1✔
1790

1791
        /* Print report */
1792
        if (report_interval && (opt_loop_on || opt_count_on) && (current_time_ns >= next_report_time)) {
1,637✔
1793
            if (opt_print_netdata_on) {
36✔
1794
                print_netdata();
3✔
1795
            }
1✔
1796
            else if (opt_print_json_on) {
33✔
1797
                print_per_system_splits_json();
3✔
1798
            }
1✔
1799
            else {
1800
                print_per_system_splits();
30✔
1801
            }
1802

1803
            while (current_time_ns >= next_report_time) {
72✔
1804
                next_report_time += report_interval;
36✔
1805
            }
1806
        }
2✔
1807
    }
1808
}
392✔
1809

1810
/************************************************************
1811

1812
  Function: signal_handler
1813

1814
*************************************************************
1815

1816
  Inputs:  int signum
1817

1818
  Description:
1819

1820
  SIGQUIT signal handler - set flag and return
1821
  SIGINT signal handler - set flag and return
1822

1823
************************************************************/
1824

1825
void signal_handler(int signum)
17✔
1826
{
1827
    switch (signum) {
17✔
1828
    case SIGINT:
10✔
1829
        finish_requested = 1;
14✔
1830
        break;
14✔
1831

1832
    case SIGQUIT:
2✔
1833
        status_snapshot = 1;
3✔
1834
        break;
3✔
1835
    }
1836
}
17✔
1837

1838
/************************************************************
1839

1840
  Function: update_current_time
1841

1842
*************************************************************/
1843

1844
void update_current_time()
5,525✔
1845
{
1846
    clock_gettime(CLOCKID, &current_time);
5,525✔
1847
    current_time_ns = timespec_ns(&current_time);
5,525✔
1848
}
5,525✔
1849

1850
/************************************************************
1851

1852
  Function: finish
1853

1854
*************************************************************
1855

1856
  Inputs:  void (none)
1857

1858
  Description:
1859

1860
  Main program clean up and exit point
1861

1862
************************************************************/
1863

1864
void finish()
392✔
1865
{
1866
    int i;
1867
    HOST_ENTRY *h;
1868

1869
    update_current_time();
392✔
1870
    end_time = current_time_ns;
392✔
1871

1872
    /* tot up unreachables */
1873
    for (i = 0; i < num_hosts; i++) {
986✔
1874
        h = table[i];
594✔
1875

1876
        if (!h->num_recv) {
594✔
1877
            num_unreachable++;
87✔
1878

1879
            if (opt_verbose_on || opt_unreachable_on) {
87✔
1880
                printf("%s", h->host);
47✔
1881

1882
                if (opt_verbose_on)
47✔
1883
                    printf(" is unreachable");
44✔
1884

1885
                printf("\n");
47✔
1886
            }
3✔
1887
        }
13✔
1888
    }
156✔
1889

1890
    if (opt_count_on || opt_loop_on) {
392✔
1891
        if (opt_print_json_on)
168✔
1892
            print_per_system_stats_json();
56✔
1893
        else
1894
            print_per_system_stats();
112✔
1895
    }
44✔
1896
#if defined(DEBUG) || defined(_DEBUG)
1897
    else if (opt_debug_print_per_system_on) {
1898
        if (opt_print_json_on)
1899
            print_per_system_stats_json();
1900
        else
1901
            print_per_system_stats();
1902
    }
1903
#endif /* DEBUG || _DEBUG */
1904

1905
    if (opt_stats_on) {
392✔
1906
        if (opt_print_json_on)
12✔
1907
            print_global_stats_json();
3✔
1908
        else
1909
            print_global_stats();
9✔
1910
    }
4✔
1911

1912
    if (opt_min_reachable) {
392✔
1913
        if ((num_hosts - num_unreachable) >= opt_min_reachable) {
9✔
1914
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", opt_min_reachable, num_hosts - num_unreachable);
6✔
1915
            exit(0);
6✔
1916
        }
1917
        else {
1918
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", opt_min_reachable, num_hosts - num_unreachable);
3✔
1919
            exit(1);
3✔
1920
        }
1921
    }
1922

1923
    if (num_noaddress)
383✔
1924
        exit(2);
3✔
1925
    else if (num_alive != num_hosts)
380✔
1926
        exit(1);
48✔
1927

1928
    exit(0);
332✔
1929
}
1930

1931
/************************************************************
1932

1933
  Function: send_ping
1934

1935
*************************************************************
1936

1937
  Inputs:  int s, HOST_ENTRY *h
1938

1939
  Description:
1940

1941
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1942
  will be added on by the kernel.  The ID field is our UNIX process ID,
1943
  and the sequence number is an index into an array of outstanding
1944
  ping requests. The sequence number will later be used to quickly
1945
  figure out who the ping reply came from.
1946

1947
************************************************************/
1948

1949
int send_ping(HOST_ENTRY *h, int index)
1,039✔
1950
{
1951
    int n;
1952
    int myseq;
1953
    int ret = 1;
1,039✔
1954
    uint8_t proto = ICMP_ECHO;
1,039✔
1955

1956
    update_current_time();
1,039✔
1957
    h->last_send_time = current_time_ns;
1,039✔
1958
    myseq = seqmap_add(h->i, index, current_time_ns);
1,039✔
1959

1960
    dbg_printf("%s [%d]: send ping\n", h->host, index);
1961

1962
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,039✔
1963
        if(opt_icmp_request_typ == 13)
916✔
1964
            proto = ICMP_TSTAMP;
18✔
1965
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
916✔
1966
    }
256✔
1967
#ifdef IPV6
1968
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
1969
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
1970
    }
15✔
1971
#endif
1972
    else {
1973
        return 0;
×
1974
    }
1975

1976
    /* error sending? */
1977
    if (
768✔
1978
        (n < 0)
271✔
1979
#if defined(EHOSTDOWN)
1980
        && errno != EHOSTDOWN
305✔
1981
#endif
1982
    ) {
1983
        if (opt_verbose_on) {
34✔
1984
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
1985
        }
1986
        else {
1987
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1988
        }
1989

1990
        h->num_sent++;
34✔
1991
        h->num_sent_i++;
34✔
1992
        if (!opt_loop_on)
34✔
1993
            h->resp_times[index] = RESP_ERROR;
34✔
1994

1995
        ret = 0;
34✔
1996
    }
1997
    else {
1998
        /* schedule timeout */
1999
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
1,005✔
2000

2001
        /* mark this trial as outstanding */
2002
        if (!opt_loop_on) {
1,005✔
2003
            h->resp_times[index] = RESP_WAITING;
872✔
2004
        }
238✔
2005
    }
2006

2007
    num_pingsent++;
1,039✔
2008
    last_send_time = h->last_send_time;
1,039✔
2009

2010
    return (ret);
1,039✔
2011
}
271✔
2012

2013
int socket_can_read(struct timeval *timeout)
1,637✔
2014
{
2015
    int nfound;
2016
    fd_set readset;
2017
    int socketmax;
2018

2019
#ifndef IPV6
2020
    socketmax = socket4;
2021
#else
2022
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,637✔
2023
#endif
2024

2025
select_again:
1,215✔
2026
    FD_ZERO(&readset);
21,094✔
2027
    if (socket4 >= 0)
1,654✔
2028
        FD_SET(socket4, &readset);
1,654✔
2029
#ifdef IPV6
2030
    if (socket6 >= 0)
1,654✔
2031
        FD_SET(socket6, &readset);
1,654✔
2032
#endif
2033

2034
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1,654✔
2035
    if (nfound < 0) {
1,654✔
2036
        if (errno == EINTR) {
17✔
2037
            /* interrupted system call: redo the select */
2038
            goto select_again;
17✔
2039
        }
2040
        else {
2041
            perror("select");
×
2042
        }
2043
    }
2044

2045
    if (nfound > 0) {
1,637✔
2046
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
915✔
2047
            return socket4;
820✔
2048
        }
2049
#ifdef IPV6
2050
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
95✔
2051
            return socket6;
95✔
2052
        }
2053
#endif
2054
    }
2055

2056
    return -1;
722✔
2057
}
434✔
2058

2059
int receive_packet(int64_t wait_time,
3,130✔
2060
#if HAVE_SO_TIMESTAMPNS
2061
    int64_t *reply_timestamp,
2062
#else
2063
    int64_t *reply_timestamp __attribute__((unused)),
2064
#endif
2065
    struct sockaddr *reply_src_addr,
2066
    size_t reply_src_addr_len,
2067
    char *reply_buf,
2068
    size_t reply_buf_len,
2069
    int *ip_header_tos,
2070
    int *ip_header_ttl)
2071
{
2072
    struct timeval to;
2073
    int s = 0;
3,130✔
2074
    int recv_len;
2075
    static unsigned char msg_control[128];
2076
    struct iovec msg_iov = {
4,500✔
2077
        reply_buf,
685✔
2078
        reply_buf_len
685✔
2079
    };
2080
    struct msghdr recv_msghdr = {0};
3,130✔
2081
    recv_msghdr.msg_name = reply_src_addr;
3,130✔
2082
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,130✔
2083
    recv_msghdr.msg_iov = &msg_iov;
3,130✔
2084
    recv_msghdr.msg_iovlen = 1;
3,130✔
2085
    recv_msghdr.msg_control = &msg_control;
3,130✔
2086
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,130✔
2087
    struct cmsghdr *cmsg;
2088

2089
    /* Wait for a socket to become ready */
2090
#if HAVE_MSG_DONTWAIT
2091
    if (wait_time == 0) {
3,130✔
2092
        /* Optimization: if wait_time is 0, we can skip select() and just try to
2093
         * read from the sockets with MSG_DONTWAIT */
2094
        recv_len = (socket4 >= 0) ? recvmsg(socket4, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT) : -1;
1,493✔
2095
#ifdef IPV6
2096
        if (recv_len <= 0 && socket6 >= 0) {
1,493✔
2097
            /* Reset fields potentially modified by failed recvmsg */
2098
            recv_msghdr.msg_namelen = reply_src_addr_len;
917✔
2099
            recv_msghdr.msg_controllen = sizeof(msg_control);
917✔
2100
            recv_len = recvmsg(socket6, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
917✔
2101
        }
251✔
2102
#endif
2103
        if (recv_len > 0) {
1,493✔
2104
            goto packet_received;
578✔
2105
        }
2106
        return 0;
915✔
2107
    }
2108
#endif
2109

2110
    int64_t wait_us = (wait_time + 999) / 1000; // round up (1 ns -> 1 us)
1,637✔
2111
    to.tv_sec = wait_us / 1000000;
1,637✔
2112
    to.tv_usec = wait_us % 1000000;
1,637✔
2113

2114
    s = socket_can_read(&to);
1,637✔
2115
    if (s == -1 || (recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC)) <= 0) {
1,637✔
2116
        return 0;
722✔
2117
    }
2118

2119
packet_received:
664✔
2120
    /* ancilliary data */
2121
    {
2122
#if HAVE_SO_TIMESTAMPNS
2123
        struct timespec reply_timestamp_ts;
2124
        struct timeval reply_timestamp_tv;
2125
#endif
2126
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,493✔
2127
             cmsg != NULL;
2,747✔
2128
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,254✔
2129
#if HAVE_SO_TIMESTAMPNS
2130
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,254✔
2131
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,242✔
2132
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,242✔
2133
            }
2134
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,254✔
2135
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2136
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2137
            }
2138
#endif
2139
#if defined(HAVE_IP_RECVTOS)
2140
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,254✔
2141
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2142
            }
2143
#endif
2144
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,254✔
2145
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2146
            }
2147
#ifdef IPV6
2148
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,254✔
2149
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2150
            }
2151
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,254✔
2152
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2153
            }
2154
#endif
2155
        }
2156
    }
2157

2158
#if defined(DEBUG) || defined(_DEBUG)
2159
    if (opt_debug_randomly_lose_on) {
2160
        if ((random() & 0x07) <= lose_factor)
2161
            return 0;
2162
    }
2163
#endif
2164

2165
    return recv_len;
1,493✔
2166
}
685✔
2167

2168
/* stats_add: update host statistics for a single packet that was received (or timed out)
2169
 * h: host entry to update
2170
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2171
 * success: 1 if response received, 0 otherwise
2172
 * latency: response time, in ns
2173
 */
2174
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
991✔
2175
{
2176
    /* sent count - we update only on receive/timeout, so that we don't get
2177
     * weird loss percentage, just because a packet was note recived yet.
2178
     */
2179
    h->num_sent++;
991✔
2180
    h->num_sent_i++;
991✔
2181

2182
    if (!success) {
991✔
2183
        if (!opt_loop_on && index >= 0) {
101✔
2184
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2185
        }
21✔
2186
        num_timeout++;
101✔
2187
        return;
101✔
2188
    }
2189

2190
    /* received count */
2191
    h->num_recv++;
890✔
2192
    h->num_recv_i++;
890✔
2193

2194
    /* maximum */
2195
    if (!h->max_reply || latency > h->max_reply) {
890✔
2196
        h->max_reply = latency;
677✔
2197
    }
188✔
2198
    if (!h->max_reply_i || latency > h->max_reply_i) {
890✔
2199
        h->max_reply_i = latency;
700✔
2200
    }
189✔
2201

2202
    /* minimum */
2203
    if (!h->min_reply || latency < h->min_reply) {
890✔
2204
        h->min_reply = latency;
545✔
2205
    }
157✔
2206
    if (!h->min_reply_i || latency < h->min_reply_i) {
890✔
2207
        h->min_reply_i = latency;
577✔
2208
    }
162✔
2209

2210
    /* total time (for average) */
2211
    h->total_time += latency;
890✔
2212
    h->total_time_i += latency;
890✔
2213

2214
    /* response time per-packet (count mode) */
2215
    if (!opt_loop_on && index >= 0) {
890✔
2216
        h->resp_times[index] = latency;
771✔
2217
    }
217✔
2218
}
267✔
2219

2220
/* stats_reset_interval: reset interval statistics
2221
 * h: host entry to update
2222
 */
2223
void stats_reset_interval(HOST_ENTRY *h)
31✔
2224
{
2225
    h->num_sent_i = 0;
31✔
2226
    h->num_recv_i = 0;
31✔
2227
    h->max_reply_i = 0;
31✔
2228
    h->min_reply_i = 0;
31✔
2229
    h->total_time_i = 0;
31✔
2230
}
31✔
2231

2232
int decode_icmp_ipv4(
1,396✔
2233
    struct sockaddr *response_addr,
2234
    size_t response_addr_len,
2235
    char *reply_buf,
2236
    size_t reply_buf_len,
2237
    unsigned short *id,
2238
    unsigned short *seq,
2239
    IP_HEADER_RESULT *ip_header_res)
2240
{
2241
    struct icmp *icp;
2242
    int hlen = 0;
1,396✔
2243

2244
    if (!using_sock_dgram4) {
1,396✔
2245
        struct ip *ip = (struct ip *)reply_buf;
1,388✔
2246
        ip_header_res->tos = ip->ip_tos;
1,388✔
2247
        ip_header_res->ttl = ip->ip_ttl;
1,388✔
2248

2249
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2250
        /* The alpha headers are decidedly broken.
2251
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2252
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2253
         */
2254
        hlen = (ip->ip_vhl & 0x0F) << 2;
2255
#else
2256
        hlen = ip->ip_hl << 2;
1,388✔
2257
#endif
2258
    }
236✔
2259

2260
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,396✔
2261
        /* too short */
2262
        if (opt_verbose_on) {
×
2263
            char buf[INET6_ADDRSTRLEN];
2264
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2265
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2266
        }
2267
        return -1;
×
2268
    }
2269

2270
    icp = (struct icmp *)(reply_buf + hlen);
1,396✔
2271

2272
    if ((opt_icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,396✔
2273
        (opt_icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
600✔
2274
        /* Handle other ICMP packets */
2275
        struct icmp *sent_icmp;
2276
        SEQMAP_VALUE *seqmap_value;
2277
        char addr_ascii[INET6_ADDRSTRLEN];
2278
        HOST_ENTRY *h;
2279

2280
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2281
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,047✔
2282
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2283
            return -1;
×
2284
        }
2285

2286
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
577✔
2287

2288
        if ((opt_icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
577✔
2289
            (opt_icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2290
            sent_icmp->icmp_id != ident4) {
×
2291
            /* not caused by us */
2292
            return -1;
577✔
2293
        }
2294

2295
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2296
        if (seqmap_value == NULL) {
×
2297
            return -1;
×
2298
        }
2299

2300
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2301

2302
        switch (icp->icmp_type) {
×
2303
        case ICMP_UNREACH:
2304
            h = table[seqmap_value->host_nr];
×
2305
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2306
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2307
                    addr_ascii, h->host);
2308
            }
2309
            else {
2310
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2311
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2312
            }
2313

2314
            print_warning("\n");
×
2315
            num_othericmprcvd++;
×
2316
            break;
×
2317

2318
        case ICMP_SOURCEQUENCH:
2319
        case ICMP_REDIRECT:
2320
        case ICMP_TIMXCEED:
2321
        case ICMP_PARAMPROB:
2322
            h = table[seqmap_value->host_nr];
×
2323
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2324
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2325
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2326
            }
2327
            else {
2328
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2329
                    icp->icmp_type, addr_ascii, h->host);
×
2330
            }
2331
            print_warning("\n");
×
2332
            num_othericmprcvd++;
×
2333
            break;
×
2334
        }
2335

2336
        return -1;
×
2337
    }
2338

2339
    *id = icp->icmp_id;
819✔
2340
    *seq = ntohs(icp->icmp_seq);
819✔
2341
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
819✔
2342

2343
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2344
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
2345
            if (opt_verbose_on) {
×
2346
                char buf[INET6_ADDRSTRLEN];
2347
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2348
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2349
            }
2350
            return -1;
×
2351
        }
2352

2353
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2354
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2355
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2356
    }
2357

2358
    return hlen;
819✔
2359
}
236✔
2360

2361
#ifdef IPV6
2362
int decode_icmp_ipv6(
97✔
2363
    struct sockaddr *response_addr,
2364
    size_t response_addr_len,
2365
    char *reply_buf,
2366
    size_t reply_buf_len,
2367
    unsigned short *id,
2368
    unsigned short *seq)
2369
{
2370
    struct icmp6_hdr *icp;
2371

2372
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
97✔
2373
        if (opt_verbose_on) {
×
2374
            char buf[INET6_ADDRSTRLEN];
2375
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2376
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2377
        }
2378
        return 0; /* too short */
×
2379
    }
2380

2381
    icp = (struct icmp6_hdr *)reply_buf;
97✔
2382

2383
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
97✔
2384
        /* Handle other ICMPv6 packets */
2385
        struct ip6_hdr *sent_ipv6;
2386
        struct icmp6_hdr *sent_icmp;
2387
        SEQMAP_VALUE *seqmap_value;
2388
        char addr_ascii[INET6_ADDRSTRLEN];
2389
        HOST_ENTRY *h;
2390

2391
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
2392
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
6✔
2393
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2394
            return 0;
×
2395
        }
2396

2397
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
2398
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
2399
            /* discard ICMPv6 message if we can't tell that it was caused by
2400
             * us, because the IPv6 header is not directly followed by an
2401
             * ICMPv6 header
2402
             */
2403
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
2404
            return 0;
×
2405
        }
2406
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
2407

2408
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
2409
            /* not caused by us */
2410
            return 0;
6✔
2411
        }
2412

2413
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2414
        if (seqmap_value == NULL) {
×
2415
            return 0;
×
2416
        }
2417

2418
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2419
        h = table[seqmap_value->host_nr];
×
2420

2421
        switch (icp->icmp6_type) {
×
2422
        case ICMP6_DST_UNREACH:
2423
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
2424
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2425
                    icp->icmp6_code, addr_ascii, h->host);
×
2426
            } else {
2427
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2428
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2429
            }
2430
            print_warning("\n");
×
2431
            num_othericmprcvd++;
×
2432
            break;
×
2433

2434
        case ICMP6_PACKET_TOO_BIG:
2435
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2436
                addr_ascii, h->host);
2437
            num_othericmprcvd++;
×
2438
            break;
×
2439

2440
        case ICMP6_TIME_EXCEEDED:
2441
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
2442
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2443
                    icp->icmp6_code, addr_ascii, h->host);
×
2444
            } else {
2445
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2446
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2447
            }
2448
            print_warning("\n");
×
2449
            num_othericmprcvd++;
×
2450
            break;
×
2451

2452
        case ICMP6_PARAM_PROB:
2453
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
2454
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2455
                    icp->icmp6_code, addr_ascii, h->host);
×
2456
            } else {
2457
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2458
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2459
            }
2460
            print_warning("\n");
×
2461
            num_othericmprcvd++;
×
2462
            break;
×
2463

2464
        default:
2465
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
2466
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
2467
            num_othericmprcvd++;
×
2468
            break;
×
2469
        }
2470

2471
        return 0;
×
2472
    }
2473

2474
    *id = icp->icmp6_id;
91✔
2475
    *seq = ntohs(icp->icmp6_seq);
91✔
2476

2477
    return 1;
91✔
2478
}
15✔
2479
#endif
2480

2481
int wait_for_reply(int64_t wait_time)
3,130✔
2482
{
2483
    int result;
2484
    static char buffer[RECV_BUFSIZE];
2485
    struct sockaddr_storage response_addr;
2486
    int n, avg;
2487
    HOST_ENTRY *h;
2488
    int64_t this_reply;
2489
    int this_count;
2490
    int64_t recv_time = 0;
3,130✔
2491
    SEQMAP_VALUE *seqmap_value;
2492
    unsigned short id;
2493
    unsigned short seq;
2494
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,130✔
2495

2496
    /* Receive packet */
2497
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,815✔
2498
        &recv_time, /* reply_timestamp */
2499
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2500
        sizeof(response_addr), /* reply_src_addr_len */
2501
        buffer, /* reply_buf */
2502
        sizeof(buffer), /* reply_buf_len */
2503
        &ip_header_res.tos, /* TOS resp. TC byte */
685✔
2504
        &ip_header_res.ttl /* TTL resp. hop limit */
685✔
2505
    );
2506

2507
    if (result <= 0) {
3,130✔
2508
        return 0;
1,637✔
2509
    }
2510

2511
    update_current_time();
1,493✔
2512
    if (recv_time == 0)
1,493✔
2513
        recv_time = current_time_ns;
251✔
2514

2515
    /* Process ICMP packet and retrieve id/seq */
2516
    if (response_addr.ss_family == AF_INET) {
1,493✔
2517
        int ip_hlen = decode_icmp_ipv4(
1,396✔
2518
            (struct sockaddr *)&response_addr,
2519
            sizeof(response_addr),
2520
            buffer,
2521
            sizeof(buffer),
2522
            &id,
2523
            &seq,
2524
            &ip_header_res);
2525
        if (ip_hlen < 0) {
1,396✔
2526
            return 1;
577✔
2527
        }
2528
        if (id != ident4) {
819✔
2529
            return 1; /* packet received, but not the one we are looking for! */
×
2530
        }
2531
        if (!using_sock_dgram4) {
819✔
2532
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2533
             * with fping with IPv6 hosts */
2534
            result -= ip_hlen;
811✔
2535
        }
235✔
2536
    }
235✔
2537
#ifdef IPV6
2538
    else if (response_addr.ss_family == AF_INET6) {
97✔
2539
        if (!decode_icmp_ipv6(
97✔
2540
                (struct sockaddr *)&response_addr,
2541
                sizeof(response_addr),
2542
                buffer,
2543
                sizeof(buffer),
2544
                &id,
2545
                &seq)) {
2546
            return 1;
6✔
2547
        }
2548
        if (id != ident6) {
91✔
2549
            return 1; /* packet received, but not the one we are looking for! */
×
2550
        }
2551
    }
15✔
2552
#endif
2553
    else {
2554
        return 1;
×
2555
    }
2556

2557
    seqmap_value = seqmap_fetch(seq, current_time_ns);
910✔
2558
    if (seqmap_value == NULL) {
910✔
2559
        return 1;
12✔
2560
    }
2561

2562
    /* find corresponding host_entry */
2563
    n = seqmap_value->host_nr;
898✔
2564
    h = table[n];
898✔
2565
    this_count = seqmap_value->ping_count;
898✔
2566
    this_reply = recv_time - seqmap_value->ping_ts;
898✔
2567

2568
    /* update stats that include invalid replies */
2569
    h->num_recv_total++;
898✔
2570
    num_pingreceived++;
898✔
2571

2572
    dbg_printf("received [%d] from %s\n", this_count, h->host);
2573

2574
    /* optionally require reply source equal to target address */
2575
    if (opt_check_source_on && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
898✔
2576
        dbg_printf("%s\n", "discarding reply from wrong source address");
2577
        return 1;
8✔
2578
    }
2579

2580
    /* discard duplicates */
2581
    if (!opt_loop_on && !(opt_count_on && opt_quiet_on) && h->resp_times[this_count] >= 0) {
890✔
2582
        if (!opt_per_recv_on) {
×
2583
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2584
                h->host, this_count, result, sprint_tm(this_reply));
2585

2586
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2587
                char buf[INET6_ADDRSTRLEN];
2588
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2589
                fprintf(stderr, " [<- %s]", buf);
×
2590
            }
2591
            fprintf(stderr, "\n");
×
2592
        }
2593
        return 1;
×
2594
    }
2595

2596
    /* discard reply if delay is larger than timeout
2597
     * (see also: github #32) */
2598
    if (this_reply > h->timeout) {
890✔
2599
        return 1;
×
2600
    }
2601

2602
    /* update stats */
2603
    stats_add(h, this_count, 1, this_reply);
890✔
2604
    // TODO: move to stats_add?
2605
    if (!max_reply || this_reply > max_reply)
890✔
2606
        max_reply = this_reply;
543✔
2607
    if (!min_reply || this_reply < min_reply)
890✔
2608
        min_reply = this_reply;
451✔
2609
    sum_replies += this_reply;
890✔
2610
    total_replies++;
890✔
2611

2612
    /* initialize timeout to initial timeout (without backoff) */
2613
    h->timeout = opt_timeout;
890✔
2614

2615
    /* remove timeout event */
2616
    struct event *timeout_event = host_get_timeout_event(h, this_count);
890✔
2617
    if (timeout_event) {
890✔
2618
        ev_remove(&event_queue_timeout, timeout_event);
890✔
2619
    }
246✔
2620

2621
    /* print "is alive" */
2622
    if (h->num_recv == 1) {
890✔
2623
        num_alive++;
507✔
2624
        if (opt_fast_reachable_on && num_alive >= opt_min_reachable)
507✔
2625
            finish_requested = 1;
3✔
2626

2627
        if (opt_verbose_on || opt_alive_on) {
507✔
2628
            printf("%s", h->host);
306✔
2629

2630
            if (opt_verbose_on)
306✔
2631
                printf(" is alive");
286✔
2632
        }
86✔
2633
    }
143✔
2634

2635
    /* print received ping (unless --quiet) */
2636
    if (opt_per_recv_on) {
890✔
2637
        avg = h->total_time / h->num_recv;
278✔
2638
        print_recv(h,
364✔
2639
            recv_time,
86✔
2640
            result,
86✔
2641
            this_count,
86✔
2642
            this_reply,
86✔
2643
            avg);
86✔
2644
    }
86✔
2645

2646
    if (opt_verbose_on || opt_alive_on || opt_per_recv_on) {
890✔
2647
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
584✔
2648
            char buf[INET6_ADDRSTRLEN];
2649
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2650
            fprintf(stderr, " [<- %s]", buf);
6✔
2651
        }
2652
        if (opt_print_json_on) {
584✔
2653
            print_recv_ext_json(&ip_header_res,
35✔
2654
                recv_time,
11✔
2655
                this_reply);
11✔
2656
        }
11✔
2657
        else {
2658
            print_recv_ext(&ip_header_res,
549✔
2659
                recv_time,
161✔
2660
                this_reply);
161✔
2661
        }
2662
    }
172✔
2663
    return 1;
890✔
2664
}
685✔
2665

2666
/************************************************************
2667

2668
  Function: add_name
2669

2670
*************************************************************
2671

2672
  Inputs:  char* name
2673

2674
  Description:
2675

2676
  process input name for addition to target list
2677
  name can turn into multiple targets via multiple interfaces (-m)
2678
  or via NIS groups
2679

2680
************************************************************/
2681

2682
void add_name(char *name)
628✔
2683
{
2684
    struct addrinfo *res0, *res, hints;
2685
    int ret_ga;
2686
    char *printname;
2687
    char namebuf[256];
2688
    char addrbuf[256];
2689

2690
    /* getaddrinfo */
2691
    memset(&hints, 0, sizeof(struct addrinfo));
628✔
2692
    hints.ai_flags = AI_UNUSABLE;
628✔
2693
    hints.ai_socktype = SOCK_RAW;
628✔
2694
    hints.ai_family = hints_ai_family;
628✔
2695
    if (hints_ai_family == AF_INET) {
628✔
2696
        hints.ai_protocol = IPPROTO_ICMP;
81✔
2697
    }
21✔
2698
#ifdef IPV6
2699
    else if (hints_ai_family == AF_INET6) {
547✔
2700
        hints.ai_protocol = IPPROTO_ICMPV6;
35✔
2701
    }
1✔
2702
#endif
2703
    else {
2704
        hints.ai_socktype = SOCK_STREAM;
512✔
2705
        hints.ai_protocol = 0;
512✔
2706
    }
2707
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
628✔
2708
    if (ret_ga) {
628✔
2709
        if (!opt_quiet_on)
29✔
2710
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
2711
        num_noaddress++;
29✔
2712

2713
        // Handle JSON output for invalid hosts
2714
        if (opt_print_json_on) {
29✔
2715
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
2716
            return;
21✔
2717
        }
2718

2719
        return;
26✔
2720
    }
2721

2722
    /* NOTE: we could/should loop with res on all addresses like this:
2723
     * for (res = res0; res; res = res->ai_next) {
2724
     * We don't do it yet, however, because is is an incompatible change
2725
     * (need to implement a separate option for this)
2726
     */
2727
    for (res = res0; res; res = res->ai_next) {
599✔
2728
        /* opt_name_on: addr -> name lookup requested) */
2729
        if (opt_name_on || opt_rdns_on) {
601✔
2730
            int do_rdns = opt_rdns_on ? 1 : 0;
3✔
2731
            if (opt_name_on) {
3✔
2732
                /* Was it a numerical address? Only then do a rdns-query */
2733
                struct addrinfo *nres;
2734
                hints.ai_flags = AI_NUMERICHOST;
×
2735
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2736
                    do_rdns = 1;
×
2737
                    freeaddrinfo(nres);
×
2738
                }
2739
            }
2740

2741
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2742
                printname = namebuf;
3✔
2743
            }
1✔
2744
            else {
2745
                printname = name;
×
2746
            }
2747
        }
1✔
2748
        else {
2749
            printname = name;
596✔
2750
        }
2751

2752
        /* opt_addr_on: name -> addr lookup requested */
2753
        if (opt_addr_on) {
599✔
2754
            int ret;
2755
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2756
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2757
            if (ret) {
3✔
2758
                if (!opt_quiet_on) {
×
2759
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2760
                }
2761
                continue;
×
2762
            }
2763

2764
            if (opt_name_on || opt_rdns_on) {
3✔
2765
                char nameaddrbuf[512 + 3];
2766
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2767
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2768
            }
2769
            else {
2770
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2771
            }
2772
        }
1✔
2773
        else {
2774
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
596✔
2775
        }
2776

2777
        if (!opt_multif_on) {
599✔
2778
            break;
599✔
2779
        }
2780
    }
2781

2782
    freeaddrinfo(res0);
599✔
2783
}
166✔
2784

2785
/************************************************************
2786

2787
  Function: add_addr
2788

2789
*************************************************************
2790

2791
  Description:
2792

2793
  add single address to list of hosts to be pinged
2794

2795
************************************************************/
2796

2797
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
599✔
2798
{
2799
    HOST_ENTRY *p;
2800
    int n;
2801
    int64_t *i;
2802

2803
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
599✔
2804
    if (!p)
599✔
2805
        crash_and_burn("can't allocate HOST_ENTRY");
×
2806

2807
    p->name = strdup(name);
599✔
2808
    p->host = strdup(host);
599✔
2809
    memcpy(&p->saddr, ipaddr, ipaddr_len);
599✔
2810
    p->saddr_len = ipaddr_len;
599✔
2811
    p->timeout = opt_timeout;
599✔
2812
    p->min_reply = 0;
599✔
2813

2814
    if (opt_print_netdata_on) {
599✔
2815
        char *s = p->name;
3✔
2816
        while (*s) {
30✔
2817
            if (!isalnum(*s))
27✔
2818
                *s = '_';
9✔
2819
            s++;
27✔
2820
        }
2821
    }
1✔
2822

2823
    if (strlen(p->host) > max_hostname_len)
599✔
2824
        max_hostname_len = strlen(p->host);
406✔
2825

2826
    /* array for response time results */
2827
    if (!opt_loop_on) {
599✔
2828
#if SIZE_MAX <= UINT_MAX
2829
        if (trials > (SIZE_MAX / sizeof(int64_t)))
2830
            crash_and_burn("resp_times array too large for memory");
2831
#endif
2832
        i = (int64_t *)malloc(trials * sizeof(int64_t));
585✔
2833
        if (!i)
585✔
2834
            crash_and_burn("can't allocate resp_times array");
×
2835

2836
        for (n = 0; n < trials; n++)
2,906✔
2837
            i[n] = RESP_UNUSED;
2,321✔
2838

2839
        p->resp_times = i;
585✔
2840
    }
153✔
2841

2842
    /* allocate event storage */
2843
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
599✔
2844
    if (!p->event_storage_ping) {
599✔
2845
        errno_crash_and_burn("can't allocate event_storage_ping");
×
2846
    }
2847
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
599✔
2848
    if (!p->event_storage_timeout) {
599✔
2849
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
2850
    }
2851

2852
    /* schedule first ping */
2853
    host_add_ping_event(p, 0, current_time_ns);
599✔
2854

2855
    num_hosts++;
599✔
2856
}
599✔
2857

2858
/************************************************************
2859

2860
  Function: crash_and_burn
2861

2862
*************************************************************
2863

2864
  Inputs:  char* message
2865

2866
  Description:
2867

2868
************************************************************/
2869

2870
void crash_and_burn(char *message)
×
2871
{
2872
    fprintf(stderr, "%s: %s\n", prog, message);
×
2873
    exit(4);
×
2874
}
2875

2876
/************************************************************
2877

2878
  Function: errno_crash_and_burn
2879

2880
*************************************************************
2881

2882
  Inputs:  char* message
2883

2884
  Description:
2885

2886
************************************************************/
2887

2888
void errno_crash_and_burn(char *message)
8✔
2889
{
2890
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2891
    exit(4);
8✔
2892
}
2893

2894
/************************************************************
2895

2896
  Function: print_warning
2897

2898
  Description: fprintf(stderr, ...), unless running with -q
2899

2900
*************************************************************/
2901

2902
void print_warning(char *format, ...)
54✔
2903
{
2904
    va_list args;
2905
    if (!opt_quiet_on) {
54✔
2906
        va_start(args, format);
54✔
2907
        vfprintf(stderr, format, args);
54✔
2908
        va_end(args);
54✔
2909
    }
8✔
2910
}
54✔
2911

2912

2913

2914
/************************************************************
2915

2916
  Function: addr_cmp
2917

2918
*************************************************************/
2919
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
602✔
2920
{
2921
    if (a->sa_family != b->sa_family) {
602✔
2922
        return a->sa_family - b->sa_family;
×
2923
    }
2924
    else {
2925
        if (a->sa_family == AF_INET) {
602✔
2926
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
538✔
2927
        }
2928
        else if (a->sa_family == AF_INET6) {
64✔
2929
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
2930
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
2931
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2932
        }
2933
    }
2934

2935
    return 0;
×
2936
}
174✔
2937

2938
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,038✔
2939
{
2940
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,038✔
2941
    event->host = h;
1,038✔
2942
    event->ping_index = index;
1,038✔
2943
    event->ev_time = ev_time;
1,038✔
2944
    ev_enqueue(&event_queue_ping, event);
1,038✔
2945

2946
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2947
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2948
}
1,038✔
2949

2950
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,005✔
2951
{
2952
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
1,005✔
2953
    event->host = h;
1,005✔
2954
    event->ping_index = index;
1,005✔
2955
    event->ev_time = ev_time;
1,005✔
2956
    ev_enqueue(&event_queue_timeout, event);
1,005✔
2957

2958
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2959
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2960
}
1,005✔
2961

2962
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
890✔
2963
{
2964
    return &h->event_storage_timeout[index % event_storage_count];
890✔
2965
}
2966

2967
/************************************************************
2968

2969
  Function: ev_enqueue
2970

2971
  Enqueue an event
2972

2973
  The queue is sorted by event->ev_time, so that queue->first always points to
2974
  the earliest event.
2975

2976
  We start scanning the queue from the tail, because we assume
2977
  that new events mostly get inserted with a event time higher
2978
  than the others.
2979

2980
*************************************************************/
2981
void ev_enqueue(struct event_queue *queue, struct event *event)
2,043✔
2982
{
2983
    struct event *i;
2984
    struct event *i_prev;
2985

2986
    /* Empty list */
2987
    if (queue->last == NULL) {
2,043✔
2988
        event->ev_next = NULL;
1,651✔
2989
        event->ev_prev = NULL;
1,651✔
2990
        queue->first = event;
1,651✔
2991
        queue->last = event;
1,651✔
2992
        return;
1,651✔
2993
    }
2994

2995
    /* Insert on tail? */
2996
    if (event->ev_time - queue->last->ev_time >= 0) {
392✔
2997
        event->ev_next = NULL;
392✔
2998
        event->ev_prev = queue->last;
392✔
2999
        queue->last->ev_next = event;
392✔
3000
        queue->last = event;
392✔
3001
        return;
392✔
3002
    }
3003

3004
    /* Find insertion point */
3005
    i = queue->last;
×
3006
    while (1) {
3007
        i_prev = i->ev_prev;
×
3008
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3009
            event->ev_prev = i_prev;
×
3010
            event->ev_next = i;
×
3011
            i->ev_prev = event;
×
3012
            if (i_prev != NULL) {
×
3013
                i_prev->ev_next = event;
×
3014
            }
3015
            else {
3016
                queue->first = event;
×
3017
            }
3018
            return;
×
3019
        }
3020
        i = i_prev;
×
3021
    }
3022
}
543✔
3023

3024
/************************************************************
3025

3026
  Function: ev_dequeue
3027

3028
*************************************************************/
3029
struct event *ev_dequeue(struct event_queue *queue)
1,105✔
3030
{
3031
    struct event *dequeued;
3032

3033
    if (queue->first == NULL) {
1,105✔
3034
        return NULL;
×
3035
    }
3036
    dequeued = queue->first;
1,105✔
3037
    ev_remove(queue, dequeued);
1,105✔
3038

3039
    return dequeued;
1,105✔
3040
}
283✔
3041

3042
/************************************************************
3043

3044
  Function: ev_remove
3045

3046
*************************************************************/
3047
void ev_remove(struct event_queue *queue, struct event *event)
1,995✔
3048
{
3049
    if (queue->first == event) {
1,995✔
3050
        queue->first = event->ev_next;
1,986✔
3051
    }
526✔
3052
    if (queue->last == event) {
1,995✔
3053
        queue->last = event->ev_prev;
1,624✔
3054
    }
415✔
3055
    if (event->ev_prev) {
1,995✔
3056
        event->ev_prev->ev_next = event->ev_next;
9✔
3057
    }
3✔
3058
    if (event->ev_next) {
1,995✔
3059
        event->ev_next->ev_prev = event->ev_prev;
371✔
3060
    }
114✔
3061
    event->ev_prev = NULL;
1,995✔
3062
    event->ev_next = NULL;
1,995✔
3063
}
1,995✔
3064

3065

3066

3067
/************************************************************
3068

3069
  Function: usage
3070

3071
*************************************************************
3072

3073
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3074

3075
  Description:
3076

3077
************************************************************/
3078

3079
void usage(int is_error)
297✔
3080
{
3081
    FILE *out = is_error ? stderr : stdout;
297✔
3082
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
297✔
3083
    fprintf(out, "\n");
297✔
3084
    fprintf(out, "Probing options:\n");
297✔
3085
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
297✔
3086
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
297✔
3087
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
297✔
3088
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
297✔
3089
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
297✔
3090
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
297✔
3091
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
297✔
3092
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
297✔
3093
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
297✔
3094
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
297✔
3095
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
297✔
3096
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", opt_interval / 1e6);
297✔
3097
#ifdef SO_BINDTODEVICE
3098
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
297✔
3099
#endif
3100
#ifdef IP_PKTINFO
3101
    fprintf(out, "       --oiface=IFACE  send pings via a specific outgoing interface (receive from any)\n");
297✔
3102
#endif
3103
#ifdef SO_MARK
3104
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
202✔
3105
#endif
3106
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
297✔
3107
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
297✔
3108
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
297✔
3109
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
297✔
3110
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
297✔
3111
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", opt_perhost_interval / 1e6);
297✔
3112
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
297✔
3113
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
297✔
3114
    fprintf(out, "   -S, --src=IP       set source address\n");
297✔
3115
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", opt_seqmap_timeout / 1e6);
297✔
3116
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", opt_timeout / 1e6);
297✔
3117
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
297✔
3118
    fprintf(out, "       --check-source discard replies not from target address\n");
297✔
3119
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
297✔
3120
    fprintf(out, "\n");
297✔
3121
    fprintf(out, "Output options:\n");
297✔
3122
    fprintf(out, "   -a, --alive        show targets that are alive\n");
297✔
3123
    fprintf(out, "   -A, --addr         show targets by address\n");
297✔
3124
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
297✔
3125
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
297✔
3126
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
297✔
3127
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
297✔
3128
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
297✔
3129
    fprintf(out, "   -J, --json         output in JSON format (-c, -C, or -l required)\n");
297✔
3130
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
297✔
3131
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
297✔
3132
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
297✔
3133
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
297✔
3134
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
297✔
3135
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
297✔
3136
    fprintf(out, "   -s, --stats        print final stats\n");
297✔
3137
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
297✔
3138
    fprintf(out, "   -v, --version      show version\n");
297✔
3139
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
297✔
3140
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
297✔
3141
    fprintf(out, "       --print-tos    show received TOS value\n");
297✔
3142
    fprintf(out, "       --print-ttl    show IP TTL value\n");
297✔
3143
    exit(is_error);
297✔
3144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc