Unity-Technologies / qstat

New official qstat repository
Artistic License 2.0
121 stars 33 forks source link

add Teeworlds master support #5

Closed illwieckz closed 7 years ago

illwieckz commented 9 years ago

We can read https://github.com/teeworlds/teeworlds/blob/master/scripts/tw_api.py

MASTERSERVER_PORT = 8300

There is two way to query master servers:

PACKET_GETLIST = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt"
PACKET_GETLIST2 = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreq2"

Then, the response format differs. We can read get_list and get_list2 functions to understand theses formats.

data, addr = sock.recvfrom(1024)
data = data[14:]
num_servers = len(data) / 6
for n in range(0, num_servers):
    ip = ".".join(map(str, map(ord, data[n*6:n*6+4])))
    port = ord(data[n*6+5]) * 256 + ord(data[n*6+4])
    servers += [[(ip, port), SERVERTYPE_LEGACY]]
data, addr = sock.recvfrom(1400)
data = data[14:]
num_servers = len(data) / 18
for n in range(0, num_servers):
    if data[n*18:n*18+12] == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff":
        ip = ".".join(map(str, map(ord, data[n*18+12:n*18+16])))
    else:
        ip = ":".join(map(str, map(ord, data[n*18:n*18+16])))
    port = (ord(data[n*18+16])<<8) + ord(data[n*18+17])
    servers += [[(ip, port), SERVERTYPE_NORMAL]]

We don't have to return SERVERTYPE, only ip and port, since the -tees qstat function I udpdated recognizes server types.

Master servers can understand the two queries, in this case, they respond a different list of servers.

illwieckz commented 9 years ago

Hi, I have a working proof of concept: :smiley:

query_status_t deal_with_teemaster_packet( struct qserver *server, char *pkt, int pktlen )
{
    char last_char;
    int num_server;
    int len_address_packet = 6;
    int len_address_packe2 = 18;
    int i;
    int shift;
    unsigned int ipv4[4];
    unsigned int port;

    // get the last character
    last_char = pkt[len_tee_masterlist_headerprefix];

    // compare the response without the last character
    if ((0 == memcmp( pkt, tee_masterlist_headerprefix, len_tee_masterlist_headerprefix)))
    {
        // compare if the last character is 't' or '2'
        if (last_char == 't')
        {
            num_server = (pktlen - len_tee_masterlist_header) / (len_address_packet);
            for (i = 0; i < num_server; i++)
            {
                shift = len_tee_masterlist_header + (len_address_packet * i);
                ipv4[0] = (unsigned char) pkt[shift];
                ipv4[1] = (unsigned char) pkt[shift + 1];
                ipv4[2] = (unsigned char) pkt[shift + 2];
                ipv4[3] = (unsigned char) pkt[shift + 3];
                port = (unsigned char) pkt[shift + 4] + ((unsigned char) pkt[shift + 5] << 8);
                printf("PoC, addr: %u.%u.%u.%u:%u\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3], port);
                // stub
            }
        }
        else if (last_char == '2')
        {
            num_server = (pktlen - len_tee_masterlist_header) / (len_address_packe2);
            for (i = 0; i < num_server; i++)
            {
                // stub 
            }
        }

    }
    return (query_status_t) NULL; // stub
}

If I do ./qstat -teem master2.teeworlds.com:8300 It prints:

PoC, addr: 77.82.62.69:8305
PoC, addr: 24.132.121.123:8333
PoC, addr: 24.132.121.123:8309
PoC, addr: 81.30.158.57:8308
PoC, addr: 24.132.121.123:8303
PoC, addr: 195.112.231.229:8312
PoC, addr: 81.30.158.57:8313
PoC, addr: 217.18.138.26:8304
PoC, addr: 81.30.158.57:6050
PoC, addr: 109.74.202.76:8404
PoC, addr: 81.30.158.57:8311
PoC, addr: 81.30.158.57:8888
PoC, addr: 24.132.121.123:8324
PoC, addr: 24.132.121.123:8328
PoC, addr: 81.30.158.57:6053
PoC, addr: 24.132.121.123:8322
PoC, addr: 24.132.121.123:8321
PoC, addr: 81.30.158.57:8314
PoC, addr: 24.132.121.123:8310
PoC, addr: 24.132.121.123:8327
PoC, addr: 109.74.202.76:8408
PoC, addr: 24.132.121.123:8308
PoC, addr: 24.132.121.123:8317
PoC, addr: 24.132.121.123:8320
PoC, addr: 195.112.231.229:8314
PoC, addr: 109.74.202.76:8406
PoC, addr: 24.132.121.123:8306
PoC, addr: 81.30.158.57:8332
PoC, addr: 46.18.200.203:8303
PoC, addr: 81.30.158.57:8373
PoC, addr: 24.132.121.123:8305
PoC, addr: 24.132.121.123:8302
PoC, addr: 195.112.231.229:8311
PoC, addr: 93.79.156.148:8303
PoC, addr: 24.132.121.123:8304

Which is the same than get_list(("master2.teeworlds.com", 8300)) from tw_api.py.

How I populate the server list? What is the internal ip addr and port format in QStat?

Thanks in advance. :smile:

illwieckz commented 9 years ago

my deal_with_teemaster_packet proof of concept now looks like that:

query_status_t deal_with_teemaster_packet(struct qserver *server, char *rawpkt, int rawpktlen)
{
    int i;
    int skip;
    int num_server;
    int len_address_packet = 6;
    int len_address_packe2 = 18;
    char last_char;
    unsigned int ipv4[4];
    unsigned int port;

    if (len_teemaster_list_headerprefix > rawpktlen) {
        return PKT_ERROR;
    }

    /* get the last character */
    last_char = rawpkt[len_teemaster_list_headerprefix];

    /* compare the response without the last character */
    if ((memcmp(rawpkt, teemaster_list_headerprefix, len_teemaster_list_headerprefix)) != 0) {
        return PKT_ERROR;
    }

    /* legacy server format, only ipv4 addresses */
    if (last_char == 't') {
        num_server = (rawpktlen - len_teemaster_list_header) / (len_address_packet);
        for (i = 0; i < num_server; i++) {
            skip = len_teemaster_list_header + (len_address_packet * i);
            if (skip + len_address_packet > rawpktlen) {
                return PKT_ERROR;
            }

            ipv4[0] = (unsigned char) rawpkt[skip];
            ipv4[1] = (unsigned char) rawpkt[skip + 1];
            ipv4[2] = (unsigned char) rawpkt[skip + 2];
            ipv4[3] = (unsigned char) rawpkt[skip + 3];
            port = (unsigned char) rawpkt[skip + 4] + ((unsigned char) rawpkt[skip + 5] << 8);
            printf("PoC, addr: %u.%u.%u.%u:%u\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3], port);
            /* stub */
        }
    }

    /* normal server format */
    else if (last_char == '2') {
        num_server = (rawpktlen - len_teemaster_list_header) / (len_address_packe2);
        for (i = 0; i < num_server; i++) {
            skip = len_teemaster_list_header + (len_address_packe2 * i);
            if (skip + len_address_packe2 > rawpktlen) {
                return PKT_ERROR;
            }

            /* ipv4 address */
            if ((0 == memcmp(rawpkt + skip, ipv4_header, len_ipv4_header))) {
                skip = len_teemaster_list_header + (len_address_packe2 * i);
                ipv4[0] = (unsigned char) rawpkt[skip + len_ipv4_header];
                ipv4[1] = (unsigned char) rawpkt[skip + len_ipv4_header + 1];
                ipv4[2] = (unsigned char) rawpkt[skip + len_ipv4_header + 2];
                ipv4[3] = (unsigned char) rawpkt[skip + len_ipv4_header + 3];
                port = ((unsigned char) rawpkt[skip + len_ipv4_header + 4] << 8) + (unsigned char) rawpkt[skip + len_ipv4_header + 5];
                printf("PoC, addr: %u.%u.%u.%u:%u\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3], port);
                /* stub */
            }
            /* ipv6 address */
            else {
                skip = len_teemaster_list_header + (len_address_packe2 * i);
                /* stub */
            }
            else {
                return PKT_ERROR;
            }
        }
    }

    /* unknown server format */
    else {
        return PKT_ERROR;
    }

    return (query_status_t) NULL; /* stub */
}

This proof of concept works for both legacy and normal servers, but I don't know how to populate the server list and what is the qstat internal ip format.