1 /* TAP - Tcpdump Analysing Parser
2 a tcpdump parser for educational purposes
3 Copyright (C) 2008 ithilgore - ithilgore.ryu.L@gmail.com
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* Usage: tcpdump -X | ./tap */
20
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26
27 #define VERSION "0.6"
28
29 /* COLOUR CODES */
30 #define RED "^[[0;31;40m"
31 #define BLUE "^[[0;34;40m"
32 #define GREEN "^[[0;32;40m"
33
34 #define WC GREEN /* Window Colour "| + -" */
35 #define FC RED /* Field Colour */
36 #define VC BLUE /* Value Colour */
37
38 #define c2hex(c) (isdigit(c)) ? ((c) - 0x30) : (((c) & 0x0f) + 0x09)
39 #define print_lim() fprintf(stderr, WC "\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")
40 #define print_lim2() fprintf(stderr, WC "\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+")
41
42
43 /* FUNCTION DECLARATIONS */
44 inline int parse4(int, int);
45 void print_ip_steady(int, int);
46 void print_ip_options(void);
47 void print_tcp_steady(int, int);
48 void print_tcp_options(void);
49 void print_data(int term);
50
51
52 /* VARIABLES TYPEDEFS */
53
54 typedef struct info_ip {
55 int protocol; /* number for upper level protocol - tcp,udp,icmp etc */
56 int total_len; /* length of the whole datagram */
57 int hlen; /* header length - we see if there are ip options by this */
58 int options; /* bool var - true for existence of ip options */
59 int left; /* how many 4hexes(2 bytes) have been
60 left after steady header (5 32bit words)
61 (for ip options) */
62 } info_ip;
63
64 typedef struct info_tcp {
65 int hlen;
66 int options;
67 int left;
68 } info_tcp;
69
70
71 /* GLOBAL VARIABLES */
72
73 info_tcp tcp;
74 info_ip ip;
75 char tok[5]; /* buffer-token for holding the 4hexes + '\0' */
76 char buf[10]; /* helper buffer for operations */
77
78 int main(void)
79 {
80
81 int col = 0; /* column in tcpdump - or 4hex */
82 int line = 0; /* row in tcpdump output */
83 while(1) {
84 if (parse4(line, col) < 0) { /* current packet ended */
85 print_data(1);
86 fprintf(stderr, "\n\n");
87 col = 0;
88 line = 0;
89 memset(&ip, 0, sizeof(ip));
90 parse4(line, col); /* parse new packet */
91 }
92
93 if ((line == 0) || (line == 1 && col < 2))
94 print_ip_steady(line, col);
95 else if (ip.options && ip.left)
96 print_ip_options();
97 else { /* here we do the switching between the rest of the protocols
98 * based on the number we get from the ip header - that is
99 * tcp,udp,icmp etc */
100 print_tcp_steady(line, col);
101 }
102
103 col++;
104 if (col == 8) { /* new line */
105 col = 0;
106 line++;
107 }
108 }
109 return (EXIT_FAILURE);
110 }
111
112
113 int parse4(int line, int col)
114 {
115 char c;
116 char temp[9];
117 char buf[9];
118 static int normal = 0; /* var defining whether we are in the middle
119 * of parsing the 4hexes in a line */
120 if (!normal) {
121 while (1) {
122 c = fgetc(stdin);
123 if (c == '\n') {
124 memset(temp, 0, sizeof(temp));
125 fgets(temp, sizeof(temp), stdin);
126 snprintf(buf, sizeof(buf), "\t0x00%d0:", line);
127 if (!strncmp(temp, buf, sizeof(temp))) {
128 normal++;
129 fgetc(stdin); /* clear out next whitespace */
130 fgetc(stdin); /* clear out next whitespace */
131 break;
132 }
133 }
134 }
135 }
136
137
138 fgets(tok, sizeof(tok), stdin); /* read 4 chars and null terminate tok[4] */
139 fgetc(stdin); /* clear out next whitespace */
140
141 if (col == 7)
142 normal = 0;
143
144
145 /* The code lacks one end case where the end of datagram is at col 7.
146 * We have to find another way to define if the datagram has ended
147 * since checking if there is a space afterwards just wont to the
148 * job here. It has to know that its the end with another way
149 */
150
151 if (strchr(tok, ' ') != NULL) { /* we reached the end of datagram */
152 normal = 0;
153 return -1;
154 }
155 else
156 return 0;
157
158 }
159
160 void print_ip_options(void)
161 {
162 static int i = 0;
163 if (!i)
164 fprintf(stderr, WC "|" FC "\t\t Options: " VC "%s", tok);
165 else {
166 fprintf(stderr, VC " %s " WC "|", tok);
167 print_lim();
168 }
169 ip.left--;
170 if (!i)
171 i++;
172 else
173 i = 0;
174 }
175
176 void print_ip_steady(int line, int col)
177 {
178 if (!line) {
179 if (!col) {
180 print_lim();
181 fprintf(stderr, WC "|" FC "Ver: " VC "%c ", tok[0]);
182 fprintf(stderr, WC "|" FC "IHL: " VC "%c ", tok[1]);
183
184 ip.hlen = c2hex(tok[1]);
185 /* if header length is > 5 (in 32bit words) then
186 we must take into account to view the additional
187 options in the ip header */
188 if (ip.hlen > 5) {
189 ip.options++;
190 ip.left = (ip.hlen - 5) << 1;
191 }
192 else
193 ip.options = 0;
194
195 fprintf(stderr, WC "|" FC " ToS: " VC "%s ", &tok[2]);
196 }
197 else if (col == 1) {
198 /* copy the next portion of XXXX inside buf
199 take into account that there is a space between
200 each of the 4-char hex pairs that get formed */
201 ip.total_len = ((((c2hex(tok[0]) & 0x0f) << 12)
202 | ((c2hex(tok[1]) & 0x0f) << 8))
203 | ((c2hex(tok[2]) & 0x0f) << 4))
204 | (c2hex(tok[3]) & 0x0f);
205
206 fprintf(stderr, WC "|" FC " Total Length: " VC "%s " WC "|", tok);
207 print_lim();
208 }
209 else if (col == 2) {
210 fprintf(stderr, WC "|" FC " Identification: " VC "%s ", tok);
211 }
212 else if (col == 3) {
213 /* this (not really)obfu thing prints the 3bit flags of the ip header */
214 char c = c2hex(tok[0]);
215 fprintf(stderr, WC "|" FC "F: " VC "%x ", (c & 0x0e) >> 1);
216
217 /* print the 13bit fragment offset */
218 unsigned int t;
219 t = ((((c & 0x01) << 12)
220 | ((c2hex(tok[1]) & 0x0f) << 8))
221 | ((c2hex(tok[2]) & 0x0f) << 4))
222 | (c2hex(tok[3]) & 0x0f);
223 fprintf(stderr, WC "|" FC " Fragment Offset:" VC "%4x " WC "|", t);
224 print_lim();
225 }
226 else if (col == 4) {
227 memset(buf, 0, sizeof(buf));
228 strncpy(buf, tok, 2);
229 fprintf(stderr, WC "|" FC " TTL: " VC "%s ", buf);
230 fprintf(stderr, WC "|" FC " Protocol: " VC "%s " WC "|", &tok[2]);
231 }
232 else if (col == 5) {
233 fprintf(stderr, FC " Header Checksum: " VC "%s " WC "|", tok);
234 print_lim();
235 }
236 else if (col == 6) {
237 strncpy(buf, tok, 4);
238 }
239 else if (col == 7) {
240 buf[4] = ' ';
241 strncat(buf, tok, 5);
242 fprintf(stderr, WC "|" FC "\t\t Source Address: " VC "%s \t\t " WC "|", buf);
243 print_lim();
244 }
245 }
246 else if (line == 1) { /* line = 1 */
247 if (!col) {
248 memset(buf, 0, sizeof(buf));
249 strncpy(buf, tok, 4);
250 }
251 else if (col == 1) {
252 buf[4] = ' ';
253 strncat(buf, tok, 5);
254 fprintf(stderr, WC "|" FC "\t\t Destination Address: " VC "%s \t\t" WC "|", buf);
255 print_lim();
256 }
257 }
258 }
259
260
261 int calc_matrix(int col)
262 {
263 /* We calculate some offsets that
264 * the protocol above ip will need.
265 * The pattern goes sth like this:
266 * Suppose this is a typical output from tcpdump
267 * consisting of 2 lines of 8 4hexes each
268 *
269 * XXXX XXXX W XXXX XXXX C XXXX XXXX XXXX XXXX
270 * XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX
271 *
272 * We want to continue from the W point regardless
273 * of how many the ip options were.
274 * So for e.g if ip.hlen was 2 then it would have
275 * stopped at C so according to the lookup table
276 * we would need to subtract 2 to be back to where
277 * we were. The pattern is repeating itself with
278 * the same values above 7, (thus mod 8 is used).
279 */
280
281 static const int ltable[8] = {
282 0, /* 0 */
283 -1, /* 1 */
284 -2, /* 2 */
285 -3, /* 3 */
286 -4, /* 4 */
287 -5, /* 5 */
288 2, /* 6 */
289 1, /* 7 */
290 };
291
292 return (ltable[col % 8]);
293 }
294
295 void print_data(int term)
296 {
297 static int j = 0;
298
299 if (term && j) {
300 /* closing the Data field */
301 switch (j) {
302 case 1:
303 fprintf(stderr, WC "\t\t\t\t\t\t|");
304 break;
305 case 2:
306 fprintf(stderr, WC "\t\t\t\t\t|");
307 break;
308 case 3:
309 fprintf(stderr, WC "\t\t\t\t|");
310 break;
311 case 4:
312 fprintf(stderr, WC "\t\t\t\t|");
313 break;
314 case 5:
315 fprintf(stderr, WC "\t\t\t|");
316 break;
317 case 6:
318 fprintf(stderr, WC "\t\t|");
319 break;
320 case 7:
321 fprintf(stderr, WC "\t\t|");
322 break;
323 }
324 print_lim();
325 j = 0;
326 }
327 else if (!term) {
328 if (!j && strchr(tok, ' ') == NULL) {
329 fprintf(stderr, WC "|" FC "\t Data: " VC "%s ", tok);
330 j++;
331 }
332 else if (j < 7) {
333 fprintf(stderr, VC "%s ", tok);
334 j++;
335 }
336 else {
337 fprintf(stderr, VC "%s " WC "|", tok);
338 print_lim();
339 j = 0;
340 }
341 }
342 }
343
344 void print_tcp_options(void)
345 {
346 static int i = 0;
347 if (!i) {
348 fprintf(stderr, WC "|" FC "\t\t TCP Options: " VC "%s", tok);
349 i++;
350 }
351 else {
352 fprintf(stderr, VC " %s " WC "|", tok);
353 print_lim();
354 i = 0;
355 }
356 tcp.left--;
357 }
358
359 void print_tcp_steady(int __line_, int __col_)
360 {
361
362 /* To account for the possible loss of the lines eaten
363 * by the ip options we subtract the offset values
364 * we kept before, from the ones used here, so that
365 * the tcp header can be printed like the ip header
366 * was steady all the way here - we use calc_matrix()
367 * for that.
368 */
369 int line = 0;
370 int iphlen_4hex = ((ip.hlen - 0x5) << 1);
371
372 if (iphlen_4hex > 5) {
373 int i = 6;
374 while(1) {
375 if (iphlen_4hex >= i)
376 line++;
377 else
378 break;
379 i += 7;
380 }
381 }
382 line = __line_ - line;
383
384 int col = __col_ + calc_matrix(iphlen_4hex);
385
386 if (line == 1) {
387 if (col == 2) {
388 fprintf(stderr, WC "|" FC " Source Port: " VC "%s ", tok);
389 }
390 else if (col == 3) {
391 fprintf(stderr, WC "|" FC " Destination Port: " VC "%s " WC "|", tok);
392 print_lim();
393 }
394 else if (col == 4) {
395 memset(buf, 0, sizeof(buf));
396 strncpy(buf, tok, 4);
397 }
398 else if (col == 5) {
399 buf[4] = ' ';
400 strncat(buf, tok, 5);
401 fprintf(stderr, WC "|" FC "\t\t Sequence Number: " VC "%s \t\t\t" WC "|", buf);
402 print_lim();
403 }
404 else if (col == 6) {
405 memset(buf, 0, sizeof(buf));
406 strncpy(buf, tok, 4);
407 }
408 else if (col == 7) {
409 buf[4] = ' ';
410 strncat(buf, tok, 5);
411 fprintf(stderr, WC "|" FC "\t\t Acknowledgment Number: " VC "%s \t\t" WC "|", buf);
412 print_lim();
413 }
414 }
415 else if (line == 2) {
416 if (col == 0) {
417 tcp.hlen = c2hex(tok[0]);
418 fprintf(stderr, WC "|" FC "DO: " VC "%x ", tcp.hlen);
419 if (tcp.hlen > 5) {
420 tcp.options++;
421 tcp.left = (tcp.hlen - 5) << 1;
422 }
423 else
424 tcp.options = 0;
425
426 /* Reserved (6 bits) */
427 unsigned int t = c2hex(tok[1]) << 2;
428 t = t | ((c2hex(tok[2]) & 0xc0) >> 6);
429 fprintf(stderr, WC "|" FC "Reserved: " VC "%x", t);
430
431 /* Flags (6 bits) */
432 t = ((c2hex(tok[2]) & 0x03) << 4) | (c2hex(tok[3]) & 0x0f);
433 fprintf(stderr, WC "|" FC " Flags: " VC "%2x ", t);
434 }
435 else if (col == 1) {
436 fprintf(stderr, WC "|" FC " Window: " VC "%s " WC "|", tok);
437 print_lim();
438 }
439 else if (col == 2) {
440 fprintf(stderr, WC "|" FC " Checksum: " VC "%s ", tok);
441 }
442 else if (col == 3) {
443 fprintf(stderr, WC "|" FC " Urgent Pointer: " VC "%s " WC "|", tok);
444 print_lim();
445 }
446 else if (tcp.options && tcp.left) {
447 print_tcp_options();
448 }
449 }
450 else if (tcp.options && tcp.left) {
451 print_tcp_options();
452 }
453 else {
454 print_data(0);
455 }
456
457 }