diff --git a/src/openrct2/network/ServerList.cpp b/src/openrct2/network/ServerList.cpp index d8a0e999cd..edcaeb01e4 100644 --- a/src/openrct2/network/ServerList.cpp +++ b/src/openrct2/network/ServerList.cpp @@ -233,16 +233,16 @@ bool ServerList::WriteFavourites(const std::vector& entries) } } -std::future> ServerList::FetchLocalServerListAsync() +std::future> ServerList::FetchLocalServerListAsync(const INetworkEndpoint& broadcastEndpoint) { - return std::async([] { + auto broadcastAddress = broadcastEndpoint.GetHostname(); + return std::async([broadcastAddress] { constexpr auto RECV_DELAY_MS = 10; constexpr auto RECV_WAIT_MS = 2000; - auto broadcastAddress = "192.168.1.255"; - std::string msg = "Are you an OpenRCT2 server?"; auto udpSocket = CreateUdpSocket(); + log_verbose("Broadcasting %zu bytes to the LAN (%s)", msg.size(), broadcastAddress); auto len = udpSocket->SendData(broadcastAddress, NETWORK_LAN_BROADCAST_PORT, msg.data(), msg.size()); if (len != msg.size()) @@ -280,11 +280,42 @@ std::future> ServerList::FetchLocalServerListAsync( } platform_sleep(RECV_DELAY_MS); } - return entries; }); } +std::future> ServerList::FetchLocalServerListAsync() +{ + return std::async([&] { + // Get all possible LAN broadcast addresses + auto broadcastEndpoints = GetBroadcastAddresses(); + + // Spin off a fetch for each broadcast address + std::vector>> futures; + for (const auto& broadcastEndpoint : broadcastEndpoints) + { + auto f = FetchLocalServerListAsync(*broadcastEndpoint); + futures.push_back(std::move(f)); + } + + // Wait and merge all results + std::vector mergedEntries; + for (auto& f : futures) + { + try + { + auto entries = std::move(f.get()); + mergedEntries.insert(mergedEntries.begin(), entries.begin(), entries.end()); + } + catch (...) + { + // Ignore any exceptions from a particular broadcast fetch + } + } + return mergedEntries; + }); +} + std::future> ServerList::FetchOnlineServerListAsync() { #ifdef DISABLE_HTTP diff --git a/src/openrct2/network/ServerList.h b/src/openrct2/network/ServerList.h index e6a147cd1b..c9b44a5746 100644 --- a/src/openrct2/network/ServerList.h +++ b/src/openrct2/network/ServerList.h @@ -18,6 +18,7 @@ #include struct json_t; +struct INetworkEndpoint; struct ServerListEntry { @@ -45,6 +46,7 @@ private: void Sort(); std::vector ReadFavourites(); bool WriteFavourites(const std::vector& entries); + std::future> FetchLocalServerListAsync(const INetworkEndpoint& broadcastEndpoint); public: ServerListEntry& GetServer(size_t index); diff --git a/src/openrct2/network/UdpSocket.cpp b/src/openrct2/network/UdpSocket.cpp index 2f02c08219..3da7991c10 100644 --- a/src/openrct2/network/UdpSocket.cpp +++ b/src/openrct2/network/UdpSocket.cpp @@ -36,13 +36,15 @@ #endif #define FLAG_NO_PIPE 0 #else - #include #include - #include - #include - #include - #include + #include #include + #include + #include + #include + #include + #include + #include #include "../common.h" using SOCKET = int32_t; #define SOCKET_ERROR -1 @@ -116,7 +118,7 @@ public: } } - std::string GetHostname() override + std::string GetHostname() const override { char hostname[256]; int res = getnameinfo(&_address, _addressLen, hostname, sizeof(hostname), nullptr, 0, NI_NUMERICHOST); @@ -395,4 +397,99 @@ std::unique_ptr CreateUdpSocket() return std::make_unique(); } +# ifdef _WIN32 +std::vector GetNetworkInterfaces() +{ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + { + printf("socket returned -1\n"); + return {}; + } + + DWORD len = 0; + size_t capacity = 2; + std::vector interfaces; + for (;;) + { + interfaces.resize(capacity); + if (WSAIoctl( + sock, SIO_GET_INTERFACE_LIST, nullptr, 0, interfaces.data(), capacity * sizeof(INTERFACE_INFO), &len, nullptr, + nullptr) + == 0) + { + break; + } + if (WSAGetLastError() != WSAEFAULT) + { + closesocket(sock); + return {}; + } + capacity *= 2; + } + interfaces.resize(len / sizeof(INTERFACE_INFO)); + interfaces.shrink_to_fit(); + return interfaces; +} +# endif + +std::vector> GetBroadcastAddresses() +{ + std::vector> baddresses; +# ifdef _WIN32 + auto interfaces = GetNetworkInterfaces(); + for (const auto& ifo : interfaces) + { + if (ifo.iiFlags & IFF_LOOPBACK) + continue; + if (!(ifo.iiFlags & IFF_BROADCAST)) + continue; + + // iiBroadcast is unusable, because it always seems to be set to 255.255.255.255. + sockaddr_storage address{}; + memcpy(&address, &ifo.iiAddress.Address, sizeof(sockaddr)); + ((sockaddr_in*)&address)->sin_addr.s_addr = ifo.iiAddress.AddressIn.sin_addr.s_addr + | ~ifo.iiNetmask.AddressIn.sin_addr.s_addr; + baddresses.push_back(std::make_unique(address, sizeof(sockaddr))); + } +# else + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + { + return baddresses; + } + + char buf[4 * 1024]{}; + ifconf ifconfx{}; + ifconfx.ifc_len = sizeof(buf); + ifconfx.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifconfx) == -1) + { + close(sock); + return baddresses; + } + + const char* buf_end = buf + ifconfx.ifc_len; + for (const char* p = buf; p < buf_end;) + { + auto req = (const ifreq*)p; + if (req->ifr_addr.sa_family == AF_INET) + { + ifreq r; + strcpy(r.ifr_name, req->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 && (r.ifr_flags & IFF_BROADCAST) && ioctl(sock, SIOCGIFBRDADDR, &r) != -1) + { + baddresses.push_back(std::make_unique(&r.ifr_broadaddr, sizeof(sockaddr))); + } + } + p += sizeof(ifreq); +# if defined(AF_LINK) && !defined(SUNOS) + p += req->ifr_addr.sa_len - sizeof(struct sockaddr); +# endif + } + close(sock); +# endif + return baddresses; +} + #endif diff --git a/src/openrct2/network/UdpSocket.h b/src/openrct2/network/UdpSocket.h index a2523e3719..35df2ee370 100644 --- a/src/openrct2/network/UdpSocket.h +++ b/src/openrct2/network/UdpSocket.h @@ -13,6 +13,7 @@ #include #include +#include enum SOCKET_STATUS { @@ -40,7 +41,7 @@ interface INetworkEndpoint { } - virtual std::string GetHostname() abstract; + virtual std::string GetHostname() const abstract; }; /** @@ -69,3 +70,4 @@ public: }; std::unique_ptr CreateUdpSocket(); +std::vector> GetBroadcastAddresses();