diff --git a/src/network/network.cpp b/src/network/network.cpp index 001de0b53..6d9b6d27a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -20,6 +20,7 @@ #include "config/user_config.hpp" #include "io/file_manager.hpp" +#include "network/network_config.hpp" #include "network/network_string.hpp" #include "network/transport_address.hpp" #include "utils/time.hpp" @@ -27,12 +28,16 @@ #include #if defined(WIN32) # include "ws2tcpip.h" +# include # define inet_ntop InetNtop #else # include # include +# include # include #endif + + #include #include @@ -225,3 +230,138 @@ void Network::closeLog() m_log_file.unlock(); } } // closeLog + +// ---------------------------------------------------------------------------- +/** Sets a list of default broadcast addresses which is used in case no valid + * broadcast address is found. This list includes default private network + * addresses. + */ +void Network::setDefaultBroadcastAddresses() +{ + // Add some common LAN addresses + m_broadcast_address.emplace_back(std::string("192.168.255.255")); + m_broadcast_address.emplace_back(std::string("192.168.0.255") ); + m_broadcast_address.emplace_back(std::string("192.168.1.255") ); + m_broadcast_address.emplace_back(std::string("172.31.255.255") ); + m_broadcast_address.emplace_back(std::string("172.16.255.255") ); + m_broadcast_address.emplace_back(std::string("172.16.0.255") ); + m_broadcast_address.emplace_back(std::string("10.255.255.255") ); + m_broadcast_address.emplace_back(std::string("10.0.255.255") ); + m_broadcast_address.emplace_back(std::string("10.0.0.255") ); + m_broadcast_address.emplace_back(std::string("255.255.255.255")); + m_broadcast_address.emplace_back(std::string("127.0.0.255") ); + m_broadcast_address.emplace_back(std::string("127.0.0.1") ); +} // setDefaultBroadcastAddresses + +// ---------------------------------------------------------------------------- +/** This masks various possible broadcast addresses. For example, in a /16 + * network it would first use *.*.255.255, then *.*.*.255. Also if the + * length of the mask is not a multiple of 8, the original value will + * be used, before multiple of 8 are create: /22 (*.3f.ff.ff), then + * /16 (*.*.ff.ff), /8 (*.*.*.ff). While this is usually an overkill, + * it can help in the case that the router does not forward a broadcast + * as expected (this problem often happens with 255.255.255.255, which is + * why this broadcast address creation code was added). + * \param a The transport address for which the broadcast addresses need + * to be created. + * \param len Number of bits to be or'ed. + */ +void Network::addAllBroadcastAddresses(const TransportAddress &a, int len) +{ + // Try different broadcast addresses - by masking on + // byte boundaries + while (len > 0) + { + unsigned int mask = (1 << len) - 1; + TransportAddress bcast(a.getIP() | mask, + NetworkConfig::get()->getServerDiscoveryPort()); + Log::info("Broadcast", "address %s length %d mask %x --> %s", + a.toString().c_str(), + len, mask, + bcast.toString().c_str()); + m_broadcast_address.push_back(bcast); + if (len % 8 != 0) + len -= (len % 8); + else + len = len - 8; + } // while len > 0 +} // addAllBroadcastAddresses + +// ---------------------------------------------------------------------------- +/** Returns a list of all possible broadcast addresses on this machine. + * It queries all adapters for active IPV4 interfaces, determines their + * netmask to create the broadcast addresses. It will also add 'smaller' + * broadcast addesses, e.g. in a /16 network, it will add *.*.255.255 and + * *.*.*.255, since it was sometimes observed that routers would not let + * all broadcast addresses through. Duplicated answers (from the same server + * to different addersses) will be filtered out in ServersManager. + */ +const std::vector& Network::getBroadcastAddresses() +{ + if (m_broadcast_address.size() > 0) return m_broadcast_address; + +#ifdef WIN32 + IP_ADAPTER_ADDRESSES *addresses; + int count = 100, return_code; + + int iteration = 0; + do + { + addresses = new IP_ADAPTER_ADDRESSES[count]; + ULONG buf_len = sizeof(IP_ADAPTER_ADDRESSES)*count; + long flags = 0; + return_code = GetAdaptersAddresses(AF_INET, flags, NULL, addresses, + &buf_len); + iteration++; + } while (return_code == ERROR_BUFFER_OVERFLOW && iteration<10); + + if (return_code == ERROR_BUFFER_OVERFLOW) + { + Log::warn("NetworkConfig", "Can not get broadcast addresses."); + setDefaultBroadcastAddresses(); + return m_broadcast_address; + } + + for (IP_ADAPTER_ADDRESSES *p = addresses; p; p = p->Next) + { + // Check all operational IP4 adapters + if (p->OperStatus == IfOperStatusUp && + p->FirstUnicastAddress->Address.lpSockaddr->sa_family == AF_INET) + { + const sockaddr_in *sa = (sockaddr_in*)p->FirstUnicastAddress->Address.lpSockaddr; + // Use sa->sin_addr.S_un.S_addr and htonl? + TransportAddress ta(sa->sin_addr.S_un.S_un_b.s_b1, + sa->sin_addr.S_un.S_un_b.s_b2, + sa->sin_addr.S_un.S_un_b.s_b3, + sa->sin_addr.S_un.S_un_b.s_b4); + int len = 32 - p->FirstUnicastAddress->OnLinkPrefixLength; + addAllBroadcastAddresses(ta, len); + } + } +#else + struct ifaddrs *addresses, *p; + + getifaddrs(&addresses); + for (p = addresses; p; p = p->ifa_next) + { + if (p->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in *sa = (struct sockaddr_in *) p->ifa_addr; + TransportAddress ta(htonl(sa->sin_addr.s_addr), 0); + uint32_t u = ((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr; + // Convert mask to #bits: SWAT algorithm + u = u - ((u >> 1) & 0x55555555); + u = (u & 0x33333333) + ((u >> 2) & 0x33333333); + u = (((u + (u >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + + printf("Interface: %s\tAddress: %s\tmask: %x\n", p->ifa_name, + ta.toString().c_str(), u); + addAllBroadcastAddresses(ta, u); + } + } + + +#endif + return m_broadcast_address; +} // getBroadcastAddresses + diff --git a/src/network/network.hpp b/src/network/network.hpp index ac7a54aaa..d6d0a5fb8 100644 --- a/src/network/network.hpp +++ b/src/network/network.hpp @@ -32,6 +32,7 @@ #include #include +#include class BareNetworkString; class NetworkString; @@ -50,6 +51,12 @@ private: /** Where to log packets. If NULL for FILE* logging is disabled. */ static Synchronised m_log_file; + /** List of broadcast addresses to use. */ + std::vector m_broadcast_address; + + void setDefaultBroadcastAddresses(); + void addAllBroadcastAddresses(const TransportAddress &a, int len); + public: Network(int peer_count, int channel_limit, uint32_t max_incoming_bandwidth, @@ -68,6 +75,8 @@ public: TransportAddress* sender, int max_tries = -1); void broadcastPacket(NetworkString *data, bool reliable = true); + const std::vector& getBroadcastAddresses(); + // ------------------------------------------------------------------------ /** Returns a pointer to the ENet host object. */ ENetHost* getENetHost() { return m_host; } diff --git a/src/network/network_config.cpp b/src/network/network_config.cpp index dbbe23975..3579074d5 100644 --- a/src/network/network_config.cpp +++ b/src/network/network_config.cpp @@ -19,6 +19,7 @@ #include "network/network_config.hpp" #include "config/stk_config.hpp" #include "config/user_config.hpp" +#include "network/transport_address.hpp" #include "online/xml_request.hpp" #include "states_screens/main_menu_screen.hpp" #include "states_screens/networking_lobby.hpp" @@ -78,7 +79,7 @@ void NetworkConfig::setIsServer(bool b) // ---------------------------------------------------------------------------- void NetworkConfig::setServerMode(RaceManager::MinorRaceModeType minor, - RaceManager::MajorRaceModeType major) + RaceManager::MajorRaceModeType major) { if (major == RaceManager::MAJOR_MODE_GRAND_PRIX) { diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index 2a5754258..6e3b7a8a7 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -24,6 +24,7 @@ #include "network/transport_address.hpp" #include "race/race_manager.hpp" +#include "utils/no_copy.hpp" #include "irrString.h" #include @@ -42,7 +43,7 @@ namespace GUIEngine class InputDevice; class PlayerProfile; -class NetworkConfig +class NetworkConfig : public NoCopy { private: /** The singleton instance. */ diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 1010604d6..1eaf927f6 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -97,7 +97,7 @@ void ClientLobby::clearPlayers() */ void ClientLobby::setAddress(const TransportAddress &address) { - m_server_address.copy(address); + m_server_address = address; } // setAddress //----------------------------------------------------------------------------- diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 71ef1e1e8..81a2d09b1 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -46,7 +46,7 @@ ConnectToPeer::ConnectToPeer(uint32_t peer_id) : Protocol(PROTOCOL_CONNECTION) ConnectToPeer::ConnectToPeer(const TransportAddress &address) : Protocol(PROTOCOL_CONNECTION) { - m_peer_address.copy(address); + m_peer_address = address; // We don't need to find the peer address, so we can start // with the state when we found the peer address. m_state = WAIT_FOR_CONNECTION; @@ -84,7 +84,7 @@ void ConnectToPeer::asynchronousUpdate() assert(get_peer_address); if (get_peer_address->getAddress().isUnset()) return; - m_peer_address.copy(get_peer_address->getAddress()); + m_peer_address = get_peer_address->getAddress(); m_current_protocol = nullptr; if (m_peer_address.isUnset()) { @@ -117,7 +117,7 @@ void ConnectToPeer::asynchronousUpdate() // The wan remote should already start its ping message to us now // so we can send packet directly to it. TransportAddress broadcast_address; - broadcast_address.copy(m_peer_address); + broadcast_address = m_peer_address; BareNetworkString aloha(std::string("aloha_stk")); STKHost::get()->sendRawPacket(aloha, broadcast_address); diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index cad785d2a..bed2b1a84 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -46,8 +46,8 @@ ConnectToServer::ConnectToServer(std::shared_ptr server) { if (server) { - m_server = server; - m_server_address.copy(m_server->getAddress()); + m_server = server; + m_server_address = m_server->getAddress(); } setHandleConnections(true); } // ConnectToServer(server, host) @@ -105,7 +105,7 @@ void ConnectToServer::asynchronousUpdate() return a->getCurrentPlayers() < b->getCurrentPlayers(); }); m_server = servers[0]; - m_server_address.copy(m_server->getAddress()); + m_server_address = m_server->getAddress(); } else { @@ -435,7 +435,7 @@ void ConnectToServer::waitingAloha(bool is_wan) if (sender.isPublicAddressLocalhost()) sender.setIP(0x7f000001); // 127.0.0.1 } - m_server_address.copy(sender); + m_server_address = sender; m_state = CONNECTING; // Reset timer for next usage m_timer = 0.0; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index e3ad0d3db..b48b39912 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -264,7 +264,7 @@ void ServerLobby::asynchronousUpdate() } else { - m_server_address.copy(STKHost::get()->getPublicAddress()); + m_server_address = STKHost::get()->getPublicAddress(); STKHost::get()->startListening(); m_state = REGISTER_SELF_ADDRESS; } diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 260e64d43..f724c37ef 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -145,7 +145,7 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, * \param buffer Pointer to the event data. */ void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, - BareNetworkString *buffer, int ticks) + BareNetworkString *buffer, int ticks) { m_rewind_queue.addNetworkEvent(event_rewinder, buffer, ticks); } // addNetworkEvent diff --git a/src/network/server.cpp b/src/network/server.cpp index 2139830ef..6e0a79ebd 100644 --- a/src/network/server.cpp +++ b/src/network/server.cpp @@ -116,11 +116,11 @@ Server::Server(unsigned server_id, const core::stringw &name, int max_players, m_server_owner = 0; m_current_players = current_players; m_max_players = max_players; - m_address.copy(address); + m_address = address; // In case of LAN server, public and private port are the same. m_private_port = m_address.getPort(); - m_difficulty = (RaceManager::Difficulty)difficulty; - m_server_mode = server_mode; + m_difficulty = (RaceManager::Difficulty)difficulty; + m_server_mode = server_mode; m_password_protected = password_protected; m_distance = 0.0f; } // server(server_id, ...) diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index ff30424ed..76ad6d9da 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -31,7 +31,6 @@ #include "utils/time.hpp" #include -#include #include #define SERVER_REFRESH_INTERVAL 5.0f @@ -136,11 +135,14 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const addr.host = STKHost::HOST_ANY; addr.port = STKHost::PORT_ANY; Network *broadcast = new Network(1, 1, 0, 0, &addr); - - BareNetworkString s(std::string("stk-server")); - TransportAddress broadcast_address(-1, - NetworkConfig::get()->getServerDiscoveryPort()); - broadcast->sendRawPacket(s, broadcast_address); + const std::vector &all_bcast = + broadcast->getBroadcastAddresses(); + for (auto &bcast_addr : all_bcast) + { + Log::info("Server Discovery", "Broadcasting to %s", + bcast_addr.toString().c_str()); + broadcast->sendRawPacket(std::string("stk-server"), bcast_addr); + } Log::info("ServersManager", "Sent broadcast message."); @@ -153,7 +155,12 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const const auto& servers = ServersManager::get()->getServers(); int cur_server_id = (int)servers.size(); assert(cur_server_id == 0); - std::vector > servers_now; + // Use a map with the server name as key to automatically remove + // duplicated answers from a server (since we potentially do + // multiple broadcasts). We can not use the sender ip address, + // because e.g. a local client would answer as 127.0.0.1 and + // 192.168.**. + std::map > servers_now; while (StkTime::getRealTime() - start_time < DURATION) { TransportAddress sender; @@ -178,9 +185,10 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const uint8_t mode = s.getUInt8(); sender.setPort(port); uint8_t password = s.getUInt8(); - servers_now.emplace_back(std::make_shared + servers_now.emplace(name, std::make_shared (cur_server_id++, name, max_players, players, - difficulty, mode, sender, password == 1)); + difficulty, mode, sender, password == 1) ); + //all_servers.[name] = servers_now.back(); } // if received_data } // while still waiting m_success = true; @@ -199,6 +207,20 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const } // getLANRefreshRequest +// ---------------------------------------------------------------------------- +/** Takes a mapping of server name to server data (to avoid having the same + * server listed more than once since the client will be doing multiple + * broadcasts to find a server), and converts this into a list of servers. + * \param servers Mapping of server name to Server object. + */ +void ServersManager::setLanServers(const std::map >& servers) +{ + m_servers.clear(); + for (auto i : servers) m_servers.emplace_back(i.second); + m_list_updated = true; + +} // ---------------------------------------------------------------------------- /** Factory function to create either a LAN or a WAN update-of-server * requests. The current list of servers is also cleared. diff --git a/src/network/servers_manager.hpp b/src/network/servers_manager.hpp index 823ce74b3..cafde353a 100644 --- a/src/network/servers_manager.hpp +++ b/src/network/servers_manager.hpp @@ -19,7 +19,10 @@ #ifndef HEADER_SERVERS_MANAGER_HPP #define HEADER_SERVERS_MANAGER_HPP +#include + #include +#include #include #include #include @@ -48,16 +51,12 @@ private: // ------------------------------------------------------------------------ void setWanServers(bool success, const XMLNode* input); // ------------------------------------------------------------------------ - void setLanServers(std::vector >& servers) - { - m_servers = std::move(servers); - m_list_updated = true; - } - // ------------------------------------------------------------------------ Online::XMLRequest* getWANRefreshRequest() const; // ------------------------------------------------------------------------ Online::XMLRequest* getLANRefreshRequest() const; - + // ------------------------------------------------------------------------ + void setLanServers(const std::map >& servers); public: // ------------------------------------------------------------------------ // Singleton diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index afa0c3182..380a4cea6 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -577,12 +577,12 @@ void STKHost::setPublicAddress() // alternate NAT traversal methods. if (!xor_addr.isUnset()) { - m_public_address.copy(xor_addr); + m_public_address = xor_addr; } else { Log::warn("STKHost", "Only non xor-mapped address returned."); - m_public_address.copy(non_xor_addr); + m_public_address = non_xor_addr; } // Succeed, save ping UserConfigParams::m_stun_list[server_name] = diff --git a/src/network/transport_address.hpp b/src/network/transport_address.hpp index fbe827094..028f0b8a2 100644 --- a/src/network/transport_address.hpp +++ b/src/network/transport_address.hpp @@ -22,7 +22,6 @@ #ifndef HEADER_TRANSPORT_ADDRESS_HPP #define HEADER_TRANSPORT_ADDRESS_HPP -#include "utils/no_copy.hpp" #include "utils/string_utils.hpp" #include "utils/types.hpp" @@ -35,7 +34,7 @@ * \brief Describes a transport-layer address. * For IP networks, a transport address is the couple ip:port. */ -class TransportAddress : public NoCopy +class TransportAddress { private: uint32_t m_ip; //!< The IPv4 address @@ -49,6 +48,14 @@ public: m_port = port; } // TransportAddress + // ------------------------------------------------------------------------ + TransportAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, + uint16_t port=0) + { + m_ip = (b1 << 24) + (b2 << 16) + (b3 << 8) + b4; + m_port = port; + } // TransportAddress(uint8_t,...) + // ------------------------------------------------------------------------ /** Construct an transport address from an ENetAddress. */ TransportAddress(const ENetAddress &a) @@ -92,15 +99,6 @@ public: ~TransportAddress() {} // ------------------------------------------------------------------------ static void unitTesting(); -private: - friend class NetworkConfig; - /** The copy constructor is private, so that the friend class - * NetworkConfig can access it to create a copy (getMyAddress), but - * no other class can. */ - TransportAddress(const TransportAddress &other) - { - copy(other); - } // TransportAddress(const TransportAddress&) public: // ------------------------------------------------------------------------ bool isPublicAddressLocalhost() const; @@ -108,15 +106,6 @@ public: bool isLAN() const; // ------------------------------------------------------------------------ bool isUnset() const { return m_ip == 0 || m_port == 0; } - // ------------------------------------------------------------------------ - /** A copy function (to replace the copy constructor which is disabled - * using NoCopy): it copies the data from the argument into this object.*/ - void copy(const TransportAddress &other) - { - m_ip = other.m_ip; - m_port = other.m_port; - } // copy - // ------------------------------------------------------------------------ /** Resets ip and port to 0. */ void clear() diff --git a/src/states_screens/online_screen.cpp b/src/states_screens/online_screen.cpp index 9480c52d5..ca9e49274 100644 --- a/src/states_screens/online_screen.cpp +++ b/src/states_screens/online_screen.cpp @@ -271,8 +271,8 @@ void OnlineScreen::eventCallback(Widget* widget, const std::string& name, return false; } - m_entered_server_address.copy( - STKHost::get()->getServerPeerForClient()->getAddress()); + m_entered_server_address = + STKHost::get()->getServerPeerForClient()->getAddress(); auto cl = LobbyProtocol::create(); cl->setAddress(m_entered_server_address); cl->requestStart();