feat: implement RNS transport (Smol Mail 1.2) over microReticulum
This commit is contained in:
parent
af1d31be83
commit
7203556186
19 changed files with 1939 additions and 1141 deletions
198
shim/udp_interface.cpp
Normal file
198
shim/udp_interface.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
#include "udp_interface.h"
|
||||
|
||||
#include <microReticulum/Transport.h>
|
||||
#include <microReticulum/Log.h>
|
||||
|
||||
#ifndef ARDUINO
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#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<size_t>(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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue