#include "includes.h"

/* UTILITY ROUTINES */
u_short chksum(u_short *buf, int len){ /* from D.Reeds ipsend */
  u_long  sum = 0;
  int     nwords = len >> 1;

  for(; nwords > 0; nwords--)
    sum += *buf++;
  sum = (sum>>16) + (sum & 0xffff);
  sum += (sum >>16);
  return (~sum);
}


void hexdump(char *pkt, int size){
  int i;

  for (i=0; i<size; i++){
    if ( i!= 0 && (!(i%20)) )
      printf("\n");
    printf(" %02x", *(u_char *)pkt);
    pkt++;
  }

  printf("\n");

}

int make_gre_packet(char *data, struct gre_packet *gre, int size){
  char *ptr;
  u_long hold1=0, hold2=0;

  if (size < (SIZE_ETHER_H + SIZE_IP_H + sizeof(struct gre_packet)))
    return(-1);

  bzero((char *)gre, sizeof(struct gre_packet));

  ptr = data;
  ptr += (SIZE_ETHER_H + SIZE_IP_H);

  gre->bitfield = *((u_short *)ptr);
  if ((gre->bitfield & 0x1000) && (gre->bitfield & 0x0080))
    memcpy((char *)gre, (char *)ptr, sizeof(struct gre_packet));
  else if ((gre->bitfield & 0x1000) && !(gre->bitfield & 0x0080)){
    memcpy((char *)gre, (char *)ptr, sizeof(struct gre_packet) - 
                                     sizeof(u_long));
  } 
  else if (!(gre->bitfield & 0x1000) && (gre->bitfield & 0x0080)){
    memcpy((char *)gre, (char *)ptr, sizeof(struct gre_packet) - 
                                     sizeof(u_long));
    gre->acknumber = gre->seqnumber;
    gre->seqnumber = 0;
  }
  else if (!(gre->bitfield & 0x1000) && !(gre->bitfield & 0x0080)){
    memcpy((char *)gre, (char *)ptr, sizeof(struct gre_packet) -   
                                     sizeof(u_long) - sizeof(u_long));  
  }
  return(0);


  ptr += 2;
  gre->proto = *((u_short *)ptr);

  ptr += 2;
  gre->KeyHW = *((u_short *)ptr);

  ptr += 2;
  gre->KeyLW = *((u_short *)ptr);

  ptr += 2;
  if ((gre->bitfield & 0x1000) && (gre->bitfield & 0x0080)){
    hold1 = *((u_long *)ptr);
    gre->seqnumber = hold1;
    ptr += 4;
    hold2 = *((u_long *)ptr);
    gre->acknumber = hold2;
  } else if (gre->bitfield & 0x1000){
    hold1 = *((u_long *)ptr);
    gre->seqnumber = hold1;
    bzero(&gre->acknumber, sizeof(u_long));
  } else if (gre->bitfield & 0x0080){
    gre->acknumber = *((u_long *)ptr);
    bzero(&gre->seqnumber, sizeof(u_long));
  } else {
    bzero(&gre->seqnumber, sizeof(u_long));
    bzero(&gre->acknumber, sizeof(u_long));
  }

  return(0);
}


void print_gre_pkt(char *data, int size){
  struct gre_packet gre;

  if (make_gre_packet(data, &gre, size) != 0){
    printf("impossible packet length\n");
    return;
  }

  printf("Checksum Present    : %d\n", (gre.bitfield & GRE_CKSUM) >> 15);
  printf("Routing Present     : %d\n", (gre.bitfield & GRE_ROUTE) >> 14); 
  printf("Key Present         : %d\n", (gre.bitfield & GRE_KEY) >> 13);
  printf("Seq Num Present     : %d\n", (gre.bitfield & GRE_SEQ) >> 12);
  printf("Strict Source Route : %d\n", (gre.bitfield & GRE_SSR) >> 11);
  printf("Recursion Control   : %d\n", (gre.bitfield & GRE_RECUR) >> 8);
  printf("Ack Num Present     : %d\n", (gre.bitfield & GRE_ACK) >> 7);
  printf("Flags               : %x\n", (gre.bitfield & GRE_FLAGS) >> 3);
  printf("Version             : %d\n", (gre.bitfield & GRE_VERS));
  printf("Protocol Type       : %04x\n", gre.proto);
  printf("Key (HW) Length     : %04x\n", gre.KeyHW);
  printf("Key (LW) CallID     : %04x\n", gre.KeyLW);
  if (gre.bitfield & 0x1000)
    printf("Seq Number          : %08x\n", gre.seqnumber);
  if (gre.bitfield & 0x0080)
    printf("Ack Number          : %08x\n", gre.acknumber);
}



/*
 *  IP address into network byte order
 */
unsigned long getaddr(char *addrStr){

  struct in_addr addr;
  struct hostent *host;

  addr.s_addr = inet_addr(addrStr);
#ifdef NIY
  if (addr.s_addr == -1){
    host = (struct hostent *)gethostbyname(addrStr);
    if (!host){
      perror("util.c - gethostbyname");
      exit(1);
    }
    memcpy(&addr, *host->h_addr_list, SIZE_IN_ADDR);
  }
#endif
  if (addr.s_addr == -1) {
    perror("inet_addr");
    exit(1);
  }
  return addr.s_addr;
}

void makehost(struct target_host *target, char *str){
  char *ptr;

  ptr = (char *)strrchr(str, ':');

  if (ptr){
    *ptr = '\0';
    ptr++;
    target->port = htons(strtoul(ptr, (char **)NULL, 10));
  } else 
    target->port = 0;

  target->addr.s_addr = getaddr(str); 
}

void make_eth_and_ip(char *pkt, char *data)
{
  struct ether_header *eth;
  struct ip *iph;
  struct tcphdr *tcph;

  eth = (struct ether_header *)pkt;
  iph = (struct ip *)(pkt + SIZE_ETHER_H);

  memcpy(eth, data, SIZE_ETHER_H);
  memcpy(iph, data + SIZE_ETHER_H, SIZE_IP_H);

  if (iph->ip_p == IPPROTO_TCP){
    tcph = (struct tcphdr *)(pkt + SIZE_ETHER_H + SIZE_IP_H);
    memcpy(tcph, data + SIZE_ETHER_H + SIZE_IP_H, SIZE_TCP_H);
  }
}

void make_reset(char *pkt2, char *pkt1){

  struct ether_header *eth1, *eth2;
  struct ip *iph1, *iph2;
  struct tcphdr *tcph1, *tcph2, ltcph;
  struct pseudo_header pheader;

  bzero(pkt2, MAXDLBUF);

  eth1 = (struct ether_header *)pkt1;
  eth2 = (struct ether_header *)pkt2;

  iph1 = (struct ip *)(pkt1 + SIZE_ETHER_H);
  iph2 = (struct ip *)(pkt2 + SIZE_ETHER_H);

  tcph1 = (struct tcphdr *)(pkt1 + SIZE_ETHER_H + SIZE_IP_H);
  tcph2 = (struct tcphdr *)(pkt2 + SIZE_ETHER_H + SIZE_IP_H);

  /* ether header */
  memcpy(&eth2->ether_dhost, &eth1->ether_shost, SIZE_ETHER_H);
  /* comment out the following line to leave eth2->shost as all 0's
     for debugging */
  memcpy(&eth2->ether_shost, &eth1->ether_dhost, SIZE_ETHER_H);
  eth2->ether_type = ETHERTYPE_IP;

#ifdef DEBUG
  printf("made ether\n");
#endif

  /* ip header */
  memset(iph2, 0x45, 1);
  iph2->ip_len = 40;
  iph2->ip_id = 0xcafe;
  iph2->ip_ttl = 60;
  iph2->ip_p = IPPROTO_TCP;
  iph2->ip_off = IP_DF;
  memcpy(&iph2->ip_dst, &iph1->ip_src, SIZE_IN_ADDR);
  memcpy(&iph2->ip_src, &iph1->ip_dst, SIZE_IN_ADDR);

  iph2->ip_sum = chksum((u_short *)iph2, SIZE_IP_H);

#ifdef DEBUG
  printf("made ip\n");
#endif

  /* tcp header */
  memcpy(&tcph2->th_sport, &tcph1->th_dport, SIZE_U_SHORT);
  memcpy(&tcph2->th_dport, &tcph1->th_sport, SIZE_U_SHORT);
  memcpy(&tcph2->th_ack, &tcph1->th_seq, SIZE_U_LONG);

  /* this is done to get around a bug in the version of
     gcc I'm running right now */
  memcpy(&ltcph, tcph2, SIZE_TCP_H);
  ltcph.th_ack = ltcph.th_ack + 1;
  ltcph.th_flags = TH_ACK | TH_RST;
  ltcph.th_off = 5;
  memcpy(tcph2, &ltcph, SIZE_TCP_H);

#ifdef DEBUG
  printf("made tcp \n");
#endif

  memcpy(&pheader.source_address, &iph2->ip_src, SIZE_IN_ADDR);
  memcpy(&pheader.dest_address, &iph2->ip_dst, SIZE_IN_ADDR);
  pheader.placeholder = 0;
  pheader.protocol = iph2->ip_p;
  pheader.tcp_length = 20;
  memcpy(&pheader.tcp, tcph2, SIZE_TCP_H);

  tcph2->th_sum = chksum((u_short *)&pheader, SIZE_PSEUDO_H);

#ifdef DEBUG
  printf("made tcp\n");
#endif

}

void usage(char *progname){
  char *foo;

  foo = (char *)strrchr(progname, '/');

  if (foo)
    foo++;
  else
    foo = progname;

  fprintf(stderr, "kill_tcp - mudge@l0pht.com 9.13.97\n");
  fprintf(stderr, "Usage: %s [-avP] [-f <x.x.x.x:y> ] [-t <x.x.x.x:y>]\n", foo);
  fprintf(stderr, "    -a        Kill all tcp connections no matter what\n");
  fprintf(stderr, "              state they are currently in. Without the\n");
  fprintf(stderr, "              -a option only SYN packets will be chased\n");
  fprintf(stderr, "              with RST's.\n");
  fprintf(stderr, "    -f <IP>   Kill all tcp connections from _host_\n");
  fprintf(stderr, "    -t <IP>   Kill all tcp connections to _host_\n");
  fprintf(stderr, "              <IP> may have the syntax of either :\n");
  fprintf(stderr, "                x.x.x.x:port\n");
  fprintf(stderr, "                x.x.x.x\n");
  fprintf(stderr, "    -v        Verbose\n");
  fprintf(stderr, "    -P        Passive - don't actually send the RST\n");
  fprintf(stderr, "    -d <dev>  interface to use (defaults to le0)\n");
  exit(1);
}

void print_info(char *pkt){
   struct ip iph;
   struct tcphdr tcph;
   struct in_addr *iaddr1, *iaddr2;

   /* OK the gnu compiler on Solaris is really starting to piss me
      off! For instance:

      inet_ntoa(iph.ip_src) gives the same string as inet_ntoa(iph.ip_dst)
      even though walking through gdb shows that two different values
      are at those locations.

      in addition, if you set up the following:
        struct ip *iph;
        iph = (struct ip *)(pkt + SIZE_ETHER_H);
        then iph->ip_src gives a bus error in real execution but
        works fine in the debugger. It also works fine if you copy
        iph to a local struct and reference it then (ie memcpy(foo, iph,
        sizeof(struct ip)); inet_ntoa(foo.ip_src);
   */

   memcpy(&iph, (pkt + SIZE_ETHER_H), SIZE_IP_H);
   memcpy(&tcph, (pkt + SIZE_ETHER_H + SIZE_IP_H), SIZE_TCP_H);

   iaddr1 = &iph.ip_src;   
   iaddr2 = &iph.ip_dst;

   printf("Zorching connection attempt from ");
   printf("%s:%d -> ", inet_ntoa(*iaddr1), ntohs(tcph.th_sport));
   printf("%s:%d\n", inet_ntoa(*iaddr2), ntohs(tcph.th_dport));

}

#ifdef BPF
void analyze(char *pkt, u_int fhost, uint thost, struct target_host *f_Host, struct target_host *t_Host){

  struct ether_header *eth;
  struct ip *iph;
  struct tcph *tcph;

  eth = (struct ether_header *)pkt;
  iph = (struct *ip)(pkt + SIZE_ETHER_H);
  tcph = (struct tcphdr *)(pkt + SIZE_ETHER_H + SIZE_IP_H);

  /* both FromHost and ToHost were specified to filter on */
  if ((fhost) && (thost)){
    if (memcmp(&(iph->ip_src), &f_Host.addr, SIZE_IN_ADDR) != 0)
      return;
    if (memcmp(&(iph->ip_dst), &t_Host.addr, SIZE_IN_ADDR) != 0)
      return;
    if ((f_Host.port != 0) && (f_Host.port != tcph->th_sport))
      return;
    if ((t_Host.port != 0) && (t_Host.port != tcph->th_dport))
      return;
    if (kill_all) {
      if (!passive){
        make_reset(pkt2, pkt1);
        sendip(fd, pkt2, SIZE_FULL_PKT);
      }
      if (verbose)
        print_info(pkt1);
    } else {
      if ((tcph->th_flags & TH_SYN ) && ((tcph->th_flags & TH_ACK) == 0)){
        if (!passive){
          make_reset(pkt2, pkt1);
          sendip(fd, pkt2, SIZE_FULL_PKT);
        }
        if (verbose)
          print_info(pkt1);
      }
    }
    return;
  } /* end (fhost) && (thost) */

  /* FromHost was specified but ToHost was not */
  if ((fhost) && (!thost)){
    if (memcmp(&(iph->ip_src), &f_Host.addr, SIZE_IN_ADDR) != 0)
      return;
    if ((f_Host.port != 0) && (f_Host.port != tcph->th_sport))
      return;
    if (kill_all) {
      if (!passive){
        make_reset(pkt2, pkt1);
        sendip(fd, pkt2, SIZE_FULL_PKT);
      }
      if (verbose)
        print_info(pkt1);
    } else {
      if ((tcph->th_flags & TH_SYN ) && ((tcph->th_flags & TH_ACK) == 0)){
        if (!passive){
          make_reset(pkt2, pkt1);
          sendip(fd, pkt2, SIZE_FULL_PKT);
        }
        if (verbose)
          print_info(pkt1);
      }
    }
    return;
  } /* end (fhost) && (!thost) */

  /* FromHost was NOT specified but ToHost was */
  if ((!fhost) && (thost)) {
    if (memcmp(&(iph->ip_dst), &t_Host.addr, SIZE_IN_ADDR) != 0)
      return;
    if ((t_Host.port != 0) && (t_Host.port != tcph->th_dport))
      return;
    if (kill_all) {
      if (!passive){
        make_reset(pkt2, pkt1);
        sendip(fd, pkt2, SIZE_FULL_PKT);
      }
      if (verbose)
        print_info(pkt1);
    } else {
      if ((tcph->th_flags & TH_SYN ) && ((tcph->th_flags & TH_ACK) == 0)){
        if (!passive){
          make_reset(pkt2, pkt1);
          sendip(fd, pkt2, SIZE_FULL_PKT);
        }
        if (verbose)
          print_info(pkt1);
      }
    }
    return;
  } /* end (!fhost) && (thost) */

  /* Neither FromHost Nor ToHost were specified */
  if ((!fhost) && (!thost)) {
    if (kill_all) {
      if (!passive){
        make_reset(pkt2, pkt1);
        sendip(fd, pkt2, SIZE_FULL_PKT);
      }
      if (verbose)
        print_info(pkt1);
    } else {
      if ((tcph->th_flags & TH_SYN ) && ((tcph->th_flags & TH_ACK) == 0)){
        if (!passive){
          make_reset(pkt2, pkt1);
          sendip(fd, pkt2, SIZE_FULL_PKT);
        }
        if (verbose)
          print_info(pkt1);
      }
    }
    return;
  } /* end (!fhost) && (!thost) */
}
#endif /* BPF */
