/*
   Ipretplog - Protolog RAW packet format interpreter
   --------------------------------------------------------------------
   Protolog - The Internet Protocols logger

   Copyright (C) 1998, 1999 Diego Javier Grigna <diego@grigna.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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "common.h"

/* Global variables */

static int flag_verbose;   /* Verbose mode, explain all. */
static int flag_printiph;  /* Interpret ICMP data as an IP header + 64
                              bits of data.
                              Used when ICMP Destination unreachable,
                              ICMP time exceeded, etc, packets are found.
                            */
 
/* Function prototypes */

void mainloop( FILE *fp);
void print_header_ip( struct plog_iphdr iph);
void print_header_ipoptions( char *buf, int size);
void print_ipoptions_route( char *buf, unsigned char opt_len, unsigned char routing_ptr);
void print_header_tcp( struct plog_tcphdr tcph);
void print_header_tcp_options( char *buf, int tcpol);
void print_header_udp( struct plog_udphdr udph);
void print_header_icmp( struct plog_icmphdr ich);
void print_data( char *buf, char *pkttype, int size);
void print_ala_hexdump( char *buf, int size);
void print_hex( char *buf, int size);
void print_ascii( char *buf, int size);
void print_ipret_icmp_printiph_again( char *buf);


int main( int argc, char *argv[])
{
 FILE *fp;
 struct stat stbuf;
 int ch;

 flag_quiet        = 0; /* ipretplog never is quiet */
 flag_dont_resolve = 1;
 flag_verbose      = 0;
 flag_printiph     = 0;

 if( argc < 2) {
     mainloop( stdin);
     return 0;
 }

 while(( ch = getopt( argc, argv, "rv")) != EOF){
         switch( ch) {
                case 'r': flag_dont_resolve = 0;
                          break;
                case 'v': flag_verbose = 1;
                          break;
         }
 }

 if( optind >= argc)
     fp = stdin;
 else {
     fp = fopen( argv[ optind], "r");

     if( fp == NULL) {
         fprintf( stderr, "%s: %s : %s\n", argv[ 0], argv[ optind], strerror( errno));
         return -1;
     }

     fstat( fileno( fp), &stbuf);

     if( ( stbuf.st_mode & S_IFMT) == S_IFDIR){
           fprintf( stderr, "\"%s\" is a directory\n", argv[ optind]);
           return -1;
     }

     if( ( stbuf.st_mode & S_IFMT) != S_IFREG){
           fprintf( stderr, "\"%s\" is not a regular file\n", argv[ optind]);
           return -1;
     }

 }

 mainloop( fp);

 return 0;
}

void mainloop( FILE *fp)
{
 struct plog_iphdr   iph; /* IP header                       */
 struct plog_tcphdr tcph; /* TCP header                      */
 struct plog_udphdr udph; /* UDP header                      */
 struct plog_icmphdr ich; /* ICMP header                     */
 char buf[ 65535];   /* Buffer to read packets          */
 time_t t;           /* Time when the packet was logged */
 int iphlen;         /* IP header length                */
 int hsize;          /* Header size (TCP, UDP or ICMP   */
 int totlen;         /* Total length size               */
 int datas;          /* Data size                       */
 int packet;         /* Number of packets               */
 int tcphl;          /* TCP header length               */
 int tcpol;          /* TCP options length              */
 char guion[ 128];
 char *pkttype;

 iphlen = hsize = totlen = datas = packet = 0;

 sprintf( guion, replicate( '-', 80));

 while( !feof( fp)) {

        flag_printiph = 0;

        if( fread( &t, sizeof( t), 1, fp) <= 0) 
            break;

        packet++;

        printf( "%-22.22s Begin of packet number: %10d %-22.22s\n"
                  , guion
                  , packet
                  , guion);

        printf( "%-20.20s arrival date: %-24.24s %-20.20s\n"
                  , guion
                  , ctime( &t)
                  , guion);

        if( fread( &iph, sizeof( iph), 1, fp) <= 0)
            break;

        print_header_ip( iph);
       
        iphlen  = iph.hlv & 0x0F;
        iphlen  = iphlen << 2;
        iphlen -=20;

        if( iphlen > 0) {
            if( fread( buf, iphlen, 1, fp) <= 0)
                break;
            print_header_ipoptions( buf, iphlen);
        }

        if( iph.protocol == IPPROTO_TCP) {
            if( fread( &tcph, sizeof( tcph), 1, fp) <= 0)
                break;
            hsize = sizeof( tcph);
            pkttype = "TCP";
            print_header_tcp( tcph);
            tcphl = ( tcph.th_do & 0xF0) >> 4;
            tcphl *= 4;
            if( tcphl > sizeof( tcph)) {
                tcpol = tcphl - sizeof( tcph);
                hsize += tcpol;
                if( fread( &buf, tcpol, 1, fp) <= 0)
                    break;
                print_header_tcp_options( buf, tcpol);
            }
        } else 
            if( iph.protocol == IPPROTO_UDP) {
                if( fread( &udph, sizeof( udph), 1, fp) <= 0)
                    break;
                hsize = sizeof( udph);
                pkttype = "UDP";
                print_header_udp( udph);
            } else
                if( iph.protocol == IPPROTO_ICMP) {
                    if( fread( &ich, sizeof( ich), 1, fp) <= 0)
                        break;
                    hsize = sizeof( ich);
                    pkttype = "ICMP";
                    print_header_icmp( ich);
                } else {
                    printf( "Protocol not supported by Protolog.\n");
                    continue;
                }

        totlen = sizeof( iph) + hsize + iphlen;

        if( totlen < htons( iph.tot_len )) {
            datas = htons( iph.tot_len) - totlen;
            if( fread( buf, datas, 1, fp) <= 0)
                break;
            print_data( buf, pkttype, datas);
        }

        printf( "%-22.22s End of packet number:   %10d %-22.22s\n\n"
                  , guion
                  , packet
                  , guion);
 }

}

void print_header_ip( struct plog_iphdr iph)
{
 struct protoent *proto;
 struct in_addr addrs;
 int ip_version;
 int ip_hl;
 int tos_precedence;
 int flags;
 int tos; /* Type of service bits */

 flags          = 0;

 ip_hl      = ( iph.hlv & 0x0F) << 2;

 ip_version = ( iph.hlv & 0xF0) >> 4;

 printf( "******* IP Header *******\n");
 printf( "IP Version                  : %-5d (IPv%d)\n", ip_version, ip_version);
 printf( "Header Length               : %-5d (%d)\n"   , ip_hl     , ip_hl     );
 printf( "Type Of Service             : %-5d (0x%x)\n" , iph.tos   , iph.tos   );

 /* Show even more details */
 if( flag_verbose) {
     /* 
      * Explain Type of Service
      * RFC 791 & RFC 1349
      */
     printf( "%-30.30sBits 0-2: Precedence\n", " ");
     printf( "%-40.40s", " ");
     tos_precedence = ( iph.tos & 0xe0) >> 5;
     switch( tos_precedence) {
             case 0: printf( "Routine"             ); break; /* 000 */
             case 1: printf( "Priority"            ); break; /* 001 */
             case 2: printf( "Immediate"           ); break; /* 010 */
             case 3: printf( "Flash"               ); break; /* 011 */
             case 4: printf( "Flash Override"      ); break; /* 100 */
             case 5: printf( "CRITIC/ECP"          ); break; /* 101 */
             case 6: printf( "Internetwork Control"); break; /* 110 */
             case 7: printf( "Network Control"     ); break; /* 111*/
     }
     printf( " (0x%x)\n", tos_precedence);
     tos = ( iph.tos & 0x1E) >> 1;

     printf( "%-30.30sBits 3-6: ", " ");

     switch( tos) {
             case 15: /* 1111 */ printf( "Maximize security"     ); break;
             case  8: /* 1000 */ printf( "Minimize delay"        ); break;
             case  4: /* 0100 */ printf( "Maximize throughput"   ); break;
             case  2: /* 0010 */ printf( "Maximize reliability"  ); break;
             case  1: /* 0001 */ printf( "Minimize monetary cost"); break;
             case  0: /* 0000 */ printf( "Normal service"        ); break;
     }

     printf( " (%d)\n", tos);
     printf( "%-30.30sBit    7: MBZ (Must Be Zero) (0x%x)\n", " ", iph.tos & 0x01);
 }

 printf( "Total lenght of packet      : %-5d (0x%x) bytes\n", htons( iph.tot_len), htons( iph.tot_len));
 printf( "Identification              : %-5d (0x%x)\n"      , htons( iph.id)     , htons( iph.id)     );

 flags = ( iph.frag_off & 0xe000) >> 13;
 printf( "3 bit flags                 : %-5d (0x%x)\n"      , flags              , flags              );

 if( flag_verbose) {
     printf( "%-30.30sBit 0: Reserved (%d)\n", " ", flags & 4);
     printf( "%-30.30sBit 1: %s (%d)\n"      , " ", flags & 5 ? "Don't Fragment" : "May Fragment ", flags & 5);
     printf( "%-30.30sBit 2: %s (%d)\n"      , " ", flags & 6 ? "More Fragments" : "Last Fragment", flags & 6);
 }

 flags = iph.frag_off & 0x1fff;
 printf( "13 bit Fragmentation offset : %-5d (0x%x)\n"      , flags              , flags              );

 printf( "Time To Live                : %-5d (0x%x)\n"      , iph.ttl            , iph.ttl            );

 proto = NULL;
 proto = getprotobynumber( iph.protocol);

 if( proto == NULL)
     printf( "Protocol                    : Protocol unknown! %-5d (0x%x)\n", iph.protocol, iph.protocol);
 else
     printf( "Protocol                    : %s (0x%x)\n", proto->p_name, iph.protocol);

 printf( "CheckSum                    : %-5d (0x%x)\n"      , htons( iph.check), htons( iph.check));

 addrs.s_addr = iph.saddr;
 printf( "IP Source address (From)    : %s (%s)\n", inet_ntoa( addrs), resolve_host_name( iph.saddr));

 addrs.s_addr = iph.daddr;
 printf( "IP Destination address      : %s (%s)\n", inet_ntoa( addrs), resolve_host_name( iph.daddr));
}

void print_header_ipoptions( char *buf, int size)
{
 printf( "IP Options                  :\n");

 /*
  * See RFC 791 ( Internet protocol specification)
  * Also see RFC 1700 ( Assigned numbers) and search for "IP OPTIONS"
  */
 if( flag_verbose) {
     unsigned char opt_type;
     unsigned char opt_len;
     unsigned char routing_ptr;
     int opt_copied;
     int opt_class;
     int opt_number;
     unsigned short stream_id;
     int timestamp_overflow;
     int timestamp_flag;
     unsigned short router_alert;
     struct in_addr ts_ipaddr;
     unsigned long ts_date;
     unsigned short tmp;
     int i, k;

     opt_type   = ( unsigned char) buf[ 0];
     opt_len    = ( unsigned char) buf[ 1];
     opt_copied = opt_type  >> 7;          /* Bit    0 */
     opt_class  = ( opt_type & 0x60) >> 5; /* Bits 1-2 */
     opt_number = opt_type & 0x1F;         /* Bits 3-7 */

     routing_ptr = ( unsigned char) buf[ 2];

     printf( "%-30.30sOption-type octet      : %-5d (0x%x)\n", " ", opt_type, opt_type);
     printf( "%-30.30sBit    0: Copied flag  : %s\n", " ", opt_copied ? "Not copied (1)" : "Copied (0)");
     printf( "%-30.30sBits 1-2: Option class : ", " ");

     switch( opt_class) {
             case 0: printf( "Control"                  ); break; 
             case 1: /* The same that 3 */
             case 3: printf( "Reserved for future use"  ); break;
             case 2: printf( "Debugging and measurement"); break;
     }
     printf( "\n%-55.55s(0x%x)\n", " ", opt_class);

     printf( "%-30.30sBits 3-7: Option number: %-5d (0x%x)\n", " ", opt_number, opt_number);
     printf( "%-30.30sClass | Number | length | Description\n", " ");
     printf( "%-30.30s------+--------+--------+-------------\n", " ");

     switch( opt_type) {
             /* See RFC 791 */
             /* End of Option */
             case   0: /* opt_copied == 0 && opt_class == 0 && opt_number == 0 */
                       printf( "%-30.30s %4d | %6d |   -    | End of Option list.\n", " ", opt_class, opt_number); 
                       break;
             /* No operation IP option */
             case   1: /* opt_copied == 0 && opt_class == 0 && opt_number == 1 */
                       printf( "%-30.30s %4d | %6d |   -    | No Operation.\n"      , " ", opt_class, opt_number); 
                       break;
             /* 
                Security option 
                DoD Basic Security
                See RFC 791 (IP) and RFC 1108 (DoD Security IP option...)
                I think that it is historic, obsolete, delete, deprecated,
                or something like this, so I won't make much detail here.
              */
             case 130: /* opt_copied == 1 && opt_class == 0 && opt_number == 2 */
                       /* 10000010 = 130 */
                       printf( "%-30.30s %4d | %6d | %6d | Security.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* DoD Extended Security, RFC 1108 */
             case 133: /* opt_copied == 1 && opt_class == 0 && opt_number == 5 */
                       printf( "%-30.30s %4d | %6d | %6d | Extended Security.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* 
              * CIPSO, Commercial IP Security Option.
              * I don't know where it came from...
              * I saw it in RFC 1700, but this RFC doesn't have information
              * about it.
              */
             case 134: /* opt_copied == 1 && opt_class == 0 && opt_number == 6 */
                       printf( "%-30.30s %4d | %6d | %6d | Commercial Security.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* Loose Source Routing option */
             case 131: /* opt_copied == 1 && opt_class == 0 && opt_number == 3 */
                       printf( "%-30.30s %4d | %6d | %6d | Loose Source Routing.\n" , " ", opt_class, opt_number, opt_len);
                       print_ipoptions_route( buf + 3, opt_len, routing_ptr);
                       break;
             /* Strict Source Routing option */
             case 137: /* opt_copied == 1 && opt_class == 0 && opt_number == 9 */
                       printf( "%-30.30s %4d | %6d | %6d | Strict Source Routing.\n", " ", opt_class, opt_number, opt_len); 
                       print_ipoptions_route( buf + 3, opt_len, routing_ptr);
                       break;
             /* 
                EIP
                The Extended Internet Protocol
                See RFC 1385.
                I think it isn't implemented.
                AFAIK it isn't an IP option (see the rfc), but it appears
                as an IP option for host & routers that don't understand it.
             */
             case 138: /* opt_copied == 1 && opt_class == 0 && opt_number == 10 */
                       printf( "%-30.30s %4d | %6d | %6d | EIP ID.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* Experimental Measurement, RFC 1700 */
             case  10: /* opt_copied == 0 && opt_class == 0 && opt_number == 10 */
                       printf( "%-30.30s %4d | %6d | %6d | Experimental Measurement.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* MTU Probe RFC1191 & RFC1700 */
             case  11: /* opt_copied == 0 && opt_class == 0 && opt_number == 11 */
                       printf( "%-30.30s %4d | %6d | %6d | MTU Probe.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* MTU Reply RFC1191 & RFC1700 */
             case  12: /* opt_copied == 0 && opt_class == 0 && opt_number == 12 */
                       printf( "%-30.30s %4d | %6d | %6d | MTU Reply.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* Experimental Flow Control RFC1700 */
             case 205: /* opt_copied == 1 && opt_class == 2 && opt_number == 13 */
                       printf( "%-30.30s %4d | %6d | %6d | Experimental Flow Control.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* Experimental Access Control RFC1700 */
             case 142: /* opt_copied == 1 && opt_class == 0 && opt_number == 14 */
                       printf( "%-30.30s %4d | %6d | %6d | Experimental Access Control.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* ENCODE ??? RFC 1700 */
             case  15: /* opt_copied == 0 && opt_class == 0 && opt_number == 15 */
                       printf( "%-30.30s %4d | %6d | %6d | ENCODE ???.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* IMI Traffic Descriptor, RFC 1700 */
             case 144: /* opt_copied == 1 && opt_class == 0 && opt_number == 16 */
                       printf( "%-30.30s %4d | %6d | %6d | IMI Traffic Descriptor.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* EIP ??? RFC 1700 */
             case 145: /* opt_copied == 1 && opt_class == 0 && opt_number == 17 */
                       printf( "%-30.30s %4d | %6d | %6d | EIP ???.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* Address Extension RFC 1700 */
             case 147: /* opt_copied == 1 && opt_class == 0 && opt_number == 19 */
                       printf( "%-30.30s %4d | %6d | %6d | Address Extension.\n", " ", opt_class, opt_number, opt_len); 
                       break;
             /* Record route option */
             case   7: /* opt_copied == 0 && opt_class == 0 && opt_number == 7 */
                       printf( "%-30.30s %4d | %6d | %6d | Record Route.\n"         , " ", opt_class, opt_number, opt_len); 
                       print_ipoptions_route( buf + 3, opt_len, routing_ptr);
                       break;
             /* 
              * Stream ID option
              * Another obsolete option. See RFC 1122.
              */
             case 136: /* opt_copied == 1 && opt_class == 0 && opt_number == 8 */
                       printf( "%-30.30s %4d | %6d | %6d | Stream ID.\n"            , " ", opt_class, opt_number, opt_len); 
                       memcpy( &stream_id, &buf[ 2], 2);
                       printf( "%-30.30sStream ID: %d (0x%x)\n", " ", stream_id, stream_id);
                       break;
             /* Router Alert option, RFC 2113 */
             case 148: /* opt_copied == 1 && opt_class == 0 && opt_number == 20 */
                       printf( "%-30.30s %4d | %6d | %6d | Router Alert.\n", " ", opt_class, opt_number, opt_len); 
                       memcpy( &router_alert, &buf[ 2], 2);
                       printf( "%-30.30sValue: %d (0x%x): ", " ", router_alert, router_alert);
                       switch( router_alert) {
                               case  0: printf( "Router shall examine packet\n");
                                        break;
                               default: printf( "Reserved\n");
                                        break;
                       }
                       break;
             /*
              * IP Traceroute option
              * (Is it implemented?)
              * See RFC 1393
              */
             case  82: /* opt_copied == 0 && opt_class == 2 && opt_number == 18 */
                       printf( "%-30.30s %4d | %6d | %6d | IP Traceroute option.\n", " ", opt_class, opt_number, opt_len); 
                       memcpy( &tmp, &buf[ 2], 2);
                       printf( "%-30.30sID number: %-5d (0x%x)\n", " ", tmp, tmp); 
                       memcpy( &tmp, &buf[ 4], 2);
                       printf( "%-30.30sOHC      : %-5d (0x%x)\n", " ", tmp, tmp); 
                       memcpy( &tmp, &buf[ 6], 2);
                       printf( "%-30.30sRHC      : %-5d (0x%x)\n", " ", tmp, tmp); 
                       memcpy( &ts_ipaddr.s_addr, &buf[ 8], 4);
                       printf( "%-30.30sOrig. IP : %s\n", " ", inet_ntoa( ts_ipaddr));
                       break;
             /* Internet Timestamp option, RFC 781 and 791 */
             case  68: /* opt_copied == 1 && opt_class == 2 && opt_number == 4  */
                       printf( "%-30.30s %4d | %6d | %6d | Internet Timestamp.\n"   , " ", opt_class, opt_number, opt_len); 
                       printf( "%-30.30sPointer : %-5d (0x%x)\n", " ", routing_ptr, routing_ptr);
                       timestamp_overflow = timestamp_flag = 0;
                       timestamp_overflow = ( buf[ 3] & 0xF0) >> 4;
                       timestamp_flag     = buf[ 3] & 0x0F;
                       printf( "%-30.30sOverflow: %-5d (0x%x)\n", " ", timestamp_overflow, timestamp_overflow);
                       printf( "%-30.30sFlags   : %-5d: "       , " ", timestamp_flag);
                       switch( timestamp_flag) {
                               case 0: printf( "Time stamps only\n");
                                       break;
                               case 1: printf( "Internet Address + Timestamp\n");
                                       break;
                               case 3: printf( "Internet address fields prespecified\n");
                                       break;
                       }

                       k = opt_len;
                       k -= 3;
                       i = 4;
                       while( k > 0) {
                              if( timestamp_flag == 0) {
                                  memcpy( &ts_date, &buf[ i], 4);
                                  printf( "%-30.30s(0x%lx)\n", " ", ts_date);
                                  k -= 4;
                                  i += 4;
                              } else {
                                  memcpy( &ts_ipaddr.s_addr, &buf[ i], 4);
                                  i += 4;
                                  printf( "%-30.30sIP address: %s\n", " ", inet_ntoa( ts_ipaddr));
                                  memcpy( &ts_date, &buf[ i], 4);
                                  i += 4;
                                  printf( "%-30.30s(0x%lx)\n", " ", ts_date);
                                  k -= 8;
                              }
                       }
                       break;
     } /* end if( opt_class == 0) */
     printf( "IP options raw hex data     :\n");
 } /* end if( verbose) */

 print_ala_hexdump( buf, size);
}

void print_ipoptions_route( char *buf, unsigned char opt_len, unsigned char routing_ptr)
{
 struct in_addr route_ip;
 int i, j, k;

 printf( "%-30.30sPointer: %d (0x%x)\n", " ", routing_ptr, routing_ptr);

 /* if opt_len < 4 then it is a wrong packet,
    it is already droped by the kernel, so don't check it. */

 k = opt_len;
 k -= 3;

 i = 0;
 j = 0;

 while( k > 0) {
        memcpy( &route_ip.s_addr, &buf[ j], 4);
        k -= 4;
        j += 4;
        i++;
        printf( "%-30.30sAddress number %-5d: %s\n", " ", i, inet_ntoa( route_ip));
 }
}

void print_header_tcp( struct plog_tcphdr tcph)
{
 int tcphl;
 char buf[ 16];
 int res;

 printf( "******* TCP Header *******\n");

 printf( "Source port address (From)  : %d/%s\n", htons( tcph.th_sport), get_serv_name( tcph.th_sport, "tcp"));
 printf( "Destination port address    : %d/%s\n", htons( tcph.th_dport), get_serv_name( tcph.th_dport, "tcp"));
 
 printf( "Sequence Number             : %lu (0x%lx)\n", htonl( tcph.th_seq), htonl( tcph.th_seq));
 printf( "Acknowledgement Number      : %lu (0x%lx)\n", htonl( tcph.th_ack), htonl( tcph.th_ack));

 tcphl = ( tcph.th_do & 0xF0) >> 4;

 printf( "TCP Header Length           : %-5d (0x%x) == %d bytes\n", tcphl, tcphl, tcphl * 4);

 buf[ 0] = tcph.th_do;
 buf[ 1] = tcph.th_flags;
 buf[ 3] = 0;

 res = atoi( buf);
 res = (res & 0x0FC0) >> 6;

 printf( "Reserved                    : %-5d (0x%x)\n", res, res);
 printf( "URG flag                    : %s\n", tcph.th_flags & PLOG_TH_URG ? "ON" : "OFF");
 printf( "ACK flag                    : %s\n", tcph.th_flags & PLOG_TH_ACK ? "ON" : "OFF");
 printf( "PUSH flag                   : %s\n", tcph.th_flags & PLOG_TH_PSH ? "ON" : "OFF");
 printf( "RST flag                    : %s\n", tcph.th_flags & PLOG_TH_RST ? "ON" : "OFF");
 printf( "SYN flag                    : %s\n", tcph.th_flags & PLOG_TH_SYN ? "ON" : "OFF");
 printf( "FIN flag                    : %s\n", tcph.th_flags & PLOG_TH_FIN ? "ON" : "OFF");
 printf( "Window size                 : %-5d (0x%x)\n", htons( tcph.th_win), htons( tcph.th_win));
 printf( "TCP checksum                : %-5d (0x%x)\n", htons( tcph.th_sum), htons( tcph.th_sum));
 printf( "Urgent pointer              : %-5d (0x%x)\n", htons( tcph.th_urp), htons( tcph.th_urp));

}

void print_header_tcp_options( char *buf, int tcpol)
{
 PLOG_U8  opt_kind; /* Option kind             */
 PLOG_U8  opt_len;  /* Option length           */
 PLOG_U16 opt_mss;  /* Maximum Segment Size    */
 PLOG_U16 opt_ro;   /* Relative Origin         */ /* RFC 1072 (Obsolete)*/
 PLOG_U16 opt_bs;   /* Block Size              */
 PLOG_U32 opt_echo; /* Echo bytes              */
 PLOG_U32 opt_ts;   /* Timestamp value         */
 PLOG_U8  pocsp;    /* TCP POC-service-profile */
 int i;

 printf( "TCP Options                 : ");

 opt_kind = ( PLOG_U8) buf[ 0];

 printf( "Kind   : %-5d (0x%x)\n", opt_kind, opt_kind);
 printf( "%-30.30sMeaning: ", " ");

 /* See RFC 1700 [Page 72] */
 switch( opt_kind) {
         /* See RFC 793 */
         case  0: printf( "End of option list.\n"  );
                  break;
         case  1: printf( "No-Operation.\n"        );
                  break;
         case  2: printf( "Maximum Segment Size.\n");
                  break;
         /* RFC 1323 */
         case  3: printf( "WSOPT - Window Scale.\n");
                  break;
         /* RFC 1072 */
         case  4: printf( "SACK Permitted.\n");
                  break;
         case  5: printf( "SACK.\n");
                  break;
         case  6: printf( "Echo.\n");
                  break;
         case  7: printf( "Echo Reply.\n");
                  break;
         /* RFC 1323 */
         case  8: printf( "TSOPT - Time Stamp Option.\n");
                  break;
         /* RFC 1693 */
         case  9: printf( "Partial Order Connection Permitted.\n");
                  break;
         case 10: printf( "Partial Order Service Profile.\n");
                  break;
         /* RFC 1700 */
         case 11: printf( "CC.\n");
                  break;
         case 12: printf( "CC.NEW.\n");
                  break;
         case 13: printf( "CC.ECHO.\n");
                  break;
         /* RFC 1146 */
         case 14: printf( "TCP Alternate Checksum Request.\n");
                  break;
         case 15: printf( "TCP Alternate Checksum Data.\n");
                  break;
         /* RFC 1700 */
         case 16: printf( "Skeeter.\n");
                  break;
         case 17: printf( "Bubba.\n");
                  break;
         case 18: printf( "Trailer Checksum Option.\n");
                  break;
 }

 printf( "%-30.30sLength : ", " ");

 opt_len  = ( PLOG_U8) buf[ 1];

 switch( opt_kind) {
         case  0: 
         case  1: printf( "N/A\n");
                  opt_len = 0;
                  break;
         case  2: 
         case  3: 
         case  4: 
         case  5: 
         case  6:
         case  7:
         case  8:
         case  9:
         case 10:
         case 11:
         case 12:
         case 13:
         case 14:
         case 15:
         case 16:
         case 17:
         case 18:
                  printf( "%-5d (0x%x)\n", opt_len, opt_len);
                  break;
 }

 switch( opt_kind) {
      /* case  0:  nothing 
         case  1: 
         case  4: 
         case  9: 
         case 11: 
         case 12: 
         case 13: 
         case 16: 
         case 17: 
         case 18: 
                  break; */
         case  2: memcpy( &opt_mss, &buf[ 2], 2);
                  opt_mss = htons( opt_mss);
                  printf( "%-30.30sMax. Seg. Size: %-5d (0x%x)\n", " ", opt_mss, opt_mss);
                  break;
         case  3: printf( "%-30.30sShift count   : %d (0x%x)\n", " ", ( PLOG_U8) buf[ 2], ( PLOG_U8) buf[ 2]);
                  break;
         case  5: i = 2;
                  while( i < opt_len) {  
                         memcpy( &opt_ro, &buf[ i], 2);
                         i += 2;
                         memcpy( &opt_bs, &buf[ i], 2);
                         i += 2;
                         printf( "%-30.30sRelat. origin : %-5d (0x%x)\n", " ", opt_ro, opt_ro);
                         printf( "%-30.30sBlock size    : %-5d (0x%x)\n", " ", opt_bs, opt_bs);
                  }
                  break;
         case  6: 
         case  7: 
                  memcpy( &opt_echo, &buf[ 2], 4);
                  printf( "%-30.30sEcho bytes    : %-5ld (0x%lx)\n", " ", opt_echo, opt_echo);
                  break;
         case  8:
                  memcpy( &opt_ts, &buf[ 2], 4);
                  printf( "%-30.30sTimestamp Val.: %-5ld (0x%lx)\n", " ", opt_ts, opt_ts);
                  memcpy( &opt_ts, &buf[ 6], 4);
                  printf( "%-30.30sTS Echo Reply : %-5ld (0x%lx)\n", " ", opt_ts, opt_ts);
                  break;
         case 10:
                  pocsp = ( PLOG_U8) buf[ 2];
                  pocsp = (pocsp & 0x80) >> 7;
                  printf( "%-30.30sStart flag    : %s (0x%x)\n", " ", pocsp ? "ON": "OFF", pocsp);
                  pocsp = ( PLOG_U8) buf[ 2];
                  pocsp = (pocsp & 0x40) >> 6;
                  printf( "%-30.30sEnd flag      : %s (0x%x)\n", " ", pocsp ? "ON": "OFF", pocsp);
                  pocsp = ( PLOG_U8) buf[ 2];
                  pocsp = pocsp & 0x3F;
                  printf( "%-30.30sFiller        : %d (0x%x)\n", " ", pocsp, pocsp); 
                  break;
         case 14:
                  printf( "%-30.30sChecksum      : %d (0x%x)\n", " ", ( PLOG_U8) buf[ 2], ( PLOG_U8) buf[ 2]);
                  printf( "%-30.30sKind of cksum : ", " ");
                  switch( ( PLOG_U8) buf[ 2]) {
                          case 0: printf( "TCP checksum.\n");
                                  break;
                          case 1: printf( "8-bit Fletcher's algorithm.\n");
                                  break;
                          case 2: printf( "16-bit Fletcher's algorithm.\n");
                                  break;
                          case 3: printf( "Redundant Checksum Avoidance.\n");
                                  break;
                  }
                  break;
         case 15: i = 2;
                  while( i < opt_len) {
                         printf( "%-30.30sData byte #%d : %-5d (0x%x)\n", " ", i - 2, ( PLOG_U8) buf[ i], ( PLOG_U8) buf[ i]);
                         i++;
                  }
                  break;
 }

 if( tcpol > opt_len) {
     printf( "TCP Options padding bytes   :\n");
     print_ala_hexdump( buf + opt_len, tcpol - opt_len);
 }

}

void print_header_udp( struct plog_udphdr udph)
{
 printf( "******* UDP Header *******\n");
 printf( "Source port address (From)  : %d/%s\n"      , htons( udph.source), get_serv_name( udph.source, "udp"));
 printf( "Destination port address    : %d/%s\n"      , htons( udph.dest  ), get_serv_name( udph.dest  , "udp"));
 printf( "UDP Length                  : %-5d (0x%x)\n", htons( udph.len   ), htons( udph.len)           );
 printf( "UDP Checksum                : %-5d (0x%x)\n", htons( udph.check ), htons( udph.check)         );
}

void print_header_icmp( struct plog_icmphdr ich)
{
 struct in_addr addrs;
 char type[ 1024];
 char code[ 1024];
 char temp[ 1024];
 int show_id;   /* Show ICMP id field       */
 int show_seq;  /* Show ICMP sequence field */
 int show_gat;  /* Show ICMP gateway field  */
 int show_poi;  /* Show ICMP pointer field  */
 int show_res;  /* Show ICMP reserved field */
 int show_tr;   /* Used by traceroute ICMP  */
 int show_routad; /* Show router advertisement fields    */
 PLOG_U32 unused; /* Used when a field is unused         */
 PLOG_U16 routad; /* Used to print router advert. fields */

 show_tr     = 0;
 show_id     = 0;
 show_seq    = 0;
 show_gat    = 0;
 show_poi    = 0;
 show_res    = 0;
 show_routad = 0;

 flag_printiph = 0;
 
 printf( "******* ICMP Header *******\n");

 sprintf( code, "%-5d", ich.code);

 switch( ich.type) {
         case  1:
         case  2:
         case  7: sprintf( type, "Unassigned ICMP types: %d", ich.type);
                  break;
         case  0: strcpy( type, "Echo Reply");
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case  3: strcpy( type, "Destination Unreachable");
                  switch( ich.code) {
                          case  0: strcpy( code, "Network Unreachable"             ); break;
                          case  1: strcpy( code, "Host Unreachable"                ); break;
                          case  2: strcpy( code, "Protocol Unreachable"            ); break;
                          case  3: strcpy( code, "Port Unreachable"                ); break;
                          case  4: strcpy( code, "Fragmentation needed and DF set" ); break;
                          case  5: strcpy( code, "Source Route Failed"             ); break;
                          case  6: strcpy( code, "Destination Network Unknown"     ); break;
                          case  7: strcpy( code, "Destination Host Unknown"        ); break;
                          case  8: strcpy( code, "Source Host Isolated"            ); break;
                          case  9: strcpy( code, "Communication with Destination Network is Administratively Prohibited"); break;
                          case 10: strcpy( code, "Communication with Destination Host is Administratively Prohibited"   ); break;
                          case 11: strcpy( code, "Destination Network Unreachable for Type of Service"       ); break;
                          case 12: strcpy( code, "Destination Host Unreachable for Type of Service"          ); break;
                          case 13: strcpy( code, "Communication Administratively Prohibited, Packet filtered"); break;
                          case 14: strcpy( code, "Host Precedence Violation"  ); break;
                          case 15: strcpy( code, "Precedence cutoff in effect"); break;
                          default: sprintf( code, "Dest Unreachable, Bad Code: %d", ich.code); break;
                  }
                  flag_printiph = 1;
                  break;
         case  4: strcpy( type, "Source Quench");
                  flag_printiph = 1;
                  break;
         case  5: strcpy( type, "Redirect (change route)");
                  switch( ich.code) {
                          case  0: strcpy( temp, "Redirect Network"                    ); break;
                          case  1: strcpy( temp, "Redirect Host"                       ); break;
                          case  2: strcpy( temp, "Redirect Type of Service and Network"); break;
                          case  3: strcpy( temp, "Redirect Type of Service and Host"   ); break;
                          default: sprintf( temp, "Redirect, Bad Code: %d", ich.code   ); break;
                  }
                  sprintf( code ,"%s (New addr: 0x%08lx)\n", temp, ich.un.gateway);
                  flag_printiph = 1;
                  show_gat = 1;
                  break;
         case  6: strcpy( type, "Alternate Host Address");
                  switch( ich.code) {
                          case  0: strcpy(  code, "Alternate Address for Host"                ); break;
                          default: sprintf( code, "Alt. Host. Addr., Bad Code: %d", ich.code ); break;
                  }
                  break;
         case  8: strcpy( type, "Echo Request");
                  show_id  = 1;
                  show_seq = 1;
                  break;
         /* See RFC 1256 */
         case  9: strcpy( type, "ICMP Router Advertisement Message" );
                  show_routad = 1;
                  break;
         case 10: strcpy( type, "ICMP Router Solicitation Message"  );
                  show_res = 1;
                  break;
         case 11: strcpy( type, "Time Exceeded");
                  switch( ich.code) {
                          case  0: strcpy( code, "Time to live exceeded in transit"      ); break;
                          case  1: strcpy( code, "Fragment reassembly time exceeded"     ); break;
                          default: sprintf( code, "Time exceeded, Bad Code: %d", ich.code); break;
                  }
                  flag_printiph = 1;
                  break;
         case 12: sprintf( type, "Parameter problem");
                  switch( ich.code) {
                          case  0: strcpy( code, "Pointer indicates the error" ); break;
                          case  1: strcpy( code, "Missing a Required Option"   ); break;
                          case  2: strcpy( code, "Bad Length"                  ); break;
                          default: sprintf( code, "Par. problem, Bad Code: %d", ich.code); break;
                  }
                  flag_printiph = 1;
                  show_poi      = 1;
                  break;
         case 13: strcpy( type, "Timestamp Request"   );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case 14: strcpy( type, "Timestamp Reply"     );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case 15: strcpy( type, "Information Request" );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case 16: strcpy( type, "Information Reply"   );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case 17: strcpy( type, "Address Mask Request");
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case 18: strcpy( type, "Address Mask Reply"  );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         /* See RFC 1393 */
         case 30: strcpy( type, "Traceroute"         );
                  switch( ich.code) {
                          case  0: strcpy( code, "Outbound Packet successfully forwarded"        ); break;
                          case  1: strcpy( code, "No route for Outbound Packet; packet discarded"); break;
                          default: sprintf( code, "Traceroute, Bad Code: %d", ich.code           ); break;
                  }
                  show_id  = 1; /* show id but not seq     */
                  show_tr  = 1; /* user sequence as unused */
                  break;
         /* See RFC 1475 */
         case 31: strcpy( type, "TP/IX: Conversion Failed"                      );
                  switch( ich.code) {
                          case  0: strcpy( code, "Unknown/unspecified error"                 ); break;
                          case  1: strcpy( code, "Don't Convert option present"              ); break;
                          case  2: strcpy( code, "Unknown mandatory option present"          ); break;
                          case  3: strcpy( code, "Known unsupported option present"          ); break;
                          case  4: strcpy( code, "Unsupported transport protocol"            ); break;
                          case  5: strcpy( code, "Overall length exceeded"                   ); break;
                          case  6: strcpy( code, "IP header length exceeded"                 ); break;
                          case  7: strcpy( code, "Transport protocol > 255"                  ); break;
                          case  8: strcpy( code, "Port conversion out of range"              ); break;
                          case  9: strcpy( code, "Transport header length exceeded"          ); break;
                          case 10: strcpy( code, "32 Bit Rollover missing and ACK set"       ); break;
                          case 11: strcpy( code, "Unknown mandatory transport option present"); break;
                          default: sprintf( code, "Conversion Failed, Bad Code: %d", ich.code           ); break;
                  }
                  break;
         /* See RFC 1700*/
         case 32: strcpy( type, "Mobile Host Redirect"        );
                  break;
         case 33: strcpy( type, "IPv6 Where-Are-You"          );
                  break;
         case 34: strcpy( type, "IPv6 I-Am-Here"              );
                  break;
         case 35: strcpy( type, "Mobile Registration Request" );
                  break;
         case 36: strcpy( type, "Mobile Registration Reply"   );
                  break;
         /* See RFC 1788 */
         case 37: strcpy( type, "Domain Name Request" );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         case 38: strcpy( type, "Domain Name Reply"   );
                  show_id  = 1;
                  show_seq = 1;
                  break;
         default: sprintf( type, "Reserved ICMP type: %d", ich.type);
                  break;
    } /* end switch( ich.type) */

 printf( "Type                        : %s (%d)\n"    , type, ich.type);
 printf( "Code                        : %s (%d)\n"    , code, ich.code);
 printf( "CheckSum                    : %-5d (0x%x)\n", htons( ich.checksum), htons( ich.checksum));

 if( show_gat) {
     addrs.s_addr = ich.un.gateway;
     printf( "Gateway                     : %s (%s)\n", inet_ntoa( addrs), resolve_host_name( ich.un.gateway));
 }

 if( show_poi)
     printf( "Pointer (error octect)      : %-5d (0x%x)\n", ich.un.pointer, ich.un.pointer);

 if( show_id)
     printf( "Identification              : %-5d (0x%x)\n", htons( ich.un.echo.id), htons( ich.un.echo.id));

 if( show_seq)
     printf( "Sequence                    : %-5d (0x%x)\n", htons( ich.un.echo.sequence), htons( ich.un.echo.sequence));

 /* Used by ICMP Traceroute message,
    use field un.echo.sequence as unused,
    see RFC 1393 */
 if( show_tr) 
     printf( "Unused                      : %-5d (0x%x)\n", htons( ich.un.echo.sequence), htons( ich.un.echo.sequence));

 if( show_res)
     printf( "Reserved                    : %ld (0x%lx)\n", htonl( ich.un.reserved), htonl( ich.un.reserved));

 if( show_routad) {
     routad = ( PLOG_U16) ( htonl( ich.un.routad) & 0xFF000000) >> 24;
     printf( "Number of address           : %-5d (0x%x)\n", routad, routad);
     routad = ( PLOG_U16) ( htonl( ich.un.routad) & 0x00FF0000) >> 16;
     printf( "Address entry size          : %-5d (0x%x)\n", routad, routad);
     routad = ( PLOG_U16) ( htonl( ich.un.routad) & 0x0000FFFF);
     printf( "Lifetime                    : %-5d (0x%x)\n", routad, routad);
 }

 if( show_seq == 0 && 
     show_id  == 0 &&
     show_gat == 0 &&
     show_res == 0 &&
     show_tr  == 0 &&
     show_routad == 0
     ) {
     if( show_poi == 0)
         unused = htonl( ich.un.unused);
     else /* If pointer exists then clear it */
         unused = htonl( ich.un.unused) & 0x00FFFFFF;

     printf( "Unused field                : %ld (0x%lx)\n", unused, unused);
 }

}

void print_data( char *buf, char *pkttype, int size)
{
 if( flag_printiph && flag_verbose) /* Print the IP header again */
     print_ipret_icmp_printiph_again( buf);
 else {
     printf( "******* %s Data *******\n", pkttype);
     print_ala_hexdump( buf, size);
 }
}

void print_ala_hexdump( char *buf, int size)
{
 int hsize = 0; /* Hex size */
 int pos   = 0;
 
 for( pos = 0; pos < size; pos += 16) {
        printf( "%07X  ", pos);

        hsize = (size - pos) > 16 ? 16: size - pos;

        print_hex(   buf + pos, hsize);
        print_ascii( buf + pos, hsize);
        printf( "\n");
 }

}

void print_hex( char *buf, int size)
{
 int j = 0, sp = 1;

 while( j < size) {
        printf( "%02X", (unsigned char) buf[ j]);
        j++;
        if( sp == 4) {
             printf( "  ");
             sp = 1;
        } else {
             printf( " ");
             sp++;
        }
 }

 while( j++ < 16) {
        printf( "  ");
        if( sp == 4) {
             printf( "  ");
             sp = 1;
        } else {
             printf( " ");
             sp++;
        }
 }

}

void print_ascii( char *buf, int size)
{
 int i;

 for( i = 0; i < size; i++)
      printf( "%c", buf[ i] > 31 && buf[ i] < 127 ? buf[ i] : '.');

 while( i++ < 16)
        printf( " ");

}

/*
 * Print the ip header with the data of an ICMP message + 64 bits of data,
 * some ICMP messages include the original IP header + 64 bits of
 * the data of the original datagram.
 */
void print_ipret_icmp_printiph_again( char *buf)
{
 struct plog_iphdr iph; /* IP header */
 int iphlen;

 flag_printiph = 0;

 memcpy( &iph, buf, sizeof( iph));

 printf( "******* ICMP data: IP header of the original datagram: *******\n");
 print_header_ip( iph);
                
 iphlen  = iph.hlv & 0x0F;
 iphlen  = iphlen << 2;
 iphlen -=20;

 if( iphlen > 0)
     print_header_ipoptions( buf + sizeof( iph), iphlen);

 printf( "******* 64 bits of the original datagram data: *******\n");

 print_ala_hexdump( buf + sizeof( iph) + iphlen, 8);

 printf( "******* End of interpreted ICMP data *******\n");
}

