/* TAP - Tcpdump Analysing Parser a tcpdump parser for educational purposes Copyright (C) 2008 ithilgore - ithilgore.ryu.L@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Usage: tcpdump -X | ./tap */ #include #include #include #include #define VERSION "0.6" /* COLOUR CODES */ #define RED "" #define BLUE "" #define GREEN "" #define WC GREEN /* Window Colour "| + -" */ #define FC RED /* Field Colour */ #define VC BLUE /* Value Colour */ #define c2hex(c) (isdigit(c)) ? ((c) - 0x30) : (((c) & 0x0f) + 0x09) #define print_lim() fprintf(stderr, WC "\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n") #define print_lim2() fprintf(stderr, WC "\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+") /* FUNCTION DECLARATIONS */ inline int parse4(int, int); void print_ip_steady(int, int); void print_ip_options(void); void print_tcp_steady(int, int); void print_tcp_options(void); void print_data(int term); /* VARIABLES TYPEDEFS */ typedef struct info_ip { int protocol; /* number for upper level protocol - tcp,udp,icmp etc */ int total_len; /* length of the whole datagram */ int hlen; /* header length - we see if there are ip options by this */ int options; /* bool var - true for existence of ip options */ int left; /* how many 4hexes(2 bytes) have been left after steady header (5 32bit words) (for ip options) */ } info_ip; typedef struct info_tcp { int hlen; int options; int left; } info_tcp; /* GLOBAL VARIABLES */ info_tcp tcp; info_ip ip; char tok[5]; /* buffer-token for holding the 4hexes + '\0' */ char buf[10]; /* helper buffer for operations */ int main(void) { int col = 0; /* column in tcpdump - or 4hex */ int line = 0; /* row in tcpdump output */ while(1) { if (parse4(line, col) < 0) { /* current packet ended */ print_data(1); fprintf(stderr, "\n\n"); col = 0; line = 0; memset(&ip, 0, sizeof(ip)); parse4(line, col); /* parse new packet */ } if ((line == 0) || (line == 1 && col < 2)) print_ip_steady(line, col); else if (ip.options && ip.left) print_ip_options(); else { /* here we do the switching between the rest of the protocols * based on the number we get from the ip header - that is * tcp,udp,icmp etc */ print_tcp_steady(line, col); } col++; if (col == 8) { /* new line */ col = 0; line++; } } return (EXIT_FAILURE); } int parse4(int line, int col) { char c; char temp[9]; char buf[9]; static int normal = 0; /* var defining whether we are in the middle * of parsing the 4hexes in a line */ if (!normal) { while (1) { c = fgetc(stdin); if (c == '\n') { memset(temp, 0, sizeof(temp)); fgets(temp, sizeof(temp), stdin); snprintf(buf, sizeof(buf), "\t0x00%d0:", line); if (!strncmp(temp, buf, sizeof(temp))) { normal++; fgetc(stdin); /* clear out next whitespace */ fgetc(stdin); /* clear out next whitespace */ break; } } } } fgets(tok, sizeof(tok), stdin); /* read 4 chars and null terminate tok[4] */ fgetc(stdin); /* clear out next whitespace */ if (col == 7) normal = 0; /* The code lacks one end case where the end of datagram is at col 7. * We have to find another way to define if the datagram has ended * since checking if there is a space afterwards just wont to the * job here. It has to know that its the end with another way */ if (strchr(tok, ' ') != NULL) { /* we reached the end of datagram */ normal = 0; return -1; } else return 0; } void print_ip_options(void) { static int i = 0; if (!i) fprintf(stderr, WC "|" FC "\t\t Options: " VC "%s", tok); else { fprintf(stderr, VC " %s " WC "|", tok); print_lim(); } ip.left--; if (!i) i++; else i = 0; } void print_ip_steady(int line, int col) { if (!line) { if (!col) { print_lim(); fprintf(stderr, WC "|" FC "Ver: " VC "%c ", tok[0]); fprintf(stderr, WC "|" FC "IHL: " VC "%c ", tok[1]); ip.hlen = c2hex(tok[1]); /* if header length is > 5 (in 32bit words) then we must take into account to view the additional options in the ip header */ if (ip.hlen > 5) { ip.options++; ip.left = (ip.hlen - 5) << 1; } else ip.options = 0; fprintf(stderr, WC "|" FC " ToS: " VC "%s ", &tok[2]); } else if (col == 1) { /* copy the next portion of XXXX inside buf take into account that there is a space between each of the 4-char hex pairs that get formed */ ip.total_len = ((((c2hex(tok[0]) & 0x0f) << 12) | ((c2hex(tok[1]) & 0x0f) << 8)) | ((c2hex(tok[2]) & 0x0f) << 4)) | (c2hex(tok[3]) & 0x0f); fprintf(stderr, WC "|" FC " Total Length: " VC "%s " WC "|", tok); print_lim(); } else if (col == 2) { fprintf(stderr, WC "|" FC " Identification: " VC "%s ", tok); } else if (col == 3) { /* this (not really)obfu thing prints the 3bit flags of the ip header */ char c = c2hex(tok[0]); fprintf(stderr, WC "|" FC "F: " VC "%x ", (c & 0x0e) >> 1); /* print the 13bit fragment offset */ unsigned int t; t = ((((c & 0x01) << 12) | ((c2hex(tok[1]) & 0x0f) << 8)) | ((c2hex(tok[2]) & 0x0f) << 4)) | (c2hex(tok[3]) & 0x0f); fprintf(stderr, WC "|" FC " Fragment Offset:" VC "%4x " WC "|", t); print_lim(); } else if (col == 4) { memset(buf, 0, sizeof(buf)); strncpy(buf, tok, 2); fprintf(stderr, WC "|" FC " TTL: " VC "%s ", buf); fprintf(stderr, WC "|" FC " Protocol: " VC "%s " WC "|", &tok[2]); } else if (col == 5) { fprintf(stderr, FC " Header Checksum: " VC "%s " WC "|", tok); print_lim(); } else if (col == 6) { strncpy(buf, tok, 4); } else if (col == 7) { buf[4] = ' '; strncat(buf, tok, 5); fprintf(stderr, WC "|" FC "\t\t Source Address: " VC "%s \t\t " WC "|", buf); print_lim(); } } else if (line == 1) { /* line = 1 */ if (!col) { memset(buf, 0, sizeof(buf)); strncpy(buf, tok, 4); } else if (col == 1) { buf[4] = ' '; strncat(buf, tok, 5); fprintf(stderr, WC "|" FC "\t\t Destination Address: " VC "%s \t\t" WC "|", buf); print_lim(); } } } int calc_matrix(int col) { /* We calculate some offsets that * the protocol above ip will need. * The pattern goes sth like this: * Suppose this is a typical output from tcpdump * consisting of 2 lines of 8 4hexes each * * XXXX XXXX W XXXX XXXX C XXXX XXXX XXXX XXXX * XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX * * We want to continue from the W point regardless * of how many the ip options were. * So for e.g if ip.hlen was 2 then it would have * stopped at C so according to the lookup table * we would need to subtract 2 to be back to where * we were. The pattern is repeating itself with * the same values above 7, (thus mod 8 is used). */ static const int ltable[8] = { 0, /* 0 */ -1, /* 1 */ -2, /* 2 */ -3, /* 3 */ -4, /* 4 */ -5, /* 5 */ 2, /* 6 */ 1, /* 7 */ }; return (ltable[col % 8]); } void print_data(int term) { static int j = 0; if (term && j) { /* closing the Data field */ switch (j) { case 1: fprintf(stderr, WC "\t\t\t\t\t\t|"); break; case 2: fprintf(stderr, WC "\t\t\t\t\t|"); break; case 3: fprintf(stderr, WC "\t\t\t\t|"); break; case 4: fprintf(stderr, WC "\t\t\t\t|"); break; case 5: fprintf(stderr, WC "\t\t\t|"); break; case 6: fprintf(stderr, WC "\t\t|"); break; case 7: fprintf(stderr, WC "\t\t|"); break; } print_lim(); j = 0; } else if (!term) { if (!j && strchr(tok, ' ') == NULL) { fprintf(stderr, WC "|" FC "\t Data: " VC "%s ", tok); j++; } else if (j < 7) { fprintf(stderr, VC "%s ", tok); j++; } else { fprintf(stderr, VC "%s " WC "|", tok); print_lim(); j = 0; } } } void print_tcp_options(void) { static int i = 0; if (!i) { fprintf(stderr, WC "|" FC "\t\t TCP Options: " VC "%s", tok); i++; } else { fprintf(stderr, VC " %s " WC "|", tok); print_lim(); i = 0; } tcp.left--; } void print_tcp_steady(int __line_, int __col_) { /* To account for the possible loss of the lines eaten * by the ip options we subtract the offset values * we kept before, from the ones used here, so that * the tcp header can be printed like the ip header * was steady all the way here - we use calc_matrix() * for that. */ int line = 0; int iphlen_4hex = ((ip.hlen - 0x5) << 1); if (iphlen_4hex > 5) { int i = 6; while(1) { if (iphlen_4hex >= i) line++; else break; i += 7; } } line = __line_ - line; int col = __col_ + calc_matrix(iphlen_4hex); if (line == 1) { if (col == 2) { fprintf(stderr, WC "|" FC " Source Port: " VC "%s ", tok); } else if (col == 3) { fprintf(stderr, WC "|" FC " Destination Port: " VC "%s " WC "|", tok); print_lim(); } else if (col == 4) { memset(buf, 0, sizeof(buf)); strncpy(buf, tok, 4); } else if (col == 5) { buf[4] = ' '; strncat(buf, tok, 5); fprintf(stderr, WC "|" FC "\t\t Sequence Number: " VC "%s \t\t\t" WC "|", buf); print_lim(); } else if (col == 6) { memset(buf, 0, sizeof(buf)); strncpy(buf, tok, 4); } else if (col == 7) { buf[4] = ' '; strncat(buf, tok, 5); fprintf(stderr, WC "|" FC "\t\t Acknowledgment Number: " VC "%s \t\t" WC "|", buf); print_lim(); } } else if (line == 2) { if (col == 0) { tcp.hlen = c2hex(tok[0]); fprintf(stderr, WC "|" FC "DO: " VC "%x ", tcp.hlen); if (tcp.hlen > 5) { tcp.options++; tcp.left = (tcp.hlen - 5) << 1; } else tcp.options = 0; /* Reserved (6 bits) */ unsigned int t = c2hex(tok[1]) << 2; t = t | ((c2hex(tok[2]) & 0xc0) >> 6); fprintf(stderr, WC "|" FC "Reserved: " VC "%x", t); /* Flags (6 bits) */ t = ((c2hex(tok[2]) & 0x03) << 4) | (c2hex(tok[3]) & 0x0f); fprintf(stderr, WC "|" FC " Flags: " VC "%2x ", t); } else if (col == 1) { fprintf(stderr, WC "|" FC " Window: " VC "%s " WC "|", tok); print_lim(); } else if (col == 2) { fprintf(stderr, WC "|" FC " Checksum: " VC "%s ", tok); } else if (col == 3) { fprintf(stderr, WC "|" FC " Urgent Pointer: " VC "%s " WC "|", tok); print_lim(); } else if (tcp.options && tcp.left) { print_tcp_options(); } } else if (tcp.options && tcp.left) { print_tcp_options(); } else { print_data(0); } }