Ticket #2305: stk_stun_wip_2016-08-28.diff

File stk_stun_wip_2016-08-28.diff, 13.9 KB (added by fcxSanya, 8 years ago)
  • binaries/data/mods/public/gui/gamesetup/gamesetup.js

     
    165165var g_ServerPort;
    166166
    167167/**
     168 * STUN endpoint.
     169 */
     170var g_StunEndpoint;
     171
     172/**
    168173 * States whether the GUI is currently updated in response to network messages instead of user input
    169174 * and therefore shouldn't send further messages to the network.
    170175 */
     
    247252    g_IsController = attribs.type != "client";
    248253    g_ServerName = attribs.serverName;
    249254    g_ServerPort = attribs.serverPort;
     255    g_StunEndpoint = attribs.stunEndpoint;
    250256
    251257    // Replace empty playername when entering a singleplayermatch for the first time
    252258    if (!g_IsNetworked)
     
    19911997    let stanza = {
    19921998        "name": g_ServerName,
    19931999        "port": g_ServerPort,
     2000        "stunEndpoint": g_StunEndpoint,
    19942001        "mapName": g_GameAttributes.map,
    19952002        "niceMapName": getMapDisplayName(g_GameAttributes.map),
    19962003        "mapSize": mapSize,
  • binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js

     
    214214                        Engine.SwitchGuiPage("page_gamesetup.xml", {
    215215                            "type": g_GameType,
    216216                            "serverName": g_ServerName,
    217                             "serverPort": g_ServerPort
     217                            "serverPort": g_ServerPort,
     218                            "stunEndpoint": g_StunEndpoint
    218219                        });
    219220                        return; // don't process any more messages - leave them for the game GUI loop
    220221                    }
     
    277278        return false;
    278279    }
    279280
     281    if (Engine.HasXmppClient() && g_UseStun) {
     282        g_StunEndpoint = Engine.FindStunEndpoint(port);
     283    }
     284
    280285    try
    281286    {
    282287        if (g_UserRating)
  • binaries/data/mods/public/gui/lobby/lobby.js

     
    687687        return;
    688688    }
    689689
     690    let ip;
     691    let port;
     692    if (g_UseStun && game.stunEndpoint !== undefined) {
     693        ip = game.stunEndpoint.ip;
     694        port = game.stunEndpoint.port;
     695    }
     696    else {
     697        ip = game.ip;
     698        port = game.port;
     699    }
     700
    690701    Engine.PushGuiPage("page_gamesetup_mp.xml", {
    691702        "multiplayerGameType": "join",
    692         "ip": game.ip,
    693         "port": game.port,
     703        "ip": ip,
     704        "port": port,
    694705        "name": g_Username,
    695706        "rating": g_UserRating
    696707    });
  • source/gui/scripting/ScriptFunctions.cpp

     
    245245    return SavedGames::GetEngineInfo(*(pCxPrivate->pScriptInterface));
    246246}
    247247
     248JS::Value FindStunEndpoint(ScriptInterface::CxPrivate* pCxPrivate, int port)
     249{
     250    return StunClient::FindStunEndpoint(*(pCxPrivate->pScriptInterface), port);
     251}
     252
    248253void StartNetworkGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
    249254{
    250255    ENSURE(g_NetClient);
     
    10471052    scriptInterface.RegisterFunction<void, int, &SendNetworkReady>("SendNetworkReady");
    10481053    scriptInterface.RegisterFunction<JS::Value, &GetAIs>("GetAIs");
    10491054    scriptInterface.RegisterFunction<JS::Value, &GetEngineInfo>("GetEngineInfo");
     1055    scriptInterface.RegisterFunction<JS::Value, &FindStunEndpoint>("FindStunEndpoint");
    10501056
    10511057    // Saved games
    10521058    scriptInterface.RegisterFunction<JS::Value, std::wstring, &StartSavedGame>("StartSavedGame");
  • source/network/StunClient.cpp

     
     1/*
     2 * Note: the code is extracted from SuperTuxKart:
     3 * https://github.com/supertuxkart/stk-code
     4 */
     5
     6#include <cstdio>
     7
     8#include <stdlib.h>
     9#include <string.h>
     10
     11#include <sys/types.h>
     12#include <sys/socket.h>
     13#include <netdb.h>
     14
     15#include <assert.h>
     16
     17#include <enet/enet.h>
     18
     19#include <string>
     20#include <vector>
     21
     22ENetHost* m_transaction_host;
     23unsigned int m_stun_server_ip;
     24const uint32_t m_stun_magic_cookie = 0x2112A442;
     25uint8_t m_stun_tansaction_id[12];
     26
     27// Discovered STUN endpoint
     28uint32_t m_ip;
     29uint16_t m_port;
     30
     31void addUInt16(std::vector<uint8_t>& m_buffer, const uint16_t value)
     32{
     33    m_buffer.push_back((value >> 8) & 0xff);
     34    m_buffer.push_back(value & 0xff);
     35}   // addUInt16
     36
     37// ------------------------------------------------------------------------
     38/** Adds unsigned 32 bit integer. */
     39void addUInt32(std::vector<uint8_t>& m_buffer, const uint32_t& value)
     40{
     41    m_buffer.push_back((value >> 24) & 0xff);
     42    m_buffer.push_back((value >> 16) & 0xff);
     43    m_buffer.push_back((value >>  8) & 0xff);
     44    m_buffer.push_back( value        & 0xff);
     45}   // addUInt32
     46
     47template<typename T, size_t n>
     48    T getFromBuffer(std::vector<uint8_t> m_buffer, int& m_current_offset)
     49    {
     50        int a = n;
     51        T result = 0;
     52        m_current_offset += n;
     53        int offset = m_current_offset -1;
     54        while (a--)
     55        {
     56            result <<= 8; // offset one byte
     57                          // add the data to result
     58            result += m_buffer[offset - a];
     59        }
     60        return result;
     61    }
     62
     63// ----------------------------------------------------------------------------
     64/** Creates a STUN request and sends it to a STUN server.
     65 *  See https://tools.ietf.org/html/rfc5389#section-6
     66 *  for details on the message structure.
     67 *  The request is send through m_transaction_host, from which the answer
     68 *  will be retrieved by parseStunResponse()
     69 */
     70void createStunRequest()
     71{
     72    // TODO: make STUN server configurable
     73    const char* server_name = "stun1.voiceeclipse.net";
     74    printf("GetPublicAddress: Using STUN server %s\n", server_name);
     75
     76    struct addrinfo hints, *res;
     77
     78    memset(&hints, 0, sizeof hints);
     79    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
     80    hints.ai_socktype = SOCK_STREAM;
     81
     82    // Resolve the stun server name so we can send it a STUN request
     83    int status = getaddrinfo(server_name, NULL, &hints, &res);
     84    if (status != 0)
     85    {
     86        printf("GetPublicAddress: Error in getaddrinfo: %s\n",
     87               gai_strerror(status));
     88        return;
     89    }
     90    // documentation says it points to "one or more addrinfo structures"
     91    assert(res != NULL);
     92    struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr);
     93    m_stun_server_ip = ntohl(current_interface->sin_addr.s_addr);
     94
     95    // Create a new socket for the stun server.
     96    m_transaction_host = enet_host_create(NULL, 1, 1, 0, 0);
     97    if (m_transaction_host == NULL) {
     98        printf("Failed to create enet host");
     99        return;
     100    }
     101
     102    // Assemble the message for the stun server
     103    std::vector<uint8_t> m_buffer;
     104
     105    // bytes 0-1: the type of the message
     106    // bytes 2-3: message length added to header (attributes)
     107    uint16_t message_type = 0x0001; // binding request
     108    uint16_t message_length = 0x0000;
     109    addUInt16(m_buffer, message_type);
     110    addUInt16(m_buffer, message_length);
     111    addUInt32(m_buffer, 0x2112A442);
     112    // bytes 8-19: the transaction id
     113    for (int i = 0; i < 12; i++)
     114    {
     115        uint8_t random_byte = rand() % 256;
     116        m_buffer.push_back(random_byte);
     117        m_stun_tansaction_id[i] = random_byte;
     118    }
     119    //m_buffer.push_back(0); -- this breaks STUN message
     120
     121    static const int m_stun_server_port = 3478;
     122
     123    // sendRawPacket
     124    struct sockaddr_in to;
     125    int to_len = sizeof(to);
     126    memset(&to,0,to_len);
     127
     128    to.sin_family = AF_INET;
     129    to.sin_port = htons(m_stun_server_port);
     130    to.sin_addr.s_addr = htonl(m_stun_server_ip);
     131
     132    printf("GetPublicAddress: Sending STUN request to: %d.%d.%d.%d:%d\n",
     133                    ((m_stun_server_ip >> 24) & 0xff), ((m_stun_server_ip >> 16) & 0xff),
     134                    ((m_stun_server_ip >>  8) & 0xff), ((m_stun_server_ip >>  0) & 0xff),
     135                    m_stun_server_port);
     136
     137    int send_result = sendto(m_transaction_host->socket, (char*)(m_buffer.data()), (int)m_buffer.size(), 0,
     138           (sockaddr*)&to, to_len);
     139    printf("GetPublicAddress: sendto result: %d\n", send_result);
     140
     141    freeaddrinfo(res);
     142}   // createStunRequest
     143
     144// ----------------------------------------------------------------------------
     145/**
     146 * Gets the response from the STUN server, checks it for its validity and
     147 * then parses the answer into address and port
     148 * \return "" if the address could be parsed or an error message
     149*/
     150std::string parseStunResponse()
     151{
     152    // TransportAddress sender;
     153    const int LEN = 2048;
     154    char buffer[LEN];
     155
     156
     157    // receiveRawPacket
     158    // int len = m_transaction_host->receiveRawPacket(buffer, LEN, &sender, 2000);
     159    int max_tries = 2000;
     160
     161    memset(buffer, 0, LEN);
     162
     163    struct sockaddr_in addr;
     164    socklen_t from_len = sizeof(addr);
     165
     166    int err;
     167    int len = recvfrom(m_transaction_host->socket, buffer, LEN, 0,
     168                       (struct sockaddr*)(&addr), &from_len);
     169
     170    int count = 0;
     171    // wait to receive the message because enet sockets are non-blocking
     172    while(len < 0 && (count<max_tries || max_tries==-1) )
     173    {
     174        count++;
     175        usleep(1000);
     176        len = recvfrom(m_transaction_host->socket, buffer, LEN, 0,
     177                       (struct sockaddr*)(&addr), &from_len);
     178    }
     179    if (len == -1) {
     180        err = errno;
     181    }
     182    printf("GetPublicAddress: recvfrom result: %d\n", len);
     183    if (len == -1) {
     184        printf("GetPublicAddress: recvfrom error: %d\n", err);
     185    }
     186
     187
     188    // No message received
     189    if (len < 0)
     190      return "No message received";
     191
     192    uint32_t sender_ip = ntohl((uint32_t)(addr.sin_addr.s_addr));
     193    uint16_t sender_port = ntohs(addr.sin_port);
     194
     195
     196    if(sender_ip != m_stun_server_ip)
     197    {
     198        printf("GetPublicAddress: Received stun response from different address: %d:%d (%d.%d.%d.%d:%d) %s\n",
     199                addr.sin_addr.s_addr, addr.sin_port,
     200                ((sender_ip >> 24) & 0xff), ((sender_ip >> 16) & 0xff),
     201                ((sender_ip >>  8) & 0xff), ((sender_ip >>  0) & 0xff),
     202                sender_port, buffer);
     203    }
     204
     205    if (len<0)
     206        return "STUN response contains no data at all";
     207
     208    // Convert to network string.
     209    // NetworkString datas((uint8_t*)buffer, len);
     210    std::vector<uint8_t> m_buffer;
     211    int m_current_offset;
     212
     213    m_buffer.resize(len);
     214    memcpy(m_buffer.data(), (uint8_t*)buffer, len);
     215
     216    m_current_offset = 0;
     217    // m_current_offset = 5;   // ignore type and token -- this breaks STUN response processing
     218
     219    // check that the stun response is a response, contains the magic cookie
     220    // and the transaction ID
     221    if (getFromBuffer<uint16_t, 2>(m_buffer, m_current_offset) != 0x0101)
     222        return "STUN response has incorrect type";
     223    int message_size = getFromBuffer<uint16_t, 2>(m_buffer, m_current_offset);
     224    if (getFromBuffer<uint32_t, 4>(m_buffer, m_current_offset) != m_stun_magic_cookie)
     225    {
     226        return "STUN response doesn't contain the magic cookie";
     227    }
     228
     229    for (int i = 0; i < 12; i++)
     230    {
     231        if (m_buffer[m_current_offset++] != m_stun_tansaction_id[i])
     232            return "STUN response doesn't contain the transaction ID";
     233    }
     234
     235    printf("GetPublicAddress: The STUN server responded with a valid answer\n");
     236
     237    // The stun message is valid, so we parse it now:
     238    if (message_size == 0)
     239        return "STUN response does not contain any information.";
     240    if (message_size < 4) // cannot even read the size
     241        return "STUN response is too short.";
     242
     243    // Those are the port and the address to be detected
     244
     245    int pos = 20;
     246    while (true)
     247    {
     248        int type = getFromBuffer<uint16_t, 2>(m_buffer, m_current_offset);
     249        int size = getFromBuffer<uint16_t, 2>(m_buffer, m_current_offset);
     250        if (type == 0 || type == 1)
     251        {
     252            assert(size == 8);
     253            m_current_offset++;  // skip 1 byte
     254            assert(m_buffer[m_current_offset++] == 0x01); // Family IPv4 only
     255            m_port = getFromBuffer<uint16_t, 2>(m_buffer, m_current_offset);
     256            m_ip   = getFromBuffer<uint32_t, 4>(m_buffer, m_current_offset);
     257            // finished parsing, we know our public transport address
     258            printf("GetPublicAddress: The public address has been found: %d.%d.%d.%d:%d\n",
     259                ((ip >> 24) & 0xff), ((ip >> 16) & 0xff),
     260                ((ip >>  8) & 0xff), ((ip >>  0) & 0xff),
     261                port);
     262            break;
     263        }   // type = 0 or 1
     264        // datas.skip(4 + size);
     265        m_current_offset += 4 + size;
     266        assert(m_current_offset >=0 &&
     267               m_current_offset < (int)m_buffer.size());
     268
     269        message_size -= 4 + size;
     270        if (message_size == 0)
     271            return "STUN response is invalid.";
     272        if (message_size < 4) // cannot even read the size
     273            return "STUN response is invalid.";
     274    }   // while true
     275
     276    return "";
     277}   // parseStunResponse
     278
     279JS::Value FindStunEndpoint(ScriptInterface& scriptInterface, int port)
     280{
     281    createStunRequest();
     282    std::string parse_result = parseStunResponse();
     283    if (!parse_result.empty())
     284            printf("Parse error: %s\n", parse_result.c_str());
     285
     286
     287    JSContext* cx = scriptInterface.GetContext();
     288    JSAutoRequest rq(cx);
     289
     290    JS::RootedValue stunEndpoint(cx);
     291    scriptInterface.Eval("({})", &stunEndpoint);
     292    scriptInterface.SetProperty(stunEndpoint, "ip", m_ip);
     293    scriptInterface.SetProperty(stunEndpoint, "port", m_port);
     294    return stunEndpoint;
     295}
     296
  • source/network/StunClient.h

     
     1#ifndef STUNCLIENT_H
     2#define STUNCLIENT_H
     3
     4namespace StunClient
     5{
     6
     7JS::Value FindStunEndpoint(ScriptInterface& scriptInterface, int port);
     8
     9}
     10
     11#endif  // STUNCLIENT_H