/*
 * UDP Toolkit
 * 
 * Simple utility for crafting UDP traffic of your choosing. 
 * Allows full control over virtually every parameter you can think
 * of and makes spoofing traffic or poking holes in your favorite firewall
 * or other pieces of networking equipment a piece of cake.
 *
 * Requires:
 *      libnet >= 1.1.x (http://www.packetfactory.net/projects/libnet/)
 *      root
 *      a basic understanding what this tool does
 *
 * Compile with something like:
 *  gcc -Wall `libnet-config --defines --cflags` -o udp-tk udp-tk.c `libnet-config --libs`
 *
 *   This code can be found in its original form here:
 *   
 *       http://spoofed.org/files/udp-tk.c
 *
 *
 * Jon Hart <warchild@spoofed.org>
 *
 * Copyright (c) 2002, Jon Hart 
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification, 
 *  are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the organization nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 *
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Changes:
 * 10/30/2002
 *    Fixed the TTL so that it is not obviously spoofed.
 *    Use random number between 1024 and 65535 for src/dst if no port provided
 * 
 *
 */

#include <libnet.h>
#if (SOLARIS || BSD)
#include <netinet/if_ether.h>
#else
#include <netinet/ether.h>
#endif

#define OPT_HELP         0
#define OPT_VERBOSE      1
#define OPT_INTERFACE   2
#define OPT_SRCIP         3
#define OPT_DSTIP         4
#define OPT_SRCETHER      5
#define OPT_DSTETHER      6
#define OPT_SRCPORTS      7
#define OPT_DSTPORTS      8
#define OPT_PAYLOAD      9
#define OPT_IPID         10
#define OPT_IPTOS         11
#define OPT_IPTTL         12


struct opt {
   char *name;   /* name of this option */
   int req;      /* required? */
   int set;      /* Does it have a value? */
}; 

struct opt opts[] = {
   {"help", 0, 0},
   {"verbose", 0, 0},
   {"interface", 1, 0},
   {"source IP", 1, 0},
   {"destination IP", 1, 0},
   {"source ethernet address", 0, 0},
   {"destination ethernet address", 0, 0},
   {"source port(s)", 1, 0},
   {"destination port(s)", 1, 0},
   {"payload", 1, 0},
   {"IP ID number", 1, 0},
   {"TOS", 1, 0},
   {"TTL", 1, 0}
};

void usage() {
   fprintf(stderr, "\tudp-tk\n\tA simple UDP Toolkit\n\tby Jon Hart <warchild@spoofed.org>\n");
   fprintf(stderr, "\thttp://spoofed.org/files/udp-tk.c\n");
   fprintf(stderr, "\nUsage:\n");
   fprintf(stderr, "\tRequired:\n");
   fprintf(stderr, "\t-i <interface>      Specify interface\n");
   fprintf(stderr, "\t-d <dst ip>         Destination IP\n");
   fprintf(stderr, "\n\tOptional:\n\n");
   fprintf(stderr, "\tLink Layer:\n");
   fprintf(stderr, "\t-e <src ethernet>   Source ethernet address (default: auto)\n");
   fprintf(stderr, "\t-E <dst ethernet>   Destination ethernet address (default: auto)\n");
   fprintf(stderr, "\tIP Layer:\n");
   fprintf(stderr, "\t-I <IP id #>        IP ID number (default: sane)\n");
   fprintf(stderr, "\t-s <src IP>         Source IP (default: auto)\n");
   fprintf(stderr, "\t-t <TTL>            IP TTL (default: sane)\n");
   fprintf(stderr, "\t-T <TOS>            IP TOS (default: 0)\n");
   fprintf(stderr, "\tUDP Layer:\n");
   fprintf(stderr, "\t-c <payload>        Packet payload (default: NULL)\n");
   fprintf(stderr, "\t-p <src port(s)>    Source ports (default: random)\n");
   fprintf(stderr, "\t-P <dst port(s)>    Destination ports (default: random)\n");
   fprintf(stderr, "\tOther:\n");
   fprintf(stderr, "\t-h                  Help (this message)\n");
   fprintf(stderr, "\t-v                  Be verbose\n");
}

int main(int argc, char *argv[]) {
   
   int c;

   libnet_t *libnet;
   libnet_ptag_t udp, ipv4, ether;

   u_long src_ip, dst_ip;
   u_short src_port, dst_port;

   u_char ip_ttl, ip_tos;
   u_short ip_id;

   /* The source and destination MAC addresses
    * for the ethernet level
    */ 
   struct libnet_ether_addr *src_ether;
   struct libnet_ether_addr *dst_ether;

   char *interface;
   struct ether_addr *ether_t;
   struct servent *servent;
   u_char *udp_payload;
   u_long udp_payload_l;
   char errbuf[LIBNET_ERRBUF_SIZE];
    
   while ((c = getopt(argc, argv, "i:c:e:p:s:E:P:d:i:u:I:t:T:vh")) != EOF) {
      switch (c) {
         case 'h':
            usage();
            exit(EXIT_SUCCESS);
         case 'v':
            opts[OPT_VERBOSE].set = 1;
            break;
         case 'i':
            interface = optarg;
            opts[OPT_INTERFACE].set = 1;
            break;
         case 'e':
            if ((ether_t = ether_aton(optarg)) == NULL) {
               printf("Invalid source ethernet address: %s\n", optarg);
               return(EXIT_FAILURE);
            }
            if ((src_ether = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
               printf("Couldn't malloc() for src_ether\n");
               return(EXIT_FAILURE);
            }
            memcpy(src_ether->ether_addr_octet, ether_t, sizeof(struct ether_addr));
            opts[OPT_SRCETHER].set = 1;
            break;
         case 'E':
            if ((ether_t = ether_aton(optarg)) == NULL) {
               printf("Invalid ethernet address: %s\n", optarg);
               return(EXIT_FAILURE);
            }
            if ((dst_ether = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
               printf("Couldn't malloc() for dst_ether\n");
               return(EXIT_FAILURE);
            }
            memcpy(dst_ether->ether_addr_octet, ether_t, sizeof(struct ether_addr));
            opts[OPT_DSTETHER].set = 1;
            break;
         case 'p':
            src_port = atoi(optarg);
            opts[OPT_SRCPORTS].set = 1;
            break; 
         case 'P':
            dst_port = atoi(optarg);
            opts[OPT_DSTPORTS].set = 1;
            break;
         case 's':
            src_ip = libnet_name2addr4(libnet, optarg, LIBNET_RESOLVE);
            opts[OPT_SRCIP].set = 1;
            break;
         case 'd':
            dst_ip = libnet_name2addr4(libnet, optarg, LIBNET_RESOLVE);
            opts[OPT_DSTIP].set = 1;
            break;
         case 't':
            ip_ttl = atoi(optarg);
            opts[OPT_IPTTL].set = 1;
            break;
         case 'T':
            ip_tos = atoi(optarg);
            opts[OPT_IPTOS].set = 1;
            break;
         case 'c':
            udp_payload = optarg;
            udp_payload_l = strlen(udp_payload);
            opts[OPT_PAYLOAD].set = 1;
            break;
         case 'I':
            ip_id = atoi(optarg);
            opts[OPT_IPID].set = 1;
            break;
         default:
            usage();
            goto pass;
      }
   }

   /* Ensure that an interface is specified or have libnet guess */
   if (!opts[OPT_INTERFACE].set) {
      usage();
      goto fail;
   }

   /* Depending on whether or not they choose to pick their own MACs,
   * libnet needs to be initialized differently for some reason */
   if (opts[OPT_SRCETHER].set || opts[OPT_DSTETHER].set) {
      if ((libnet = libnet_init(LIBNET_LINK, interface, errbuf)) == NULL) {
         fprintf(stderr, "libnet_init() failed: %s", errbuf);
         goto fail;
      }
   } else {
      if ((libnet = libnet_init(LIBNET_RAW4, interface, errbuf)) == NULL) {
         fprintf(stderr, "libnet_init() failed: %s", errbuf);
         goto fail;
      }
   }

   libnet_seed_prand(libnet);

   /* If they didn't set a source ethernet address, figure out */
   if (!opts[OPT_SRCETHER].set) {
      if ((src_ether = libnet_get_hwaddr(libnet)) == NULL) {
         fprintf(stderr, "libnet_get_hwaddr() failed: %s", errbuf);
         libnet_destroy(libnet);
         goto fail;
      }
   }

   /* If they didn't set a destination ethernet address, figure out */
   if (!opts[OPT_DSTETHER].set) {
      if ((dst_ether = libnet_get_hwaddr(libnet)) == NULL) {
         fprintf(stderr, "libnet_get_hwaddr() failed: %s", errbuf);
         libnet_destroy(libnet);
         goto fail;
      }
   }

   /* If they didn't set a source IP, have libnet figure it out */
   if (!opts[OPT_SRCIP].set) {
      src_ip = libnet_get_ipaddr4(libnet);
      opts[OPT_SRCIP].set = 1;
   } 

   /* If no source port is given, make one up */
   if (!opts[OPT_SRCPORTS].set) {
      src_port = 1024 + (libnet_get_prand(LIBNET_PRu16) % (65535 - 1024));
      opts[OPT_SRCPORTS].set = 1;
   }

   /* If no destination port is given, make one up */
   if (!opts[OPT_DSTPORTS].set) {
      dst_port = 1024 + (libnet_get_prand(LIBNET_PRu16) % (65535 - 1024));
      opts[OPT_DSTPORTS].set = 1;
   }

   /* If no IP id is set, pick a random one */
   if (!opts[OPT_IPID].set) {
      ip_id = libnet_get_prand(LIBNET_PRu16);
      opts[OPT_IPID].set = 1;
   }

   /* If no IP TOS is set, pick 0 */
   if (!opts[OPT_IPTOS].set) {
      ip_tos = 0;
      opts[OPT_IPTOS].set = 1;
   }
   
   /* No payload? Then NULL */
   if (!opts[OPT_PAYLOAD].set) {
      udp_payload = NULL;
      udp_payload_l = 0;
      opts[OPT_PAYLOAD].set = 1;
   }
    
   /* If no TTL is set, default to something sly. */
   if (!opts[OPT_IPTTL].set) {
      /* 255 is too high, others are too low.  I'll take the midground! */
      ip_ttl = 200 + (libnet_get_prand(LIBNET_PR8) % 55);
      opts[OPT_IPTTL].set = 1;
   }
    
   udp = libnet_build_udp(
         src_port,   
         dst_port,   
         LIBNET_UDP_H + udp_payload_l,
         0,
         udp_payload,
         udp_payload_l,
         libnet,
         0);


   if (udp == -1) {
      fprintf(stderr, "Can't build udp: %s\n", libnet_geterror(libnet));
      libnet_destroy(libnet);
      goto fail;
   }
   
   
   ipv4 = libnet_build_ipv4(
            LIBNET_IPV4_H + LIBNET_UDP_H + udp_payload_l,
            ip_tos,
            ip_id,
            IP_DF,
            ip_ttl,
            IPPROTO_UDP,
            0,
            src_ip,
            dst_ip,
            NULL,
            0,
            libnet,
            0);

   if (ipv4 == -1) {
      fprintf(stderr, "Can't build ipv4: %s\n", libnet_geterror(libnet));
      libnet_destroy(libnet);
      goto fail;
   }
   
   /* If they wish to set their own src/dst ethernet addresses
   * we need to actually build the ethernet.  If they don't,
   * libnet does it automagically for us. */ 
   if (opts[OPT_SRCETHER].set || opts[OPT_DSTETHER].set) {
      ether = libnet_build_ethernet(
      dst_ether->ether_addr_octet,
      src_ether->ether_addr_octet,
      ETHERTYPE_IP,
      NULL,
      0,
      libnet,
      0);

      if (ether == -1) {
         fprintf(stderr, "Can't build ethernet: %s\n", libnet_geterror(libnet));
         libnet_destroy(libnet);
         goto fail;
      }
   }

   if ((c = libnet_write(libnet)) == -1) {
      fprintf(stderr, "Couldn't write packet to the wire: %s\n", libnet_geterror(libnet));
   } else {
   if (opts[OPT_VERBOSE].set) {
         if (opts[OPT_SRCETHER].set || opts[OPT_DSTETHER].set) {
            printf("Link Layer: ");
            printf("%s -> ", opts[OPT_SRCETHER].set ? ether_ntoa((struct ether_addr *) src_ether->ether_addr_octet) : interface);
            printf("%s\n",   opts[OPT_DSTETHER].set ? ether_ntoa((struct ether_addr *) dst_ether->ether_addr_octet) : "(auto)");
         }
         printf("  IP Layer: %s -> %s\n", libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE), libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE));
         printf("  IP Layer: TTL: %d TOS: 0x%X ID: %d\n", ip_ttl, ip_tos, ip_id);
         printf(" UDP Layer: SrcPort: %d (%s) ", src_port, (servent = getservbyport(htons(src_port), "udp")) != NULL ? servent->s_name : "unknown"); 
         printf("DstPort: %d (%s)\n", dst_port, (servent = getservbyport(htons(dst_port), "udp")) != NULL ? servent->s_name : "unknown");
         printf(" App Layer: %ld byte payload\n", udp_payload_l);
      }
   }

   libnet_destroy(libnet);
   goto pass;

fail:
   if (opts[OPT_SRCETHER].set) {
      free(src_ether);
   }
   if (opts[OPT_DSTETHER].set) {
      free(dst_ether);
   }
   return(EXIT_FAILURE);

pass:
   if (opts[OPT_SRCETHER].set) {
      free(src_ether);
   }
   if (opts[OPT_DSTETHER].set) {
      free(dst_ether);
   }
   return(EXIT_SUCCESS);
}
