diff options
Diffstat (limited to 'ping.c')
-rw-r--r-- | ping.c | 318 |
1 files changed, 247 insertions, 71 deletions
@@ -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); } |