#include <includes.h>
#include <nessusraw.h>


#undef SHOW_RETRIES
#undef SHOW_RTT_REMOVAL

#define NUM_RETRIES 2

/*----------------------------------------------------------------------------*/
struct pseudohdr
{
        struct in_addr saddr;
        struct in_addr daddr;
        u_char zero;
        u_char protocol;
        u_short length;
        struct tcphdr tcpheader;
};


static int in_cksum(p,n)
u_short *p; int n;
{
  register u_short answer;
  register unsigned long sum = 0;
  u_short odd_byte = 0;

  while( n > 1 )  { sum += *p++; n -= 2; }

  /* mop up an odd byte, if necessary */
  if( n == 1 ) {
      *(u_char *)(&odd_byte) = *(u_char *)p;
      sum += odd_byte;
  }

  sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
  sum += (sum >> 16);			/* add carry */
  answer = (int)~sum;			/* ones-complement, truncate*/
  return (answer);
}



unsigned long maketime()
{
 struct timeval tv;
 unsigned long ret;
 
 gettimeofday(&tv, NULL);
 
 
  
 ret = ((tv.tv_sec & 0x0000000F) << 28) | (((tv.tv_usec) & 0xFFFFFFF0) >> 4);

 
 return htonl(ret);
}


struct timeval timeval(unsigned long val)
{
 struct timeval ret;
 
 val = ntohl(val);
 
 ret.tv_sec = ((val & 0xFF000000) >> 28);
 ret.tv_usec = ((val & 0x00FFFFFF) << 4);
 
 return ret;
}
 






unsigned long compute_rtt(unsigned long then)
{
 unsigned long now = maketime();
 unsigned long res;
#if DEBUG > 4
 printf("now = 0x%.8x, then = 0x%.8x\n", ntohl(now), ntohl(then));
#endif 
 res = (unsigned long)ntohl(now) - (unsigned long)ntohl(then);

 return htonl( res + 5000 );
}


int packetdead(unsigned long then, unsigned long rtt)
{
 unsigned long now = maketime();
 
 then = ntohl(then);
 now  = ntohl(now);
 rtt  = ntohl(rtt);
 
 
 
 if((now - then) > rtt)
  {
  return 1;
  }
 else
  {
  return 0;
  }
}
 
int rawsocket()
{
 int soc = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
 int opt = 1;
 
 if(soc < 0)
 {
  perror("socket ");
  return -1;
 }
 
#ifdef IP_HDRINCL
 if(setsockopt(soc, IPPROTO_IP, IP_HDRINCL, (char*)&opt, sizeof(opt)) < 0)
  {
   perror("setsockopt ");
   close(soc);
   return -1;
  }
#endif

 return soc;
}


int openbpf(struct in_addr dst, struct in_addr * src, int magic)
{
 char * iface;
 char filter[255];
 int bpf;
 
 iface = routethrough(&dst, src);
 snprintf(filter, sizeof(filter), "tcp and src host %s and dst port %d", inet_ntoa(dst), magic);
 bpf = bpf_open_live(iface, filter);
 return bpf;
}

/*----------------------------------------------------------------------------*/

struct list {
	short dport;
	unsigned long when;
	int retries;
	struct list * prev;
	struct list * next;
};

struct list * get_packet(struct list * l, short dport)
{
 while ( l != NULL )
 {
  if( l->dport == dport )
   return l;
  else
   l = l->next;
 }
 return NULL;
}


struct list * add_packet(struct list * l, short dport, unsigned long ack)
{
 struct list * ret;
 
 ret = get_packet(l, dport);
 if(ret != NULL)
 {
#ifdef SHOW_RETRIES
   printf("RETRIES FOR %d = %d\n",dport,  ret->retries);
#endif
   ret->retries ++;
   ret->when = ack;
   return l;
 }
  
  
 ret = emalloc(sizeof(struct list));
 

 ret->next = l;
 ret->prev = NULL;
 if(ret->next != NULL)
  ret->next->prev = ret;
 
 ret->dport = dport;
 ret->when  = ack;
 ret->retries = 0;
 return ret;
}



struct list * rm_packet(struct list * l, short dport)
{
 struct list * ret = l;
 struct list * p = get_packet(l, dport);
 
 if(p == NULL)
 { 
#if DEBUG > 1 
  fprintf(stderr, "Odd - no entry for %d - RTT too low ?!\n", dport);
#endif 
  return l;
 }
 
 if(p->next != NULL)
  p->next->prev = p->prev;
 
 if(p->prev != NULL)
  p->prev->next = p->next;
 else
  ret = p->next;
 
 efree(&p);
 return ret;
}

struct list * rm_dead_packets(struct list * l, unsigned long rtt, int * retry)
{
 struct list * ret = l;
 struct list * p = l;

 
 *retry = 0;
 while ( p != NULL )
 {
  struct list * next = p->next;
  if(packetdead(p->when, rtt))
   {
    if(p->retries < NUM_RETRIES)
     {
#ifdef SHOW_RETRIES
      printf("Will retry port %d\n", p->dport);
#endif      
      *retry = p->dport;
     }
    else
    {
#ifdef SHOW_RTT_REMOVAL
    printf("Removing port %d (RTT elapsed)\n", p->dport);
#endif    
    if(p->next != NULL)
     p->next->prev = p->prev;
    
    if(p->prev != NULL)
     p->prev->next = p->next;
    else
     ret = p->next;
    efree(&p);  
    }
   }
  p = next;
 }
 return ret;
}

	


/*-----------------------------------------------------------------------------*/



unsigned long extractack(char * pkt)
{
 struct tcphdr * tcp = (struct tcphdr*)(&(pkt[sizeof(struct ip)]));
 unsigned long ret;
 
 ret = htonl(ntohl(tcp->th_ack) - 1);
 return ret;
}


unsigned short extractsport(char * pkt)
{
 struct tcphdr * tcp = (struct tcphdr*)(&(pkt[sizeof(struct ip)]));
 unsigned long ret;
 
 return ntohs(tcp->th_sport);
}

int issynack(char * pkt)
{
 struct tcphdr * tcp = (struct tcphdr*)(&(pkt[sizeof(struct ip)]));
 unsigned long ret;
 
 return tcp->th_flags == (TH_SYN|TH_ACK);
}

char * mktcp(struct in_addr src, int sport, struct in_addr dst, int dport, unsigned long th_ack)
{
 static char pkt[sizeof(struct ip) + sizeof(struct tcphdr)];
 struct ip * ip;
 struct tcphdr * tcp;
 struct pseudohdr pseudohdr;
 char tcpsumdata[sizeof(pseudohdr)];

 ip = (struct ip*)(&pkt);
 ip->ip_hl = 5;
 ip->ip_v  = 4;
 ip->ip_tos = 0;
 ip->ip_len = FIX(sizeof(struct ip) + sizeof(struct tcphdr));
 ip->ip_id = rand();
 ip->ip_off = 0;
 ip->ip_ttl = 64;
 ip->ip_p   = IPPROTO_TCP;
 ip->ip_sum = 0;
 ip->ip_src.s_addr = src.s_addr;
 ip->ip_dst.s_addr = dst.s_addr;
 ip->ip_sum = in_cksum((u_short*)pkt, sizeof(struct ip));

 tcp = (struct tcphdr*)(&(pkt[sizeof(struct ip)]));
 tcp->th_sport = htons(sport);
 tcp->th_dport = htons(dport);
 tcp->th_seq = th_ack;
 tcp->th_ack = 0;
 tcp->th_x2  = 0;
 tcp->th_off = 5;
 tcp->th_flags = TH_SYN;
 tcp->th_win   = 4096;
 tcp->th_sum   = 0;
 tcp->th_urp   = 0;
 
 bzero(&pseudohdr, 12);
 pseudohdr.saddr.s_addr = src.s_addr;
 pseudohdr.daddr.s_addr = dst.s_addr;
 pseudohdr.protocol = IPPROTO_TCP;
 pseudohdr.length = htons(sizeof(struct tcphdr));
 bcopy((char*)tcp, (char*)&pseudohdr.tcpheader, sizeof(struct tcphdr));
 bcopy(&pseudohdr, tcpsumdata, sizeof(struct pseudohdr));
 tcp->th_sum = in_cksum((unsigned short*)tcpsumdata, 12 + sizeof(struct tcphdr));
 
 return pkt;
}
/*--------------------------------------------------------------------*/

int find_rtt(struct in_addr dst, unsigned long * rtt)
{
 int soc;
 unsigned short ports[] = {21, 22, 34, 25, 53, 79, 80, 110, 113, 135, 139, 143, 264, 389, 443, 993, 1454, 1723, 3389,8080, 0};
 int i;
 int bpf;
 int magic = 4441 + (rand() % 1200);
 struct sockaddr_in soca;
 int len;
 int skip;
 struct in_addr src;
 
 
 soc = rawsocket();
 if(soc < 0)
  return -1;

 bpf = openbpf(dst, &src, magic);
 
 if(bpf < 0)
 {
  close(soc);
  return -1;
 }

 skip =  get_datalink_size(bpf_datalink(bpf));
 bzero(&soca, sizeof(soca));
 soca.sin_family = AF_INET;
 soca.sin_addr   = dst;

 for(i = 0; ports[i] != 0; i++) 
 {
 char * res;
 unsigned long ack = maketime();
 char * pkt = mktcp(src, magic, dst, ports[i], ack);
 int e;
 struct timeval tv = {2, 0};
 int err = 0;
 
#if DEBUG > 1
 printf("send to port %d\n", ports[i]);
#endif 

 e =  sendto(soc, pkt, sizeof(struct ip) + sizeof(struct tcphdr), 0, (struct sockaddr*)&soca, sizeof(soca));
 if(e < 0)
 {
  perror("sendto ");
  close(soc);
  return -1;
 }
 
 
 res = (char*)bpf_next_tv(bpf, &len, &tv);
 if(res != NULL)
 {
  int value = extractack( res + skip );
  unsigned short p = extractsport(res + skip);
  int j;
  unsigned long max, max_max;
  
  
  max_max = compute_rtt(value);
  max = 0;
  
#if DEBUG > 1
  printf("Found port %d\n", p);
#endif
  for(j = 1; j < 10 ;j++)
  {
   ack = maketime();
   pkt = mktcp(src, magic, dst, p, ack);
   e = sendto(soc, pkt, sizeof(struct ip) + sizeof(struct tcphdr), 0, (struct sockaddr*)&soca, sizeof(soca));
   res = (char*)bpf_next_tv(bpf, &len, &tv);
   if(res != NULL)
   {
    unsigned long val = compute_rtt(extractack( res + skip ));
    if(val && val > max_max)
    { 
     if(max != 0)
     {
      if(val < max * 2)
      {
      max = max_max;
      max_max = val;
      }
    }
    else
    {  
     max = max_max;
     max_max = val;
    }
   }
   }
   else {
#if DEBUG > 1
   	printf("No reply ?!\n");
#endif	
	j --;
	err ++;
	if(err > 100){
		*rtt = 0;
		return 0;
		}
	}
  }
  
  
 
  
  
  close(soc);
  bpf_close(bpf);
  *rtt = max;
  return 0;
 }
}


#if DEBUG > 1
printf("Found nothing\n");
#endif
*rtt = htonl( 2 << 28 ); /* Two seconds */


bpf_close(bpf);
close(soc);
return 1;
}


struct list * sendpacket(int soc, int bpf, int skip, struct in_addr dst, struct in_addr src, int dport, int magic, struct list * packets, struct timeval rtt_tv, int sniff, struct arglist * env)
{
 unsigned long ack = maketime();
 char* pkt = mktcp(src, magic, dst, dport, ack);
 int len;
 char * res;
 struct sockaddr_in soca;
 
 bzero(&soca, sizeof(soca));
 soca.sin_family = AF_INET;
 soca.sin_addr   = dst;
 
 if(dport != 0)
 {
  int e;
  packets = add_packet(packets, dport, ack);
  e =  sendto(soc, pkt, sizeof(struct ip) + sizeof(struct tcphdr), 0, (struct sockaddr*)&soca, sizeof(soca));
  if(e < 0)
  {
  perror("sendto ");
  close(soc);
  bpf_close(bpf);
  return NULL;
  }
 }
 
 if(sniff != 0)
 {
  struct timeval t, n;
again:
  gettimeofday(&t, NULL);
  res = (char*)bpf_next_tv(bpf, &len, &rtt_tv);
  gettimeofday(&n, NULL);
#if 0
  if(n.tv_sec == t.tv_sec && 
     (n.tv_usec - t.tv_usec > rtt_tv.tv_usec))printf("Woops - %ld (instead of %ld)\n", n.tv_usec - t.tv_usec, rtt_tv.tv_usec);
#endif     
  if(res != NULL)
  {
   unsigned short sport = extractsport(res + skip );
   int synack = issynack(res + skip);
   if(synack)
   {
#ifdef DEBUG   
   printf("=> Port %d is open\n", sport);
#endif
   scanner_add_port(env, sport, "tcp");
   }
   packets = rm_packet(packets, sport);
   rtt_tv.tv_sec = 0;
   rtt_tv.tv_usec = 100;
   goto again;
  }
 }
 return packets;
}

int scan(struct arglist * env, struct in_addr dst, unsigned long rtt)
{
 int num;
 int soc = rawsocket();
 int bpf;
 struct in_addr src;
 int magic = 4441 + (rand() % 1200);
 int skip;
 int i;
 struct sockaddr_in soca;
 struct list * packets = NULL;
 struct timeval rtt_tv = timeval(htonl(ntohl(rtt) / 2));
 struct arglist * globals = arg_get_value(env, "globals");
 struct arglist * hostinfos = arg_get_value(env, "HOSTNAME");
 char * hname = arg_get_value(hostinfos, "NAME");
 int retry;
 char * range = get_preference(env, "port_range");
 unsigned short * ports;
 
#ifdef DEBUG 
 printf("===> port range = %d\n", range);
#endif
 
 ports = (unsigned short*)getpts(range, &num);
 
 if(soc < 0)
  return -1;
 
 bpf = openbpf(dst, &src, magic);
 skip =  get_datalink_size(bpf_datalink(bpf));
 bzero(&soca, sizeof(soca));
 soca.sin_family = AF_INET;
 soca.sin_addr   = dst;
 
 
 for(i = 0 ; ports [i] ; i +=2 )
 {
  int retry = 0;
  

  if(i % 100 == 0)comm_send_status(globals, hname ,"portscan", i, num);
  packets = sendpacket(soc, bpf, skip, dst, src, ports[i], magic, packets, rtt_tv, 0, env);
  packets = sendpacket(soc, bpf, skip, dst, src, ports[i+1], magic, packets, rtt_tv, 1, env);
 }

#ifdef DEBUG 
 printf("Done with the sending\n");
#endif
 
 while(packets != NULL)
 {
  i = 0;
  retry = 0;
  packets = rm_dead_packets(packets, rtt, &retry);
  while ( retry != 0 && i < 3)
  {
   packets = sendpacket(soc, bpf, skip, dst, src, retry, magic, packets, rtt_tv, 0, env);
   packets = rm_dead_packets(packets, rtt, &retry);
   i ++;
  }
  packets = sendpacket(soc, bpf, skip, dst, src, retry, magic, packets, rtt_tv, 0, env);
 }
 
 comm_send_status(globals, hname ,"portscan", num, num);
 return 0;
}




#define EN_NAME "SYN Scan"
#define EN_DESC "\n\
This plugins performs a supposedly fast SYN port scan\n\
It does so by computing the RTT (round trip time) of the packets\n\
coming back and forth between the nessusd host and the target,\n\
then it uses that to quicky send SYN packets to the remote host\n"


#define COPYRIGHT "Copyright (C) Renaud Deraison <deraison@cvs.nessus.org>"

#define EN_SUMMARY "Performs a TCP SYN scan"
#define EN_FAMILY "Port scanners"

PlugExport int plugin_init(struct arglist * desc)
{
 plug_set_id(desc, 11219);
 plug_set_version(desc, "$Revision: 1.5 $");
   
         
 
 plug_set_name(desc, EN_NAME, NULL);
 plug_set_summary(desc, EN_SUMMARY, NULL);
 plug_set_description(desc, EN_DESC, NULL);
 
 plug_set_copyright(desc, COPYRIGHT,NULL);
 plug_set_category(desc, ACT_SCANNER);
 plug_set_family(desc, EN_FAMILY, NULL);
 
 plug_set_dep(desc, "ping_host.nasl");
 return(0);
}



PlugExport int plugin_run(struct arglist * env)
{
  int ret = maketime();
  unsigned long rtt;
  struct in_addr *dst = plug_get_host_ip(env);
  struct timeval tv;

 
  
 if(find_rtt(*dst, &rtt) < 0)
 {
  fprintf(stderr, "Something went wrong, bailing out\n");
  return (1);
 }
 
#ifdef DEBUG
 printf("RTT = 0x%.8x\n", ntohl(rtt));
#endif

 tv = timeval(rtt);

#ifdef DEBUG
 printf("That %d seconds and %d usecs\n", tv.tv_sec, tv.tv_usec);
#endif


 scan(env, *dst, rtt);
 plug_set_key(env, "Host/scanned", ARG_INT, (void*)1);
 return 0;
}
