/************************************************************************/
/*                                                                      */
/* Psionic PortSentry - Port Scan Detector				*/
/*                                                                      */
/* Author: Craig H. Rowland <crowland@psionic.com>                      */
/* Created: 10-12-97                                                    */
/* Modified: 04-12-99                                                   */
/*                                                                      */
/* Send all changes/modifications/bugfixes to the above address.        */
/*                                                                      */
/*                                                                      */
/* This software is Copyright(c) 1997-99 Craig H. Rowland               */
/*                                                                      */
/* Disclaimer:                                                          */
/*                                                                      */
/* All software distributed by Craig H. Rowland ("the author") and      */
/* Psionic Systems is distributed AS IS and carries NO WARRANTY or      */
/* GUARANTEE OF ANY KIND. End users of the software acknowledge that    */
/* they will not hold the author, Psionic Systems, and any employer of  */
/* the author liable for failure or non-function of a software          */
/* product. YOU ARE USING THIS PRODUCT AT YOUR OWN RISK                 */
/*                                                                      */
/* Licensing restrictions apply. See the license that came with this    */
/* file for more information or visit http://www.psionic.com for more   */
/* information.                                                         */
/*                                                                      */
/*$Id: portsentry.c,v 1.26 1999/08/04 05:26:27 crowland Exp crowland $ */
/************************************************************************/

#include "portsentry.h"
#include "portsentry_io.h"
#include "portsentry_util.h"

/*#include "Portsentry_io.h"
#include "Portsentry_util.h"
*/

/* Global variables...ugghhh.... */
char gblScanDetectHost[MAXSTATE][IPMAXBUF];
char gblKillRoute[MAXBUF];
char gblKillHostsDeny[MAXBUF];
char gblKillRunCmd[MAXBUF];
char gblBlockedFile[MAXBUF];
char gblHistoryFile[MAXBUF];
char gblIgnoreFile[MAXBUF];
char gblDetectionType[MAXBUF];

int gblScanDetectCount = 0;
int gblBlockTCP = 0;
int gblBlockUDP = 0;
int gblConfigTriggerCount = 0;


int
/* portsentry_ */
main (int argc, char *argv[])
{
  if (argc != 2)
    {
      Usage ();
      Exit (ERROR);
    }

  if ((geteuid ()) && (getuid ()) != 0)
    {
      printf ("Must be root to run.\n");
      Exit (ERROR);
    }


  /* Cheesy arg parser. Some systems don't support getopt and I don't want to port it. */
  if ((strcmp (argv[1], "-tcp")) && (strcmp (argv[1], "-udp"))
      && (strcmp (argv[1], "-stcp")) && (strcmp (argv[1], "-atcp"))
      && (strcmp (argv[1], "-sudp")) && (strcmp (argv[1], "-audp")) != 0)
    {
      Usage ();
      Exit (ERROR);
    }
  else
    {
      Start ();
      /* This copies the startup type to a global for later use */
      if ((SafeStrncpy (gblDetectionType, strstr (argv[1], "-") + 1, MAXBUF))
	  == NULL)
	{
	  Log("adminalert: ERROR: Error setting internal scan detection type.\n");
	  printf ("ERROR: Error setting scan type.\n");
	  printf ("ERROR: PortSentry shut down!\n");
	  Exit (ERROR);
	}
      else if (CheckConfig () == FALSE)
	{
	  Log ("adminalert: ERROR: Configuration files are missing/corrupted. Shutting down.\n");
	  /*  printf ("ERROR: Configuration files are missing/corrupted.\n");
	  printf ("ERROR: Check your syslog for a more detailed error message.\n");
	  printf ("ERROR: PortSentry is shutting down!\n"); */
	  Exit (ERROR);
	}
      else if (InitConfig () == FALSE)
	{
	  Log ("adminalert: ERROR: Your config file is corrupted/missing mandatory option! Shuting down.\n");
	  /*  printf ("ERROR: Your config file is corrupted/missing mandatory option!\n");
	  printf ("ERROR: Check your syslog for a more detailed error message.\n");
	  printf ("ERROR: PortSentry is shutting down!\n"); */
	  Exit (ERROR);
	}
#ifndef NODAEMON
      else if (DaemonSeed () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into daemon mode. Shutting down.\n");
	  /*  printf ("ERROR: could not go into daemon mode. Shutting down.\n"); */
	  Exit (ERROR);
	}
#endif
    }


  if (strcmp (argv[1], "-tcp") == 0)
    {
      if (PortSentryModeTCP () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n");
	  Exit (ERROR);
	}
    }
#ifdef SUPPORT_STEALTH
  else if (strcmp (argv[1], "-stcp") == 0)
    {
      if (PortSentryStealthModeTCP () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n");
	  Exit (ERROR);
	}
    }
  else if (strcmp (argv[1], "-atcp") == 0)
    {
      if (PortSentryAdvancedStealthModeTCP () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n");
	  Exit (ERROR);
	}
    }
  else if (strcmp (argv[1], "-sudp") == 0)
    {
      if (PortSentryStealthModeUDP () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n");
	  Exit (ERROR);
	}
    }
  else if (strcmp (argv[1], "-audp") == 0)
    {
      if (PortSentryAdvancedStealthModeUDP () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n");
	  Exit (ERROR);
	}
    }
#endif
  else if (strcmp (argv[1], "-udp") == 0)
    {
      if (PortSentryModeUDP () == ERROR)
	{
	  Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n");
	  Exit (ERROR);
	}
    }

  Exit (TRUE);
  /* shuts up compiler warning */
  return (0);
}

/****************************************************************/
/* Reads generic config options into global variables           */
/****************************************************************/
int
InitConfig (void)
{
  FILE *input;
  char configToken[MAXBUF];

  gblBlockTCP = DoBlockTCP ();
  gblBlockUDP = DoBlockUDP ();

  memset (gblKillRoute, '\0', MAXBUF);
  memset (gblKillHostsDeny, '\0', MAXBUF);
  memset (gblKillRunCmd, '\0', MAXBUF);

  if ((ConfigTokenRetrieve ("SCAN_TRIGGER", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read SCAN_TRIGGER option from config file. Disabling SCAN DETECTION TRIGGER");
      gblConfigTriggerCount = 0;
    }
  else
    {
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved SCAN_TRIGGER option: %s \n",
	   configToken);
#endif
      gblConfigTriggerCount = atoi (configToken);
    }

  if ((ConfigTokenRetrieve ("KILL_ROUTE", gblKillRoute)) == TRUE)
    {
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved KILL_ROUTE option: %s \n",
	   gblKillRoute);
#endif
    }
  else
    {
#ifdef DEBUG
      Log ("debug: InitConfig: KILL_ROUTE option NOT FOUND.\n");
#endif
    }

  if ((ConfigTokenRetrieve ("KILL_HOSTS_DENY", gblKillHostsDeny)) == TRUE)
    {
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved KILL_HOSTS_DENY option: %s \n",
	   gblKillHostsDeny);
#endif
    }
  else
    {
#ifdef DEBUG
      Log ("debug: InitConfig: KILL_HOSTS_DENY option NOT FOUND.\n");
#endif
    }

  if ((ConfigTokenRetrieve ("KILL_RUN_CMD", gblKillRunCmd)) == TRUE)
    {
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved KILL_RUN_CMD option: %s \n",
	   gblKillRunCmd);
#endif
    }
  else
    {
#ifdef DEBUG
      Log ("debug: InitConfig: KILL_RUN_CMD option NOT FOUND.\n");
#endif
    }

  if ((ConfigTokenRetrieve ("BLOCKED_FILE", gblBlockedFile)) == TRUE)
    {
      if (strlen (gblBlockedFile) < MAXBUF - 5)
	{
	  strncat (gblBlockedFile, ".", 1);
	  strncat (gblBlockedFile, gblDetectionType, 4);
	}
      else
	{
	  Log ("adminalert: ERROR: Blocked filename is too long to append detection type file extension: %s.\n",
	     gblBlockedFile);
	  return (FALSE);
	}
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved BLOCKED_FILE option: %s \n",
	   gblBlockedFile);
      Log ("debug: CheckConfig: Removing old block file: %s \n",
	   gblBlockedFile);
#endif

      if ((input = fopen (gblBlockedFile, "w")) == NULL)
	{
	  Log
	    ("adminalert: ERROR: Cannot delete blocked file on startup: %s.\n",
	     gblBlockedFile);
	  return (FALSE);
	}
      else
	fclose (input);
    }
  else
    {
      Log ("InitConfig: Cannot retrieve BLOCKED_FILE option! Aborting\n");
      return (FALSE);
    }


  if ((ConfigTokenRetrieve ("HISTORY_FILE", gblHistoryFile)) == TRUE)
    {
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved HISTORY_FILE option: %s \n",
	   gblHistoryFile);
#endif
    }
  else
    {
      Log ("InitConfig: Cannot retrieve HISTORY_FILE option! Aborting\n");
      return (FALSE);
    }

  if ((ConfigTokenRetrieve ("IGNORE_FILE", gblIgnoreFile)) == TRUE)
    {
#ifdef DEBUG
      Log ("debug: InitConfig: retrieved IGNORE_FILE option: %s \n",
	   gblIgnoreFile);
#endif
    }
  else
    {
      Log ("InitConfig: Cannot retrieve IGNORE_FILE option! Aborting\n");
      return (FALSE);
    }

  return (TRUE);
}


#ifdef SUPPORT_STEALTH

/* Read in a TCP packet taking into account IP options and other */
/* errors */
int
PacketReadTCP (int socket, struct iphdr *ipPtr, struct tcphdr *tcpPtr)
{
  char packetBuffer[TCPPACKETLEN];
  struct in_addr addr;

  bzero (ipPtr, sizeof (struct iphdr));
  bzero (tcpPtr, sizeof (struct tcphdr));

  if(read (socket, packetBuffer, TCPPACKETLEN) == ERROR)
    return(ERROR);

  memcpy (ipPtr, (struct iphdr *) packetBuffer, sizeof (struct iphdr));

  if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15))
    {
      addr.s_addr = (u_int) ipPtr->saddr;
      Log ("attackalert: Illegal IP header length detected: %d from (possible) host: %s\n",
	 ipPtr->ihl, inet_ntoa (addr));
      return (FALSE);
    }
  else
    {
      memcpy (tcpPtr, (struct tcphdr *) (packetBuffer + ((ipPtr->ihl) * 4)),
	      sizeof (struct tcphdr));
      return (TRUE);
    }

}

/* Read in a UDP packet taking into account IP options and other */
/* errors */
int
PacketReadUDP (int socket, struct iphdr *ipPtr, struct udphdr *udpPtr)
{
  char packetBuffer[UDPPACKETLEN];
  struct in_addr addr;

  bzero (ipPtr, sizeof (struct iphdr));
  bzero (udpPtr, sizeof (struct udphdr));

  if(read (socket, packetBuffer, UDPPACKETLEN) == ERROR)
    return(ERROR);

  memcpy (ipPtr, (struct iphdr *) packetBuffer, sizeof (struct iphdr));

  if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15))
    {
      addr.s_addr = (u_int) ipPtr->saddr;
      Log ("attackalert: Illegal IP header length detected: %d from (possible) host: %s\n",
	 ipPtr->ihl, inet_ntoa (addr));
      return (FALSE);
    }
  else
    {
      memcpy (udpPtr, (struct udphdr *) (packetBuffer + ((ipPtr->ihl) * 4)),
	      sizeof (struct udphdr));
      return (TRUE);
    }

}

/****************************************************************/
/* Stealth scan detection Mode One                              */
/*                                                              */
/* This mode will read in a list of ports to monitor and will   */
/* then open a raw socket to look for packets matching the port. */
/*                                                              */
/****************************************************************/
int
PortSentryStealthModeTCP (void)
{
  struct sockaddr_in client, server;
  int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS];
  int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE, result = TRUE;
  int openSockfd = 0, incomingPort = 0;
  char *temp, target[IPMAXBUF], configToken[MAXBUF];
  char resolvedHost[DNSMAXBUF], *packetType;
  struct hostent *hostPtr = NULL;
  struct in_addr addr;
  struct iphdr ip;
  struct tcphdr tcp;

  if ((ConfigTokenRetrieve ("TCP_PORTS", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read TCP_PORTS option from config file");
      return (ERROR);
    }

  /* break out the ports */
  if ((temp = (char *) strtok (configToken, ",")) != NULL)
    {
      ports[0] = atoi (temp);
      for (count = 1; count < MAXSOCKS; count++)
	{
	  if ((temp = (char *) strtok (NULL, ",")) != NULL)
	    ports[count] = atoi (temp);
	  else
	    break;
	}
      portCount = count;
    }
  else
    {
      Log ("adminalert: ERROR: No TCP ports supplied in config file. Aborting");
      return (ERROR);
    }

  /* ok, now check if they have a network daemon on the socket already, if they do */
  /* then skip that port because it will cause false alarms */
  for (count = 0; count < portCount; count++)
    {
      Log ("adminalert: Going into stealth listen mode on TCP port: %d\n",
	   ports[count]);
      if ((openSockfd = OpenTCPSocket ()) == ERROR)
	{
	  Log ("adminalert: ERROR: could not open TCP socket. Aborting.\n");
	  return (ERROR);
	}

      if (BindSocket (openSockfd, client, server, ports[count]) == ERROR)
	Log ("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n",
	   ports[count]);
      else /* well we at least bound to one socket so we'll continue */
	{
	  gotBound = TRUE;
	  ports2[portCount2++] = ports[count];
	}
      close (openSockfd);
    }

  /* if we didn't bind to anything then abort */
  if (gotBound == FALSE)
    {
      Log ("adminalert: ERROR: All supplied TCP sockets are in use and will not be listened to. Shutting down.\n");
      return (ERROR);
    }

  /* Open our raw socket for network IO */
  if ((openSockfd = OpenRAWTCPSocket ()) == ERROR)
    {
      Log ("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n");
      return (ERROR);
    }

  Log ("adminalert: PortSentry is now active and listening.\n");

  /* main detection loop */
  for (;;)
    {
      if (PacketReadTCP (openSockfd, &ip, &tcp) != TRUE)
	continue;
      

      incomingPort = ntohs (tcp.dest);

      /* check for an ACK/RST to weed out established connections in case the user */
      /* is monitoring high ephemeral port numbers */
      if ((tcp.ack != 1) && (tcp.rst != 1))
	{
	  /* this iterates the list of ports looking for a match */
	  for (count = 0; count < portCount; count++)
	    {
	      if (incomingPort == ports2[count])
		{
		  if (SmartVerifyTCP (client, server, incomingPort) == TRUE)
		    break;

		  /* copy the clients address into our buffer for nuking */
		  addr.s_addr = (u_int) ip.saddr;
		  SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF);
		  /* check if we should ignore this IP */
		  result = NeverBlock (target, gblIgnoreFile);

		  if (result == ERROR)
		    {
		      Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
		      result = FALSE;
		    }

		  if (result == FALSE)
		    {
		      /* check if they've visited before */
		      scanDetectTrigger = CheckStateEngine (target);
		      if (scanDetectTrigger == TRUE)
			{
			hostPtr = gethostbyaddr ((char *) &ip.saddr,
				sizeof (ip.saddr), AF_INET);
			if (hostPtr != NULL)
		    		snprintf (resolvedHost, DNSMAXBUF, "%s",
			     		hostPtr->h_name);
			else
				snprintf (resolvedHost, DNSMAXBUF, "%s", target);


			  packetType = ReportPacketType (tcp);
			  Log ("attackalert: %s from host: %s/%s to TCP port: %d",
			     packetType, resolvedHost, target,
			     ports2[count]);
			  /* Report on options present */
			  if (ip.ihl > 5)
			    Log ("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).",
			       resolvedHost, target, ports2[count]);

			  /* check if this target is already blocked */
			  if (IsBlocked (target, gblBlockedFile) == FALSE)
			    {
			      /* toast the prick */
			      if (DisposeTCP (target, ports2[count]) != TRUE)
				Log ("attackalert: ERROR: Could not block host %s/%s !!",
				   resolvedHost, target);
			      else
				WriteBlocked (target, resolvedHost,
					      ports2[count], gblBlockedFile,
					      gblHistoryFile, "TCP");
			    }	/* end IsBlocked check */
			  else
			    Log ("attackalert: Host: %s/%s is already blocked Ignoring",
			       resolvedHost, target);
			}	/* end if(scanDetectTrigger) */
		    }		/* end if(never block) check */
		  break;	/* get out of for(count) loop above */
		}		/* end if(incoming port) ==  protected port */
	    }			/* end for( check for protected port loop ) loop */
	}			/* end if(TH_ACK) check */
    }				/* end for( ; ; ) loop */

}				/* end PortSentryStealthModeTCP */


/****************************************************************/
/* Advanced Stealth scan detection Mode One                     */
/*                                                              */
/* This mode will see what ports are listening below 1024       */
/* and will then monitor all the rest. This is very sensitive   */
/* and will react on any packet hitting any monitored port,     */
/* regardless of TCP flags set                                  */
/*                                                              */
/****************************************************************/
int
PortSentryAdvancedStealthModeTCP (void)
{
  struct sockaddr_in client, server;
  int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE;
  int openSockfd = 0, incomingPort = 0, smartVerify = FALSE;
  unsigned int advancedPorts = 1024;
  unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0;
  char target[IPMAXBUF], configToken[MAXBUF];
  char resolvedHost[DNSMAXBUF], *temp, *packetType;
  struct hostent *hostPtr;
  struct in_addr addr;
  struct iphdr ip;
  struct tcphdr tcp;

  if ((ConfigTokenRetrieve ("ADVANCED_PORTS_TCP", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read ADVANCED_PORTS_TCP option from config file. Assuming 1024.");
      advancedPorts = 1024;
    }
  else
    advancedPorts = atoi (configToken);

  Log ("adminalert: Advanced mode will monitor first %d ports",
       advancedPorts);

  /* try to bind to all ports below 1024, any that are taken we exclude later */
  for (count = 0; count < advancedPorts; count++)
    {
      if ((openSockfd = OpenTCPSocket ()) == ERROR)
	{
	  Log ("adminalert: ERROR: could not open TCP socket. Aborting.\n");
	  return (ERROR);
	}
      if (BindSocket (openSockfd, client, server, count) == ERROR)
	inUsePorts[portCount++] = count;

      close (openSockfd);
    }

  if ((ConfigTokenRetrieve ("ADVANCED_EXCLUDE_TCP", configToken)) != FALSE)
    {
      /* break out the ports */
      if ((temp = (char *) strtok (configToken, ",")) != NULL)
	{
	  inUsePorts[portCount++] = atoi (temp);
	  Log ("adminalert: Advanced mode will manually exclude port: %d ",
	       inUsePorts[portCount - 1]);
	  for (count = 0; count < MAXSOCKS; count++)
	    {
	      if ((temp = (char *) strtok (NULL, ",")) != NULL)
		{
		  inUsePorts[portCount++] = atoi (temp);
		  Log ("adminalert: Advanced mode will manually exclude port: %d ",
		     inUsePorts[portCount - 1]);
		}
	      else
		break;
	    }
	}
    }
  else
    Log ("adminalert: Advanced mode will manually exclude no ports");


  for (count = 0; count < portCount; count++)
    Log ("adminalert: Advanced Stealth scan detection mode activated. Ignored TCP port: %d\n",
       inUsePorts[count]);

  /* open raw socket for reading */
  if ((openSockfd = OpenRAWTCPSocket ()) == ERROR)
    {
      Log ("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n");
      return (ERROR);
    }

  Log ("adminalert: PortSentry is now active and listening.\n");

  /* main detection loop */
  for (;;)
    {
      if (PacketReadTCP (openSockfd, &ip, &tcp) != TRUE)
	continue;

      incomingPort = ntohs (tcp.dest);

      /* don't monitor packets with ACK set (established) or RST */
      /* This could be a hole in some cases */
      if ((tcp.ack != 1) && (tcp.rst != 1))
	{
	  /* check if we should ignore this connection to this port */
	  for (count = 0; count < portCount; count++)
	    {
	      if ((incomingPort == inUsePorts[count])
		  || (incomingPort > advancedPorts))
		{
		  hotPort = FALSE;
		  break;
		}
	      else
		hotPort = TRUE;
	    }

	  if (hotPort)
	    {
	      smartVerify = SmartVerifyTCP (client, server, incomingPort);

	      if (smartVerify != TRUE)
		{
		  addr.s_addr = (u_int) ip.saddr;
		  SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF);
		  /* check if we should ignore this IP */
		  result = NeverBlock (target, gblIgnoreFile);

		  if (result == ERROR)
		    {
		      Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
		      result = FALSE;
		    }

		  if (result == FALSE)
		    {
		      /* check if they've visited before */
		      scanDetectTrigger = CheckStateEngine (target);

		      if (scanDetectTrigger == TRUE)
			{
			  hostPtr = gethostbyaddr ((char *) &ip.saddr,
					   sizeof (ip.saddr), AF_INET);
			  if (hostPtr != NULL)
			    snprintf (resolvedHost, DNSMAXBUF, "%s",
				      hostPtr->h_name);
			  else
			    snprintf (resolvedHost, DNSMAXBUF, "%s", target);

			  packetType = ReportPacketType (tcp);
			  Log ("attackalert: %s from host: %s/%s to TCP port: %d",
			     packetType, resolvedHost, target, incomingPort);
			  /* Report on options present */
			  if (ip.ihl > 5)
			    Log ("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).",
			       resolvedHost, target, incomingPort);

			  /* check if this target is already blocked */
			  if (IsBlocked (target, gblBlockedFile) == FALSE)
			    {
			      /* toast the prick */
			      if (DisposeTCP (target, incomingPort) != TRUE)
				Log ("attackalert: ERROR: Could not block host %s/%s!!",
				   resolvedHost, target);
			      else
				WriteBlocked (target, resolvedHost,
					      incomingPort, gblBlockedFile,
					      gblHistoryFile, "TCP");
			    }	/* end IsBlocked check */
			  else
			    Log ("attackalert: Host: %s/%s is already blocked Ignoring",
			       resolvedHost, target);
			}	/* end if(scanDetectTrigger) */
		    }		/* end if(never block) check */
		}		/* end if(smartVerify) */
	    }			/* end if(hotPort) */
	}			/* end if(TH_ACK) */
    }				/* end for( ; ; ) loop */
}
/* end PortSentryAdvancedStealthModeTCP */



/****************************************************************/
/* UDP "stealth" scan detection                                 */
/*                                                              */
/* This mode will read in a list of ports to monitor and will   */
/* then open a raw socket to look for packets matching the port. */
/*                                                              */
/****************************************************************/
int
PortSentryStealthModeUDP (void)
{
  struct sockaddr_in client, server;
  int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS],
    result = TRUE;
  int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE;
  int openSockfd = 0, incomingPort = 0;
  char *temp, target[IPMAXBUF], configToken[MAXBUF];
  char resolvedHost[DNSMAXBUF];
  struct hostent *hostPtr;
  struct in_addr addr;
  struct iphdr ip;
  struct udphdr udp;


  if ((ConfigTokenRetrieve ("UDP_PORTS", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read UDP_PORTS option from config file");
      return (ERROR);
    }

  /* break out the ports */
  if ((temp = (char *) strtok (configToken, ",")) != NULL)
    {
      ports[0] = atoi (temp);
      for (count = 1; count < MAXSOCKS; count++)
	{
	  if ((temp = (char *) strtok (NULL, ",")) != NULL)
	    ports[count] = atoi (temp);
	  else
	    break;
	}
      portCount = count;
    }
  else
    {
      Log ("adminalert: ERROR: No UDP ports supplied in config file. Aborting");
      return (ERROR);
    }

  /* ok, now check if they have a network daemon on the socket already, if they do */
  /* then skip that port because it will cause false alarms */
  for (count = 0; count < portCount; count++)
    {
      Log ("adminalert: Going into stealth listen mode on UDP port: %d\n",
	   ports[count]);
      if ((openSockfd = OpenUDPSocket ()) == ERROR)
	{
	  Log ("adminalert: ERROR: could not open UDP socket. Aborting.\n");
	  return (ERROR);
	}

      if (BindSocket (openSockfd, client, server, ports[count]) == ERROR)
	Log ("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n",
	   ports[count]);
      else
	{
	  gotBound = TRUE;
	  ports2[portCount2++] = ports[count];
	}
      close (openSockfd);
    }

  if (gotBound == FALSE)
    {
      Log ("adminalert: ERROR: All supplied UDP sockets are in use and will not be listened to. Shutting down.\n");
      return (ERROR);
    }

  if ((openSockfd = OpenRAWUDPSocket ()) == ERROR)
    {
      Log ("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n");
      return (ERROR);
    }

  Log ("adminalert: PortSentry is now active and listening.\n");

  /* main detection loop */
  for (;;)
    {
      if (PacketReadUDP (openSockfd, &ip, &udp) != TRUE)
	continue;

      incomingPort = ntohs (udp.dest);

      /* this iterates the list of ports looking for a match */
      for (count = 0; count < portCount; count++)
	{
	  if (incomingPort == ports2[count])
	    {
	      if (SmartVerifyUDP (client, server, incomingPort) == TRUE)
		break;

	      addr.s_addr = (u_int) ip.saddr;
	      SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF);
	      /* check if we should ignore this IP */
	      result = NeverBlock (target, gblIgnoreFile);

	      if (result == ERROR)
		{
		  Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
		  result = FALSE;
		}

	      if (result == FALSE)
		{
		  /* check if they've visited before */
		  scanDetectTrigger = CheckStateEngine (target);
		  if (scanDetectTrigger == TRUE)
		    {
		      hostPtr = gethostbyaddr ((char *) &ip.saddr,
				sizeof (ip.saddr), AF_INET);
		      if (hostPtr != NULL)
			snprintf (resolvedHost, DNSMAXBUF, "%s",
				  hostPtr->h_name);
		      else
			snprintf (resolvedHost, DNSMAXBUF, "%s", target);

		      Log ("attackalert: UDP scan from host: %s/%s to UDP port: %d",
			 resolvedHost, target, ports2[count]);
		      /* Report on options present */
		      if (ip.ihl > 5)
			Log ("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).",
			       resolvedHost, target, incomingPort);

		      /* check if this target is already blocked */
		      if (IsBlocked (target, gblBlockedFile) == FALSE)
			{
			  if (DisposeUDP (target, ports2[count]) != TRUE)
			    Log ("attackalert: ERROR: Could not block host %s/%s!!",
			       resolvedHost, target);
			  else
			    WriteBlocked (target, resolvedHost, ports2[count],
					  gblBlockedFile, gblHistoryFile, "UDP");
			}	/* end IsBlocked check */
		      else
			{
			  Log ("attackalert: Host: %s/%s is already blocked Ignoring",
			     resolvedHost, target);
			}
		    }		/* end if(scanDetectTrigger) */
		}		/* end if(never block) check */
	      break;		/* get out of for(count) loop above */
	    }			/* end if(incoming port) ==  protected port */
	}			/* end for( check for protected port loop ) loop */
    }				/* end for( ; ; ) loop */

}				/* end PortSentryStealthModeUDP */


/****************************************************************/
/* Advanced Stealth scan detection mode for UDP                 */
/*                                                              */
/* This mode will see what ports are listening below 1024       */
/* and will then monitor all the rest. This is very sensitive   */
/* and will react on any packet hitting any monitored port.     */
/* This is a very dangerous option and is for advanced users    */
/*                                                              */
/****************************************************************/
int
PortSentryAdvancedStealthModeUDP (void)
{
  struct sockaddr_in client, server;
  int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE;
  int openSockfd = 0, incomingPort = 0, smartVerify = FALSE;
  unsigned int advancedPorts = 1024;
  unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0;
  char target[IPMAXBUF], configToken[MAXBUF];
  char resolvedHost[DNSMAXBUF], *temp;
  struct hostent *hostPtr;
  struct in_addr addr;
  struct iphdr ip;
  struct udphdr udp;


  if ((ConfigTokenRetrieve ("ADVANCED_PORTS_UDP", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read ADVANCED_PORTS_UDP option from config file. Assuming 1024.");
      advancedPorts = 1024;
    }
  else
    advancedPorts = atoi (configToken);

  Log ("adminalert: Advanced mode will monitor first %d ports",
       advancedPorts);

  /* try to bind to all ports below 1024, any that are taken we exclude later */
  for (count = 0; count < advancedPorts; count++)
    {
      if ((openSockfd = OpenUDPSocket ()) == ERROR)
	{
	  Log ("adminalert: ERROR: could not open UDP socket. Aborting.\n");
	  return (ERROR);
	}
      if (BindSocket (openSockfd, client, server, count) == ERROR)
	inUsePorts[portCount++] = count;

      close (openSockfd);
    }

  if ((ConfigTokenRetrieve ("ADVANCED_EXCLUDE_UDP", configToken)) != FALSE)
    {
      /* break out the ports */
      if ((temp = (char *) strtok (configToken, ",")) != NULL)
	{
	  inUsePorts[portCount++] = atoi (temp);
	  Log ("adminalert: Advanced mode will manually exclude port: %d ",
	       inUsePorts[portCount - 1]);
	  for (count = 0; count < MAXSOCKS; count++)
	    {
	      if ((temp = (char *) strtok (NULL, ",")) != NULL)
		{
		  inUsePorts[portCount++] = atoi (temp);
		  Log ("adminalert: Advanced mode will manually exclude port: %d ",
		     inUsePorts[portCount - 1]);
		}
	      else
		break;
	    }
	}
    }
  else
    Log ("adminalert: Advanced mode will manually exclude no ports");


  for (count = 0; count < portCount; count++)
    Log
      ("adminalert: Advanced Stealth scan detection mode activated. Ignored UDP port: %d\n",
       inUsePorts[count]);

  if ((openSockfd = OpenRAWUDPSocket ()) == ERROR)
    {
      Log ("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n");
      return (ERROR);
    }

  Log ("adminalert: PortSentry is now active and listening.\n");

  /* main detection loop */
  for (;;)
    {
      if (PacketReadUDP (openSockfd, &ip, &udp) != TRUE)
	continue;

      incomingPort = ntohs (udp.dest);

      /* check if we should ignore this connection to this port */
      for (count = 0; count < portCount; count++)
	{
	  if ((incomingPort == inUsePorts[count])
	      || (incomingPort > advancedPorts))
	    {
	      hotPort = FALSE;
	      break;
	    }
	  else
	    hotPort = TRUE;
	}

      if (hotPort)
	{
	  smartVerify = SmartVerifyUDP (client, server, incomingPort);

	  if (smartVerify != TRUE)
	    {
	      /* copy the clients address into our buffer for nuking */
	      addr.s_addr = (u_int) ip.saddr;
	      SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF);
	      /* check if we should ignore this IP */
	      result = NeverBlock (target, gblIgnoreFile);

	      if (result == ERROR)
		{
		  Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
		  result = FALSE;
		}

	      if (result == FALSE)
		{
		  /* check if they've visited before */
		  scanDetectTrigger = CheckStateEngine (target);

		  if (scanDetectTrigger == TRUE)
		    {
		      hostPtr = gethostbyaddr ((char *) &ip.saddr,
				       sizeof (ip.saddr), AF_INET);
		      if (hostPtr != NULL)
			snprintf (resolvedHost, DNSMAXBUF, "%s",
				  hostPtr->h_name);
		      else
			snprintf (resolvedHost, DNSMAXBUF, "%s", target);

		      Log ("attackalert: UDP scan from host: %s/%s to UDP port: %d",
			 resolvedHost, target, incomingPort);
		      /* Report on options present */
		      if (ip.ihl > 5)
			Log ("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).",
			       resolvedHost, target, incomingPort);

		      /* check if this target is already blocked */
		      if (IsBlocked (target, gblBlockedFile) == FALSE)
			{
			  if (DisposeUDP (target, incomingPort) != TRUE)
			    Log ("attackalert: ERROR: Could not block host %s/%s!!",
			       resolvedHost, target);
			  else
			    WriteBlocked (target, resolvedHost, incomingPort,
					  gblBlockedFile, gblHistoryFile, "UDP");
			}	/* end IsBlocked check */
		      else
			Log ("attackalert: Host: %s/%s is already blocked Ignoring",
			   resolvedHost, target);
		    }		/* end if(scanDetectTrigger) */
		}		/* end if(never block) check */
	    }			/* end if (smartVerify) */
	}			/* end if(hotPort) */
    }				/* end for( ; ; ) loop */
}
/* end PortSentryAdvancedStealthModeUDP */

#endif




/****************************************************************/
/* Classic detection Mode                                       */
/*                                                              */
/* This mode will bind to a list of TCP sockets and wait for    */
/* connections to happen. Although the least prone to false     */
/* alarms, it also won't detect stealth scans                   */
/*                                                              */
/****************************************************************/
int
PortSentryModeTCP (void)
{

  struct sockaddr_in client, server;
  int length, portCount = 0, ports[MAXSOCKS];
  int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
  int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
  int selectResult = 0, gotBound = FALSE;
  char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF], configToken[MAXBUF];
  char resolvedHost[DNSMAXBUF];
  fd_set selectFds;
  struct hostent *hostPtr;

  if ((ConfigTokenRetrieve ("TCP_PORTS", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read TCP_PORTS option from config file");
      return (ERROR);
    }

  /* break out the ports */
  if ((temp = (char *) strtok (configToken, ",")) != NULL)
    {
      ports[0] = atoi (temp);
      for (count = 1; count < MAXSOCKS; count++)
	{
	  if ((temp = (char *) strtok (NULL, ",")) != NULL)
	    ports[count] = atoi (temp);
	  else
	    break;
	}
      portCount = count;
    }
  else
    {
      Log ("adminalert: ERROR: No TCP ports supplied in config file. Aborting");
      return (ERROR);
    }

  /* read in the banner if one is given */
  if ((ConfigTokenRetrieve ("PORT_BANNER", configToken)) == TRUE)
    {
      showBanner = TRUE;
      SafeStrncpy (bannerBuffer, configToken, MAXBUF);
    }


  /* setup select call */
  FD_ZERO (&selectFds);

  for (count = 0; count < portCount; count++)
    {
      Log ("adminalert: Going into listen mode on TCP port: %d\n",
	   ports[count]);
      if ((openSockfd[count] = OpenTCPSocket ()) == ERROR)
	{
	  Log ("adminalert: ERROR: could not open TCP socket. Aborting.\n");
	  return (ERROR);
	}

      if (BindSocket (openSockfd[count], client, server, ports[count]) ==
	  ERROR)
	{
	  Log ("adminalert: ERROR: could not bind TCP socket: %d. Attempting to continue\n",
	     ports[count]);
	}
      else			/* well we at least bound to one socket so we'll continue */
	gotBound = TRUE;
    }

  /* if we didn't bind to anything then abort */
  if (gotBound == FALSE)
    {
      Log ("adminalert: ERROR: could not bind ANY TCP sockets. Shutting down.\n");
      return (ERROR);
    }

  length = sizeof (client);

  Log ("adminalert: PortSentry is now active and listening.\n");

  /* main loop for multiplexing/resetting */
  for (;;)
    {
      /* set up select call */
      for (count = 0; count < portCount; count++)
	FD_SET (openSockfd[count], &selectFds);
      selectResult =
	select (MAXSOCKS, &selectFds, NULL, NULL, (struct timeval *) NULL);

      /* something blew up */
      if (selectResult < 0)
	{
	  Log ("adminalert: ERROR: select call failed. Shutting down.\n");
	  return (ERROR);
	}
      else if (selectResult == 0)
	{
#ifdef DEBUG
	  Log ("Select timeout");
#endif
	}

      /* select is reporting a waiting socket. Poll them all to find out which */
      else if (selectResult > 0)
	{
	  for (count = 0; count < portCount; count++)
	    {
	      if (FD_ISSET (openSockfd[count], &selectFds))
		{
		  incomingSockfd =
		    accept (openSockfd[count], (struct sockaddr *) &client,
			    &length);
		  if (incomingSockfd < 0)
		    {
		      Log ("attackalert: Possible stealth scan from unknown host to TCP port: %d (accept failed)",
			 ports[count]);
		      break;
		    }

		  /* copy the clients address into our buffer for nuking */
		  SafeStrncpy (target, (char *) inet_ntoa (client.sin_addr),
			       IPMAXBUF);
		  /* check if we should ignore this IP */
		  result = NeverBlock (target, gblIgnoreFile);

		  if (result == ERROR)
		    {
		      Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
		      result = FALSE;
		    }

		  if (result == FALSE)
		    {
		      /* check if they've visited before */
		      scanDetectTrigger = CheckStateEngine (target);

		      if (scanDetectTrigger == TRUE)
			{
			  /* show the banner if one was selected */
			  if (showBanner == TRUE)
			    write (incomingSockfd, bannerBuffer,
				   strlen (bannerBuffer));
			  /* we don't need the bonehead anymore */
			  close (incomingSockfd);
			  hostPtr = gethostbyaddr ((char *) &client.sin_addr.s_addr,
				    sizeof (client.sin_addr.s_addr), AF_INET);
			  if (hostPtr != NULL)
			    snprintf (resolvedHost, DNSMAXBUF, "%s",
				      hostPtr->h_name);
			  else
			    snprintf (resolvedHost, DNSMAXBUF, "%s", target);

			  Log ("attackalert: Connect from host: %s/%s to TCP port: %d",
			     resolvedHost, target, ports[count]);

			  /* check if this target is already blocked */
			  if (IsBlocked (target, gblBlockedFile) == FALSE)
			    {
			      if (DisposeTCP (target, ports[count]) != TRUE)
				Log ("attackalert: ERROR: Could not block host %s !!",
				   target);
			      else
				WriteBlocked (target, resolvedHost,
					      ports[count], gblBlockedFile,
					      gblHistoryFile, "TCP");
			    }
			  else
			    Log ("attackalert: Host: %s is already blocked. Ignoring",
			       target);
			}
		    }
		  close (incomingSockfd);
		  break;
		}		/* end if(FD_ISSET) */
	    }			/* end for() */
	}			/* end else (selectResult > 0) */
    }				/* end main for(; ; ) loop */

/* not reached */
  close (incomingSockfd);
}





/****************************************************************/
/* Classic detection Mode                                       */
/*                                                              */
/* This mode will bind to a list of UDP sockets and wait for    */
/* connections to happen. Stealth scanning really doesn't apply */
/* here.                                                        */
/*                                                              */
/****************************************************************/
int
PortSentryModeUDP (void)
{
  struct sockaddr_in client, server;
  int length, ports[MAXSOCKS], openSockfd[MAXSOCKS], result = TRUE;
  int count = 0, portCount = 0, selectResult = 0, scanDetectTrigger = 0;
  int gotBound = FALSE, showBanner = FALSE;
  char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF], configToken[MAXBUF];
  char buffer[MAXBUF];
  char resolvedHost[DNSMAXBUF];
  fd_set selectFds;
  struct hostent *hostPtr;

  if ((ConfigTokenRetrieve ("UDP_PORTS", configToken)) == FALSE)
    {
      Log ("adminalert: ERROR: Could not read UDP_PORTS option from config file");
      return (ERROR);
    }

  /* break out the ports */
  if ((temp = (char *) strtok (configToken, ",")) != NULL)
    {
      ports[0] = atoi (temp);
      for (count = 1; count < MAXSOCKS; count++)
	{
	  if ((temp = (char *) strtok (NULL, ",")) != NULL)
	    ports[count] = atoi (temp);
	  else
	    break;
	}
      portCount = count;
    }
  else
    {
      Log ("adminalert: ERROR: No UDP ports supplied in config file. Aborting");
      return (ERROR);
    }

  /* read in the banner if one is given */
  if ((ConfigTokenRetrieve ("PORT_BANNER", configToken)) == TRUE)
    {
      showBanner = TRUE;
      SafeStrncpy (bannerBuffer, configToken, MAXBUF);
    }

  /* setup select call */
  FD_ZERO (&selectFds);

  for (count = 0; count < portCount; count++)
    {
      Log ("adminalert: Going into listen mode on UDP port: %d\n",
	   ports[count]);
      if ((openSockfd[count] = OpenUDPSocket ()) == ERROR)
	{
	  Log ("adminalert: ERROR: could not open UDP socket. Aborting\n");
	  return (ERROR);
	}
      if (BindSocket (openSockfd[count], client, server, ports[count]) == ERROR)
	{
	  Log ("adminalert: ERROR: could not bind UDP socket: %d. Attempting to continue\n",
	     ports[count]);
	}
      else			/* well we at least bound to one socket so we'll continue */
	gotBound = TRUE;
    }

/* if we didn't bind to anything then abort */
  if (gotBound == FALSE)
    {
      Log ("adminalert: ERROR: could not bind ANY UDP sockets. Shutting down.\n");
      return (ERROR);
    }


  length = sizeof (client);
  Log ("adminalert: PortSentry is now active and listening.\n");

/* main loop for multiplexing/resetting */
  for (;;)
    {
      /* set up select call */
      for (count = 0; count < portCount; count++)
	FD_SET (openSockfd[count], &selectFds);
      /* setup the select multiplexing (blocking mode) */
      selectResult =
	select (MAXSOCKS, &selectFds, NULL, NULL, (struct timeval *) NULL);

      if (selectResult < 0)
	{
	  Log ("adminalert: ERROR: select call failed. Shutting down.\n");
	  return (ERROR);
	}
      else if (selectResult == 0)
	{
#ifdef DEBUG
	  Log ("Select timeout");
#endif
	}

      /* select is reporting a waiting socket. Poll them all to find out which */
      else if (selectResult > 0)
	{
	  for (count = 0; count < portCount; count++)
	    {
	      if (FD_ISSET (openSockfd[count], &selectFds))
		{
		  /* here just read in one byte from the UDP socket, that's all we need to */
		  /* know that this person is a jerk */
		  if (recvfrom (openSockfd[count], buffer, 1, 0,
		       (struct sockaddr *) &client, &length) < 0)
		    {
		      Log ("adminalert: ERROR: could not accept incoming socket for UDP port: %d\n",
			 ports[count]);
		      break;
		    }

		  /* copy the clients address into our buffer for nuking */
		  SafeStrncpy (target, (char *) inet_ntoa (client.sin_addr),
			       IPMAXBUF);
#ifdef DEBUG
		  Log ("debug: PortSentryModeUDP: accepted UDP connection from: %s\n",
		     target);
#endif
		  /* check if we should ignore this IP */
		  result = NeverBlock (target, gblIgnoreFile);
		  if (result == ERROR)
		    {
		      Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
		      result = FALSE;
		    }
		  if (result == FALSE)
		    {
		      /* check if they've visited before */
		      scanDetectTrigger = CheckStateEngine (target);
		      if (scanDetectTrigger == TRUE)
			{
			  /* show the banner if one was selected */
			  if (showBanner == TRUE)
			    sendto (openSockfd[count], bannerBuffer,
				    strlen (bannerBuffer), 0,
				    (struct sockaddr *) &client, length);

			  hostPtr = gethostbyaddr ((char *) &client.sin_addr.s_addr,
			  	    sizeof (client.sin_addr.s_addr), AF_INET);
			  if (hostPtr != NULL)
			    snprintf (resolvedHost, DNSMAXBUF, "%s",
				      hostPtr->h_name);
			  else
			    snprintf (resolvedHost, DNSMAXBUF, "%s", target);

			  Log
			    ("attackalert: Connect from host: %s/%s to UDP port: %d",
			     resolvedHost, target, ports[count]);
			  /* check if this target is already blocked */
			  if (IsBlocked (target, gblBlockedFile) == FALSE)
			    {
			      if (DisposeUDP (target, ports[count]) != TRUE)
				Log ("attackalert: ERROR: Could not block host %s !!",
				   target);
			      else
				WriteBlocked (target, resolvedHost,
					      ports[count], gblBlockedFile,
					      gblHistoryFile, "UDP");
			    }
			  else
			    Log ("attackalert: Host: %s is already blocked. Ignoring",
			       target);
			}
		    }
		  break;
		}		/* end if(FD_ISSET) */
	    }			/* end for() */
	}			/* end else (selectResult > 0) */
    }				/* end main for(; ; ) loop */

}				/* end UDP PortSentry */




/* kill the TCP connection depending on config option */
int
DisposeTCP (char *target, int port)
{
  int status = TRUE;

#ifdef DEBUG
  Log ("debug: DisposeTCP: disposing of host %s on port %d with option: %d",
       target, port, gblBlockTCP);
  Log ("debug: DisposeTCP: killRunCmd: %s", gblKillRunCmd);
  Log ("debug: DisposeTCP: killHostsDeny: %s", gblKillHostsDeny);
  Log ("debug: DisposeTCP: killRoute: %s  %d", gblKillRoute,
       strlen (gblKillRoute));
#endif
/* Should we ignore TCP from active response? */
  if (gblBlockTCP == 1)
    {
      /* run external command first, hosts.deny second, dead route last */
      if (strlen (gblKillRunCmd) > 0)
	if (KillRunCmd (target, port, gblKillRunCmd) != TRUE)
	  status = FALSE;
      if (strlen (gblKillHostsDeny) > 0)
	if (KillHostsDeny (target, port, gblKillHostsDeny) != TRUE)
	  status = FALSE;
      if (strlen (gblKillRoute) > 0)
	if (KillRoute (target, port, gblKillRoute) != TRUE)
	  status = FALSE;
    }
  else if (gblBlockTCP == 2)
    {
      /* run external command only */
      if (strlen (gblKillRunCmd) > 0)
	if (KillRunCmd (target, port, gblKillRunCmd) != TRUE)
	  status = FALSE;
    }
  else
    Log ("attackalert: Ignoring TCP response per configuration file setting.");

  return (status);
}


/* kill the UDP connection depending on config option */
int
DisposeUDP (char *target, int port)
{
  int status = TRUE;


#ifdef DEBUG
  Log ("debug: DisposeUDP: disposing of host %s on port %d with option: %d",
       target, port, gblBlockUDP);
#endif
/* Should we ignore TCP from active response? */
  if (gblBlockUDP == 1)
    {
      /* run external command first, hosts.deny second, dead route last */
      if (strlen (gblKillRunCmd) > 0)
	if (KillRunCmd (target, port, gblKillRunCmd) != TRUE)
	  status = FALSE;
      if (strlen (gblKillHostsDeny) > 0)
	if (KillHostsDeny (target, port, gblKillHostsDeny) != TRUE)
	  status = FALSE;
      if (strlen (gblKillRoute) > 0)
	if (KillRoute (target, port, gblKillRoute) != TRUE)
	  status = FALSE;
    }
  else if (gblBlockUDP == 2)
    {
      /* run external command only */
      if (strlen (gblKillRunCmd) > 0)
	if (KillRunCmd (target, port, gblKillRunCmd) != TRUE)
	  status = FALSE;
    }
  else
    Log ("attackalert: Ignoring UDP response per configuration file setting.");

  return (status);
}


/* duh */
void
Usage (void)
{
  /*
  printf ("Psionic PortSentry - Port Scan Detector.\n");
  printf ("Copyright 1997,98 Craig H. Rowland <crowland@psionic.com>\n");
  printf ("Licensing restrictions apply. Please see documentation\n");
  printf ("Version: %s\n\n", VERSION);
#ifdef SUPPORT_STEALTH
  printf ("usage: portsentry [-tcp -udp -stcp -atcp -sudp -audp]\n\n");
#else
  printf ("Stealth scan detection not supported on this platform\n");
  printf ("usage: portsentry [-tcp -udp]\n\n");
#endif
  printf ("*** PLEASE READ THE DOCS BEFORE USING *** \n\n");
  */
}



/* our cheesy state engine to monitor who has connected here before */
int
CheckStateEngine (char *target)
{
  int count = 0, scanDetectTrigger = TRUE;
  int gotOne = 0;

/* This is the rather dumb scan state engine. It maintains     */
/* an array of past hosts who triggered a connection on a port */
/* when a new host arrives it is compared against the array */
/* if it is found in the array it increments a state counter by */
/* one and checks the remainder of the array. It does this until */
/* the end is reached or the trigger value has been exceeded */
/* This would probably be better as a linked list/hash table, */
/* but for the number of hosts we are tracking this is just as good. */
/* I'll probably change this in the future */

  gotOne = 1;			/* our flag counter if we get a match */
  scanDetectTrigger = TRUE;	/* set to TRUE until set otherwise */

  if (gblConfigTriggerCount > 0)
    {
      for (count = 0; count < MAXSTATE; count++)
	{
	  /* if the array has the IP address then increment the gotOne counter and */
	  /* check the trigger value. If it is exceeded break out of the loop and */
	  /* set the detecttrigger to TRUE */
	  if (strcmp (gblScanDetectHost[count], target) == 0 )
	    {
	      /* compare the number of matches to the configured trigger value */
	      /* if we've exceeded we can stop this noise */
	      if (++gotOne >= gblConfigTriggerCount)
		{
		  scanDetectTrigger = TRUE;
#ifdef DEBUG
		  Log ("debug: CheckStateEngine: host: %s has exceeded trigger value: %d\n",
		     gblScanDetectHost[count], gblConfigTriggerCount);
#endif
		  break;
		}
	    }
	  else
	    scanDetectTrigger = FALSE;
	}

      /* now add the fresh meat into the state engine */
      /* if our array is still less than MAXSTATE large add it to the end */
      if (gblScanDetectCount < MAXSTATE)
	{
	  gblScanDetectCount++;
	  SafeStrncpy (gblScanDetectHost[gblScanDetectCount], target,
		       IPMAXBUF);
	}
      else
	{
	  /* otherwise tack it to the beginning and start overwriting older ones */
	  gblScanDetectCount = 0;
	  SafeStrncpy (gblScanDetectHost[gblScanDetectCount], target,
		       IPMAXBUF);
	}

#ifdef DEBUG
      for (count = 0; count < MAXSTATE; count++)
	Log ("debug: CheckStateEngine: state engine host: %s -> position: %d Detected: %d\n",
	   gblScanDetectHost[count], count, scanDetectTrigger);
#endif
      /* end catch to set state if gblConfigTriggerCount == 0 */
      if (gotOne >= gblConfigTriggerCount)
	scanDetectTrigger = TRUE;
    }


  if (gblConfigTriggerCount > MAXSTATE)
    {
      Log ("securityalert: WARNING: Trigger value %d is larger than state engine capacity of %d.\n",
	gblConfigTriggerCount);
      Log ("Adjust the value lower or recompile with a larger state engine value.\n",
	 MAXSTATE);
      Log ("securityalert: Blocking host anyway because of invalid trigger value");
      scanDetectTrigger = TRUE;
    }
  return (scanDetectTrigger);
}


#ifdef SUPPORT_STEALTH
/* This takes a tcp packet and reports what type of scan it is */
char *
ReportPacketType (struct tcphdr tcpPkt)
{
  static char packetDesc[MAXBUF];
  static char *packetDescPtr = packetDesc;

  if ((tcpPkt.syn == 0) && (tcpPkt.fin == 0) && (tcpPkt.ack == 0) \
      && (tcpPkt.psh == 0) && (tcpPkt.rst == 0) && (tcpPkt.urg == 0))
    snprintf (packetDesc, MAXBUF, "NULL scan");
  else if ((tcpPkt.fin == 1) && (tcpPkt.urg == 1) && (tcpPkt.psh == 1))
    snprintf (packetDesc, MAXBUF, "XMAS scan");
  else if ((tcpPkt.fin == 1) && (tcpPkt.syn != 1) && (tcpPkt.ack != 1) \
	   &&(tcpPkt.psh != 1) && (tcpPkt.rst != 1) && (tcpPkt.urg != 1))
    snprintf (packetDesc, MAXBUF, "FIN scan");
  else if ((tcpPkt.syn == 1) && (tcpPkt.fin != 1) && (tcpPkt.ack != 1) \
	   &&(tcpPkt.psh != 1) && (tcpPkt.rst != 1) && (tcpPkt.urg != 1))
    snprintf (packetDesc, MAXBUF, "SYN/Normal scan");
  else
    snprintf (packetDesc, MAXBUF,
	      "Unknown Type: Packet Flags: SYN: %d FIN: %d ACK: %d PSH: %d URG: %d RST: %d", 
	      tcpPkt.syn, tcpPkt.fin, tcpPkt.ack, tcpPkt.psh, tcpPkt.urg,
	      tcpPkt.rst);

  return (packetDescPtr);
}

int
SmartVerifyTCP (struct sockaddr_in client, struct sockaddr_in server,
		int port)
{

  int testSockfd;

/* Ok here is where I "Smart-Verify" the socket. If the port was previously */
/* unbound, but now appears to have someone there, then we will skip responding */
/* to this inbound packet. This essentially is a (lame) "stateful" inspection of the */
/* the connection */

  if ((testSockfd = OpenTCPSocket ()) == ERROR)
    {
      Log ("adminalert: ERROR: could not open TCP socket to smart-verify.\n");
      return (FALSE);
    }
  else
    {
      if (BindSocket (testSockfd, client, server, port) == ERROR)
	{
#ifdef DEBUG
	  Log ("debug: SmartVerify: Smart-Verify Port In Use: %d", port);
#endif
	  close (testSockfd);
	  return (TRUE);
	}
    }

  close (testSockfd);
  return (FALSE);
}

int
SmartVerifyUDP (struct sockaddr_in client, struct sockaddr_in server,
		int port)
{
  int testSockfd;


/* Ok here is where I "Smart-Verify" the socket. If the port was previously */
/* unbound, but now appears to have someone there, then we will skip responding */
/* to this inbound packet. This essentially is a "stateful" inspection of the */
/* the connection */

  if ((testSockfd = OpenUDPSocket ()) == ERROR)
    {
      Log ("adminalert: ERROR: could not open TCP socket to smart-verify.\n");
      return (FALSE);
    }
  else
    {
      if (BindSocket (testSockfd, client, server, port) == ERROR)
	{
#ifdef DEBUG
	  Log ("debug: SmartVerify: Smart-Verify Port In Use: %d", port);
#endif
	  close (testSockfd);
	  return (TRUE);
	}
    }

  close (testSockfd);
  return (FALSE);
}

#endif /* SUPPORT_STEALTH */

