mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
296 lines
9.4 KiB
C++
296 lines
9.4 KiB
C++
#include "udp_discovery.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <stdexcept>
|
|
#include <cstring>
|
|
#if defined(_WIN32)
|
|
#include <winsock2.h>
|
|
#include <WS2tcpip.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include "logger.h"
|
|
|
|
namespace service_discovery
|
|
{
|
|
// Lil helper function
|
|
void cleanup_socket(int fd)
|
|
{
|
|
#if defined(_WIN32)
|
|
closesocket(fd);
|
|
WSACleanup();
|
|
#else
|
|
close(fd);
|
|
#endif
|
|
}
|
|
|
|
void sendUdpBroadcast(int port, uint8_t *data, int len)
|
|
{
|
|
#if defined(_WIN32)
|
|
WSADATA wsa;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
throw std::runtime_error("Couldn't startup WSA socket!");
|
|
#endif
|
|
|
|
struct sockaddr_in send_addr;
|
|
int fd = -1;
|
|
|
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
|
throw std::runtime_error("Error creating socket!");
|
|
|
|
int val_true = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char *)&val_true, sizeof(val_true)) < 0)
|
|
throw std::runtime_error("Error setting socket option!");
|
|
|
|
memset(&send_addr, 0, sizeof(send_addr));
|
|
send_addr.sin_family = AF_INET;
|
|
send_addr.sin_port = htons(port);
|
|
send_addr.sin_addr.s_addr = INADDR_BROADCAST;
|
|
|
|
if (sendto(fd, (const char *)data, len, 0, (struct sockaddr *)&send_addr, sizeof(sockaddr)) < 0)
|
|
throw std::runtime_error(std::strerror(errno));
|
|
|
|
cleanup_socket(fd);
|
|
}
|
|
|
|
void sendUdpPacket(const char *address, int port, uint8_t *data, int len)
|
|
{
|
|
#if defined(_WIN32)
|
|
WSADATA wsa;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
throw std::runtime_error("Couldn't startup WSA socket!");
|
|
#endif
|
|
|
|
struct sockaddr_in send_addr;
|
|
int fd = -1;
|
|
|
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
|
throw std::runtime_error("Error creating socket!");
|
|
|
|
memset(&send_addr, 0, sizeof(send_addr));
|
|
send_addr.sin_family = AF_INET;
|
|
send_addr.sin_port = htons(port);
|
|
// send_addr.sin_addr.s_addr = INADDR_BROADCAST;
|
|
#if defined(_WIN32)
|
|
send_addr.sin_addr.S_un.S_addr = inet_addr(address);
|
|
#else
|
|
inet_aton(address, &send_addr.sin_addr);
|
|
#endif
|
|
|
|
int ret = sendto(fd, (char *)data, len, 0, (struct sockaddr *)&send_addr, sizeof(sockaddr));
|
|
if (ret < 0)
|
|
throw std::runtime_error("Error on send!");
|
|
|
|
cleanup_socket(fd);
|
|
}
|
|
|
|
void UDPDiscoveryServerRunner::discovery_thread()
|
|
{
|
|
#if defined(_WIN32)
|
|
WSADATA wsa;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
{
|
|
logger->error("Couldn't startup WSA socket for UDP discovery requests!");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
struct sockaddr_in recv_addr;
|
|
int fd = -1;
|
|
|
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
|
{
|
|
logger->error("Error creating UDP discovery socket!");
|
|
cleanup_socket(fd);
|
|
return;
|
|
}
|
|
|
|
int val_true = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&val_true, sizeof(val_true)) < 0)
|
|
{
|
|
logger->error("Error setting UDP discovery socket option!");
|
|
cleanup_socket(fd);
|
|
return;
|
|
}
|
|
|
|
memset(&recv_addr, 0, sizeof(recv_addr));
|
|
recv_addr.sin_family = AF_INET;
|
|
recv_addr.sin_port = htons(cfg.req_port);
|
|
recv_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (bind(fd, (struct sockaddr*)&recv_addr, sizeof(recv_addr)) < 0)
|
|
{
|
|
logger->error("Error binding UDP discovery socket!");
|
|
cleanup_socket(fd);
|
|
return;
|
|
}
|
|
|
|
uint8_t *buffer_rx = new uint8_t[65536];
|
|
while (should_run)
|
|
{
|
|
struct sockaddr_in response_addr;
|
|
socklen_t response_addr_len = sizeof(response_addr);
|
|
int nrecv = recvfrom(fd, (char *)buffer_rx, 65536, 0, (struct sockaddr *)&response_addr, &response_addr_len);
|
|
if (nrecv < 0)
|
|
{
|
|
logger->error("Error on UDP discovery recvfrom!");
|
|
break;
|
|
}
|
|
|
|
bool is_valid = true;
|
|
if (nrecv == (int)cfg.req_pkt.size())
|
|
{
|
|
for (size_t i = 0; i < cfg.req_pkt.size(); i++)
|
|
if (buffer_rx[i] != cfg.req_pkt[i])
|
|
is_valid = false;
|
|
}
|
|
else
|
|
{
|
|
is_valid = false;
|
|
}
|
|
|
|
if (is_valid)
|
|
{
|
|
char *ip_add = inet_ntoa(response_addr.sin_addr);
|
|
// logger->info("PKT FROM %s", ip_add);
|
|
auto pkt = cfg.rep_pkt;
|
|
pkt.push_back((cfg.discover_port >> 24) & 0xFF);
|
|
pkt.push_back((cfg.discover_port >> 16) & 0xFF);
|
|
pkt.push_back((cfg.discover_port >> 8) & 0xFF);
|
|
pkt.push_back(cfg.discover_port & 0xFF);
|
|
sendUdpPacket(ip_add, cfg.rep_port, (uint8_t *)pkt.data(), pkt.size());
|
|
logger->trace("Replying to %s", ip_add);
|
|
}
|
|
}
|
|
|
|
delete[] buffer_rx;
|
|
cleanup_socket(fd);
|
|
}
|
|
|
|
UDPDiscoveryServerRunner::UDPDiscoveryServerRunner(UDPDiscoveryConfig cfg) : cfg(cfg)
|
|
{
|
|
discovery_th = std::thread(&UDPDiscoveryServerRunner::discovery_thread, this);
|
|
}
|
|
|
|
UDPDiscoveryServerRunner::~UDPDiscoveryServerRunner()
|
|
{
|
|
should_run = false;
|
|
if (discovery_th.joinable())
|
|
discovery_th.join();
|
|
}
|
|
|
|
std::vector<std::pair<std::string, int>> discoverUDPServers(UDPDiscoveryConfig cfg, int wait_millis)
|
|
{
|
|
std::vector<std::pair<std::string, int>> servers;
|
|
bool should_wait = true;
|
|
auto fun_rx = [&should_wait, &servers, &cfg]()
|
|
{
|
|
#if defined(_WIN32)
|
|
WSADATA wsa;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
{
|
|
logger->trace("Error in Remote SDR Discovery: Couldn't startup WSA socket!");
|
|
return;
|
|
}
|
|
#endif
|
|
struct sockaddr_in recv_addr;
|
|
int fd = -1;
|
|
|
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
|
{
|
|
logger->trace("Error in Remote SDR Discovery: could not create socket!");
|
|
cleanup_socket(fd);
|
|
return;
|
|
}
|
|
|
|
int val_true = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&val_true, sizeof(val_true)) < 0)
|
|
{
|
|
logger->trace("Error in Remote SDR Discovery: error setting socket options!");
|
|
cleanup_socket(fd);
|
|
return;
|
|
}
|
|
|
|
memset(&recv_addr, 0, sizeof(recv_addr));
|
|
recv_addr.sin_family = AF_INET;
|
|
recv_addr.sin_port = htons(cfg.rep_port);
|
|
recv_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (bind(fd, (struct sockaddr*)&recv_addr, sizeof(recv_addr)) < 0)
|
|
{
|
|
logger->trace("Error in Remote SDR Discovery: error binding socket!");
|
|
cleanup_socket(fd);
|
|
return;
|
|
}
|
|
|
|
uint8_t *buffer_rx = new uint8_t[65536];
|
|
while (should_wait)
|
|
{
|
|
struct sockaddr_in response_addr;
|
|
socklen_t response_addr_len = sizeof(response_addr);
|
|
int nrecv = recvfrom(fd, (char *)buffer_rx, 65536, 0, (struct sockaddr *)&response_addr, &response_addr_len);
|
|
if (nrecv < 0)
|
|
{
|
|
logger->trace("Error in Remote SDR Discovery: error on recvfrom!");
|
|
break;
|
|
}
|
|
|
|
bool is_valid = true;
|
|
if (nrecv - 4 == (int)cfg.rep_pkt.size())
|
|
{
|
|
for (size_t i = 0; i < cfg.rep_pkt.size(); i++)
|
|
if (buffer_rx[i] != cfg.rep_pkt[i])
|
|
is_valid = false;
|
|
}
|
|
else
|
|
{
|
|
is_valid = false;
|
|
}
|
|
|
|
if (is_valid)
|
|
{
|
|
char *ip_add = inet_ntoa(response_addr.sin_addr);
|
|
// logger->info("PKT FROM %s", ip_add);
|
|
int dport = buffer_rx[nrecv - 4] << 24 | buffer_rx[nrecv - 3] << 16 | buffer_rx[nrecv - 2] << 8 | buffer_rx[nrecv - 1];
|
|
servers.push_back({std::string(ip_add), dport});
|
|
}
|
|
|
|
// logger->info("EXIT %d %d %d", (int)is_valid, nrecv, cfg.rep_pkt.size());
|
|
}
|
|
|
|
delete[] buffer_rx;
|
|
cleanup_socket(fd);
|
|
};
|
|
std::thread funrx_th(fun_rx);
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
try
|
|
{
|
|
sendUdpBroadcast(cfg.req_port, cfg.req_pkt.data(), cfg.req_pkt.size());
|
|
}
|
|
catch(std::exception &e)
|
|
{
|
|
logger->trace("Error on Remote SDR Discovery - " + std::string(e.what()));
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(wait_millis));
|
|
should_wait = false; // Force close with a new packet
|
|
try
|
|
{
|
|
sendUdpPacket("127.0.0.1", cfg.rep_port, cfg.req_pkt.data(), cfg.req_pkt.size());
|
|
}
|
|
catch(std::exception &e)
|
|
{
|
|
logger->trace("Error on Remote SDR Discovery - " + std::string(e.what()));
|
|
}
|
|
|
|
if (funrx_th.joinable())
|
|
funrx_th.join();
|
|
|
|
return servers;
|
|
}
|
|
}
|