Get and broadcast to all broadcast address

This commit is contained in:
Ted John 2019-05-05 16:23:58 +00:00
parent 51117432f0
commit 59ddd7e1ea
4 changed files with 144 additions and 12 deletions

View File

@ -233,16 +233,16 @@ bool ServerList::WriteFavourites(const std::vector<ServerListEntry>& entries)
}
}
std::future<std::vector<ServerListEntry>> ServerList::FetchLocalServerListAsync()
std::future<std::vector<ServerListEntry>> 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<std::vector<ServerListEntry>> ServerList::FetchLocalServerListAsync(
}
platform_sleep(RECV_DELAY_MS);
}
return entries;
});
}
std::future<std::vector<ServerListEntry>> ServerList::FetchLocalServerListAsync()
{
return std::async([&] {
// Get all possible LAN broadcast addresses
auto broadcastEndpoints = GetBroadcastAddresses();
// Spin off a fetch for each broadcast address
std::vector<std::future<std::vector<ServerListEntry>>> futures;
for (const auto& broadcastEndpoint : broadcastEndpoints)
{
auto f = FetchLocalServerListAsync(*broadcastEndpoint);
futures.push_back(std::move(f));
}
// Wait and merge all results
std::vector<ServerListEntry> 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<std::vector<ServerListEntry>> ServerList::FetchOnlineServerListAsync()
{
#ifdef DISABLE_HTTP

View File

@ -18,6 +18,7 @@
#include <vector>
struct json_t;
struct INetworkEndpoint;
struct ServerListEntry
{
@ -45,6 +46,7 @@ private:
void Sort();
std::vector<ServerListEntry> ReadFavourites();
bool WriteFavourites(const std::vector<ServerListEntry>& entries);
std::future<std::vector<ServerListEntry>> FetchLocalServerListAsync(const INetworkEndpoint& broadcastEndpoint);
public:
ServerListEntry& GetServer(size_t index);

View File

@ -36,13 +36,15 @@
#endif
#define FLAG_NO_PIPE 0
#else
#include <cerrno>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cerrno>
#include <fcntl.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#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<IUdpSocket> CreateUdpSocket()
return std::make_unique<UdpSocket>();
}
# ifdef _WIN32
std::vector<INTERFACE_INFO> 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<INTERFACE_INFO> 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<std::unique_ptr<INetworkEndpoint>> GetBroadcastAddresses()
{
std::vector<std::unique_ptr<INetworkEndpoint>> 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<NetworkEndpoint>(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<NetworkEndpoint>(&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

View File

@ -13,6 +13,7 @@
#include <memory>
#include <string>
#include <vector>
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<IUdpSocket> CreateUdpSocket();
std::vector<std::unique_ptr<INetworkEndpoint>> GetBroadcastAddresses();