/***************************************************************************
                          ipsorcery.c  -  main for console
                             -------------------
    begin                : Sun Sep 23 2001
    copyright            : (C) 2001 by Josiah Zayner
    email                : phric@legions.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/



#include "wand.h"



void usage(char *progname)
{

  printf("\nUsage: %s [options]\n"
         "IP: [-is|-id|-ih|-iv|-il|-it|-io|-id|-ip]\n"
         "-is: source host or address def. 127.0.0.1\n"
         "-id: source destination or adress def. 127.0.0.1\n"
         "-ih: IP header length def. 5\n"
         "-iv: IP version def. 4\n"
         "-il: Time-to-Live def. 64\n"
         "-it: Type-of-Service def. 0\n"
         "-io: IP frag offset [(D)on't Fragment|(M)ore Fragments|(F)ragment|(N)one]\n"
         "-ii: IP packet ID for fragmentaion def. 0\n"
         "-ip: IP protocol [TCP|UDP|ICMP|IP] def. TCP\n"
         "-iO: IP options\n"
         "TCP: [-ts|-td|-to|-tq|-ta|-tf|-tw|-tu]\n"
         "-ts: TCP source port, def. rand()\n"
         "-td: TCP destination port def. 80\n"
         "-to: TCP data offset of header def. 5\n"
         "-tq: TCP sequence number def. rand()\n"
         "-ta: TCP ack sequence number def. 0\n"
         "-tf: TCP flags [(S)yn|(A)ck|(F)in|(P)ush|(R)st|(U)rg|(N)one] def. S\n"
         "-tw: TCP Window Size def. rand()\n"
         "-tu: TCP urg pointer def. 0\n"
         "UDP: [-us|-ud|-ul]\n"
         "-us: UDP source port def. rand()\n"
         "-ud: UDP destination port def. 161\n"
         "-ul: UDP length\n"
         "     RIP: [-uR|-uRc|-uRv]\n"
         "     -uR: Send default RIP packet to port 520\n"
         "     -uRc: RIP command [RQ|RS|TN|TF|SR|TQ|TS|TA|UQ|US|UA] def. RQ\n"
         "     For a list of RIP commands run program with -h rip \n"
         "     -uRv: RIP version [1|2] def. 2\n"
         "Note: Entry Tables should be used with response packets[RS|TS|US]\n"	
         "     -uRa(1|2|etc.): RIP Entry table Address exmp. -uRa1 \n"
         "     -uRn(1|2|etc.): RIP Entry table Netmask, exmp. -uRn2\n"
         "     -uRh(1|2|etc.): RIP Entry table Next Hop, exmp. -uRn(num)\n"
         "     -uRm(1|2|etc.): RIP Entry table Metric\n"
         "     -uRr(1|2|etc.): RIP Entry table Route Tag\n"
         "     -uRe: Add default RIP entry table to packet\n"
         "ICMP: [-ct|-cs]\n"
         "-ct: ICMP type def. ECHO REQUEST\n"
         "-cs: ICMP sub code def. 0\n"
         "-ci: ICMP sequence ID def. 0\n"
         "For list of ICMP Types and Subcodes run program with -h icmp.\n"
         "IGMP:[-gt|-gc|-ga|-gn]\n"
         "-gt: IGMP type [D|L|M|MT|MR|P|R1|R2|R3] def. M\n"
         "-gc: IGMP sub code for types P and D def. 0\n"
         "-gm: IGMP Max. resp. Time for Queries ie. MR\n"
         "-ga: IGMP group address def. 0\n"
         "-gn: IGMP no router alert or no internetwork Type-Of-Service [r|i||]\n"
         "For list of IGMP Types and Subcodes run program with -h igmp.\n"
         "OSPF:[-ov|-ot|-or|-oe|-oa|-ou]\n"
         "-ov: OSPF Version\n"
         "-ot: OSPF Type[(H)ello|(D)b Desc.|(R)equest|(U)pdate|(A)ck]\n"
         "-or: OSPF Router ID\n"
         "-oe: OSPF Area ID\n"
         "-oa: OSPF Auth Type[(N)one|(P)ass|(C)rypto]\n"
         "-ou <data>: OSPF Authentication Data\n"
         "-D \"<data>\": for datapayload\n"
         "-N <num packets>: send <num packets> number packets \n"
         "-S <verbosity>: (v)erbose, (s)hort, (t)urn off packet snoop\n"
         "-v: print version \n"
         ,progname
        );

  exit(-1);
}

void icmp_info(void)
{
  printf("ICMP Types and Subcodes:\n"
         "Types:\n"
         "Echo Reply                   0\n"
         "Destination Unreachable      3\n"
         "  Subcodes:\n"
         "     Network Unreachable   0\n"
         "     Host Unreachable      1\n"
         "     Protocol Unreachable  2\n"
         "     Port Unreachable      3\n"
         "     Fragmentation Needed  4\n"
         "     Source Route Failed   5\n"
         "     Network Unknown       6\n"
         "     Host Unknown          7\n"
         "     Host Isolated         8\n"
         "     Network Prohibited    9\n"
         "     Host Prohibited      10\n"
         "     Bad TOS for Network  11\n"
         "     Bad TOS for Host     12\n"
         "     Packet Filtered      13\n"
         "     Precedence Violation 14\n"
         "     Precedence Cutoff    15\n\n"
         "Source Quench                4\n"
         "Redirect                     5\n"
         "  Subcodes:\n"
         "     Redirect Network      0\n"
         "     Redirect Host         1\n"
         "     Redirect Network TOS  2\n"
         "     Redirect Host TOS     3\n\n"
         "Echo Request                 8\n"
         "Router Advertisement         9\n"
         "Router Solicitation         10\n"
         "Time Exceeded               11\n"
         "  Subcodes:\n"
         "     Time to Live          0\n"
         "     Frag Reassembly       1\n\n"
         "Parameter Problems          12\n"
         "  Subcode:\n"
         "     Option Missing        0\n\n"
         "Timestamp Request           13\n"
         "Timestamp Reply             14\n"
         "Information Request         15\n"
         "Information Reply           16\n"
         "Address Mask Request        17\n"
         "Address Mask Reply          18\n"
         "Traceroute                  30\n"
         "  Subcodes:\n"
         "     Successful Forward    0\n"
         "     No Route              1\n\n"
         "Conversion Error            31\n"
         "Mobile Host Redirect        32\n"
         "IPv6 Where Are You?         33\n"
         "IPv6 I am Here              34\n"
         "Mobile Reg. Request         35\n"
         "Mobile Reg. Reply           36\n"
         "Domain Name Request         37\n"
         "Domain Name Reply           38\n"

        );
  exit(0);

}

void igmp_info(void)
{

  printf("IGMP Types: [D|L|M|MR|MT|P|R1|R2|R3]\n"
         "  DVMRP:                        D\n"
         "    Subcode:\n"
         "          Probe:            1\n"
         "          Report:           2\n"
         "          Ask Neighbors:    3\n"
         "          Neighbors:        4\n"
         "          Ask Neighbors 2:  5\n"
         "          Neighbors V. 1:   6\n"
         "          Prune:            7\n"
         "          Graft:            8\n"
         "          Graft ACK:        9\n"
         "  Membership Query:             M\n"
         "    Subcode = Max Response Time\n"
         "  Membership Report Version 1:  R1\n"
         "  Membership Report Version 2:  R2\n"
         "  Membership Report Version 3:  R3\n"
         "  Member Trace:                 MT\n"
         "  Member Response:              MR\n"
         "    Subcode = Max Response Time\n"
         "  Leave Group:                  L\n"
         "  PIM Version 1:                P\n"
         "    Subcode:\n"
         "          Register:         1\n"
         "          Register Stop:    2\n"
         "          Join/Prune:       3\n"
         "          RP Reachable:     4\n"
         "          Assert:           5\n"
         "          Graft:            6\n"
         "          Graft ACK:        7\n"
         "          Mode:             8\n"
        );
  exit(0);

}

void rip_info(void)
{

  printf("RIP Commands [RQ|RS|TN|TF|SR|TQ|TS|TA|UQ|US|UA]\n"
         "	RS: Response Packet\n"
         "	RQ: Request Packet\n"
         "	TN: Trace On\n"
         "	TF: Trace Off\n"
         "	SR: Sun Reserved\n"
         "	TQ: Triggered Request\n"
         "	TR: Triggered Response\n"
         "	TA: Triggered Acknowledgement\n"
         "	UQ: Update Request\n"
         "	UR: Update Response\n"
         "	UA: Update Acknowledgment\n"
        );
	exit(0);
}

int main(int argc, char **argv)
{
  int d_size = 65535;
  int ripe_size = 0;
  static int x = 1, optlen = 0;
  static int rip_go = 0, y = 0;
  static int *opts;
  struct ip *eyep = NULL;
  struct tcphdr *tcpea = NULL;
  struct udphdr *udpea = NULL;
  struct nmrhdr *nmr_magic = NULL;
  struct icmp *icmpea = NULL;
  struct igmp *igmpea = NULL;
  struct rip *rip = NULL;
  struct rip_ent rip_e[25];
  struct ospf *ohspef = NULL;
  struct extra_ingredients ex_in;
  struct ip_option ip_option[2];
  static char *data = NULL;
  char packet[65535];
  static int oper;

  if(argc <= 1){ usage(argv[0]); }  /* if no arguments print usage */

  srand(time(NULL)); /* seed current time for random numbers*/
  memset(packet, 0, 65535);
  memset(&ex_in, '\0', sizeof(struct extra_ingredients));

  ex_in.READ_IT = 0; /* initialize our scooby style snooper
                        to NOT SNOOP!*/
  ex_in.num = 1; /* one scooby snack please! */

  eyep = (struct ip *)packet; /* point ip header into packet */
  /* Everyone of our packets use IP! */
  ip_sorcery(eyep,
             wherefromto("127.0.0.1"),
             wherefromto("127.0.0.1"),
             0,
             5,
             4,
             0,
             40,
             0,
             64,
             0
            );

/* if we got one argument lets parse it*/
if(argc > 1)
{

for(;x < argc; x++)
{

  if(argv[x][0] != '-')
  {
    fprintf(stderr, "Bad Option: %s, see usage -h\n",argv[x]);
  }
 /* first it checks the first letter to see what protocol
    to pick it then initializes the headers and parses any
    further options by another function get_<protocol>_arg
 */

  switch((int)argv[x][1])
  {


    case 'i':
    d_size = 65535 - S_IP;
    optlen = get_ip_arg(argv[x], argv[x + 1], eyep, opts);
    x++; /* increment x so we skip the next argument */
    break; /* end of IP header arguments */

    case 'T':
    case 't':
    if(eyep->ip_p && eyep->ip_p != 6)
    { fprintf(stderr, "Only one protocol at a time please!\n"); exit(-1); }

    /* intialize tcp packet */
    if(eyep->ip_p != 6)
    {
      eyep->ip_p = 6;
      d_size = 65535 - (S_IP + sizeof(struct tcphdr));
      tcpea = (struct tcphdr *)&packet[S_IP + optlen + S_NMR];
      tcpea->th_sport = (u_short)htons(rand() % 3000);  /* random source port <= 3000 */
      tcpea->th_dport = htons(23);

      tcpea->th_off = 5;
      tcpea->th_seq = 0;
      tcpea->th_ack = 0;    /* ack sequence */
      tcpea->th_flags = TH_SYN; /* default tcp SYN packet */
      tcpea->th_win = htons((rand() % 1024) + 300); /* 300 <= random window size <= 1324 */
      tcpea->th_urp = 0;
    }
     get_tcp_arg(argv[x], argv[x + 1], tcpea);


    x++;
    break; /* end of TCP header arguments */

    case 'u':
    if(eyep->ip_p && eyep->ip_p != 17) {
     fprintf(stderr, "Only one protocol at a time please!\n"); exit(-1); 
    }

    if(eyep->ip_p != 17)
    {
      eyep->ip_p = 17;
      d_size = 65535 - S_IP + sizeof(struct udphdr);  /* max data size */
      /* put udp header in place and psuedo no miss routed header */
      udpea = (struct udphdr *)&packet[S_IP + optlen + S_NMR];
      udpea->uh_ulen = sizeof(struct udphdr);
    }

    /* make sure we don't try and get UDP args for RIP */
    if(argv[x][2] == 'R')
    {
      if(!rip_go)
      {
        /* RIP broadcast */
        if(eyep->ip_dst.s_addr == 0x1000007f)
          eyep->ip_dst = wherefromto("224.0.0.9");

        memset(rip_e, 0, (sizeof(struct rip_ent) * 3));
        udpea->uh_sport = htons(520);
        udpea->uh_dport = htons(520);
        rip = (struct rip *)&packet[S_IP + optlen + S_NMR + S_UDP];
        rip->comm = 0x01;
        rip->vers = 0x02; /* vers 2 */
        rip->none = 0;
        rip_go++;
      }
      get_rip_arg(argv[x],argv[x + 1], rip, rip_e);

    }

    else {
           udpea->uh_sport = htons(rand() % 3000);  /* source port */
           udpea->uh_dport = htons(20);   /* destination port */
           get_udp_arg(argv[x], argv[x + 1], udpea);
         }

    x++;
    break; /* end of UDP header arguments */

    /* start ICMP arguments */
    case 'c':
    if(eyep->ip_p && eyep->ip_p != 1)
    { fprintf(stderr, "Only one protocol at a time please!\n"); exit(-1); }

    if(argv[x][2] && !argv[x][3])
    {
    if(argv[x + 1])
    {

    /* intialize our ICMP header */
    if(eyep->ip_p != 1)
    {
      eyep->ip_p = 1;
      d_size = 65535 - (S_IP + S_ICMP);
      icmpea = (struct icmp *)&packet[S_IP + optlen];
      icmpea->icmp_type = ICMP_ECHO;
      icmpea->icmp_code = 0;
    }

    get_icmp_arg(argv[x], argv[x + 1], icmpea);
    }
    else { printf("Bad input: %s, for %s\n", argv[x + 1], argv[x]); exit(-1); }
    }
    else { printf("Bad option: %s\n", argv[x]); exit(-1); }

    x++;
    break; /* end of ICMP header arguments */

    /* start IGMP */
    case 'g':

    if(eyep->ip_p && eyep->ip_p != 2)
    { fprintf(stderr, "Only one protocol at a time please!\n"); exit(-1); }

    if(argv[x][2] && !argv[x][3])
    {

    /* intialize our IGMP header */
    if(eyep->ip_p != 2)
    {
      eyep->ip_p = 2;
      d_size = 65535 - (S_IP + S_IGMP);
      eyep->ip_tos = 0xc0; /* internetwork traffic */
      /* inet_ntoa("224.0.0.1", &igmpea->group); */
      eyep->ip_ttl = 1; /* IGMP ttl needs to always be one */
      ip_option[0].opt = htons(0x9404);
      ip_option[1].opt = htons(0x0000);
      memcpy(&packet[S_IP], &ip_option, sizeof(&ip_option));
      optlen = 4;
      igmpea = (struct igmp *)&packet[S_IP + optlen];

    }
    get_igmp_arg(argv[x], argv[x + 1], igmpea);
    }
    else { printf("Bad option: %s\n", argv[x]); exit(-1); }

    x++;
    break; /* end of IGMP header arguments */

    case 'o':

    if(eyep->ip_p != 89)
    {

      eyep->ip_p = 89;
      /* destination AllSPFRouters group */
      eyep->ip_dst = wherefromto("224.0.0.6");
      ohspef = (struct ospf *)&packet[S_IP + optlen];
      ohspef->vers = 0x02;
      ohspef->type = 0x01;
      ohspef->length = htons(20);
      ohspef->area_id = 0;
      d_size = 65535 - (S_IP + S_OSPF);
      eyep->ip_tos = 0xc0; /* internetwork traffic */

    }

    get_ospf_arg(argv[x], argv[x + 1], ohspef);
    x++;
    break; /* end of OSPF header args */

    /* get data and make sure it is small enough for one packet

    */
    case 'D':
    if(argv[x + 1])
    {
      /* make sure they don't try to stuff in too much data */
      if(strlen(argv[x + 1]) > d_size)
      { fprintf(stderr,"Data size too big!\n"); exit(-1); }

      data = clalloc(d_size); /* allocate data */
      memcpy(data, argv[x + 1], strlen(argv[x + 1]));
      d_size = strlen(argv[x + 1]);
    }
    x++;
    break;

    /* for fragmentation later! */
    case 'F':
    eyep->ip_off = htons(0x0002);
    break;

    case 'N':
    if(argv[x + 1])
    {
      ex_in.num = atoi(argv[x + 1]);
    }
    x++;
    break;

    case 'S':
    if(argv[x + 1])
    {
      if(strpbrk(argv[x +1], "vV"))
        ex_in.READ_IT = 2; /* verbose */
      else if(strpbrk(argv[x +1], "sS"))
        ex_in.READ_IT = 1;/* short */
      else if(strpbrk(argv[x +1], "tT"))
        ex_in.READ_IT = 0; /* turn off snooping*/
    }
    x++;
    break;

    case 'h':
    if(!argv[x + 1]){ usage(argv[0]); }
    else if(strpbrk(argv[x + 1], "cC")){ icmp_info(); }
    else if(strpbrk(argv[x + 1], "gG")){ igmp_info(); }
    else if(strpbrk(argv[x + 1], "rR")){ rip_info(); }
    break;

    case 'v':
    printf("IP Sorcery %s\n", _VERSION_);
    exit(0);
    break;

    default:
      fprintf(stderr, "Bad Option: %s, see usage -h\n", argv[x]);
      exit(-1);
    break;

  }/* switch argv[x][1] */

} /* for(... */

}/* if(argc > 1) */



if(!data){ d_size = 0; }
if(optlen){

  /* optlen = x8bits  we need to convert to x32bits
     if the options length in bytes (%)modulous 4
     returns a remainder we take that remainder and add it to
     the options length in bytes divided by 4(optlen /4)
     Remember we must pad with NULLs thats why we convert
     the remainder to 32 bytes.

     ex. 5 bytes @ 8bits = 2 @ 32bits
         4 bytes @ 8bits = 1 @ 32bits

  */
  if(optlen % 4){ oper = (optlen / 4) + (optlen % 4); }
  else { oper = (optlen / 4); }
  eyep->ip_hl = 5 + oper; /* hl is in x32 bits */
}

/* for psuedo no miss routed packets header */
if(eyep->ip_p == 6 || eyep->ip_p == 17)
{

  /* create psuedo header */
  nmr_magic = (struct nmrhdr *)&packet[S_IP];
  nmr_magic->saddr = eyep->ip_src;
  nmr_magic->daddr = eyep->ip_dst;
  nmr_magic->none = 0;
  nmr_magic->protocol = eyep->ip_p;

}

/* Do we have ICMP YES!! We have a winner */
if(eyep->ip_p == 1)
{
  if(d_size) {
    memcpy(&packet[S_IP + optlen + S_ICMP], data, d_size);
  }
  icmpea->icmp_cksum = in_cksum((u_short *)&packet[S_IP + optlen], S_ICMP + d_size);
  ex_in.pack_sz = S_IP + optlen + S_ICMP + d_size;

}

else if(eyep->ip_p == 2)
{
  if(d_size) {
    memcpy(&packet[S_IP + optlen + S_IGMP], data, d_size);
  }
  igmpea->cksum = in_cksum((u_short *)&packet[S_IP + optlen], S_IGMP + d_size);

  ex_in.pack_sz = S_IP + optlen + S_IGMP + d_size;
}

else if(eyep->ip_p == 6)
{

  nmr_magic->length = htons(S_TCP + d_size);
  if(d_size) {
    memcpy(&packet[S_IP + optlen + S_TCP + S_NMR], data, d_size);
  }

  tcpea->th_sum = in_cksum((u_short *)&packet[S_IP],
                           S_TCP + S_NMR + d_size
                          );

  /* move the packet to cover up the psuedo header */
  memmove(&packet[S_IP], &packet[S_IP + optlen + S_NMR], S_TCP + d_size);

  memcpy(&ex_in.dport, &packet[22], sizeof(u_short));
  ex_in.pack_sz = S_TCPIP + optlen + d_size;

}

else if(eyep->ip_p == 17)
{
  if(rip_go)
  {

    if(rip_e)
    {
      /* wow, for the rip entry tables, if a table is specified
         one of these three fields will always be non-zero
     !!!!!fix address family!!!!!
      */
      for(y = 0;(rip_e[y].addr_fam > 0 ||
                rip_e[y].route_tag > 0 ||
                rip_e[y].metric > 0) && y < 25;
                y++
         )
      {
         memcpy(packet + S_IP + optlen + S_NMR +
                        S_UDP + S_RIP +
                        (sizeof(struct rip_ent) * y),
                        &rip_e[y],
                        sizeof(struct rip_ent)
               );

      }
        ripe_size = S_RIP + (sizeof(struct rip_ent) * y);
        if(data)
        {
             memcpy(packet + S_IP + optlen + S_NMR + S_UDP + S_RIP +
                       (sizeof(struct rip_ent) * y), data, d_size);

        d_size = d_size + S_RIP + (sizeof(struct rip_ent) * y);
        }
    }
    else
    {
      ripe_size = S_RIP;
      if(data)
      {
        memcpy(packet + S_IP +  optlen + S_NMR + S_UDP + S_RIP, data, d_size);


      }
    }
  }

  nmr_magic->length = htons(S_UDP + d_size + ripe_size);
  if(data) {
    memcpy(&packet[S_IP + optlen + S_UDP + S_NMR], data, d_size);
  }

  /* calculate checksum */
  udpea->uh_sum = in_cksum((u_short *)&packet[S_IP + optlen], S_NMR + S_UDP + d_size + ripe_size);

  /* move the packet to cover up the psuedo header */
  memmove(&packet[20], &packet[S_IP + optlen + S_NMR], S_UDP + d_size + ripe_size);

  ex_in.dport = udpea->uh_dport;
  ex_in.pack_sz = S_IP + optlen + S_UDP + d_size + ripe_size;

}

else if(eyep->ip_p == 89)
{
  if(d_size) {
    memcpy(&packet[S_IP + optlen + S_OSPF], data, d_size);
  }
  ohspef->cs = in_cksum((u_short *)&packet[S_IP + optlen], S_OSPF +d_size);

  ex_in.pack_sz = S_IP + optlen + S_OSPF + d_size;
}

  /* finally send out packet */
  packet_sorcery(packet,
                 ex_in,
                 eyep->ip_dst,
                 eyep->ip_src
                );

/* free our memory */
if(data){ free(data); }

  return 0;
}


