/*
     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.

fsp.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/udp.h>
#include <ctype.h>
#include <sys/time.h>
#include <math.h>


#define UBUF_HSIZE 12                           /* 12 bytes for the header */
#define UBUF_SPACE 1024			        /* maximum payload.        */

typedef struct UBUF {            char   cmd;  /* message code.             */
                        unsigned char   sum;  /* message checksum.         */
                        unsigned short  key;  /* message key.              */
                        unsigned short  seq;  /* message sequence number.  */
                        unsigned short  len;  /* number of bytes in buf 1. */
                        unsigned long   pos;  /* location in the file.     */

                        char   buf[UBUF_SPACE];
                    } UBUF;

extern float microsec(struct timeval);
extern struct timeval subtime(struct timeval, struct timeval);
extern struct timeval addtime(struct timeval, struct timeval);

struct fsp_sess {
     struct fsp_sess *next;
     struct fsp_sess *prev;
     struct in_addr dstaddr;
     struct timeval tlp;
     unsigned long fspcount_client;
     unsigned long fspcount_server;
     unsigned long fspmiss;
     unsigned short dstport;
};


static struct fsp_sess *head = (struct fsp_sess *)0;

void fsp_dump(unsigned long, unsigned short,
	      unsigned long, unsigned short,
	      struct ip *, struct udphdr *, struct timeval);

void
checkfsp(struct ip *ip, struct udphdr *udp, struct timeval timebuf)
{
     unsigned long srchost, dsthost;
     unsigned short srcport, dstport;
     char *ipaddr;
     char *udpport;

     if(!ip || !udp)
	  return;
	  
     ipaddr = (char *)&ip->ip_src.s_addr;
     memcpy((char *)&srchost, ipaddr, 4);

     udpport = (char *)&udp->uh_sport;
     memcpy((char *)&srcport, udpport, 2);

     ipaddr = (char *)&ip->ip_dst.s_addr;
     memcpy((char *)&dsthost, ipaddr, 4);

     udpport = (char *)&udp->uh_dport;
     memcpy((char *)&dstport, udpport, 2);
     
     fsp_dump(srchost, srcport, dsthost, dstport, ip, udp, timebuf);
}

void
fsp_dump(unsigned long srchost, unsigned short srcport,
	 unsigned long dsthost, unsigned short dstport,
	 struct ip *ip, struct udphdr *udp, struct timeval timebuf)
{
     unsigned long host;
     unsigned short port;
     struct fsp_sess *next;
     struct fsp_sess *rove;
     int server;
     struct timeval delta;
     UBUF buf;
     unsigned char offset;
     short len;
     int i;
     char *bp;
     unsigned sum, n, presum, buflen, thesum;

     memcpy((char *)&len, (char *)&udp->uh_ulen, 2);
     bp = ((char *)udp)+sizeof(struct udphdr);
     buflen = len - sizeof(struct udphdr);
     if(buflen <= sizeof(buf)){
	  memcpy((char *)&buf, bp, buflen);
	  presum = buf.sum;
	  buf.sum = 0;
	  /*
	    By some extremely lucky mistake, the server doesn't
	    add in the length of the packet the same way the client
	    does, we we have a way of telling the difference
	  */
	  sum = 0;
	  for(bp = (char *) &buf, n = buflen;n--;
	      sum += *(unsigned char *)bp++)
	       ;

	  thesum = (sum + (sum >> 8)) & 0xff;
	  server = 1;
	  if(thesum != presum){
	       server = 0;
	       thesum = ((sum + buflen) + (sum >> 8)) & 0xff;
	  }
#if 0
	  if(rove->dstport == 1200){
	       printf("%08X.%d (%d): CMD: %d, %d <=> %d, %d <=> %d\n",
		      rove->dstaddr.s_addr,
		      rove->dstport,
		      rove->fspcount,
		      buf.cmd,
		      buf.len + UBUF_HSIZE, buflen,
		      presum, thesum);
	       fflush(stdout);
	  }
#endif
	  if(server){
	       host = srchost;
	       port = srcport;
	  }
	  else {
	       host = dsthost;
	       port = dstport;
	  }

	  for(rove = head;rove;rove=rove->next){
	       if(rove->dstaddr.s_addr == host &&
		  rove->dstport == port)
		    break;
	  }

	  if(buf.len+UBUF_HSIZE == buflen){
	       if(thesum == presum){

		    if(!rove){
			 rove = (struct fsp_sess *)malloc(sizeof(struct fsp_sess));
			 memcpy((char *)&rove->dstaddr.s_addr, (char *)&host, 4);
			 rove->dstport = port;
			 rove->next = head;
			 rove->fspcount_client = rove->fspmiss =
			      rove->fspcount_server = 0;
			 rove->prev = (struct fsp_sess *)0;
			 if(head)
			      head->prev = rove;
			 head = rove;
		    }

		    rove->tlp = timebuf;
		    if(server)
			 rove->fspcount_server++;
		    else
			 rove->fspcount_client++;
		    if((rove->fspcount_client+rove->fspcount_server) == 8){
			 int pktcount=rove->fspcount_server +rove->fspcount_client;
			 if((float)pktcount/(pktcount+rove->fspmiss) > 0.10){
			      outtime(rove->tlp);
			      printf(" [udp] ");
			      outhost(rove->dstaddr);
			      printf(".%d", rove->dstport);
			      if(!rove->fspcount_server)
				   printf(" possible unresponding FSP server.\n");
			      else if(!rove->fspcount_client)
				   printf(" possible FSP server.\n");
			      else
				   printf(" FSP server.\n");
			      fflush(stdout);
			 }
		    }
	       }
	       else if(rove)
		    rove->fspmiss++;
	  }
	  else if(rove)
	       rove->fspmiss++;
	       
     }

     next = head;
     while(next){
	  /* Eight hour time out */
	  if(timebuf.tv_sec - next->tlp.tv_sec > (3600*8)){
	       struct fsp_sess *nextnext = next->next;

	       if(next == head)
		    head = next->next;
	       if(next->prev)
		    next->prev->next = next->next;
	       if(next->next)
		    next->next->prev = next->prev;
	       free(next);
	       next = nextnext;
	  }
	  else
	       next = next->next;
     }
}
