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 }