add notice about where devurandom.c is from
[detour.git] / pulserecord.c
1 // most of this copied from http://www.tcpdump.org/pcap.html and other places
2
3 #include <pcap.h>
4 #include "common.c"
5 #include "uthash.h"
6
7
8 /* Ethernet addresses are 6 bytes */
9 #define ETHER_ADDR_LEN  6
10
11 /* ethernet headers are always exactly 14 bytes */
12 #define SIZE_ETHERNET 14
13
14 /* Ethernet header */
15 struct sniff_ethernet {
16   u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
17   u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
18   u_short ether_type; /* IP? ARP? RARP? etc */
19 };
20
21 /* IP header */
22 struct sniff_ip {
23   u_char ip_vhl;                /* version << 4 | header length >> 2 */
24   u_char ip_tos;                /* type of service */
25   u_short ip_len;               /* total length */
26   u_short ip_id;                /* identification */
27   u_short ip_off;               /* fragment offset field */
28 #define IP_RF 0x8000            /* reserved fragment flag */
29 #define IP_DF 0x4000            /* dont fragment flag */
30 #define IP_MF 0x2000            /* more fragments flag */
31 #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
32   u_char ip_ttl;                /* time to live */
33   u_char ip_p;                  /* protocol */
34   u_short ip_sum;               /* checksum */
35   struct in_addr ip_src,ip_dst; /* source and dest address */
36 };
37 #define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
38 #define IP_V(ip)                (((ip)->ip_vhl) >> 4)
39
40 /* TCP header */
41 typedef u_int tcp_seq;
42
43 struct sniff_tcp {
44   u_short th_sport;             /* source port */
45   u_short th_dport;             /* destination port */
46   tcp_seq th_seq;               /* sequence number */
47   tcp_seq th_ack;               /* acknowledgement number */
48   u_char th_offx2;              /* data offset, rsvd */
49 #define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
50   u_char th_flags;
51 #define TH_FIN 0x01
52 #define TH_SYN 0x02
53 #define TH_RST 0x04
54 #define TH_PUSH 0x08
55 #define TH_ACK 0x10
56 #define TH_URG 0x20
57 #define TH_ECE 0x40
58 #define TH_CWR 0x80
59 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
60   u_short th_win;               /* window */
61   u_short th_sum;               /* checksum */
62   u_short th_urp;               /* urgent pointer */
63 };
64
65
66
67 struct con {
68   UT_hash_handle hh;
69   char name[47];
70   unsigned int bytesA, bytesB;
71   unsigned int idle;
72 };
73 struct con *cons = NULL;
74
75 time_t t;
76 bool even;
77
78
79 void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *packet) {
80   unsigned int len = h->len;
81
82   /* The ethernet header */
83   //const struct sniff_ethernet *ethernet = (struct sniff_ethernet*)(packet);
84   /* The IP header */
85   const struct sniff_ip *ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
86   u_int size_ip = IP_HL(ip)*4;
87   if (size_ip < 20) {
88     printf("   * Invalid IP header length: %u bytes\n", size_ip);
89     return;
90   }
91   /* The TCP header */
92   const struct sniff_tcp *tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
93   u_int size_tcp = TH_OFF(tcp)*4;
94   if (size_tcp < 20) {
95     printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
96     return;
97   }
98
99   // construct name string
100   // length of IP: 3+1+3+1+3+1+3=15
101   // length of port: 5
102   // total: 2*(15+1+5)+4+1=47 including '\0'
103   char name[47];
104   uint32_t ipA = ntohl(ip->ip_src.s_addr);
105   uint32_t ipB = ntohl(ip->ip_dst.s_addr);
106   sprintf(name, "%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u",
107       ipA>>24, (ipA>>16)&0xff, (ipA>>8)&0xff, ipA&0xff, ntohs(tcp->th_sport),
108       ipB>>24, (ipB>>16)&0xff, (ipB>>8)&0xff, ipB&0xff, ntohs(tcp->th_dport));
109
110   //printf("%s: %u\n", name, len);
111
112   struct con *c;
113   HASH_FIND_STR(cons, name, c);
114   if (!c) {
115     c = calloc(1, sizeof(struct con));
116     if (!c) printf("calloc fail"), exit(1);
117     strcpy(c->name, name);
118     HASH_ADD_STR(cons, name, c);
119   }
120
121   if (even)
122     c->bytesB += len;
123   else
124     c->bytesA += len;
125   c->idle = 0;
126 }
127
128 int main(int argc, char **argv) {
129   if (argc != 2) puts("invocation: ./pulserecord <interface>"), exit(1);
130   char *dev = argv[1];
131
132   setbuf(stdout, NULL);
133   char errbuf[PCAP_ERRBUF_SIZE];
134   printf("Device: %s\n", dev);
135
136   mkdir("out", 0700);
137   if (chdir("out")) perror("unable to enter directory 'out'"), exit(1);
138
139   // We use a zero-timeout. The pcap manual says:
140   // "to_ms is the read time out in milliseconds (a value of 0 means
141   //  no time out; on at least some platforms, this means that you may
142   //  wait until a sufficient number of packets arrive before seeing
143   //  any packets, so you should use a non-zero timeout)."
144   // That's simply not acceptable for us, so we can use a zero timeout
145   // just as well and tell everyone to use a sensible OS. :D
146   pcap_t *handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
147   if (!handle) printf("can't open device %s: %s\n", dev, errbuf), exit(1);
148   if (pcap_datalink(handle) != DLT_EN10MB)
149     printf("Device %s doesn't provide Ethernet headers - not supported\n", dev), exit(1);
150   if (pcap_setnonblock(handle, 1, errbuf) == -1) printf("unable to go nonblocking\n"), exit(1);
151   int pcap_fd = pcap_get_selectable_fd(handle);
152   if (pcap_fd == -1) printf("unable to get a pcap fd\n"), exit(1);
153
154   struct bpf_program fp; /* The compiled filter */
155   if (pcap_compile(handle, &fp, "tcp", 1, PCAP_NETMASK_UNKNOWN) == -1) {
156     printf("Couldn't parse filter: %s\n", pcap_geterr(handle));
157     exit(1);
158   }
159   if (pcap_setfilter(handle, &fp) == -1) {
160     printf("Couldn't install filter: %s\n", pcap_geterr(handle));
161     exit(1);
162   }
163
164   t = round_up(real_seconds(), 2);
165   even = (t&3) == 0; /* are we processing part 2/2 of one encoded bit? */
166   while (1) {
167     struct timespec dst, delta;
168     dst.tv_sec = t;
169     dst.tv_nsec = 0;
170
171     // this inner loop runs until we hit a 2s-boundary
172     while (1) {
173       fd_set rfds;
174       FD_ZERO(&rfds);
175       FD_SET(pcap_fd, &rfds);
176       struct timespec cur;
177       int r = clock_gettime(CLOCK_REALTIME, &cur);
178       assert(r==0);
179       if (timespec_subtract(&delta, &dst, &cur)) break;
180       r = pselect(pcap_fd+1, &rfds, NULL, NULL, &delta, NULL);
181       if (r == -1) perror("select failed"), exit(1);
182       if (r == 0) {
183         // timeout
184         break;
185       } else {
186         r = pcap_dispatch(handle, -1, packet_handler, NULL);
187         if (r < 0) printf("pcap_dispatch failed\n"), exit(1);
188       }
189     }
190
191     if (even) { // cycle complete
192       struct con *c, *tmp;
193       HASH_ITER(hh, cons, c, tmp) {
194         if (c->idle >= 10) {
195           HASH_DEL(cons, c);
196           free(c);
197           c = NULL; // just to be sure
198           continue;
199         }
200         char bit = '_';
201         if (!c->idle)
202           bit = (c->bytesA < c->bytesB) ? '1' : '0';
203         int fd = open(c->name, O_WRONLY|O_APPEND|O_CREAT, 0666);
204         if (fd == -1) {
205           perror("unable to open confile");
206         } else {
207 w:;       ssize_t r = write(fd, &bit, 1);
208           if (r == -1 && errno == EINTR) goto w;
209           if (r == -1) perror("confile write failed");
210           if (r == 0) puts("confile write failed");
211           close(fd);
212         }
213         c->bytesA = 0;
214         c->bytesB = 0;
215         c->idle++;
216       }
217     }
218
219     t += 2;
220     even = !even;
221   }
222
223   return 0;
224 }