summaryrefslogtreecommitdiff
path: root/ping.c
diff options
context:
space:
mode:
Diffstat (limited to 'ping.c')
-rw-r--r--ping.c318
1 files changed, 247 insertions, 71 deletions
diff --git a/ping.c b/ping.c
index 2b41f44..25bbc3c 100644
--- a/ping.c
+++ b/ping.c
@@ -62,6 +62,16 @@ char copyright[] =
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
+#ifndef WITHOUT_IFADDRS
+#include <ifaddrs.h>
+#endif
+
+#ifndef ICMP_FILTER
+#define ICMP_FILTER 1
+struct icmp_filter {
+ __u32 data;
+};
+#endif
#ifdef ANDROID
#include <sys/auxv.h>
@@ -72,6 +82,7 @@ char copyright[] =
#define MAXICMPLEN 76
#define NROUTES 9 /* number of record route slots */
#define TOS_MAX 255 /* 8-bit TOS field */
+#define MAX_HOSTNAMELEN NI_MAXHOST
static int ts_type;
@@ -116,7 +127,12 @@ main(int argc, char **argv)
int ch, hold, packlen;
int socket_errno;
u_char *packet;
- char *target, hnamebuf[MAXHOSTNAMELEN];
+ char *target;
+#ifdef USE_IDN
+ char *hnamebuf = NULL;
+#else
+ char hnamebuf[MAX_HOSTNAMELEN];
+#endif
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
#ifdef ANDROID
@@ -126,6 +142,14 @@ main(int argc, char **argv)
}
#endif
+ limit_capabilities();
+
+#ifdef USE_IDN
+ setlocale(LC_ALL, "");
+#endif
+
+ enable_capability_raw();
+
icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
if (icmp_sock != -1)
using_ping_socket = 1;
@@ -133,10 +157,7 @@ main(int argc, char **argv)
icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
socket_errno = errno;
- uid = getuid();
-#ifndef ANDROID
- setuid(uid);
-#endif
+ disable_capability_raw();
source.sin_family = AF_INET;
@@ -144,7 +165,7 @@ main(int argc, char **argv)
while ((ch = getopt(argc, argv, COMMON_OPTSTR "bRT:")) != EOF) {
switch(ch) {
case 'b':
- broadcast_pings = 1;
+ broadcast_pings = 1;
break;
case 'Q':
settos = parsetos(optarg);
@@ -181,6 +202,7 @@ main(int argc, char **argv)
break;
case 'I':
{
+#if 0
char dummy;
int i1, i2, i3, i4;
@@ -196,6 +218,12 @@ main(int argc, char **argv)
} else {
device = optarg;
}
+#else
+ if (inet_pton(AF_INET, optarg, &source.sin_addr) > 0)
+ options |= F_STRICTSOURCE;
+ else
+ device = optarg;
+#endif
break;
}
case 'M':
@@ -211,7 +239,7 @@ main(int argc, char **argv)
}
break;
case 'V':
- printf("ping utility, iputils-ss%s\n", SNAPSHOT);
+ printf("ping utility, iputils-%s\n", SNAPSHOT);
exit(0);
COMMON_OPTIONS
common_options(ch);
@@ -223,7 +251,7 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (argc == 0)
+ if (argc == 0)
usage();
if (argc > 1) {
if (options & F_RROUTE)
@@ -242,21 +270,51 @@ main(int argc, char **argv)
while (argc > 0) {
target = *argv;
- bzero((char *)&whereto, sizeof(whereto));
+ memset((char *)&whereto, 0, sizeof(whereto));
whereto.sin_family = AF_INET;
if (inet_aton(target, &whereto.sin_addr) == 1) {
hostname = target;
if (argc == 1)
options |= F_NUMERIC;
} else {
- hp = gethostbyname(target);
+ char *idn;
+#ifdef USE_IDN
+ int rc;
+
+ if (hnamebuf) {
+ free(hnamebuf);
+ hnamebuf = NULL;
+ }
+
+ rc = idna_to_ascii_lz(target, &idn, 0);
+ if (rc != IDNA_SUCCESS) {
+ fprintf(stderr, "ping: IDN encoding failed: %s\n", idna_strerror(rc));
+ exit(2);
+ }
+#else
+ idn = target;
+#endif
+ hp = gethostbyname(idn);
if (!hp) {
fprintf(stderr, "ping: unknown host %s\n", target);
exit(2);
}
+#ifdef USE_IDN
+ free(idn);
+#endif
memcpy(&whereto.sin_addr, hp->h_addr, 4);
+#ifdef USE_IDN
+ if (idna_to_unicode_lzlz(hp->h_name, &hnamebuf, 0) != IDNA_SUCCESS) {
+ hnamebuf = strdup(hp->h_name);
+ if (!hnamebuf) {
+ perror("ping: strdup");
+ exit(-1);
+ }
+ }
+#else
strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hnamebuf[sizeof(hnamebuf) - 1] = 0;
+#endif
hostname = hnamebuf;
}
if (argc > 1)
@@ -266,7 +324,7 @@ main(int argc, char **argv)
}
if (source.sin_addr.s_addr == 0) {
- int alen;
+ socklen_t alen;
struct sockaddr_in dst = whereto;
int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
@@ -276,9 +334,16 @@ main(int argc, char **argv)
}
if (device) {
struct ifreq ifr;
+ int rc;
+
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
- if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
+
+ enable_capability_raw();
+ rc = setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1);
+ disable_capability_raw();
+
+ if (rc == -1) {
if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) {
struct ip_mreqn imr;
if (ioctl(probe_fd, SIOCGIFINDEX, &ifr) < 0) {
@@ -291,6 +356,9 @@ main(int argc, char **argv)
perror("ping: IP_MULTICAST_IF");
exit(2);
}
+ } else {
+ perror("ping: SO_BINDTODEVICE");
+ exit(2);
}
}
}
@@ -329,6 +397,30 @@ main(int argc, char **argv)
exit(2);
}
source.sin_port = 0;
+
+#ifndef WITHOUT_IFADDRS
+ if (device) {
+ struct ifaddrs *ifa0, *ifa;
+ int ret;
+
+ ret = getifaddrs(&ifa0);
+ if (ret) {
+ fprintf(stderr, "gatifaddrs() failed.\n");
+ exit(2);
+ }
+ for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) &&
+ !memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
+ &source.sin_addr, sizeof(source.sin_addr)))
+ break;
+ }
+ freeifaddrs(ifa0);
+ if (!ifa)
+ fprintf(stderr, "ping: Warning: source address might be selected on device other than %s.\n", device);
+ }
+#endif
close(probe_fd);
} while (0);
@@ -428,7 +520,7 @@ main(int argc, char **argv)
/* record route option */
if (options & F_RROUTE) {
- bzero(rspace, sizeof(rspace));
+ memset(rspace, 0, sizeof(rspace));
rspace[0] = IPOPT_NOP;
rspace[1+IPOPT_OPTVAL] = IPOPT_RR;
rspace[1+IPOPT_OLEN] = sizeof(rspace)-1;
@@ -440,7 +532,7 @@ main(int argc, char **argv)
}
}
if (options & F_TIMESTAMP) {
- bzero(rspace, sizeof(rspace));
+ memset(rspace, 0, sizeof(rspace));
rspace[0] = IPOPT_TIMESTAMP;
rspace[1] = (ts_type==IPOPT_TS_TSONLY ? 40 : 36);
rspace[2] = 5;
@@ -461,8 +553,8 @@ main(int argc, char **argv)
optlen = 40;
}
if (options & F_SOURCEROUTE) {
- int i;
- bzero(rspace, sizeof(rspace));
+ int i;
+ memset(rspace, 0, sizeof(rspace));
rspace[0] = IPOPT_NOOP;
rspace[1+IPOPT_OPTVAL] = (options & F_SO_DONTROUTE) ? IPOPT_SSRR
: IPOPT_LSRR;
@@ -470,7 +562,7 @@ main(int argc, char **argv)
rspace[1+IPOPT_OFFSET] = IPOPT_MINOFF;
for (i=0; i<nroute; i++)
*(__u32*)&rspace[4+i*4] = route[i];
-
+
if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, 4 + nroute*4) < 0) {
perror("ping: record route");
exit(2);
@@ -490,7 +582,7 @@ main(int argc, char **argv)
perror ("ping: can't set broadcasting");
exit(2);
}
- }
+ }
if (options & F_NOLOOP) {
int loop = 0;
@@ -585,7 +677,7 @@ int receive_error_msg()
if (options & F_QUIET)
goto out;
if (options & F_FLOOD)
- write(STDOUT_FILENO, "E", 1);
+ write_stdout("E", 1);
else if (e->ee_errno != EMSGSIZE)
fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno));
else
@@ -630,8 +722,9 @@ int receive_error_msg()
goto out;
if (options & F_FLOOD) {
if (error_pkt)
- write(STDOUT_FILENO, "\bE", 2);
+ write_stdout("\bE", 2);
} else {
+ print_timestamp();
printf("From %s: icmp_seq=%u ", pr_addr(sin->sin_addr.s_addr), ntohs(icmph.un.echo.sequence));
pr_icmph(e->ee_type, e->ee_code, e->ee_info, NULL);
fflush(stdout);
@@ -664,16 +757,13 @@ int send_probe()
icp->un.echo.sequence = htons(ntransmitted+1);
icp->un.echo.id = ident; /* ID */
- CLR((ntransmitted+1) % mx_dup_ck);
+ rcvd_clear(ntransmitted+1);
if (timing) {
if (options&F_LATENCY) {
- static volatile int fake_fucked_egcs = sizeof(struct timeval);
struct timeval tmp_tv;
gettimeofday(&tmp_tv, NULL);
- /* egcs is crap or glibc is crap, but memcpy
- does not copy anything, if len is constant! */
- memcpy(icp+1, &tmp_tv, fake_fucked_egcs);
+ memcpy(icp+1, &tmp_tv, sizeof(tmp_tv));
} else {
memset(icp+1, 0, sizeof(struct timeval));
}
@@ -685,16 +775,13 @@ int send_probe()
icp->checksum = in_cksum((u_short *)icp, cc, 0);
if (timing && !(options&F_LATENCY)) {
- static volatile int fake_fucked_egcs = sizeof(struct timeval);
- struct timeval tmp_tv;
+ struct timeval tmp_tv;
gettimeofday(&tmp_tv, NULL);
- /* egcs is crap or glibc is crap, but memcpy
- does not copy anything, if len is constant! */
- memcpy(icp+1, &tmp_tv, fake_fucked_egcs);
- icp->checksum = in_cksum((u_short *)(icp+1), fake_fucked_egcs, ~icp->checksum);
+ memcpy(icp+1, &tmp_tv, sizeof(tmp_tv));
+ icp->checksum = in_cksum((u_short *)&tmp_tv, sizeof(tmp_tv), ~icp->checksum);
}
- do {
+ do {
static struct iovec iov = {outpack, 0};
static struct msghdr m = { &whereto, sizeof(whereto),
&iov, 1, &cmsg, 0, 0 };
@@ -715,6 +802,12 @@ int send_probe()
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*/
+void pr_echo_reply(__u8 *_icp, int len)
+{
+ struct icmphdr *icp = (struct icmphdr *)_icp;
+ printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence));
+}
+
int
parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
{
@@ -769,16 +862,17 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
if (icp->type == ICMP_ECHOREPLY) {
if (icp->un.echo.id != ident)
return 1; /* 'Twas not our ECHO */
- if (gather_statistics((__u8*)(icp+1), cc,
+ if (gather_statistics((__u8*)icp, sizeof(*icp), cc,
ntohs(icp->un.echo.sequence),
- ttl, 0, tv, pr_addr(from->sin_addr.s_addr)))
+ ttl, 0, tv, pr_addr(from->sin_addr.s_addr),
+ pr_echo_reply))
return 0;
} else {
/* We fall here when a redirect or source quench arrived.
* Also this branch processes icmp errors, when IP_RECVERR
* is broken. */
-
- switch (icp->type) {
+
+ switch (icp->type) {
case ICMP_ECHO:
/* MUST NOT */
return 1;
@@ -821,9 +915,10 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
return !error_pkt;
if (options & F_FLOOD) {
if (error_pkt)
- write(STDOUT_FILENO, "\bE", 2);
+ write_stdout("\bE", 2);
return !error_pkt;
}
+ print_timestamp();
printf("From %s: icmp_seq=%u ",
pr_addr(from->sin_addr.s_addr),
ntohs(icp1->un.echo.sequence));
@@ -832,19 +927,24 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp);
return !error_pkt;
}
- default:
+ default:
/* MUST NOT */
break;
}
if ((options & F_FLOOD) && !(options & (F_VERBOSE|F_QUIET))) {
if (!csfailed)
- write(STDOUT_FILENO, "!E", 2);
+ write_stdout("!E", 2);
else
- write(STDOUT_FILENO, "!EC", 3);
+ write_stdout("!EC", 3);
return 0;
}
if (!(options & F_VERBOSE) || uid)
return 0;
+ if (options & F_PTIMEOFDAY) {
+ struct timeval recv_time;
+ gettimeofday(&recv_time, NULL);
+ printf("%lu.%06lu ", (unsigned long)recv_time.tv_sec, (unsigned long)recv_time.tv_usec);
+ }
printf("From %s: ", pr_addr(from->sin_addr.s_addr));
if (csfailed) {
printf("(BAD CHECKSUM)\n");
@@ -861,10 +961,22 @@ parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
putchar('\a');
putchar('\n');
fflush(stdout);
+ } else {
+ putchar('\a');
+ fflush(stdout);
}
return 0;
}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define ODDBYTE(v) (v)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define ODDBYTE(v) ((u_short)(v) << 8)
+#else
+# define ODDBYTE(v) htons((u_short)(v) << 8)
+#endif
+
u_short
in_cksum(const u_short *addr, register int len, u_short csum)
{
@@ -886,7 +998,7 @@ in_cksum(const u_short *addr, register int len, u_short csum)
/* mop up an odd byte, if necessary */
if (nleft == 1)
- sum += htons(*(u_char *)w << 8);
+ sum += ODDBYTE(*(u_char *)w); /* le16toh() may be unavailable on old systems */
/*
* add back carry outs from top 16 bits to low 16 bits
@@ -928,9 +1040,36 @@ void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp)
case ICMP_SR_FAILED:
printf("Source Route Failed\n");
break;
+ case ICMP_NET_UNKNOWN:
+ printf("Destination Net Unknown\n");
+ break;
+ case ICMP_HOST_UNKNOWN:
+ printf("Destination Host Unknown\n");
+ break;
+ case ICMP_HOST_ISOLATED:
+ printf("Source Host Isolated\n");
+ break;
+ case ICMP_NET_ANO:
+ printf("Destination Net Prohibited\n");
+ break;
+ case ICMP_HOST_ANO:
+ printf("Destination Host Prohibited\n");
+ break;
+ case ICMP_NET_UNR_TOS:
+ printf("Destination Net Unreachable for Type of Service\n");
+ break;
+ case ICMP_HOST_UNR_TOS:
+ printf("Destination Host Unreachable for Type of Service\n");
+ break;
case ICMP_PKT_FILTERED:
printf("Packet filtered\n");
break;
+ case ICMP_PREC_VIOLATION:
+ printf("Precedence Violation\n");
+ break;
+ case ICMP_PREC_CUTOFF:
+ printf("Precedence Cutoff\n");
+ break;
default:
printf("Dest Unreachable, Bad Code: %d\n", code);
break;
@@ -1076,9 +1215,9 @@ void pr_options(unsigned char * cp, int hlen)
i = j;
i -= IPOPT_MINOFF;
if (i <= 0)
- continue;
+ break;
if (i == old_rrlen
- && !bcmp((char *)cp, old_rr, i)
+ && !memcmp(cp, old_rr, i)
&& !(options & F_FLOOD)) {
printf("\t(same route)");
i = ((i + 3) / 4) * 4;
@@ -1086,7 +1225,7 @@ void pr_options(unsigned char * cp, int hlen)
break;
}
old_rrlen = i;
- bcopy((char *)cp, old_rr, i);
+ memcpy(old_rr, (char *)cp, i);
printf("\nRR: ");
cp++;
for (;;) {
@@ -1113,7 +1252,7 @@ void pr_options(unsigned char * cp, int hlen)
i = j;
i -= 5;
if (i <= 0)
- continue;
+ break;
flags = *++cp;
printf("\nTS: ");
cp++;
@@ -1204,12 +1343,28 @@ pr_addr(__u32 addr)
struct hostent *hp;
static char buf[4096];
- if ((options & F_NUMERIC) ||
+ in_pr_addr = !setjmp(pr_addr_jmp);
+
+ if (exiting || (options & F_NUMERIC) ||
!(hp = gethostbyaddr((char *)&addr, 4, AF_INET)))
sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr));
- else
- snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
+ else {
+ char *s;
+#if USE_IDN
+ if (idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS)
+ s = NULL;
+#else
+ s = NULL;
+#endif
+ snprintf(buf, sizeof(buf), "%s (%s)", s ? s : hp->h_name,
inet_ntoa(*(struct in_addr *)&addr));
+#if USE_IDN
+ free(s);
+#endif
+ }
+
+ in_pr_addr = 0;
+
return(buf);
}
@@ -1217,27 +1372,27 @@ pr_addr(__u32 addr)
/* Set Type of Service (TOS) and other Quality of Service relating bits */
int parsetos(char *str)
{
- const char *cp;
- int tos;
- char *ep;
+ const char *cp;
+ int tos;
+ char *ep;
- /* handle both hex and decimal values */
- if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
+ /* handle both hex and decimal values */
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
cp = str + 2;
tos = (int)strtol(cp, &ep, 16);
- } else
- tos = (int)strtol(str, &ep, 10);
-
- /* doesn't look like decimal or hex, eh? */
- if (*ep != '\0') {
- fprintf(stderr, "ping: \"%s\" bad value for TOS\n", str);
- exit(2);
- }
-
- if (tos > TOS_MAX) {
- fprintf(stderr, "ping: the decimal value of TOS bits must be 0-254 (or zero)\n");
- exit(2);
- }
+ } else
+ tos = (int)strtol(str, &ep, 10);
+
+ /* doesn't look like decimal or hex, eh? */
+ if (*ep != '\0') {
+ fprintf(stderr, "ping: \"%s\" bad value for TOS\n", str);
+ exit(2);
+ }
+
+ if (tos > TOS_MAX) {
+ fprintf(stderr, "ping: the decimal value of TOS bits must be 0-254 (or zero)\n");
+ exit(2);
+ }
return(tos);
}
@@ -1266,19 +1421,40 @@ void install_filter(void)
once = 1;
/* Patch bpflet for current identifier. */
- insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __constant_htons(ident), 0, 1);
+ insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1);
if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
perror("WARNING: failed to install socket filter\n");
}
+#define USAGE_NEWLINE "\n "
void usage(void)
{
fprintf(stderr,
-"Usage: ping [-LRUbdfnqrvVaA] [-c count] [-i interval] [-w deadline]\n"
-" [-p pattern] [-s packetsize] [-t ttl] [-I interface or address]\n"
-" [-M mtu discovery hint] [-S sndbuf]\n"
-" [ -T timestamp option ] [ -Q tos ] [hop1 ...] destination\n");
+ "Usage: ping"
+ " [-"
+ "aAbBdDfhLnOqrRUvV"
+ "]"
+ " [-c count]"
+ " [-i interval]"
+ " [-I interface]"
+ USAGE_NEWLINE
+ " [-m mark]"
+ " [-M pmtudisc_option]"
+ " [-l preload]"
+ " [-p pattern]"
+ " [-Q tos]"
+ USAGE_NEWLINE
+ " [-s packetsize]"
+ " [-S sndbuf]"
+ " [-t ttl]"
+ " [-T timestamp_option]"
+ USAGE_NEWLINE
+ " [-w deadline]"
+ " [-W timeout]"
+ " [hop1 ...] destination"
+ "\n"
+ );
exit(2);
}