From b26b784b6ae417b43e1a471ca763198f609e9065 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 24 Feb 2018 15:48:30 +0800 Subject: [PATCH] Various improvements to WAN and LAN connection --- src/main_loop.cpp | 9 +- src/network/network.cpp | 2 + src/network/protocols/connect_to_server.cpp | 80 ++------- src/network/protocols/connect_to_server.hpp | 1 + src/network/protocols/server_lobby.cpp | 89 ++++++--- src/network/protocols/server_lobby.hpp | 9 +- src/network/stk_host.cpp | 190 ++++++++++---------- src/network/stk_host.hpp | 25 ++- src/network/transport_address.cpp | 99 ++++++++-- src/network/transport_address.hpp | 3 + src/states_screens/create_server_screen.cpp | 21 --- 11 files changed, 286 insertions(+), 242 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 4b6206743..9c78f11d2 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -280,6 +280,12 @@ void MainLoop::run() if (STKHost::existHost() && STKHost::get()->requestedShutdown()) { + SFXManager::get()->quickSound("anvil"); + core::stringw msg = _("Connection to server is lost."); + if (!STKHost::get()->getErrorMessage().empty()) + { + msg = STKHost::get()->getErrorMessage(); + } STKHost::get()->shutdown(); if (World::getWorld()) { @@ -293,8 +299,7 @@ void MainLoop::run() OnlineScreen::getInstance(), NULL }; StateManager::get()->resetAndSetStack(new_stack); - MessageQueue::add(MessageQueue::MT_ERROR, - _("Connection to server is lost.")); + MessageQueue::add(MessageQueue::MT_ERROR, msg); } NetworkConfig::get()->unsetNetworking(); } diff --git a/src/network/network.cpp b/src/network/network.cpp index b8dd1027f..a6cbb061e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -58,6 +58,8 @@ Network::Network(int peer_count, int channel_limit, return; if (change_port_if_bound) { + Log::warn("Network", "%d port is in used, use another port", + address->port); ENetAddress new_addr; new_addr.host = address->host; // Any port diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 742178e8f..65a0af5ea 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -32,12 +32,6 @@ #include "utils/time.hpp" #include "utils/log.hpp" -#ifdef WIN32 -# include -#else -#include -#endif - // ---------------------------------------------------------------------------- /** Connects to a server. This is the quick connect constructor, which * will pick a server randomly. @@ -84,8 +78,7 @@ void ConnectToServer::setup() // In case of LAN we already have the server's and our ip address, // so we can immediately start requesting a connection. m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : - REGISTER_SELF_ADDRESS; - + SET_PUBLIC_ADDRESS; } // setup // ---------------------------------------------------------------------------- @@ -93,10 +86,17 @@ void ConnectToServer::asynchronousUpdate() { switch(m_state) { + case SET_PUBLIC_ADDRESS: + { + STKHost::get()->setPublicAddress(); + // Set to DONE will stop STKHost is not connected + m_state = STKHost::get()->getPublicAddress().isUnset() ? + DONE : REGISTER_SELF_ADDRESS; + } + break; case REGISTER_SELF_ADDRESS: { registerWithSTKServer(); // Register us with STK server - if (m_quick_join) { handleQuickConnect(); @@ -226,6 +226,9 @@ void ConnectToServer::asynchronousUpdate() if (STKHost::get()->getPeerCount() == 0) { // Shutdown STKHost (go back to online menu too) + STKHost::get()->setErrorMessage( + _("Cannot connect to server with address: %s.", + m_server_address.toString().c_str())); STKHost::get()->requestShutdown(); } break; @@ -266,7 +269,10 @@ void ConnectToServer::registerWithSTKServer() } else { - Log::error("ConnectToServer", "Failed to register address."); + irr::core::stringc error(request->getInfo().c_str()); + Log::error("ConnectToServer", "Failed to register client address: %s", + error.c_str()); + m_state = DONE; } delete request; @@ -349,58 +355,8 @@ void ConnectToServer::waitingAloha(bool is_wan) sender.toString().c_str()); if (!is_wan) { -#ifndef WIN32 - // just check if the ip is ours : if so, - // then just use localhost (127.0.0.1) - struct ifaddrs *ifap, *ifa; - struct sockaddr_in *sa; - getifaddrs(&ifap); // get the info - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr->sa_family == AF_INET) - { - sa = (struct sockaddr_in *) ifa->ifa_addr; - - // This interface is ours - if (ntohl(sa->sin_addr.s_addr) == sender.getIP()) - sender.setIP(0x7f000001); // 127.0.0.1 - } - } - freeifaddrs(ifap); -#else - // Query the list of all IP addresses on the local host - // First call to GetIpAddrTable with 0 bytes buffer - // will return insufficient buffer error, and size - // will contain the number of bytes needed for all - // data. Repeat the process of querying the size - // using GetIpAddrTable in a while loop since it - // can happen that an interface comes online between - // the previous call to GetIpAddrTable and the next - // call. - MIB_IPADDRTABLE *table = NULL; - unsigned long size = 0; - int error = GetIpAddrTable(table, &size, 0); - // Also add a count to limit the while loop - in - // case that something strange is going on. - int count = 0; - while (error == ERROR_INSUFFICIENT_BUFFER && count < 10) - { - delete[] table; // deleting NULL is legal - table = (MIB_IPADDRTABLE*)new char[size]; - error = GetIpAddrTable(table, &size, 0); - count++; - } // while insufficient buffer - for (unsigned int i = 0; i < table->dwNumEntries; i++) - { - unsigned int ip = ntohl(table->table[i].dwAddr); - if (sender.getIP() == ip) // this interface is ours - { - sender.setIP(0x7f000001); // 127.0.0.1 - break; - } - } - delete[] table; -#endif + if (sender.isPublicAddressLAN()) + sender.setIP(0x7f000001); // 127.0.0.1 } m_server_address.copy(sender); m_state = CONNECTING; diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index 1c83af53c..927a570c2 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -40,6 +40,7 @@ private: /** State for finite state machine. */ enum { + SET_PUBLIC_ADDRESS, REGISTER_SELF_ADDRESS, GOT_SERVER_ADDRESS, REQUESTING_CONNECTION, diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 78d538171..6db382669 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -83,6 +83,8 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) { setHandleDisconnections(true); + m_state = SET_PUBLIC_ADDRESS; + // We use maximum 16bit unsigned limit auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_t = track_manager->getAllTrackIdentifiers(); @@ -113,11 +115,6 @@ void ServerLobby::setup() m_game_setup = STKHost::get()->setupNewGame(); m_game_setup->setNumLocalPlayers(0); // no local players on a server m_next_player_id.setAtomic(0); - - // In case of LAN we don't need our public address or register with the - // STK server, so we can directly go to the accepting clients state. - m_state = NetworkConfig::get()->isLAN() ? ACCEPTING_CLIENTS - : INIT_WAN; m_selection_enabled = false; Log::info("ServerLobby", "Starting the protocol."); @@ -185,7 +182,6 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) case LE_VOTE_LAPS: playerLapsVote(event); break; case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break; } // switch - } // if (event->getType() == EVENT_TYPE_MESSAGE) else if (event->getType() == EVENT_TYPE_DISCONNECTED) { @@ -195,21 +191,34 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) } // notifyEventAsynchronous //----------------------------------------------------------------------------- -/** Simple finite state machine. First get the public ip address. Once this - * is known, register the server and its address with the stk server so that - * client can find it. - */ -void ServerLobby::update(float dt) +/** Find out the public IP server or poll STK server asynchronously. */ +void ServerLobby::asynchronousUpdate() { switch (m_state.load()) { - case INIT_WAN: + case SET_PUBLIC_ADDRESS: { - // Start the protocol to find the public ip address. - m_state = GETTING_PUBLIC_ADDRESS; + // In case of LAN we don't need our public address or register with the + // STK server, so we can directly go to the accepting clients state. + if (NetworkConfig::get()->isLAN()) + { + m_state = ACCEPTING_CLIENTS; + STKHost::get()->startListening(); + return; + } + STKHost::get()->setPublicAddress(); + if (STKHost::get()->getPublicAddress().isUnset()) + { + m_state = ERROR_LEAVE; + } + else + { + STKHost::get()->startListening(); + m_state = REGISTER_SELF_ADDRESS; + } break; } - case GETTING_PUBLIC_ADDRESS: + case REGISTER_SELF_ADDRESS: { // Register this server with the STK server. This will block // this thread, but there is no need for the protocol manager @@ -221,10 +230,39 @@ void ServerLobby::update(float dt) case ACCEPTING_CLIENTS: { // Only poll the STK server if this is a WAN server. - if(NetworkConfig::get()->isWAN()) + if (NetworkConfig::get()->isWAN()) checkIncomingConnectionRequests(); break; } + case ERROR_LEAVE: + { + requestTerminate(); + m_state = EXITING; + STKHost::get()->setErrorMessage(_("Failed to setup server.")); + STKHost::get()->requestShutdown(); + break; + } + default: + break; + } +} // asynchronousUpdate + +//----------------------------------------------------------------------------- +/** Simple finite state machine. Once this + * is known, register the server and its address with the stk server so that + * client can find it. + */ +void ServerLobby::update(float dt) +{ + switch (m_state.load()) + { + case SET_PUBLIC_ADDRESS: + case REGISTER_SELF_ADDRESS: + case ACCEPTING_CLIENTS: + { + // Waiting for asynchronousUpdate + break; + } case SELECTING: // The function playerTrackVote will trigger the next state // once all track votes have been received. @@ -280,7 +318,8 @@ void ServerLobby::update(float dt) sendMessageToPeersChangingToken(exit_result_screen, /*reliable*/true); delete exit_result_screen; - m_state = ACCEPTING_CLIENTS; + m_state = NetworkConfig::get()->isLAN() ? + ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS; RaceResultGUI::getInstance()->backToLobby(); // notify the network world that it is stopped RaceEventManager::getInstance()->stop(); @@ -293,10 +332,7 @@ void ServerLobby::update(float dt) setup(); } break; - case DONE: - m_state = EXITING; - requestTerminate(); - break; + case ERROR_LEAVE: case EXITING: break; } @@ -337,14 +373,11 @@ void ServerLobby::registerServer() { irr::core::stringc error(request->getInfo().c_str()); Log::error("ServerLobby", "%s", error.c_str()); - STKHost::get()->setErrorMessage(_("Failed to register server: %s", - error.c_str())); - STKHost::get()->requestShutdown(); + m_state = ERROR_LEAVE; } delete request; } // registerServer - //----------------------------------------------------------------------------- /** Unregister this server (i.e. its public address) with the STK server, * currently when karts enter kart selection screen it will be done. @@ -472,6 +505,12 @@ void ServerLobby::checkIncomingConnectionRequests() if (StkTime::getRealTime() < last_poll_time + POLL_INTERVAL) return; + // Keep the port open, it can be sent to anywhere as we will send to the + // correct peer later in ConnectToPeer. + BareNetworkString data; + data.addUInt8(0); + STKHost::get()->sendRawPacket(data, STKHost::get()->getStunAddress()); + // Now poll the stk server last_poll_time = StkTime::getRealTime(); Online::XMLRequest* request = new Online::XMLRequest(); diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 6448a3cf9..b2ec2e03a 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -14,18 +14,17 @@ public: /* The state for a small finite state machine. */ enum ServerState : unsigned int { - INIT_WAN, // Start state for WAN game - GETTING_PUBLIC_ADDRESS, // Waiting to receive its public ip address + SET_PUBLIC_ADDRESS, // Waiting to receive its public ip address + REGISTER_SELF_ADDRESS, // Register with STK online server ACCEPTING_CLIENTS, // In lobby, accepting clients SELECTING, // kart, track, ... selection started LOAD_WORLD, // Server starts loading world WAIT_FOR_WORLD_LOADED, // Wait for clients and server to load world WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race - START_RACE, // Inform clients to start race DELAY_SERVER, // Additional server delay RACING, // racing RESULT_DISPLAY, // Show result screen - DONE, // shutting down server + ERROR_LEAVE, // shutting down server EXITING }; private: @@ -92,7 +91,7 @@ public: virtual bool notifyEvent(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void update(float dt) OVERRIDE; - virtual void asynchronousUpdate() OVERRIDE {}; + virtual void asynchronousUpdate() OVERRIDE; void signalRaceStartToClients(); void startSelection(const Event *event=NULL); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index d3cd29101..c97e82b30 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -261,13 +261,14 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) // server is made. m_host_id = 0; init(); - + ENetAddress ea; ea.host = STKHost::HOST_ANY; - ea.port = STKHost::PORT_ANY; + ea.port = NetworkConfig::get()->getClientPort(); m_network = new Network(/*peer_count*/1, /*channel_limit*/2, - /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea); + /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, + &ea, true/*change_port_if_bound*/); if (!m_network) { Log::fatal ("STKHost", "An error occurred while trying to create " @@ -275,15 +276,7 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) } setPrivatePort(); - if (NetworkConfig::get()->isWAN()) - { - setPublicAddress(); - } - // Don't connect to server if no public address in WAN game - if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) - { - std::make_shared(server_id, host_id)->requestStart(); - } + std::make_shared(server_id, host_id)->requestStart(); } // STKHost // ---------------------------------------------------------------------------- @@ -300,12 +293,13 @@ STKHost::STKHost(const irr::core::stringw &server_name) ENetAddress addr; addr.host = STKHost::HOST_ANY; - addr.port = STKHost::PORT_ANY; + addr.port = NetworkConfig::get()->getServerPort(); m_network= new Network(NetworkConfig::get()->getMaxPlayers(), /*channel_limit*/2, /*max_in_bandwidth*/0, - /*max_out_bandwidth*/ 0, &addr); + /*max_out_bandwidth*/ 0, &addr, + true/*change_port_if_bound*/); if (!m_network) { Log::fatal("STKHost", "An error occurred while trying to create an " @@ -313,16 +307,8 @@ STKHost::STKHost(const irr::core::stringw &server_name) } setPrivatePort(); - // We need the public address for server no matter what to determine - // local lan connection - setPublicAddress(); - // Don't construct server if no public address in WAN game - if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) - { - startListening(); - ProtocolManager::lock() - ->requestStart(LobbyProtocol::create()); - } + ProtocolManager::lock() + ->requestStart(LobbyProtocol::create()); } // STKHost(server_name) @@ -335,7 +321,6 @@ void STKHost::init() m_shutdown = false; m_network = NULL; m_game_setup = NULL; - m_error_message = ""; m_exit_flag.clear(); m_exit_flag.test_and_set(); @@ -435,17 +420,19 @@ void STKHost::setPublicAddress() // documentation says it points to "one or more addrinfo structures" assert(res != NULL); struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); - uint32_t stun_server_ip = ntohl(current_interface->sin_addr.s_addr); + m_stun_address.setIP(ntohl(current_interface->sin_addr.s_addr)); + m_stun_address.setPort(3478); // Assemble the message for the stun server BareNetworkString s(20); + constexpr uint32_t magic_cookie = 0x2112A442; // bytes 0-1: the type of the message // bytes 2-3: message length added to header (attributes) uint16_t message_type = 0x0001; // binding request uint16_t message_length = 0x0000; s.addUInt16(message_type).addUInt16(message_length) - .addUInt32(0x2112A442); + .addUInt32(magic_cookie); uint8_t stun_tansaction_id[12]; // bytes 8-19: the transaction id for (int i = 0; i < 12; i++) @@ -455,7 +442,7 @@ void STKHost::setPublicAddress() stun_tansaction_id[i] = random_byte; } - m_network->sendRawPacket(s, TransportAddress(stun_server_ip, 3478)); + m_network->sendRawPacket(s, m_stun_address); freeaddrinfo(res); // Recieve now @@ -464,33 +451,37 @@ void STKHost::setPublicAddress() char buffer[LEN]; int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000); - if (sender.getIP() != stun_server_ip) + if (sender.getIP() != m_stun_address.getIP()) { - TransportAddress stun(stun_server_ip, 3478); Log::warn("STKHost", - "Received stun response from %s instead of %s.", - sender.toString().c_str(), stun.toString().c_str()); + "Received stun response from %s instead of %s.", + sender.toString().c_str(), m_stun_address.toString().c_str()); } - if (len < 0) + if (len <= 0) { Log::error("STKHost", "STUN response contains no data at all"); continue; } // Convert to network string. - BareNetworkString datas(buffer, len); - - // check that the stun response is a response, contains the magic cookie - // and the transaction ID - if (datas.getUInt16() != 0x0101) + BareNetworkString response(buffer, len); + if (response.size() < 20) { - Log::error("STKHost", "STUN response doesn't contain the magic " - "cookie"); + Log::error("STKHost", "STUN response should be at least 20 bytes."); continue; } - int message_size = datas.getUInt16(); - if (datas.getUInt32() != 0x2112A442) + + if (response.getUInt16() != 0x0101) + { + Log::error("STKHost", "STUN has no binding success response."); + continue; + } + + // Skip message size + response.getUInt16(); + + if (response.getUInt32() != magic_cookie) { Log::error("STKHost", "STUN response doesn't contain the magic " "cookie"); @@ -499,7 +490,7 @@ void STKHost::setPublicAddress() for (int i = 0; i < 12; i++) { - if (datas.getUInt8() != stun_tansaction_id[i]) + if (response.getUInt8() != stun_tansaction_id[i]) { Log::error("STKHost", "STUN response doesn't contain the " "transaction ID"); @@ -511,64 +502,77 @@ void STKHost::setPublicAddress() "The STUN server responded with a valid answer"); // The stun message is valid, so we parse it now: - if (message_size == 0) - { - Log::error("STKHost", "STUN response does not contain any " - "information."); - continue; - } - // Cannot even read the size - if (message_size < 4) - { - Log::error("STKHost", "STUN response is too short."); - continue; - } // Those are the port and the address to be detected bool found = false; while (true) { - int type = datas.getUInt16(); - int size = datas.getUInt16(); - if (type == 0 || type == 1) - { - assert(size == 8); - datas.getUInt8(); // skip 1 byte -#ifdef DEBUG - uint8_t skip = datas.getUInt8(); - // Family IPv4 only - assert(skip == 0x01); -#else - datas.getUInt8(); -#endif - m_public_address.setPort(datas.getUInt16()); - m_public_address.setIP(datas.getUInt32()); - // finished parsing, we know our public transport address - Log::debug("STKHost", "The public address has been found: %s", - m_public_address.toString().c_str()); - found = true; - break; - } // type = 0 or 1 - datas.skip(4 + size); - message_size -= 4 + size; - if (message_size == 0) + if (response.size() < 4) { Log::error("STKHost", "STUN response is invalid."); break; } - // Cannot even read the size - if (message_size < 4) + unsigned type = response.getUInt16(); + unsigned size = response.getUInt16(); + + // Bit determining whether comprehension of an attribute is optional. + // Described in section 15 of RFC 5389. + constexpr uint16_t comprehension_optional = 0x1 << 15; + + // Bit determining whether the bit was assigned by IETF Review. + // Described in section 18.1. of RFC 5389. + constexpr uint16_t IETF_review = 0x1 << 14; + + // Defined in section 15.1 of RFC 5389 + constexpr uint8_t ipv4 = 0x01; + + // Defined in section 18.2 of RFC 5389 + constexpr uint16_t mapped_address = 0x001; + constexpr uint16_t xor_mapped_address = 0x0020; + // The first two bits are irrelevant to the type + type &= ~(comprehension_optional | IETF_review); + if (type == mapped_address || type == xor_mapped_address) { - Log::error("STKHost", "STUN response is invalid."); + if (size != 8 || response.size() < 8) + { + Log::error("STKHost", "Invalid STUN mapped address " + "length"); + break; + } + // Ignore the first byte as mentioned in Section 15.1 of RFC + // 5389. + uint8_t ip_type = response.getUInt8(); + ip_type = response.getUInt8(); + if (ip_type != ipv4) + { + Log::error("STKHost", "Only IPv4 is supported"); + break; + } + + uint16_t port = response.getUInt16(); + uint32_t ip = response.getUInt32(); + if (type == xor_mapped_address) + { + // Obfuscation is described in Section 15.2 of RFC 5389. + port ^= magic_cookie >> 16; + ip ^= magic_cookie; + } + m_public_address.setPort(port); + m_public_address.setIP(ip); + found = true; break; + } // type == mapped_address || type == xor_mapped_address + else + { + response.skip(size); + int padding = size % 4; + if (padding != 0) + response.skip(4 - padding); } } // while true // Found public address and port if (found) untried_server.clear(); } - // We shutdown next frame if no public address - if (m_public_address.isUnset()) - requestShutdown(); } // setPublicAddress //----------------------------------------------------------------------------- @@ -629,18 +633,14 @@ void STKHost::abort() */ void STKHost::setErrorMessage(const irr::core::stringw &message) { - irr::core::stringc s(message.c_str()); - Log::error("STKHost", "%s", s.c_str()); + if (!message.empty()) + { + irr::core::stringc s(message.c_str()); + Log::error("STKHost", "%s", s.c_str()); + } m_error_message = message; } // setErrorMessage -// -------------------------------------------------------------------- -/** Returns the last error (or "" if no error has happened). */ -const irr::core::stringw& STKHost::getErrorMessage() const -{ - return m_error_message; -} // getErrorMessage - // -------------------------------------------------------------------- /** \brief Try to establish a connection to a given transport address. * \param peer : The transport address which you want to connect to. @@ -831,7 +831,7 @@ void STKHost::handleDirectSocketRequest(Network* lan_network) { // In case of a LAN connection, we only allow connections from // a LAN address (192.168*, ..., and 127.*). - if (!sender.isLAN() && sender.getIP() != m_public_address.getIP()) + if (!sender.isLAN() && !sender.isPublicAddressLAN()) { Log::error("STKHost", "Client trying to connect from '%s'", sender.toString().c_str()); diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index fbaa118bc..421afe97a 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -101,6 +101,9 @@ private: /** The public address found by stun (if WAN is used). */ TransportAddress m_public_address; + /** The public address stun server used. */ + TransportAddress m_stun_address; + /** The private port enet socket is bound. */ uint16_t m_private_port; @@ -113,8 +116,6 @@ private: void init(); void handleDirectSocketRequest(Network* lan_network); // ------------------------------------------------------------------------ - void setPublicAddress(); - // ------------------------------------------------------------------------ void mainLoop(); public: @@ -149,11 +150,16 @@ public: const TransportAddress& getPublicAddress() const { return m_public_address; } // ------------------------------------------------------------------------ + const TransportAddress& getStunAddress() const + { return m_stun_address; } + // ------------------------------------------------------------------------ uint16_t getPrivatePort() const { return m_private_port; } // ------------------------------------------------------------------------ void setPrivatePort(); // ------------------------------------------------------------------------ + void setPublicAddress(); + // ------------------------------------------------------------------------ virtual GameSetup* setupNewGame(); void abort(); void deleteAllPeers(); @@ -190,9 +196,11 @@ public: std::vector getMyPlayerProfiles(); void setErrorMessage(const irr::core::stringw &message); bool isAuthorisedToControl() const; - const irr::core::stringw& - getErrorMessage() const; + // -------------------------------------------------------------------- + /** Returns the last error (or "" if no error has happened). */ + const irr::core::stringw& getErrorMessage() const + { return m_error_message; } // -------------------------------------------------------------------- /** Returns true if a shutdown of the network infrastructure was * requested. */ @@ -214,15 +222,6 @@ public: { m_network->sendRawPacket(buffer, dst); } // sendRawPacket - - // -------------------------------------------------------------------- - /** Returns the IP address of this host. */ - uint32_t getAddress() const - { - return m_network->getENetHost()->address.host; - } // getAddress - - // -------------------------------------------------------------------- /** Returns a const reference to the list of peers. */ const std::vector &getPeers() { return m_peers; } diff --git a/src/network/transport_address.cpp b/src/network/transport_address.cpp index 1a9c3cdd0..51909dccf 100644 --- a/src/network/transport_address.cpp +++ b/src/network/transport_address.cpp @@ -18,8 +18,15 @@ #include "network/transport_address.hpp" +#ifdef WIN32 +# include +#else +#include +#endif + +// ---------------------------------------------------------------------------- /** Returns if this IP address belongs to a LAN, i.e. is in 192.168* or - * 10*, 172.16-31.*, or is on the same host, i.e. 127*. + * 10*, 172.16-31.*, or is on the same host, i.e. 127* or same public address. */ bool TransportAddress::isLAN() const { @@ -33,6 +40,60 @@ bool TransportAddress::isLAN() const else if (ip >> 24 == 0x7f ) // 127.* localhost return true; return false; +} + +// ---------------------------------------------------------------------------- +/** Returns this IP address is localhost (127.0.0.1). + */ +bool TransportAddress::isPublicAddressLAN() const +{ +#ifndef WIN32 + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sa; + getifaddrs(&ifap); // get the info + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr->sa_family == AF_INET) + { + sa = (struct sockaddr_in *) ifa->ifa_addr; + // This interface is ours + if (ntohl(sa->sin_addr.s_addr) == getIP()) + return true; + } + } + freeifaddrs(ifap); +#else + // Query the list of all IP addresses on the local host. First call to + // GetIpAddrTable with 0 bytes buffer will return insufficient buffer + // error, and size will contain the number of bytes needed for all data. + // Repeat the process of querying the size using GetIpAddrTable in a while + // loop since it can happen that an interface comes online between the + // previous call to GetIpAddrTable and the next call. + MIB_IPADDRTABLE *table = NULL; + unsigned long size = 0; + int error = GetIpAddrTable(table, &size, 0); + // Also add a count to limit the while loop - in case that something + // strange is going on. + int count = 0; + while (error == ERROR_INSUFFICIENT_BUFFER && count < 10) + { + delete[] table; // deleting NULL is legal + table = (MIB_IPADDRTABLE*)new char[size]; + error = GetIpAddrTable(table, &size, 0); + count++; + } // while insufficient buffer + for (unsigned int i = 0; i < table->dwNumEntries; i++) + { + unsigned int ip = ntohl(table->table[i].dwAddr); + if (getIP() == ip) // this interface is ours + { + delete[] table; + return true; + } + } + delete[] table; +#endif + return false; } // isLAN // ---------------------------------------------------------------------------- @@ -42,71 +103,71 @@ bool TransportAddress::isLAN() const void TransportAddress::unitTesting() { TransportAddress t1("192.168.0.0"); - assert(t1.getIP() == (192 << 24) + (168 << 16)); + assert(t1.getIP() == (192u << 24) + (168u << 16)); assert(t1.isLAN()); TransportAddress t2("192.168.255.255"); - assert(t2.getIP() == (192 << 24) + (168 << 16) + (255 << 8) + 255); + assert(t2.getIP() == (192u << 24) + (168u << 16) + (255u << 8) + 255u); assert(t2.isLAN()); TransportAddress t3("193.168.0.1"); - assert(t3.getIP() == (193 << 24) + (168 << 16) + 1); + assert(t3.getIP() == (193u << 24) + (168u << 16) + 1); assert(!t3.isLAN()); TransportAddress t4("192.167.255.255"); - assert(t4.getIP() == (192 << 24) + (167 << 16) + (255 << 8) + 255); + assert(t4.getIP() == (192u << 24) + (167u << 16) + (255u << 8) + 255u); assert(!t4.isLAN()); TransportAddress t5("192.169.0.0"); - assert(t5.getIP() == (192 << 24) + (169 << 16)); + assert(t5.getIP() == (192u << 24) + (169u << 16)); assert(!t5.isLAN()); TransportAddress t6("172.16.0.0"); - assert(t6.getIP() == (172 << 24) + (16 << 16)); + assert(t6.getIP() == (172u << 24) + (16u << 16)); assert(t6.isLAN()); TransportAddress t7("172.31.255.255"); - assert(t7.getIP() == (172 << 24) + (31 << 16) + (255 << 8) + 255); + assert(t7.getIP() == (172u << 24) + (31u << 16) + (255u << 8) + 255u); assert(t7.isLAN()); TransportAddress t8("172.15.255.255"); - assert(t8.getIP() == (172 << 24) + (15 << 16) + (255 << 8) + 255); + assert(t8.getIP() == (172u << 24) + (15u << 16) + (255u << 8) + 255u); assert(!t8.isLAN()); TransportAddress t9("172.32.0.0"); - assert(t9.getIP() == (172 << 24) + (32 << 16)); + assert(t9.getIP() == (172u << 24) + (32u << 16)); assert(!t9.isLAN()); TransportAddress t10("10.0.0.0"); - assert(t10.getIP() == (10 << 24)); + assert(t10.getIP() == (10u << 24)); assert(t10.isLAN()); TransportAddress t11("10.255.255.255"); - assert(t11.getIP() == (10 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t11.getIP() == (10u << 24) + (255u << 16) + (255u << 8) + 255u); assert(t11.isLAN()); TransportAddress t12("9.255.255.255"); - assert(t12.getIP() == (9 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t12.getIP() == (9u << 24) + (255u << 16) + (255u << 8) + 255u); assert(!t12.isLAN()); TransportAddress t13("11.0.0.0"); - assert(t13.getIP() == (11 << 24) ); + assert(t13.getIP() == (11u << 24)); assert(!t13.isLAN()); TransportAddress t14("127.0.0.0"); - assert(t14.getIP() == (127 << 24)); + assert(t14.getIP() == (127u << 24)); assert(t14.isLAN()); TransportAddress t15("127.255.255.255"); - assert(t15.getIP() == (127 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t15.getIP() == (127u << 24) + (255u << 16) + (255u << 8) + 255u); assert(t15.isLAN()); TransportAddress t16("126.255.255.255"); - assert(t16.getIP() == (126 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t16.getIP() == (126u << 24) + (255u << 16) + (255u << 8) + 255u); assert(!t16.isLAN()); TransportAddress t17("128.0.0.0"); - assert(t17.getIP() == (128 << 24)); + assert(t17.getIP() == (128u << 24)); assert(!t17.isLAN()); -} // unitTesting \ No newline at end of file +} // unitTesting diff --git a/src/network/transport_address.hpp b/src/network/transport_address.hpp index 3a5cab8bd..3ca3c0379 100644 --- a/src/network/transport_address.hpp +++ b/src/network/transport_address.hpp @@ -88,6 +88,9 @@ private: copy(other); } // TransportAddress(const TransportAddress&) public: + // ------------------------------------------------------------------------ + bool isPublicAddressLAN() const; + // ------------------------------------------------------------------------ bool isLAN() const; // ------------------------------------------------------------------------ bool isUnset() const { return m_ip == 0 || m_port == 0; } diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp index bd8157aac..2d268a14d 100644 --- a/src/states_screens/create_server_screen.cpp +++ b/src/states_screens/create_server_screen.cpp @@ -136,27 +136,6 @@ void CreateServerScreen::onUpdate(float delta) if(!STKHost::existHost()) return; - // First check if an error happened while registering the server: - // -------------------------------------------------------------- - const irr::core::stringw &error = STKHost::get()->getErrorMessage(); - if(error!="") - { - SFXManager::get()->quickSound("anvil"); - m_info_widget->setErrorColor(); - m_info_widget->setText(error, false); - return; - } - - // Otherwise wait till we get an answer from the server: - // ----------------------------------------------------- - if (!LobbyProtocol::get()) - { - m_info_widget->setDefaultColor(); - m_info_widget->setText(StringUtils::loadingDots(_("Creating server")), - false); - return; - } - //FIXME If we really want a gui, we need to decide what else to do here // For now start the (wrong i.e. client) lobby, to prevent to create // a server more than once.