/*
     etherscan - A real time network security monitor
     Copyright (C) 1993 Douglas Lee Schales, David K. Hess, David R. Safford

     Please see the file `RESTRICTIONS' for the complete copyright notice.

etherscan.c - 05/23/93

*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <sys/time.h>
#include <netdb.h>
#include <memory.h>

#define BUFSIZE (1024*32)

extern char *optarg;
extern int optind, opterr;

#include "defs.h"
#include "version.h"

struct ipaddr {
     unsigned int addr:32;
};

static void etherhandler(char *, int, struct timeval);
static void tcpswitch(struct ip *, struct tcphdr *, struct timeval);
static void udpswitch(struct ip *, struct udphdr *, struct timeval);
static char *gettcpportname(unsigned short);
extern char *gettimestr(struct timeval);

extern void pushfilter(int);

extern void telnet_dump();
extern void smtp_dump();
extern void rlogin_dump();
extern void rsh_dump();
extern void ftp_dump();

extern char *getdefaultif(char *);

extern void check_fsp();

struct val_str_hndl tcp_ports[] = {
     23, "telnet", telnet_dump,
     25, "smtp", smtp_dump,
     513, "login", rlogin_dump,
     514, "shell", rsh_dump,
     21, "ftp", ftp_dump,
     540, "uucp", 0,
     512, "exec", 0,
     0, (char *)0, 0
     };

struct val_str_hndl udp_ports[] = {
     0, (char *)0, 0
     };

char **ports = (char **)0;

static int udpmon = 0;

void
pushfilter(int fd)
{
     pktfilt(fd, ports, udpmon);
}

static FILE *logfile = stdout;

void
usage(char *prog)
{
     fprintf(stderr, "Usage: %s [-i interface] [-f logfile] tcp_port_list\n",
	     prog);
}

main(int argc, char **argv)
{
     char *eif = getdefaultif((char *)0);
     int eifflag = 0;
     int fd;
     int c;
     int promisc = 1;
     
     while((c=getopt(argc, argv, "vpi:f:u")) != -1){
	  switch(c){
	  case 'v':
	       printf("netwatch, version %s\n", toolversion);
	       exit(0);
	       break;
	  case 'p':
	       promisc = 0;
	       break;
	  case 'u':
	       udpmon=1;
	       break;
	  case 'i':
	       eif = optarg;
	       eifflag++;
	       break;
	  case 'f':
	       if(!(logfile = fopen(optarg, "a"))){
		    perror(optarg);
		    exit(1);
	       }
	       break;
	  default:
	       fprintf(stderr, "Unknown option: %c\n", c);
	       usage(argv[0]);
	       exit(1);
	  }
     }

     if(!eifflag)
	  printf("Using interface %s\n", eif);

     ports = &argv[optind];

     fd = opennit(eif, promisc, pushfilter);
     procpkts(fd, etherhandler);
}

void
etherhandler(char *pkt, int size, struct timeval tp)
{
     struct ip *ip = (struct ip *)(pkt+14);
     unsigned char ipproto;

     if(pkt){
	  memcpy((char *)&ipproto, (char *)&ip->ip_p, 1);
	  if(ipproto == IPPROTO_TCP){
	       struct tcphdr *tcp =
		    (struct tcphdr *)(((char *)ip)+sizeof(struct ip));
	       tcpswitch(ip, tcp, tp);
	  }
	  else if(ipproto == IPPROTO_UDP){
	       struct udphdr *udp =
		    (struct udphdr *)(((char *)ip)+sizeof(struct ip));
	       udpswitch(ip, udp, tp);
	  }
     }
}

void
tcpswitch(struct ip *ip, struct tcphdr *tcp, struct timeval tp)
{
     int i;

     for(i=0;tcp_ports[i].text;i++)
	  if(tcp_ports[i].value == tcp->th_dport){
	       if(tcp_ports[i].handler){
		    (*tcp_ports[i].handler)(ip, tcp, tp);
	       }
	       return;
	  }
     for(i=0;tcp_ports[i].text;i++)
	  if(tcp_ports[i].value == tcp->th_sport){
	       if(tcp_ports[i].handler){
		    (*tcp_ports[i].handler)(ip, tcp, tp);
	       }
	       return;
	  }
}

void
udpswitch(struct ip *ip, struct udphdr *udp, struct timeval tp)
{
     int i;
     unsigned short port;


     checkfsp(ip, udp, tp);
     checkrpc(ip, udp, tp);
     checktftp(ip, udp, tp);

     memcpy((char *)&port, (char *)&udp->uh_dport, 2);
     for(i=0;udp_ports[i].text;i++)
	  if(udp_ports[i].value == port){
	       if(udp_ports[i].handler){
		    (*udp_ports[i].handler)(ip, udp, tp);
	       }
	       return;
	  }
     memcpy((char *)&port, (char *)&udp->uh_sport, 2);
     for(i=0;udp_ports[i].text;i++)
	  if(udp_ports[i].value == port){
	       if(udp_ports[i].handler){
		    (*udp_ports[i].handler)(ip, udp, tp);
	       }
	       return;
	  }
}

char *
gettcpportname(unsigned short p)
{
     struct servent *se;
     static char result[80];

     if(se = getservbyport((unsigned int)ntohs(p), "tcp"))
	  strcpy(result, se->s_name);
     else
	  sprintf(result, "%u", (unsigned int)ntohs(p));
     
     return result;
}
