/*
   Plogicmp - ICMP logging routines
   --------------------------------------------------------------------
   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"

void usage( void);

int main( int argc, char *argv[])
{
 FILE *fplog;          /* Simple log file pointer            */
 FILE *fpraw;          /* Raw log file poinetr               */
 char buf[     65535]; /* The buffer we use to read a packet */
 char type[      256]; /* ICMP type string                   */
 struct plog_iphdr   iph; /* IP header                          */
 struct plog_icmphdr ich; /* ICMP header                        */
 struct in_addr addrs;
 struct sockaddr_in name;
 /* Mask to apply for IP addresses to ignore */
 struct in_addr mask[ PRO_IGN_HOST];
 /* Mask to apply for ignored packets        */
 struct in_addr ignoremask[ PRO_IGN_HOST];
 struct tm *ltm;       /* Used by localtime(3)               */
 time_t t;             /* Used by time(2)                    */
 char *ti;             /* Used by ctime(3)                   */
 int flag_log;         /* Make a simple human readeable log  */
 int flag_raw;         /* Log time + raw data                */
 int qty_ignore;       /* Quantity of ignored hosts          */
 int ch;               /* For command line parsing           */
 int sock;             /* socket fd                          */
 int length;           /* size of struct sockaddr_in name    */
 int iphlen;           /* IP header length (Used to skip     */
                       /* options)                           */
 int i;

 progname = plog_basename( strdup( argv[ 0]));

 /* Check if we are root */
 check_uid();

 flag_quiet        = 0;
 flag_log          = 0;
 flag_raw          = 0;
 flag_dont_resolve = 1;
 qty_ignore        = 0;

 if( argc < 2) 
     usage();

 /* Parse command line */

 while(( ch = getopt( argc, argv, "qlwri:")) != EOF){
         switch( ch) {
                case 'q': flag_quiet = 1;
                          break;
                case 'l': flag_log   = 1;
                          break;
                case 'w': flag_raw   = 1;
                          break;
                case 'r': flag_dont_resolve = 0;
                          break;
                case 'i': if( qty_ignore >= PRO_IGN_HOST) {
                              fprintf( stderr, "%s: Quantity of ignored host too long: %d\n", progname, qty_ignore);
                              exit( -1);
                          }
                          process_host_and_mask( optarg, &mask[ qty_ignore], &ignoremask[ qty_ignore]);
                          qty_ignore++;
                          break;
                default : usage();
         }
 }

 if( !flag_log && !flag_raw)
     usage();

 /* Open a raw icmp socket in order to receive all ICMP packets */

 if( (sock = socket( AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
     fprintf( stderr, "%s: %s\n", progname, strerror( errno));
     exit( -1);
 }

 name.sin_family      = AF_INET;
 name.sin_addr.s_addr = INADDR_ANY;
 name.sin_port        = 0;

 if( bind( sock, ( struct sockaddr *) &name, sizeof name ) < 0) {
     fprintf( stderr, "%s: %s\n", progname, strerror( errno));
     exit( -1);
 }

 length = sizeof( name);

 if( getsockname( sock, (struct sockaddr *) &name, &length) < 0) {
     fprintf( stderr, "%s: %s\n", progname, strerror( errno));
     exit( -1);
 }

 if( flag_quiet == 0) {
     printf( "\nProtolog Release %s - ", PRO_VERSION);
     printf( "plogicmp (ICMP logger)\n\n");
 }

 gobackground();
 init_signals();

 if( flag_log) {
     fplog = fopen( PRO_ICMP_LOG, "a+");

     t  = time( NULL);
     ti = ctime( &t);

     /* Write log headings */
     fprintf( fplog, "%s\n", replicate( '-', 80));
     fprintf( fplog, "Protolog ICMP logger - Begins at %-24.24s\n", ti);
     fprintf( fplog, "%s\n", replicate( '-', 80));
     if( qty_ignore != 0) {
         fprintf( fplog, "Ignore hostname/mask enabled.\n");
         for( i = 0; i < qty_ignore; i++) {
              fprintf( fplog, "%-5d: Mask            : %s\n", i, inet_ntoa( mask[       i]));
              fprintf( fplog, "%-5d: Ignore host mask: %s\n", i, inet_ntoa( ignoremask[ i]));
         }
         fprintf( fplog, "%s\n", replicate( '-', 80));
     }
     fprintf( fplog, "-      Date      -   Source IP   -    Domain Name    -     ICMP type      -code-\n");
     fprintf( fplog, "%s\n", replicate( '-', 80));

     fclose( fplog);
 }

 while( 1) {
again:
        if( read( sock, buf, sizeof( buf)) <= 0)
            continue;

        /* Fill the IP header */
        memcpy( &iph, buf, sizeof( iph));

        /* Do we ignore some IP addresses? */
        if( qty_ignore != 0) {
            /*
             * Take the IP source address
             * 'and' it with the mask
             * and if it is equal to the ignore mask
             * ignore the packet
             */

            addrs.s_addr = iph.saddr;

            for( i = 0; i < qty_ignore; i++)
                 if( (addrs.s_addr & mask[ i].s_addr) == ignoremask[ i].s_addr)
                      goto again;
        }

        /* Skip IP options */
        iphlen  = ( iph.hlv & 0x0F) << 2;
        iphlen -=20;

        /* Fill the ICMP header */
        memcpy( &ich, buf + sizeof( iph) + iphlen, sizeof( ich));

        t = time( NULL);

        /* Make a simple human readeable log */
        if( flag_log) {
            fplog = fopen( PRO_ICMP_LOG, "a+");

            ltm = localtime( &t);

            fprintf( fplog, "%02d:%02d:%02d %02d/%02d/%02d "
                             , ltm->tm_hour    , ltm->tm_min , ltm->tm_sec
                             , ltm->tm_mon + 1 , ltm->tm_mday, ltm->tm_year);

            switch( ich.type) {
                    case  1: /* Unassigned ICMP types */
                    case  2: 
                    case  7: sprintf( type, "Unassigned: %d", ich.type); break;
                    case  0: strcpy( type, "Echo Reply"             ); break;
                    case  3: strcpy( type, "Dest. Unreachable"      ); break;
                    case  4: strcpy( type, "Source Quench"          ); break;
                    case  5: strcpy( type, "Redirect ch. route"     ); break;
                    case  6: strcpy( type, "Alternate Host Address" ); break;
                    case  8: strcpy( type, "Echo Request"           ); break;
                    case  9: strcpy( type, "Router Advertisement"   ); break;
                    case 10: strcpy( type, "Router Solicitation"    ); break;
                    case 11: strcpy( type, "Time Exceeded"          ); break;
                    case 12: strcpy( type, "Parameter problem"      ); break;
                    case 13: strcpy( type, "Timestamp Request"      ); break;
                    case 14: strcpy( type, "Timestamp Reply"        ); break;
                    case 15: strcpy( type, "Information Request"    ); break;
                    case 16: strcpy( type, "Information Reply"      ); break;
                    case 17: strcpy( type, "Address Mask Request"   ); break;
                    case 18: strcpy( type, "Address Mask Reply"     ); break;
                    case 30: strcpy( type, "Traceroute"             ); break;
                    case 31: strcpy( type, "Conversion Failed"      ); break;
                    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 Reg. Request"    ); break;
                    case 36: strcpy( type, "Mobile Reg. Reply"      ); break;
                    case 37: strcpy( type, "Domain Name Request"    ); break;
                    case 38: strcpy( type, "Domain Name Reply"      ); break;
                    default: sprintf( type, "Reserved: %d", ich.type); break;
            } /* end switch( ich.type) */
            
            addrs.s_addr = iph.saddr;
            fprintf( fplog, "%-15.15s (%-17.17s) ", inet_ntoa( addrs), resolve_host_name( iph.saddr));

            fprintf( fplog, "%-20.20s %4d\n", type, ich.code);

            fclose( fplog);
        }

        /* Dump time + raw data */

        if( flag_raw) {
            fpraw = fopen( PRO_ICMP_RAW, "a+");
            fwrite(  &t,          sizeof( t), 1, fpraw);
            fwrite( buf, htons( iph.tot_len), 1, fpraw);
            fclose( fpraw);
        }

 } /* end while( 1) */

 close( sock);

 return 0;
}

void usage( void)
{
 fprintf( stderr, "\nProtolog Release %s - ", PRO_VERSION);
 fprintf( stderr, "plogicmp (ICMP logger)\n\n         by  Diego J. Grigna (diego@grigna.com)\n\n");
 fprintf( stderr, "Usage:\n%s [-q] [-lwr] [-i hostname[/mask]]\n", progname);
 fprintf( stderr, "\t-q\t Quiet mode, don't send output to stdout.\n");
 fprintf( stderr, "\t-l\t Make a simple human readeable log\n");
 fprintf( stderr, "\t-w\t Log time + raw data\n");
 fprintf( stderr, "\t-r\t Resolve domain names\n");
 fprintf( stderr, "\t-i\t Ignore packets from hostname/mask\n\n");
 exit( -1);
}

