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