1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2013-2015 SuperTuxKart-Team
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #include "network/protocols/connect_to_server.hpp"
20 
21 #include "config/user_config.hpp"
22 #include "io/file_manager.hpp"
23 #include "io/xml_node.hpp"
24 #include "guiengine/engine.hpp"
25 #include "network/crypto.hpp"
26 #include "network/event.hpp"
27 #include "network/network.hpp"
28 #include "network/network_config.hpp"
29 #include "network/protocols/client_lobby.hpp"
30 #include "network/protocol_manager.hpp"
31 #include "network/servers_manager.hpp"
32 #include "network/server.hpp"
33 #include "network/child_loop.hpp"
34 #include "network/socket_address.hpp"
35 #include "network/stk_ipv6.hpp"
36 #include "network/stk_host.hpp"
37 #include "network/stk_peer.hpp"
38 #include "online/xml_request.hpp"
39 #include "states_screens/online/networking_lobby.hpp"
40 #include "utils/time.hpp"
41 #include "utils/log.hpp"
42 #include "utils/string_utils.hpp"
43 #include "utils/translation.hpp"
44 
45 #ifdef ANDROID
46 #include <jni.h>
47 #include "SDL_system.h"
48 #include "utils/utf8/unchecked.h"
49 #endif
50 
51 #ifdef WIN32
52 #  include <windns.h>
53 #  include <ws2tcpip.h>
54 #ifndef __MINGW32__
55 #  pragma comment(lib, "dnsapi.lib")
56 #endif
57 #else
58 #  include <arpa/nameser.h>
59 #  include <arpa/nameser_compat.h>
60 #  include <netdb.h>
61 #  include <netinet/in.h>
62 #  include <resolv.h>
63 #endif
64 
65 #include <algorithm>
66 // ============================================================================
67 ENetAddress ConnectToServer::m_server_address;
68 int ConnectToServer::m_retry_count = 0;
69 bool ConnectToServer::m_done_intecept = false;
70 // ----------------------------------------------------------------------------
71 /** Specify server to connect to.
72  *  \param server Server to connect to (if nullptr than we use quick play).
73  */
ConnectToServer(std::shared_ptr<Server> server)74 ConnectToServer::ConnectToServer(std::shared_ptr<Server> server)
75                : Protocol(PROTOCOL_CONNECTION)
76 {
77     m_server_address = {};
78     if (server)
79         m_server = server;
80 }   // ConnectToServer(server, host)
81 
82 // ----------------------------------------------------------------------------
83 /** Destructor.
84  */
~ConnectToServer()85 ConnectToServer::~ConnectToServer()
86 {
87     auto cl = LobbyProtocol::get<ClientLobby>();
88     if (!cl && m_server && m_server->supportsEncryption())
89     {
90         auto request = std::make_shared<Online::XMLRequest>();
91         NetworkConfig::get()->setServerDetails(request,
92             "clear-user-joined-server");
93         request->queue();
94     }
95 }   // ~ConnectToServer
96 
97 // ----------------------------------------------------------------------------
98 /** Initialise the protocol.
99  */
setup()100 void ConnectToServer::setup()
101 {
102     Log::info("ConnectToServer", "SETUP");
103     // In case of LAN or client-server we already have the server's
104     // and our ip address, so we can immediately start requesting a connection.
105     if (NetworkConfig::get()->isLAN())
106     {
107         m_state = GOT_SERVER_ADDRESS;
108         // For graphical client server the IPv6 socket is handled by server
109         // process
110         if (!STKHost::get()->isClientServer())
111         {
112             if (m_server->useIPV6Connection())
113                 setIPv6Socket(1);
114             else
115                 setIPv6Socket(0);
116         }
117     }
118     else
119         m_state = SET_PUBLIC_ADDRESS;
120 }   // setup
121 
122 // ----------------------------------------------------------------------------
getClientServerInfo()123 void ConnectToServer::getClientServerInfo()
124 {
125     assert(m_server);
126     // Allow up to 10 seconds for the separate process to fully start-up
127     bool started = false;
128     uint64_t timeout = StkTime::getMonoTimeMs() + 10000;
129     uint16_t port = 0;
130     unsigned server_id = 0;
131     ChildLoop* sl = STKHost::get()->getChildLoop();
132     assert(sl);
133     while (!ProtocolManager::lock()->isExiting() &&
134         StkTime::getMonoTimeMs() < timeout)
135     {
136         port = sl->getPort();
137         server_id = sl->getServerOnlineId();
138         started = port != 0;
139         if (started)
140             break;
141         StkTime::sleep(1);
142     }
143     if (!started)
144     {
145         Log::error("ConnectToServer",
146             "Separate server process failed to started");
147         m_state = DONE;
148         return;
149     }
150     else
151     {
152         assert(port != 0);
153         m_server->setAddress(SocketAddress("127.0.0.1", port));
154         m_server->setPrivatePort(port);
155         // The server will decide if to use IPv6 socket
156         if (isIPv6Socket())
157         {
158             m_server->setIPV6Address(SocketAddress("::1", port));
159             m_server->setIPV6Connection(true);
160         }
161         if (server_id != 0)
162         {
163             m_server->setSupportsEncryption(true);
164             m_server->setServerId(server_id);
165         }
166     }
167 }   // getClientServerInfo
168 
169 // ----------------------------------------------------------------------------
asynchronousUpdate()170 void ConnectToServer::asynchronousUpdate()
171 {
172     if (STKHost::get()->isClientServer() &&
173         m_server->getAddress().getPort() == 0)
174     {
175         getClientServerInfo();
176     }
177 
178     switch(m_state.load())
179     {
180         case SET_PUBLIC_ADDRESS:
181         {
182             if (!m_server)
183             {
184                 auto server_list =
185                     ServersManager::get()->getWANRefreshRequest();
186                 while (!server_list->m_list_updated)
187                 {
188                     if (ProtocolManager::lock()->isExiting())
189                         return;
190                     StkTime::sleep(1);
191                 }
192                 auto& servers = server_list->m_servers;
193 
194                 // Remove password protected servers
195                 servers.erase(std::remove_if(servers.begin(), servers.end(), []
196                     (const std::shared_ptr<Server> a)->bool
197                     {
198                         return a->isPasswordProtected();
199                     }), servers.end());
200 
201                 if (!servers.empty())
202                 {
203                     // For quick play we choose the server with the shortest
204                     // distance and not empty and full server
205                     std::sort(servers.begin(), servers.end(), []
206                         (const std::shared_ptr<Server> a,
207                         const std::shared_ptr<Server> b)->bool
208                         {
209                             return a->getDistance() < b->getDistance();
210                         });
211                     std::stable_partition(servers.begin(), servers.end(), []
212                         (const std::shared_ptr<Server> a)->bool
213                         {
214                             return a->getCurrentPlayers() != 0 &&
215                                 a->getCurrentPlayers() != a->getMaxPlayers();
216                         });
217                     m_server = servers[0];
218                 }
219                 else
220                 {
221                     // Shutdown STKHost (go back to online menu too)
222                     STKHost::get()->setErrorMessage(_("No quick play server available."));
223                     STKHost::get()->requestShutdown();
224                     m_state = EXITING;
225                     return;
226                 }
227                 servers.clear();
228             }
229             // Always use IPv6 connection for IPv6 only server
230             if (m_server->getAddress().isUnset() &&
231                 NetworkConfig::get()->getIPType() != NetworkConfig::IP_V4)
232                 m_server->setIPV6Connection(true);
233 
234             // Auto enable IPv6 socket in client with NAT64, then we convert
235             // the IPv4 address to NAT64 one in GOT_SERVER_ADDRESS
236             bool ipv6_socket = m_server->useIPV6Connection() ||
237                 NetworkConfig::get()->getIPType() == NetworkConfig::IP_V6_NAT64;
238             if (STKHost::get()->getNetwork()->isIPv6Socket() != ipv6_socket)
239             {
240                 // Free the bound socket first
241                 delete STKHost::get()->getNetwork();
242                 setIPv6Socket(ipv6_socket ? 1 : 0);
243                 ENetAddress addr = {};
244                 addr.port = NetworkConfig::get()->getClientPort();
245                 auto new_network = new Network(/*peer_count*/1,
246                     /*channel_limit*/EVENT_CHANNEL_COUNT,
247                     /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &addr,
248                     true/*change_port_if_bound*/);
249                 STKHost::get()->replaceNetwork(new_network);
250             }
251 
252             if (m_server->supportsEncryption())
253             {
254                 STKHost::get()->setPublicAddress(
255                     m_server->useIPV6Connection() ? AF_INET6 : AF_INET);
256                 if (STKHost::get()->getValidPublicAddress().empty() ||
257                     registerWithSTKServer() == false)
258                 {
259                     // Set to DONE will stop STKHost is not connected
260                     m_state = DONE;
261                     break;
262                 }
263             }
264             m_state = GOT_SERVER_ADDRESS;
265             break;
266         }
267         case GOT_SERVER_ADDRESS:
268         {
269             // Convert to a NAT64 address from IPv4
270             if (!m_server->useIPV6Connection() &&
271                 NetworkConfig::get()->getIPType() == NetworkConfig::IP_V6_NAT64)
272             {
273                 // From IPv4
274                 std::string addr_string =
275                     m_server->getAddress().toString(false/*show_port*/);
276                 addr_string =
277                     NetworkConfig::get()->getNAT64Prefix() + addr_string;
278                 SocketAddress nat64(addr_string,
279                     m_server->getAddress().getPort());
280                 if (nat64.isUnset() || !nat64.isIPv6())
281                 {
282                     Log::error("ConnectToServer", "Failed to synthesize IPv6 "
283                         "address from %s", addr_string.c_str());
284                     STKHost::get()->requestShutdown();
285                     m_state = EXITING;
286                     return;
287                 }
288                 m_server->setIPV6Address(nat64);
289                 m_server->setIPV6Connection(true);
290             }
291 
292             // Detect port from possible connect-now or enter server address
293             // dialog
294             if ((m_server->useIPV6Connection() &&
295                 m_server->getIPV6Address()->getPort() == 0) ||
296                 (!m_server->useIPV6Connection() &&
297                 m_server->getAddress().getPort() == 0))
298             {
299                 if (!detectPort())
300                     return;
301             }
302 
303             if (!STKHost::get()->getPublicAddress().isUnset() &&
304                 !STKHost::get()->isClientServer() &&
305                 m_server->getAddress().getIP() ==
306                 STKHost::get()->getPublicAddress().getIP())
307             {
308                 Log::info("ConnectToServer", "Server is in the same lan");
309                 std::string str_msg("connection-request");
310                 BareNetworkString message(str_msg +
311                     StringUtils::toString(m_server->getPrivatePort()));
312                 // If use lan connection for wan server, send to all broadcast
313                 // addresses
314                 for (auto& addr : ServersManager::get()
315                     ->getBroadcastAddresses(isIPv6Socket()))
316                 {
317                     for (int i = 0; i < 5; i++)
318                     {
319                         STKHost::get()->sendRawPacket(message, addr);
320                         StkTime::sleep(1);
321                     }
322                 }
323             }
324             // 30 seconds connecting timeout total, use another port to try
325             // direct connection to server first, if failed than use the one
326             // that has stun mapped, the first 8 seconds allow the server to
327             // start the connect to peer protocol first before the port is
328             // remapped.
329             if (tryConnect(2000, 4, true/*another_port*/, isIPv6Socket()))
330                 break;
331             if (!tryConnect(2000, 11, false/*another_port*/, isIPv6Socket()))
332                 m_state = DONE;
333             break;
334         }
335         case DONE:
336         case EXITING:
337             break;
338     }
339 }   // asynchronousUpdate
340 
341 // ----------------------------------------------------------------------------
update(int ticks)342 void ConnectToServer::update(int ticks)
343 {
344     switch(m_state.load())
345     {
346         case GOT_SERVER_ADDRESS:
347         {
348             // Make sure lobby display the quick play server name
349             assert(m_server);
350             if (!GUIEngine::isNoGraphics())
351                 NetworkingLobby::getInstance()->setJoinedServer(m_server);
352             break;
353         }
354         case DONE:
355         {
356             // lobby room protocol if we're connected only
357             if (STKHost::get()->getPeerCount() > 0 &&
358                 STKHost::get()->getServerPeerForClient()->isConnected())
359             {
360                 m_server->saveServer();
361                 // Let main thread create ClientLobby for better
362                 // synchronization with GUI
363                 NetworkConfig::get()->clearActivePlayersForClient();
364                 auto cl = LobbyProtocol::create<ClientLobby>(m_server);
365                 STKHost::get()->startListening();
366                 cl->requestStart();
367             }
368             if (STKHost::get()->getPeerCount() == 0)
369             {
370                 // Shutdown STKHost (go back to online menu too)
371                 core::stringw err =
372                     _("Cannot connect to server %s.", m_server->getName());
373                 if (!m_error_msg.empty())
374                 {
375                     err += L"\n";
376                     err += m_error_msg;
377                 }
378                 STKHost::get()->setErrorMessage(err);
379                 STKHost::get()->requestShutdown();
380             }
381             requestTerminate();
382             m_state = EXITING;
383             break;
384         }
385         default:
386             break;
387     }
388 }   // update
389 
390 // ----------------------------------------------------------------------------
391 /** Intercept callback in enet to allow change server address and port if
392  *  needed (Happens when there is firewall in between)
393  */
interceptCallback(ENetHost * host,ENetEvent * event)394 int ConnectToServer::interceptCallback(ENetHost* host, ENetEvent* event)
395 {
396     if (m_done_intecept)
397         return 0;
398     // The first two bytes of a valid ENet protocol packet will never be 0xFFFF
399     // and then try decode the string "aloha-stk"
400     if (host->receivedDataLength == 12 &&
401         host->receivedData[0] == 0xFF && host->receivedData[1]  == 0xFF &&
402         host->receivedData[2] == 0x09 && host->receivedData[3] == 'a' &&
403         host->receivedData[4]  == 'l' && host->receivedData[5] == 'o' &&
404         host->receivedData[6] == 'h' && host->receivedData[7] == 'a' &&
405         host->receivedData[8] == '-' && host->receivedData[9] == 's' &&
406         host->receivedData[10] == 't' && host->receivedData[11] == 'k')
407     {
408 #ifdef ENABLE_IPV6
409         if (enet_ip_not_equal(host->receivedAddress.host, m_server_address.host) ||
410 #else
411         if (host->receivedAddress.host != m_server_address.host ||
412 #endif
413             host->receivedAddress.port != m_server_address.port)
414         {
415             SocketAddress new_address(host->receivedAddress);
416             Log::info("ConnectToServer", "Using new server address %s",
417                 new_address.toString().c_str());
418             m_retry_count = 15;
419             m_server_address = host->receivedAddress;
420             m_done_intecept = true;
421             return 1;
422         }
423     }
424     // 0 means let enet handle this packet
425     return 0;
426 }   // interceptCallback
427 
428 // ----------------------------------------------------------------------------
tryConnect(int timeout,int retry,bool another_port,bool ipv6)429 bool ConnectToServer::tryConnect(int timeout, int retry, bool another_port,
430                                  bool ipv6)
431 {
432     m_retry_count = retry;
433     ENetEvent event;
434     ENetAddress ea = {};
435     Network* nw = another_port ? new Network(/*peer_count*/1,
436         /*channel_limit*/EVENT_CHANNEL_COUNT,
437         /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea,
438         true/*change_port_if_bound*/) : STKHost::get()->getNetwork();
439     assert(nw);
440 
441     m_done_intecept = false;
442     nw->getENetHost()->intercept = ConnectToServer::interceptCallback;
443 
444     const SocketAddress* sa;
445     if (ipv6)
446     {
447         sa = m_server->getIPV6Address();
448         if (!sa)
449             return false;
450     }
451     else
452         sa = &m_server->getAddress();
453     m_server_address = sa->toENetAddress();
454 
455     while (--m_retry_count >= 0 && !ProtocolManager::lock()->isExiting())
456     {
457         std::string connecting_address =
458             SocketAddress(m_server_address).toString();
459         ENetPeer* p = nw->connectTo(m_server_address);
460         if (!p)
461             break;
462         Log::info("ConnectToServer", "Trying connecting to %s from port %d, "
463             "retry remain: %d", connecting_address.c_str(),
464             nw->getPort(), m_retry_count);
465         while (enet_host_service(nw->getENetHost(), &event, timeout) != 0)
466         {
467             if (event.type == ENET_EVENT_TYPE_CONNECT)
468             {
469                 Log::info("ConnectToServer", "Connected to %s",
470                     connecting_address.c_str());
471                 nw->getENetHost()->intercept = NULL;
472                 STKHost::get()->initClientNetwork(event, nw);
473                 m_state = DONE;
474                 return true;
475             }
476         }
477         // Reset old peer in case server address differs due to intercept
478         enet_peer_reset(p);
479     }
480     if (another_port)
481         delete nw;
482     return false;
483 }   // tryConnect
484 
485 // ----------------------------------------------------------------------------
486 /** Register this client with the STK server.
487  */
registerWithSTKServer()488 bool ConnectToServer::registerWithSTKServer()
489 {
490     // Our public address is now known, register details with
491     // STK server
492     const SocketAddress& addr = STKHost::get()->getPublicAddress();
493     auto request = std::make_shared<Online::XMLRequest>();
494     NetworkConfig::get()->setServerDetails(request, "join-server-key");
495     request->addParameter("server-id", m_server->getServerId());
496     request->addParameter("address", addr.getIP());
497     request->addParameter("address-ipv6",
498         STKHost::get()->getPublicIPv6Address());
499     request->addParameter("port", addr.getPort());
500 
501     Crypto::initClientAES();
502     request->addParameter("aes-key", Crypto::getClientKey());
503     request->addParameter("aes-iv", Crypto::getClientIV());
504 
505     Log::info("ConnectToServer", "Registering addr %s",
506         STKHost::get()->getValidPublicAddress().c_str());
507 
508     // This can be done blocking: till we are registered with the
509     // stk server, there is no need to to react to any other
510     // network requests
511     request->executeNow();
512 
513     const XMLNode* result = request->getXMLData();
514 
515     std::string success;
516     if(result->get("success", &success) && success == "yes")
517     {
518         Log::debug("ConnectToServer", "Address registered successfully.");
519         return true;
520     }
521     else
522     {
523         m_error_msg = request->getInfo();
524         Log::error("ConnectToServer", "Failed to register client address: %s",
525             StringUtils::wideToUtf8(m_error_msg).c_str());
526         return false;
527     }
528 }   // registerWithSTKServer
529 
530 #ifdef ANDROID
531 auto get_txt_record = [](const core::stringw& name)->std::vector<std::string>
__anonacdf4e120402(const core::stringw& name)532 {
533     std::vector<std::string> result;
534     JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
535     if (env == NULL)
536     {
537         Log::error("ConnectToServer",
538             "getDNSSrvRecords unable to SDL_AndroidGetJNIEnv.");
539         return result;
540     }
541 
542     jobject native_activity = (jobject)SDL_AndroidGetActivity();
543 
544     if (native_activity == NULL)
545     {
546         Log::error("ConnectToServer",
547             "getDNSSrvRecords unable to SDL_AndroidGetActivity.");
548         return result;
549     }
550 
551     jclass class_native_activity = env->GetObjectClass(native_activity);
552 
553     if (class_native_activity == NULL)
554     {
555         Log::error("ConnectToServer",
556             "getDNSTxtRecords unable to find object class.");
557         env->DeleteLocalRef(native_activity);
558         return result;
559     }
560 
561     jmethodID method_id = env->GetMethodID(class_native_activity,
562         "getDNSTxtRecords", "(Ljava/lang/String;)[Ljava/lang/String;");
563 
564     if (method_id == NULL)
565     {
566         Log::error("ConnectToServer",
567             "getDNSTxtRecords unable to find method id.");
568         env->DeleteLocalRef(class_native_activity);
569         env->DeleteLocalRef(native_activity);
570         return result;
571     }
572 
573     std::vector<uint16_t> jstr_data;
574     utf8::unchecked::utf32to16(
575         name.c_str(), name.c_str() + name.size(), std::back_inserter(jstr_data));
576     jstring text =
577         env->NewString((const jchar*)jstr_data.data(), jstr_data.size());
578     if (text == NULL)
579     {
580         Log::error("ConnectToServer",
581             "Failed to create text for domain name.");
582         env->DeleteLocalRef(class_native_activity);
583         env->DeleteLocalRef(native_activity);
584         return result;
585     }
586 
587     jobjectArray arr =
588         (jobjectArray)env->CallObjectMethod(native_activity, method_id, text);
589     if (arr == NULL)
590     {
591         Log::error("ConnectToServer", "No array is created.");
592         env->DeleteLocalRef(text);
593         env->DeleteLocalRef(class_native_activity);
594         env->DeleteLocalRef(native_activity);
595         return result;
596     }
597     int len = env->GetArrayLength(arr);
598     for (int i = 0; i < len; i++)
599     {
600         jstring jstr = (jstring)(env->GetObjectArrayElement(arr, i));
601         if (!jstr)
602             continue;
603         const uint16_t* utf16_text =
604             (const uint16_t*)env->GetStringChars(jstr, NULL);
605         if (utf16_text == NULL)
606         {
607             env->DeleteLocalRef(jstr);
608             continue;
609         }
610         const size_t str_len = env->GetStringLength(jstr);
611         std::string tmp;
612         utf8::unchecked::utf16to8(
613             utf16_text, utf16_text + str_len, std::back_inserter(tmp));
614         result.push_back(tmp);
615         env->ReleaseStringChars(jstr, utf16_text);
616         env->DeleteLocalRef(jstr);
617     }
618     env->DeleteLocalRef(arr);
619     env->DeleteLocalRef(text);
620     env->DeleteLocalRef(class_native_activity);
621     env->DeleteLocalRef(native_activity);
622     return result;
623 };
624 #endif
625 
626 // ----------------------------------------------------------------------------
detectPort()627 bool ConnectToServer::detectPort()
628 {
629     // DNS txt record lookup, we will do stk-server-port server discovery port
630     // too to get an updated sever address, in case it's differ then the
631     // A / AAAA record (possible in LAN environment with multiple IPs assigned)
632     int port_from_dns = 0;
633     auto get_port = [](const std::string& txt_record)->int
634     {
635         const char* match = "stk-server-port=";
636         size_t len = strlen(match);
637         auto it = txt_record.find(match);
638         if (it != std::string::npos && it + len < txt_record.size())
639         {
640             const std::string& ss =
641                 txt_record.substr(it + len, txt_record.size());
642             int result = atoi(ss.c_str());
643             if (result > 65535)
644                 result = 0;
645             if (result != 0)
646             {
647                 Log::info("ConnectToServer",
648                     "Port %d found in DNS txt record %s", result,
649                     txt_record.c_str());
650                 return result;
651             }
652         }
653         return 0;
654     };
655 #if defined(WIN32)
656     PDNS_RECORD dns_record = NULL;
657     DnsQuery(m_server->getName().c_str(), DNS_TYPE_TEXT,
658         DNS_QUERY_STANDARD, NULL, &dns_record, NULL);
659     if (dns_record)
660     {
661         for (PDNS_RECORD curr = dns_record; curr; curr = curr->pNext)
662         {
663             if (curr->wType == DNS_TYPE_TEXT)
664             {
665                 for (unsigned i = 0; i < curr->Data.TXT.dwStringCount; i++)
666                 {
667                     std::string txt_record = StringUtils::wideToUtf8(
668                         curr->Data.TXT.pStringArray[i]);
669                     port_from_dns = get_port(txt_record);
670                     if (port_from_dns != 0)
671                         break;
672                 }
673             }
674             if (port_from_dns != 0)
675                 break;
676         }
677         DnsRecordListFree(dns_record, DnsFreeRecordListDeep);
678     }
679 #elif defined(ANDROID)
680     std::vector<std::string> txt_records = get_txt_record(m_server->getName());
681     for (auto& txt_record : txt_records)
682     {
683         port_from_dns = get_port(txt_record);
684         if (port_from_dns != 0)
685             break;
686     }
687 #elif !defined(__CYGWIN__)
688     unsigned char response[512] = {};
689     const std::string& utf8name = StringUtils::wideToUtf8(m_server->getName());
690     int response_len = res_query(utf8name.c_str(), C_IN, T_TXT, response, 512);
691     if (response_len > 0)
692     {
693         ns_msg query;
694         if (ns_initparse(response, response_len, &query) >= 0)
695         {
696             unsigned msg_count = ns_msg_count(query, ns_s_an);
697             for (unsigned i = 0; i < msg_count; i++)
698             {
699                 ns_rr rr;
700                 if (ns_parserr(&query, ns_s_an, i, &rr) >= 0)
701                 {
702                     // obtain the record data
703                     const unsigned char* rd = ns_rr_rdata(rr);
704                     // the first byte is the length of the data
705                     size_t length = rd[0];
706                     if (length == 0)
707                         continue;
708                     std::string txt_record((char*)rd + 1, length);
709                     port_from_dns = get_port(txt_record);
710                     if (port_from_dns != 0)
711                         break;
712                 }
713             }
714         }
715     }
716 #endif
717 
718     ENetAddress ea = {};
719     std::unique_ptr<Network> nw(new Network(/*peer_count*/1,
720         /*channel_limit*/EVENT_CHANNEL_COUNT,
721         /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea,
722         true/*change_port_if_bound*/));
723     BareNetworkString s(std::string("stk-server-port"));
724     SocketAddress address;
725     if (m_server->useIPV6Connection())
726         address = *m_server->getIPV6Address();
727     else
728         address = m_server->getAddress();
729     address.setPort(stk_config->m_server_discovery_port);
730     nw->sendRawPacket(s, address);
731     SocketAddress sender;
732     const int LEN = 2048;
733     char buffer[LEN];
734     int len = nw->receiveRawPacket(buffer, LEN, &sender, 2000);
735     if (len == 2)
736     {
737         BareNetworkString server_port(buffer, len);
738         uint16_t port = server_port.getUInt16();
739         sender.setPort(port);
740         // Use the DNS detected port over direct socket one, because only
741         // one direct socket exists in a host even they have many stk servers
742         if (port_from_dns != 0)
743             sender.setPort((uint16_t)port_from_dns);
744         // We replace the server address with sender with the detected port
745         // completely, so we can use input like ff02::1 and then get the
746         // correct local link server address
747         if (m_server->useIPV6Connection())
748             m_server->setIPV6Address(sender);
749         else
750             m_server->setAddress(sender);
751         Log::info("ConnectToServer",
752             "Detected new address %s for server address: %s.",
753             sender.toString().c_str(), address.toString(false).c_str());
754     }
755     else if (port_from_dns != 0)
756     {
757         // Use only from dns record
758         if (m_server->useIPV6Connection())
759             m_server->getIPV6Address()->setPort(port_from_dns);
760         else
761         {
762             SocketAddress addr = m_server->getAddress();
763             addr.setPort(port_from_dns);
764             m_server->setAddress(addr);
765         }
766     }
767     else
768     {
769         const core::stringw& n = m_server->getName();
770         //I18N: Show the failed detect port server name
771         core::stringw e = _("Failed to detect port number for server %s.", n);
772         STKHost::get()->setErrorMessage(e);
773         STKHost::get()->requestShutdown();
774         m_state = EXITING;
775         return false;
776     }
777     return true;
778 }   // detectPort
779