#include "udp_interface.h" #include #include #ifndef ARDUINO #include #include #include #include #include #include #endif using namespace RNS; UDPInterface::UDPInterface(const char* name, const std::string& local_host, int local_port, const std::string& remote_host, int remote_port) : RNS::InterfaceImpl(name) { _IN = true; _OUT = true; _bitrate = BITRATE_GUESS; _HW_MTU = 1064; _local_host = local_host; _local_port = local_port; if (!remote_host.empty()) { _forward_configured = true; _remote_host = remote_host; _remote_port = remote_port; } } /*virtual*/ UDPInterface::~UDPInterface() { stop(); } /*virtual*/ bool UDPInterface::start() { _online = false; #ifdef ARDUINO udp.begin(_local_port); #else // resolve local host struct in_addr local_addr; if (inet_aton(_local_host.c_str(), &local_addr) == 0) { struct hostent* host_ent = gethostbyname(_local_host.c_str()); if (host_ent == nullptr || host_ent->h_addr_list[0] == nullptr) { ERRORF("Unable to resolve local host %s", _local_host.c_str()); return false; } _local_address = *((in_addr_t*)(host_ent->h_addr_list[0])); } else { _local_address = local_addr.s_addr; } _remote_address = INADDR_NONE; if (_forward_configured) { struct in_addr remote_addr; if (inet_aton(_remote_host.c_str(), &remote_addr) == 0) { struct hostent* host_ent = gethostbyname(_remote_host.c_str()); if (host_ent == nullptr || host_ent->h_addr_list[0] == nullptr) { ERRORF("Unable to resolve remote host %s", _remote_host.c_str()); return false; } _remote_address = *((in_addr_t*)(host_ent->h_addr_list[0])); } else { _remote_address = remote_addr.s_addr; } } _socket = socket( PF_INET, SOCK_DGRAM, 0 ); if (_socket < 0) { ERRORF("Unable to create socket with error %d", errno); return false; } int broadcast = 1; setsockopt(_socket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)); int reuse = 1; setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); #ifdef SO_REUSEPORT setsockopt(_socket, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); #endif INFOF("Binding UDP socket %d to %s:%d", _socket, _local_host.c_str(), _local_port); sockaddr_in bind_addr; memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_addr.s_addr = _local_address; bind_addr.sin_port = htons(_local_port); if (bind(_socket, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1) { ERRORF("Unable to bind socket with error %d", errno); close(_socket); _socket = -1; return false; } #endif _online = true; return true; } /*virtual*/ void UDPInterface::stop() { #ifndef ARDUINO if (_socket > -1) { close(_socket); _socket = -1; } #endif _online = false; } /*virtual*/ void UDPInterface::loop() { if (!_online) { return; } #ifdef ARDUINO udp.parsePacket(); size_t len = udp.read(_buffer.writable(Type::Reticulum::MTU), Type::Reticulum::MTU); if (len > 0) { _buffer.resize(len); on_incoming(_buffer); } #else // One datagram per recvfrom() with MSG_DONTWAIT, looping until the // kernel queue is empty — the same drain pattern as the microReticulum // example, which avoids stale/zero FIONREAD counts on some platforms. while (true) { sockaddr_in src_addr{}; socklen_t src_addr_len = sizeof(src_addr); ssize_t len = recvfrom(_socket, _buffer.writable(_HW_MTU), _HW_MTU, MSG_DONTWAIT, (struct sockaddr*)&src_addr, &src_addr_len); if (len <= 0) { break; } _buffer.resize(static_cast(len)); if (!_forward_configured) { _last_src_addr = src_addr; _have_src = true; } on_incoming(_buffer); } #endif } /*virtual*/ bool UDPInterface::send_outgoing(const RNS::Bytes& data) { bool success = true; try { if (_online) { #ifdef ARDUINO udp.beginPacket(_remote_host.c_str(), _remote_port); udp.write(data.data(), data.size()); udp.endPacket(); #else sockaddr_in sock_addr; if (_forward_configured) { memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = _remote_address; sock_addr.sin_port = htons(_remote_port); } else if (_have_src) { // No forward target: reply to whoever spoke to us last. sock_addr = _last_src_addr; } else { WARNING("UDPInterface: no forward target and no peer heard from yet, dropping outgoing packet"); return false; } ssize_t sent = sendto(_socket, data.data(), data.size(), 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); if (sent != (ssize_t)data.size()) { WARNINGF("Failed sending %d bytes via UDP", (int)data.size()); success = false; } #endif } InterfaceImpl::handle_outgoing(data); } catch (const std::exception& e) { ERRORF("Could not transmit on %s. The contained exception was: %s", toString().c_str(), e.what()); success = false; } return success; } void UDPInterface::on_incoming(const RNS::Bytes& data) { InterfaceImpl::handle_incoming(data); }