Skip to content

Instantly share code, notes, and snippets.

@scvalex
Last active August 29, 2015 14:14
Show Gist options
  • Save scvalex/148b2b4fbd1fe19d4d55 to your computer and use it in GitHub Desktop.
Save scvalex/148b2b4fbd1fe19d4d55 to your computer and use it in GitHub Desktop.
pingcat
all: pingcat.c
gcc -o pingcat pingcat.c
#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;
}
}
@scvalex
Copy link
Author

scvalex commented Feb 2, 2015

Blog post for this code: here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment