mirror of
https://git.savannah.gnu.org/git/inetutils.git
synced 2026-01-12 00:19:39 +08:00
309 lines
5.9 KiB
C
309 lines
5.9 KiB
C
/*
|
|
Copyright (C) 2005-2025 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Inetutils.
|
|
|
|
GNU Inetutils is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or (at
|
|
your option) any later version.
|
|
|
|
GNU Inetutils is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see `http://www.gnu.org/licenses/'. */
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <xalloc.h>
|
|
#include <attribute.h>
|
|
#include <timespec.h>
|
|
|
|
#include "ping_common.h"
|
|
|
|
extern unsigned char *data_buffer;
|
|
extern size_t data_length;
|
|
|
|
static void _ping_freebuf (PING * p);
|
|
extern unsigned int options;
|
|
|
|
size_t
|
|
ping_cvt_number (const char *optarg, size_t maxval, int allow_zero)
|
|
{
|
|
char *p;
|
|
unsigned long int n;
|
|
|
|
n = strtoul (optarg, &p, 0);
|
|
if (*p)
|
|
error (EXIT_FAILURE, 0, "invalid value (`%s' near `%s')", optarg, p);
|
|
|
|
if (n == 0 && !allow_zero)
|
|
error (EXIT_FAILURE, 0, "option value too small: %s", optarg);
|
|
|
|
if (maxval && n > maxval)
|
|
error (EXIT_FAILURE, 0, "option value too big: %s", optarg);
|
|
|
|
return n;
|
|
}
|
|
|
|
void
|
|
init_data_buffer (unsigned char *pat, size_t len)
|
|
{
|
|
size_t i = 0;
|
|
unsigned char *p;
|
|
|
|
if (data_length == 0)
|
|
return;
|
|
|
|
data_buffer = xmalloc (data_length);
|
|
|
|
if (pat)
|
|
{
|
|
for (p = data_buffer; p < data_buffer + data_length; p++)
|
|
{
|
|
*p = pat[i];
|
|
if (++i >= len)
|
|
i = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < data_length; i++)
|
|
data_buffer[i] = i;
|
|
}
|
|
}
|
|
|
|
void
|
|
decode_pattern (const char *text, int *pattern_len,
|
|
unsigned char *pattern_data)
|
|
{
|
|
int i, c, off;
|
|
|
|
for (i = 0; *text && i < *pattern_len; i++)
|
|
{
|
|
if (sscanf (text, "%2x%n", &c, &off) != 1)
|
|
error (EXIT_FAILURE, 0, "error in pattern near %s", text);
|
|
|
|
text += off;
|
|
pattern_data[i] = c;
|
|
}
|
|
*pattern_len = i;
|
|
}
|
|
|
|
double
|
|
nabs (double a)
|
|
{
|
|
return (a < 0) ? -a : a;
|
|
}
|
|
|
|
double
|
|
nsqrt (double a, double prec)
|
|
{
|
|
double x0, x1;
|
|
|
|
if (a < 0)
|
|
return 0;
|
|
if (a < prec)
|
|
return 0;
|
|
x1 = a / 2;
|
|
do
|
|
{
|
|
x0 = x1;
|
|
x1 = (x0 + a / x0) / 2;
|
|
}
|
|
while (nabs (x1 - x0) > prec);
|
|
|
|
return x1;
|
|
}
|
|
|
|
int
|
|
is_normed_time (n_time t)
|
|
{
|
|
/* A set MSB indicates non-normalised time standard. */
|
|
return (t & (1UL << 31)) ? 0 : 1;
|
|
}
|
|
|
|
const char *
|
|
ping_cvt_time (char *buf, size_t buflen, n_time t)
|
|
{
|
|
n_time t_red;
|
|
|
|
t_red = t & ((1UL << 31) - 1);
|
|
|
|
if (is_normed_time (t))
|
|
snprintf (buf, buflen, "%u", t_red);
|
|
else
|
|
snprintf (buf, buflen, "<%u>", t_red);
|
|
|
|
return buf;
|
|
}
|
|
|
|
int
|
|
_ping_setbuf (PING *p, bool use_ipv6)
|
|
{
|
|
if (!p->ping_buffer)
|
|
{
|
|
p->ping_buffer = malloc (_PING_BUFLEN (p, use_ipv6));
|
|
if (!p->ping_buffer)
|
|
return -1;
|
|
}
|
|
if (!p->ping_cktab)
|
|
{
|
|
p->ping_cktab = malloc (p->ping_cktab_size);
|
|
if (!p->ping_cktab)
|
|
return -1;
|
|
memset (p->ping_cktab, 0, p->ping_cktab_size);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ping_set_data (PING *p, void *data, size_t off, size_t len, bool use_ipv6)
|
|
{
|
|
icmphdr_t *icmp;
|
|
|
|
if (_ping_setbuf (p, use_ipv6))
|
|
return -1;
|
|
if (p->ping_datalen < off + len)
|
|
return -1;
|
|
|
|
icmp = (icmphdr_t *) p->ping_buffer;
|
|
memcpy (icmp->icmp_data + off, data, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ping_set_count (PING *ping, size_t count)
|
|
{
|
|
ping->ping_count = count;
|
|
}
|
|
|
|
void
|
|
ping_set_sockopt (PING *ping, int opt, void *val, int valsize)
|
|
{
|
|
setsockopt (ping->ping_fd, SOL_SOCKET, opt, (char *) &val, valsize);
|
|
}
|
|
|
|
void
|
|
ping_set_interval (PING *ping, size_t interval)
|
|
{
|
|
ping->ping_interval = interval;
|
|
}
|
|
|
|
void
|
|
_ping_freebuf (PING *p)
|
|
{
|
|
if (p->ping_buffer)
|
|
{
|
|
free (p->ping_buffer);
|
|
p->ping_buffer = NULL;
|
|
}
|
|
if (p->ping_cktab)
|
|
{
|
|
free (p->ping_cktab);
|
|
p->ping_cktab = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
ping_unset_data (PING *p)
|
|
{
|
|
_ping_freebuf (p);
|
|
}
|
|
|
|
bool
|
|
ping_timeout_p (struct timespec *start_time, int timeout)
|
|
{
|
|
if (timeout == -1)
|
|
return false;
|
|
return timespec_sub (current_timespec (), *start_time).tv_sec >= timeout;
|
|
}
|
|
|
|
char *
|
|
ipaddr2str (struct sockaddr *from, socklen_t fromlen)
|
|
{
|
|
int err;
|
|
size_t len;
|
|
char *buf, ipstr[INET6_ADDRSTRLEN], hoststr[256];
|
|
|
|
err = getnameinfo (from, fromlen, ipstr, sizeof (ipstr),
|
|
NULL, 0, NI_NUMERICHOST);
|
|
if (err)
|
|
{
|
|
const char *errmsg;
|
|
|
|
if (err == EAI_SYSTEM)
|
|
errmsg = strerror (errno);
|
|
else
|
|
errmsg = gai_strerror (err);
|
|
|
|
fprintf (stderr, "ping: getnameinfo: %s\n", errmsg);
|
|
return xstrdup ("unknown");
|
|
}
|
|
|
|
if (options & OPT_NUMERIC)
|
|
return xstrdup (ipstr);
|
|
|
|
err = getnameinfo (from, fromlen, hoststr, sizeof (hoststr), NULL, 0,
|
|
#ifdef NI_IDN
|
|
NI_IDN | NI_NAMEREQD
|
|
#else
|
|
NI_NAMEREQD
|
|
#endif
|
|
);
|
|
if (err)
|
|
return xstrdup (ipstr);
|
|
|
|
len = strlen (ipstr) + strlen (hoststr) + 4; /* Pair of parentheses, a space
|
|
and a NUL. */
|
|
buf = xmalloc (len);
|
|
snprintf (buf, len, "%s (%s)", hoststr, ipstr);
|
|
|
|
return buf;
|
|
}
|
|
|
|
char *
|
|
sinaddr2str (struct in_addr ina)
|
|
{
|
|
struct hostent *hp;
|
|
|
|
if (options & OPT_NUMERIC)
|
|
return xstrdup (inet_ntoa (ina));
|
|
|
|
hp = gethostbyaddr ((char *) &ina, sizeof (ina), AF_INET);
|
|
if (hp == NULL)
|
|
return xstrdup (inet_ntoa (ina));
|
|
else
|
|
{
|
|
char *buf, *ipstr;
|
|
int len;
|
|
|
|
ipstr = inet_ntoa (ina);
|
|
len = strlen (ipstr) + 1;
|
|
|
|
if (hp->h_name)
|
|
len += strlen (hp->h_name) + 4; /* parentheses, space, and NUL */
|
|
|
|
buf = xmalloc (len);
|
|
if (hp->h_name)
|
|
snprintf (buf, len, "%s (%s)", hp->h_name, ipstr);
|
|
else
|
|
snprintf (buf, len, "%s", ipstr);
|
|
return buf;
|
|
}
|
|
}
|