/*
 *	        	SyNsCaN - by psychoid
 *
 *             you need to be root for running this :P
 *             parts of the code are derived from sscan
 *     but it was buggy. i removed the bugs and got it working
 *
 *  Modified 2001-2004 by John Anderson and released under the GPL
 */

#ifdef LINUX

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <linux/sockios.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <setjmp.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <time.h>
#else /* solaris or IRIX */
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <signal.h>
#endif

#include "synscan.h"

#define TH_FIN  0x01
#define TH_SYN  0x02
#define TH_RST  0x04
#define TH_PUSH 0x08
#define TH_ACK  0x10
#define TH_URG  0x20

#define DEBUG_NET 0
#define DEBUG_PARSE 0
#define DEBUG_CHECKOS 0

#define ALARM_PCAP 2
#define ALARM_BLOCKING_IO 3

char deviceglobal[100];
u_long myip;
unsigned long long total = 0;
unsigned long long totalip = 0;

#ifdef FAST_STATUS
unsigned long long xspeed = 0;
unsigned long long dipsrt = 0;
#endif

int iiport;
int eeport;			/* new - scanning to port */
int delay;
unsigned long long numports = 0;
int sleeptime = 0;
char outfilename[120];
int ltime, start, end;
unsigned long long dips = 0;
unsigned long long ips = 0;
unsigned long extdest;
unsigned int cport;

int gpass = 0;

#ifndef LINUX
int rawsock;
unsigned long send_seq, ack_seq, srcport;

unsigned long int
lookup (char *hostname)
{
  struct hostent *name;
  unsigned long int address;

  if ((address = inet_addr (hostname)) != -1)
    return address;
  if ((name = gethostbyname (hostname)) == NULL)
    return -1;

  memcpy (&address, name->h_addr, name->h_length);
  return address;
}


#endif


#ifdef LINUX
struct tcphdr
{
  u_int16_t th_sport;		/* source port */
  u_int16_t th_dport;		/* destination port */
  u_int32_t th_seq;		/* sequence number */
  u_int32_t th_ack;		/* acknowledgement number */
  u_int8_t th_x2:4;		/* (unused) */
  u_int8_t th_off:4;		/* data offset */
  u_int8_t th_flags;
  u_int16_t th_win;		/* window */
  u_int16_t th_sum;		/* checksum */
  u_int16_t th_urp;		/* urgent pointer */
};

struct ifmap
{
  unsigned long mem_start;
  unsigned long mem_end;
  unsigned short base_addr;
  unsigned char irq;
  unsigned char dma;
  unsigned char port;
};

struct ifreq
{
#define IFHWADDRLEN	 6
#define IFNAMSIZ	 16
  union
  {
    char ifrn_name[IFNAMSIZ];
  } ifr_ifrn;

  union
  {
    struct sockaddr ifru_addr;
    struct sockaddr ifru_dstaddr;
    struct sockaddr ifru_broadaddr;
    struct sockaddr ifru_netmask;
    struct sockaddr ifru_hwaddr;
    short ifru_flags;
    int ifru_metric;
    int ifru_mtu;
    struct ifmap ifru_map;
    char ifru_slave[IFNAMSIZ];
    caddr_t ifru_data;
  } ifr_ifru;
};

#define ifr_name 	ifr_ifrn.ifrn_name
#define ifr_hwaddr 	ifr_ifru.ifru_hwaddr
#define ifr_addr	ifr_ifru.ifru_addr
#define ifr_dstaddr	ifr_ifru.ifru_dstaddr
#define ifr_broadaddr	ifr_ifru.ifru_broadaddr
#define ifr_netmask	ifr_ifru.ifru_netmask
#define ifr_flags	ifr_ifru.ifru_flags
#define ifr_metric	ifr_ifru.ifru_metric
#define ifr_mtu		ifr_ifru.ifru_mtu
#define ifr_map		ifr_ifru.ifru_map
#define ifr_slave	ifr_ifru.ifru_slave
#define ifr_data	ifr_ifru.ifru_data
#define ifc_buf ifc_ifcu.ifcu_buf
#define ifc_req ifc_ifcu.ifcu_req

struct ifconf
{
  int ifc_len;
  union
  {
    caddr_t ifcu_buf;
    struct ifreq *ifcu_req;

  } ifc_ifcu;
};
#endif

/*
int
checkpass ()
{
  char in[64];
  char good[64];
  bzero (good, sizeof (good));
  strcat (good, p1);
  strcat (good, p2);
  strcat (good, p3);
  strcat (good, p4);

  if (fgets (in, sizeof (in), stdin) == NULL)
    {
      kill (getpid (), 11);
      exit (0);
    }
  strcat (good, p5);
  strcat (good, p6);
  strcat (good, p7);
  strcat (good, p8);

  if (strncmp (in, good, 8) == 0)
    {
      return 1;
    }
  else
    {
      kill (getpid (), 11);
      exit (0);
    }

}
*/
#ifdef LINUX
u_long
getlocalip (void)
{
  int f, count, traverse;
  struct sockaddr_in *blob;
  char buf[512];
  struct ifreq *i;
  struct ifconf ic;
  f = socket (AF_INET, SOCK_STREAM, 0);
  ic.ifc_len = 512;
  ic.ifc_buf = buf;
  ioctl (f, SIOCGIFCONF, (char *) &ic);
  i = ic.ifc_req;
  count = ic.ifc_len / sizeof (struct ifreq);
  for (traverse = 0; traverse < count; traverse++)
    {
      ioctl (f, SIOCGIFADDR, (char *) &i);
      blob = (struct sockaddr_in *) &i->ifr_ifru.ifru_addr;
      if (!strcmp (i->ifr_name, deviceglobal))
	return blob->sin_addr.s_addr;
      i++;
    }
  return 0x0;
}
#else /* solaris requires its own getlocalip */
u_long
getlocalip (void)
{
  FILE *localip;
  char temps[128];
  unsigned long locip;

  localip = fopen ("localip", "r");
  if (localip == NULL)
    {
      printf ("Make the localip file\n");
      exit (0);
    }
  fgets (temps, sizeof (temps), localip);
  locip = lookup (temps);
  return locip;
}
#endif

#ifdef LINUX			/* solaris/irix dont use this */
unsigned short
in_cksum (unsigned short *ptr, int nbytes)
{

  register long sum;
  u_short oddbyte;
  register u_short answer;

  sum = 0;
  while (nbytes > 1)
    {
      sum += *ptr++;
      nbytes -= 2;
    }

  if (nbytes == 1)
    {
      oddbyte = 0;
      *((u_char *) & oddbyte) = *(u_char *) ptr;
      sum += oddbyte;
    }

  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;
  return (answer);
}
#endif

#ifdef LINUX
void
send_raw_tcp (u_long srcip, u_long dstip, u_short th_sport,
	      u_short th_dport, u_short th_flags, u_short th_win)
{

  int sockfd, fromlen;
  int off = 1, i = 0;
  struct pseudo
  {
    u_long saddr;
    u_long daddr;
    u_char zero;
    u_char protocol;
    u_short length;
  };
  int retval;
  struct sockaddr_in sheep;

  u_char packet[sizeof (struct iphdr) + sizeof (struct pseudo) +
		sizeof (struct tcphdr)];
  struct iphdr *ip = (struct iphdr *) packet;
  struct pseudo *pseudo = (struct pseudo *) (packet + sizeof (struct iphdr));
  struct tcphdr *tcp =
    (struct tcphdr *) (packet + sizeof (struct iphdr) +
		       sizeof (struct pseudo));

  bzero (packet, sizeof (packet));
  bzero (&sheep, sizeof (sheep));

  i = random () % 65000;
  pseudo->saddr = srcip;
  pseudo->daddr = dstip;
  pseudo->zero = 0;
  pseudo->protocol = IPPROTO_TCP;
  pseudo->length = htons (sizeof (struct tcphdr));	/* is this right? */
  ip->version = 4;
  ip->ihl = 5;
  ip->id = htons (i);
  ip->saddr = srcip;

  ip->daddr = dstip;
  ip->protocol = IPPROTO_TCP;

  ip->ttl = 255;
  ip->frag_off = 0;
  ip->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr);
  tcp->th_sport = htons (th_sport);
  tcp->th_dport = htons (th_dport);
  tcp->th_seq = htonl (rand ());
  tcp->th_ack = htonl (rand ());
  tcp->th_off = 5;
  tcp->th_flags = th_flags;
  tcp->th_win = htons (th_win);
  tcp->th_urp = 0;
  tcp->th_sum =
    in_cksum ((u_short *) pseudo,
	      sizeof (struct pseudo) + sizeof (struct tcphdr));

  sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
  retval = setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, &off, sizeof (off));
  memcpy (pseudo, tcp, 20);
  sheep.sin_family = AF_INET;
  sheep.sin_addr.s_addr = dstip;
  fromlen = sizeof (sheep);
  sendto (sockfd, packet, sizeof (struct iphdr) + sizeof (struct tcphdr),
	  0, (struct sockaddr *) &sheep, sizeof (sheep));
  close (sockfd);
}
#else

unsigned short
ip_sum (addr, len)
     u_short *addr;
     int len;
{
  register int nleft = len;
  register u_short *w = addr;
  register int sum = 0;
  u_short answer = 0;

  while (nleft > 1)
    {
      sum += *w++;
      nleft -= 2;
    }
  if (nleft == 1)
    {
      *(u_char *) (&answer) = *(u_char *) w;
      sum += answer;
    }
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;
  return (answer);
}

void
send_tcp_segment (struct ip *ih, struct tcphdr *th, char *data, int dlen)
{
  char buf[65536];
  struct
  {				/* rfc 793 tcp pseudo-header */
    unsigned long saddr, daddr;
    char mbz;
    char ptcl;
    unsigned short tcpl;
  } ph;
  struct sockaddr_in sin;

  ph.saddr = ih->ip_src.s_addr;
  ph.daddr = ih->ip_dst.s_addr;
  ph.mbz = 0;
  ph.ptcl = IPPROTO_TCP;
  ph.tcpl = htons (sizeof (*th) + dlen);

  memcpy (buf, &ph, sizeof (ph));
  memcpy (buf + sizeof (ph), th, sizeof (*th));
  memcpy (buf + sizeof (ph) + sizeof (*th), data, dlen);
  memset (buf + sizeof (ph) + sizeof (*th) + dlen, 0, 4);
  th->th_sum = ip_sum (buf, (sizeof (ph) + sizeof (*th) + dlen + 1) & ~1);

  memcpy (buf, ih, 4 * ih->ip_hl);
  memcpy (buf + 4 * ih->ip_hl, th, sizeof (*th));
  memcpy (buf + 4 * ih->ip_hl + sizeof (*th), data, dlen);
  memset (buf + 4 * ih->ip_hl + sizeof (*th) + dlen, 0, 4);

  ih->ip_sum = ip_sum (buf, (4 * ih->ip_hl + sizeof (*th) + dlen + 1) & ~1);
  memcpy (buf, ih, 4 * ih->ip_hl);
  sin.sin_family = AF_INET;
  sin.sin_port = th->th_dport;
  sin.sin_addr.s_addr = ih->ip_dst.s_addr;
  if (gpass = 1)
    {
      sendto (rawsock, buf, 4 * ih->ip_hl + sizeof (*th) + dlen, 0,
	      (struct sockaddr *) &sin, sizeof (sin));
    }
}

void
send_raw_tcp (u_long srcip, u_long dstip, u_short th_sport,
	      u_short th_dport, u_short th_flags, u_short th_win)
{
  int i, s;
  unsigned long my_ip;
  struct ip ih;
  struct tcphdr th;
  struct sockaddr_in sin;
  int sinsize;
  unsigned short myport = 6969;
  char buf[1024], b[1024];

  ih.ip_v = 4;
  ih.ip_hl = 5;
  ih.ip_tos = 0;		/* XXX is this normal? */
  ih.ip_len = sizeof (ih) + sizeof (th);
  ih.ip_id = htons (random ());
  ih.ip_off = 0;
  ih.ip_ttl = 30;
  ih.ip_p = IPPROTO_TCP;
  ih.ip_sum = 0;
  ih.ip_src.s_addr = srcip;
  ih.ip_dst.s_addr = dstip;

  srcport = th_sport;
  th.th_sport = htons (srcport);
  th.th_dport = th_dport;
  th.th_seq = htonl (rand ());
  th.th_off = sizeof (th) / 4;
  th.th_ack = (random ());
  th.th_flags = th_flags;

  th.th_win = random () % 65000;;
  th.th_sum = 0;
  th.th_urp = (random ());

  send_tcp_segment (&ih, &th, "", 0);
  send_seq = th.th_seq + 1 + strlen (buf);

}
#endif


int
syn_hostportscan (u_long dest)
{
  int cnt;
  unsigned long long onepc = total / 100;
  char bufx[200];
  struct in_addr ami;

  cnt = 0;
  sleeptime++;
  if (sleeptime == delay)
    {
      sleeptime = 0;
      usleep (1);
    }

  send_raw_tcp (myip, dest, cport, cport, TH_SYN, 40);
  ips++;
#ifdef FAST_STATUS
      ami.s_addr = dest;

      snprintf (bufx, sizeof (bufx),
	    "| %15s | %5d | %4d | %5llu | %3llu | %13llu | %13llu |\r",
		inet_ntoa (ami), cport, ltime - start, xspeed,
		dipsrt / onepc, dipsrt, total - dipsrt);
      fprintf (stdout, "%s", bufx);
      fflush (stdout);
      dipsrt=ips;

  if (time (NULL) > ltime)
    {                         /* a second has passed */
        ltime = time (NULL);
        xspeed=ips - dips;
        dips = ips;
        
    }
#else
  if (time (NULL) > ltime)
    {				/* a second has passed */
      ami.s_addr = dest;
      ltime = time (NULL);

      snprintf (bufx, sizeof (bufx),
	    "| %15s | %5d | %4d | %5llu | %3llu | %13llu | %13llu |\r",
		inet_ntoa (ami), cport, ltime - start, ips - dips,
		dips / onepc, dips, total - dips);
      fprintf (stdout, "%s", bufx);
      fflush (stdout);
      dips = ips;
    }
#endif

/* for the final status line, nasty kludge.. shame on me */
  extdest = dest;
  return 0;
}

void
calc_bufxs (u_long dest)
{
  char bufx[200], tot;
  struct in_addr ami;


  ami.s_addr = dest;
  ltime = time (NULL);
  tot = ltime - start;
  snprintf (bufx, sizeof (bufx),
	    "| %15s | %5d | %4d | %5llu | %3d | %13llu | %13d |\r",
	    inet_ntoa (ami), cport - 1, ltime - start, ips / tot, 100, total,
	    0);
  fprintf (stdout, "%s", bufx);
  fflush (stdout);
}

int
syn_portscan (char *hv)
{
  int i, j, k;
  char *pt;
  char mehost[200];
  char ehost[400];
  FILE *inp;

  if ((inp = fopen (hv, "r")) != NULL)
    {
      total = totalip * numports;
      while (fgets (ehost, sizeof (ehost), inp))
	{
	  sscanf (ehost, "%s\n", ehost);
	  syn_hostportscan (inet_addr (ehost));
	}
      fclose (inp);
      goto sen;
    }
  pt = strchr (hv, '.');
  if (pt == NULL)
    {				/* ANET */
      total = 16777216 * numports;
      for (k = 0; k <= 255; k++)
	{
	  for (j = 0; j <= 255; j++)
	    {
	      for (i = 0; i <= 255; i++)
		{
		  snprintf (mehost, sizeof (mehost), "%s.%u.%u.%u", hv, i, j,
			    k);
		  syn_hostportscan (inet_addr (mehost));
		}
	    }
	}
    }
  else
    {
      pt++;
      pt = strchr (pt, '.');
      if (pt == NULL)
	{			/* BNET */
	  total = 65536 * numports;
	  for (j = 0; j <= 255; j++)
	    {
	      for (i = 0; i <= 255; i++)
		{
		  snprintf (mehost, sizeof (mehost), "%s.%u.%u", hv, j, i);
		  syn_hostportscan (inet_addr (mehost));
		}
	    }
	}
      else
	{			/* CNET */
	  total = 256 * numports;
	  pt++;
	  pt = strchr (pt, '.');
	  if (pt != NULL)
	    {
/* single ip */
	      total = 65535;
	      syn_hostportscan (inet_addr (hv));
	    }
	  else
	    {
	      for (i = 0; i <= 255; i++)
		{
		  snprintf (mehost, sizeof (mehost), "%s.%u", hv, i);
		  syn_hostportscan (inet_addr (mehost));
		}
	    }
	}
    }
sen:
  return 0x0;
}


int
main (int argc, char **argv)
{
  FILE *logo;
  char ports[600], ehost[400];
  struct sigaction alarmaction;
  int tot;
  char *ept;
  char *upt = NULL;
  FILE *inp;

/* randomize here */
  srand (time (NULL));
  bzero (&alarmaction, sizeof (struct sigaction));
  ips = 0;
  if ((logo = fopen ("synscan.asc", "r")) != NULL)
    {
/* use ports.. no point allocating extra vars since the logo is already displayed before the ports are set */
      while (!feof (logo))
	{
	  fgets (ports, sizeof (ports) - 1, logo);
	  if (feof (logo))
	    break;
	  printf ("%s", ports);
	}
      fclose (logo);
    }
  fprintf (stdout,
	   "\nSynScan %s by MadMax, based on code by psychoid/tCl\n",
	   SYNSCAN_VERSION);
  fprintf (stdout, "%s\n\n", SPEC);
  if (argc < 4)
    {
      fprintf (stdout,
	       "Usage: %s 1[.2[.3[.4]]]|infile device(=ppp0/eth0) speed [port[,port,port-port...]]\n",
	       argv[0]);
      fprintf (stdout,
	       "speed is the number of syns sent before theres a 1 ms delay\n");
      fprintf (stdout,
	       "1 = dialup (3kb/sec), higher values are dependant on connection and cpu speed\n");
      fprintf (stdout,
	       "ports may be port,port,port also, port-port,port-port\n");
      fprintf (stdout,
	       "port,port,port scans subnets at the ports given, port-port scans each host from port to port\n");
      fprintf (stdout, "infile must be List of numerical ips\n");
      fprintf (stdout,
	       "Results will be logged by the synscand or sslog process\n");
      exit (0x0);
    }
  if (argv[4] != NULL)
    {
      snprintf (ports, sizeof (ports), "%s", argv[4]);
      upt = ports;
    }
  else
    {
      iiport = 111;
      *ports = 0;
    }
  delay = atoi (argv[3]);

  snprintf (deviceglobal, sizeof (deviceglobal), "%s", argv[2]);
  myip = getlocalip ();
  if (myip == 0)
    {
      fprintf (stdout, "No IP found for device %s\n", deviceglobal);
    }
  fprintf (stdout, "My IP : %s\n", nlookup (myip));

  ept = NULL;
nextport:
  if (*ports != 0)
    {
      ept = strchr (upt, '-');
      if (ept == NULL)
	ept = strchr (upt, ',');
      if (ept != NULL)
	{
	  iiport = atoi (upt);
	  eeport = iiport;
	  if (*ept == '-')
	    {
	      *ept = 0;
	      ept++;
	      upt = ept;
	      ept = strchr (upt, ',');
	      if (ept != NULL)
		{
		  *ept++ = 0;
		  eeport = atoi (upt);
		  upt = ept;
		}
	      else
		{
		  eeport = atoi (upt);
		  *ports = 0;
		}
	    }
	  else
	    {
	      *ept = 0;
	    }
	  ept++;
	  upt = ept;
	}
      else
	{
	  iiport = atoi (upt);
	  eeport = iiport;
	  *ports = 0;
	}
    }
  start = time (NULL);
  ltime = time (NULL);

#ifndef LINUX
  rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
#endif
/* calculate the number of ports */
  numports = eeport - iiport + 1;
  cport = 0;

  if ((inp = fopen (argv[1], "r")) != NULL)
    {
/* loop 1, determine number of lines */
      while (fgets (ehost, sizeof (ehost), inp))
	{
	  totalip++;
	}

      fflush (stdout);
    }

  if (inp != NULL)
    {
      fprintf (stdout, "Scanning from iplist \"%s\" %llu addresses found\n",
	       argv[1], totalip);
    }
  else
    {
      fprintf (stdout, "Scanning range %s\n", argv[1]);
    }

  fprintf (stdout, "Scanning %llu ports\n", numports);
  fprintf (stdout, "scanning ports %d to %d..\n\n", iiport, eeport);


  fprintf (stdout, "Scanning:\n");
  fprintf (stdout,
	   "--------------------------------------------------------------------------------\n");
  fprintf (stdout,
	   "| IP              | PORT  | TIME | SPEED |  %%  | DONE          | REMAINING     |\n");

  for (cport = iiport; cport <= eeport; cport++)
    {
      syn_portscan (argv[1]);
    }

  calc_bufxs (extdest);
  fprintf (stdout,
	   "\n--------------------------------------------------------------------------------\n");
  fprintf (stdout, "\nDone.\n");
  end = time (NULL);

  tot = end - start;

  if (tot > 0)
    {
      fprintf (stdout,
	       "%llu ips/ports scanned in %d seconds, avg %llu p/sec\n", ips,
	       tot, ips / tot);
    }
  else
    {
      fprintf (stdout, "%llu ips/ports scanned in <1 second\n", ips);
    }

  wait (0x0);
  if (*ports != 0)
    goto nextport;
  exit (0x0);
  if (*ports != 0)
    goto nextport;
  exit (0x0);
}
