X-Git-Url: http://git.thejh.net/?p=detour.git;a=blobdiff_plain;f=pulserecord.c;fp=pulserecord.c;h=f7bec563de532bb5d2ca6f35d7c97730a47255cd;hp=0000000000000000000000000000000000000000;hb=b6172a42cb1cc9e60c23696f20f5cdbd86e64cec;hpb=b2a2f3d5cc67240d1450c48700a20f07270a42c2 diff --git a/pulserecord.c b/pulserecord.c new file mode 100644 index 0000000..f7bec56 --- /dev/null +++ b/pulserecord.c @@ -0,0 +1,224 @@ +// most of this copied from http://www.tcpdump.org/pcap.html and other places + +#include +#include "common.c" +#include "uthash.h" + + +/* Ethernet addresses are 6 bytes */ +#define ETHER_ADDR_LEN 6 + +/* ethernet headers are always exactly 14 bytes */ +#define SIZE_ETHERNET 14 + +/* Ethernet header */ +struct sniff_ethernet { + u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */ + u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */ + u_short ether_type; /* IP? ARP? RARP? etc */ +}; + +/* IP header */ +struct sniff_ip { + u_char ip_vhl; /* version << 4 | header length >> 2 */ + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +}; +#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) +#define IP_V(ip) (((ip)->ip_vhl) >> 4) + +/* TCP header */ +typedef u_int tcp_seq; + +struct sniff_tcp { + u_short th_sport; /* source port */ + u_short th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ + u_char th_offx2; /* data offset, rsvd */ +#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4) + u_char th_flags; +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 +#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) + u_short th_win; /* window */ + u_short th_sum; /* checksum */ + u_short th_urp; /* urgent pointer */ +}; + + + +struct con { + UT_hash_handle hh; + char name[47]; + unsigned int bytesA, bytesB; + unsigned int idle; +}; +struct con *cons = NULL; + +time_t t; +bool even; + + +void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *packet) { + unsigned int len = h->len; + + /* The ethernet header */ + //const struct sniff_ethernet *ethernet = (struct sniff_ethernet*)(packet); + /* The IP header */ + const struct sniff_ip *ip = (struct sniff_ip*)(packet + SIZE_ETHERNET); + u_int size_ip = IP_HL(ip)*4; + if (size_ip < 20) { + printf(" * Invalid IP header length: %u bytes\n", size_ip); + return; + } + /* The TCP header */ + const struct sniff_tcp *tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip); + u_int size_tcp = TH_OFF(tcp)*4; + if (size_tcp < 20) { + printf(" * Invalid TCP header length: %u bytes\n", size_tcp); + return; + } + + // construct name string + // length of IP: 3+1+3+1+3+1+3=15 + // length of port: 5 + // total: 2*(15+1+5)+4+1=47 including '\0' + char name[47]; + uint32_t ipA = ntohl(ip->ip_src.s_addr); + uint32_t ipB = ntohl(ip->ip_dst.s_addr); + sprintf(name, "%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u", + ipA>>24, (ipA>>16)&0xff, (ipA>>8)&0xff, ipA&0xff, ntohs(tcp->th_sport), + ipB>>24, (ipB>>16)&0xff, (ipB>>8)&0xff, ipB&0xff, ntohs(tcp->th_dport)); + + //printf("%s: %u\n", name, len); + + struct con *c; + HASH_FIND_STR(cons, name, c); + if (!c) { + c = calloc(1, sizeof(struct con)); + if (!c) printf("calloc fail"), exit(1); + strcpy(c->name, name); + HASH_ADD_STR(cons, name, c); + } + + if (even) + c->bytesB += len; + else + c->bytesA += len; + c->idle = 0; +} + +int main(int argc, char **argv) { + if (argc != 2) puts("invocation: ./pulserecord "), exit(1); + char *dev = argv[1]; + + setbuf(stdout, NULL); + char errbuf[PCAP_ERRBUF_SIZE]; + printf("Device: %s\n", dev); + + mkdir("out", 0700); + if (chdir("out")) perror("unable to enter directory 'out'"), exit(1); + + // We use a zero-timeout. The pcap manual says: + // "to_ms is the read time out in milliseconds (a value of 0 means + // no time out; on at least some platforms, this means that you may + // wait until a sufficient number of packets arrive before seeing + // any packets, so you should use a non-zero timeout)." + // That's simply not acceptable for us, so we can use a zero timeout + // just as well and tell everyone to use a sensible OS. :D + pcap_t *handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf); + if (!handle) printf("can't open device %s: %s\n", dev, errbuf), exit(1); + if (pcap_datalink(handle) != DLT_EN10MB) + printf("Device %s doesn't provide Ethernet headers - not supported\n", dev), exit(1); + if (pcap_setnonblock(handle, 1, errbuf) == -1) printf("unable to go nonblocking\n"), exit(1); + int pcap_fd = pcap_get_selectable_fd(handle); + if (pcap_fd == -1) printf("unable to get a pcap fd\n"), exit(1); + + struct bpf_program fp; /* The compiled filter */ + if (pcap_compile(handle, &fp, "tcp", 1, PCAP_NETMASK_UNKNOWN) == -1) { + printf("Couldn't parse filter: %s\n", pcap_geterr(handle)); + exit(1); + } + if (pcap_setfilter(handle, &fp) == -1) { + printf("Couldn't install filter: %s\n", pcap_geterr(handle)); + exit(1); + } + + t = round_up(real_seconds(), 2); + even = (t&3) == 0; /* are we processing part 2/2 of one encoded bit? */ + while (1) { + struct timespec dst, delta; + dst.tv_sec = t; + dst.tv_nsec = 0; + + // this inner loop runs until we hit a 2s-boundary + while (1) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(pcap_fd, &rfds); + struct timespec cur; + int r = clock_gettime(CLOCK_REALTIME, &cur); + assert(r==0); + if (timespec_subtract(&delta, &dst, &cur)) break; + r = pselect(pcap_fd+1, &rfds, NULL, NULL, &delta, NULL); + if (r == -1) perror("select failed"), exit(1); + if (r == 0) { + // timeout + break; + } else { + r = pcap_dispatch(handle, -1, packet_handler, NULL); + if (r < 0) printf("pcap_dispatch failed\n"), exit(1); + } + } + + if (even) { // cycle complete + struct con *c, *tmp; + HASH_ITER(hh, cons, c, tmp) { + if (c->idle >= 10) { + HASH_DEL(cons, c); + free(c); + c = NULL; // just to be sure + continue; + } + char bit = '_'; + if (!c->idle) + bit = (c->bytesA < c->bytesB) ? '1' : '0'; + int fd = open(c->name, O_WRONLY|O_APPEND|O_CREAT, 0666); + if (fd == -1) { + perror("unable to open confile"); + } else { +w:; ssize_t r = write(fd, &bit, 1); + if (r == -1 && errno == EINTR) goto w; + if (r == -1) perror("confile write failed"); + if (r == 0) puts("confile write failed"); + close(fd); + } + c->bytesA = 0; + c->bytesB = 0; + c->idle++; + } + } + + t += 2; + even = !even; + } + + return 0; +}