Last active
August 29, 2015 14:14
-
-
Save scvalex/148b2b4fbd1fe19d4d55 to your computer and use it in GitHub Desktop.
pingcat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: pingcat.c | |
gcc -o pingcat pingcat.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <netinet/in.h> | |
#include <netinet/ip.h> | |
#include <netinet/ip_icmp.h> | |
#define fail_if(v, msg) \ | |
if (v) { \ | |
fprintf(stderr, "error: %s\n", msg); \ | |
return 1;\ | |
} | |
int receive_ping(int icmp_sock, int expect, int cookie, int count, | |
int tries, int *id, u_char **data, int *datalen_out) { | |
static u_char buf[0x10000]; | |
struct msghdr msg; | |
int datalen; | |
struct iovec iov = { buf, sizeof(buf) }; | |
struct iphdr *ip = (struct iphdr *)buf; | |
struct icmphdr *icp; | |
while (tries-- > 0) { | |
memset(&msg, 0, sizeof(msg)); | |
msg.msg_name = 0; | |
msg.msg_namelen = 0; | |
msg.msg_iov = &iov; | |
msg.msg_iovlen = 1; | |
errno = 0; | |
datalen = recvmsg(icmp_sock, &msg, MSG_DONTWAIT); | |
if (errno == EAGAIN) { | |
struct timeval one_sec = {1, 0}; | |
select(0, 0, 0, 0, &one_sec); | |
continue; | |
} else { | |
fail_if(datalen < 0, "recvmsg"); | |
} | |
int hlen = ip->ihl*4; | |
datalen -= hlen; | |
if (datalen >= 4) { | |
icp = (struct icmphdr *)(buf + hlen); | |
char *payload = (char*)icp + 8; | |
int seqnum = ntohs(icp->un.echo.sequence); | |
if (icp->type == expect) { | |
if (*(uint32_t *)payload == cookie | |
&& seqnum == count) { | |
break; | |
} | |
fprintf(stderr, "wrong cookie (%c%c%c%c) or seqnum (%d != %d)\n", | |
payload[0], payload[1], payload[2], payload[3], | |
seqnum, count); | |
} | |
} | |
} | |
if (tries < 0) { | |
fprintf(stderr, "ran out of retries: %d", tries); | |
return 1; | |
} | |
/* printf("payload_len=%d: [", datalen); */ | |
/* int i; */ | |
/* for (i = 0; i < datalen; ++i) { */ | |
/* printf("%x ", buf[i]); */ | |
/* } */ | |
/* printf("]\n"); */ | |
/* printf(" icmp_seq=%u\n", ntohs(icp->un.echo.sequence)); */ | |
if (data != 0) { | |
*data = ((u_char*)icp) + 12; | |
*datalen_out = datalen - 12; | |
} | |
if (id != 0) { | |
*id = icp->un.echo.id; | |
} | |
return 0; | |
} | |
int server(uint32_t cookie) { | |
int icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); | |
fail_if(icmp_sock < 0, "socket()"); | |
int i; | |
for (i = 0; 1; ++i) { | |
u_char *data; | |
int datalen; | |
fail_if(receive_ping(icmp_sock, ICMP_ECHOREPLY, | |
cookie, i, 10, 0, &data, &datalen) != 0, | |
"receive_ping"); | |
if (datalen == 1) { | |
break; | |
} | |
fwrite(data, 1, datalen, stdout); | |
fflush(stdout); | |
} | |
return 0; | |
} | |
/* Original code from iputils. | |
http://www.linuxfoundation.org/collaborate/workgroups/networking/iputils | |
*/ | |
u_short in_cksum(const u_short *addr, register int len, u_short csum) | |
{ | |
register int nleft = len; | |
const u_short *w = addr; | |
register u_short answer; | |
register int sum = csum; | |
while (nleft > 1) { | |
sum += *w++; | |
nleft -= 2; | |
} | |
if (nleft == 1) { | |
sum += htons((u_short)(*(u_char *)w) << 8); | |
} | |
sum = (sum >> 16) + (sum & 0xffff); | |
sum += (sum >> 16); | |
answer = ~sum; | |
return (answer); | |
} | |
int send_ping(int icmp_sock, uint32_t cookie, int id, int count, | |
struct sockaddr_in addr, char *data, int datalen) { | |
static u_char buf[0x10000]; | |
static struct icmphdr *icp = (struct icmphdr *)buf; | |
icp->type = ICMP_ECHO; | |
icp->code = 0; | |
icp->checksum = 0; | |
icp->un.echo.sequence = htons(count); | |
icp->un.echo.id = id; | |
memset(buf + 8, 0, datalen + 5); | |
fail_if(0x10000 < datalen + 13, "too much data"); | |
*(uint32_t *)(buf + 8) = cookie; | |
memcpy(buf + 12, data, datalen); | |
icp->checksum = in_cksum((u_short*)icp, datalen + 13, 0); | |
struct iovec iov = { buf, 0 }; | |
iov.iov_len = datalen + 13; | |
struct msghdr hdr = { &addr, sizeof(addr), | |
&iov, 1, 0, 0, 0 }; | |
fail_if(sendmsg(icmp_sock, &hdr, 0) < 0, "sendmsg"); | |
return 0; | |
} | |
int client(uint32_t cookie, char *target) { | |
int icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); | |
fail_if(icmp_sock < 0, "socket()"); | |
struct sockaddr_in addr; | |
memset((char *)&addr, 0, sizeof(addr)); | |
addr.sin_family = AF_INET; | |
fail_if(inet_aton(target, &addr.sin_addr) != 1, "inet_aton"); | |
int id = htons(getpid() & 0xFFFF); | |
int i; | |
int terminating = 0; | |
for (i = 0; 1; ++i) { | |
char buf[1024]; | |
int size = 0; | |
if (terminating) { | |
break; | |
} | |
if ((size = fread(buf, 1, 1024, stdin)) <= 0) { | |
terminating = 1; | |
size = 0; | |
} | |
for (;;) { | |
fail_if(send_ping(icmp_sock, cookie, id, | |
i, addr, buf, size) != 0, | |
"send_ping"); | |
int re_id; | |
if (0 == receive_ping(icmp_sock, ICMP_ECHO, cookie, | |
i, 2, &re_id, 0, 0) | |
&& re_id == id) | |
{ | |
break; | |
} | |
fprintf(stderr, "ping lost\n"); | |
struct timeval one_sec = {1, 0}; | |
select(0, 0, 0, 0, &one_sec); | |
} | |
} | |
close(icmp_sock); | |
return 0; | |
} | |
uint32_t cookie_int(char *cookie) { | |
if (strlen(cookie) != 4) { | |
printf("cookie must be 4 bytes: %s\n", cookie); | |
exit(1); | |
} | |
return *(uint32_t*)cookie; | |
} | |
int main(int argc, char *argv[]) { | |
if (2 == argc) { | |
return server(cookie_int(argv[1])); | |
} else if (3 == argc) { | |
return client(cookie_int(argv[1]), argv[2]); | |
} else { | |
printf("USAGE: %s COOKIE [ADDRESS]\n", argv[0]); | |
return 1; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Blog post for this code: here.