23#include <sys/socket.h>
30constexpr uint32_t kPairingMagic = 0x42434E50U;
37uint32_t
LoadU32(
const uint8_t* data) {
38 return (uint32_t(data[0]) << 24) |
39 (uint32_t(data[1]) << 16) |
40 (uint32_t(data[2]) << 8) |
48void LogErr(
const char* message) {
49 std::cerr <<
"UDP adapter error: " << message <<
" errno=" << errno << std::endl;
65 m_socket = ::socket(AF_INET, SOCK_DGRAM, 0);
72 if (setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(yes)) < 0) {
73 LogErr(
"setsockopt(SO_REUSEADDR)");
76 if (fcntl(m_socket, F_SETFL, O_NONBLOCK) < 0) {
77 LogErr(
"fcntl(O_NONBLOCK)");
84 m_bind.sin_family = AF_INET;
85 m_bind.sin_port = htons(listenPort);
86 m_bind.sin_addr.s_addr = INADDR_ANY;
88 if (bind(m_socket,
reinterpret_cast<sockaddr*
>(&m_bind),
sizeof(m_bind)) < 0) {
95 m_requirePairing = (listenPort > 0);
97 if (targetIp && targetPort > 0) {
99 m_lastPeer.sin_family = AF_INET;
100 m_lastPeer.sin_port = htons(targetPort);
101 if (inet_pton(AF_INET, targetIp, &m_lastPeer.sin_addr) > 0) {
104 m_initialPeer = m_lastPeer;
105 m_pairingComplete =
true;
106 m_fixedPeerConfigured =
true;
108 LogErr(
"inet_pton (invalid target IP)");
132 if (!data || length == 0) {
135 if (!m_hasPeer || m_socket < 0) {
138 const auto sent = ::sendto(m_socket, data, length, 0,
139 reinterpret_cast<sockaddr*
>(&m_lastPeer),
sizeof(m_lastPeer));
140 return sent ==
static_cast<ssize_t
>(length);
152 m_peerLocked = locked;
154 m_pairingComplete =
false;
158 if (m_fixedPeerConfigured && m_hasPeer) {
159 m_initialPeer = m_lastPeer;
160 m_pairingComplete =
true;
164 if (m_requirePairing) {
165 m_pairingComplete =
false;
179 m_pairingToken = token;
180 if (m_peerLocked && m_requirePairing && !m_fixedPeerConfigured) {
181 m_pairingComplete =
false;
192 if (!m_fixedPeerConfigured) {
193 m_pairingComplete =
false;
211 if (m_socket < 0 || !buffer || maxLength == 0) {
216 const auto now = std::chrono::steady_clock::now();
217 if (m_peerLocked && m_hasPeer && !m_fixedPeerConfigured &&
218 m_lastPeerRx != std::chrono::steady_clock::time_point{} &&
219 now - m_lastPeerRx > kPeerTimeout) {
224 socklen_t slen =
sizeof(src);
225 const auto received = ::recvfrom(m_socket, buffer, maxLength, MSG_DONTWAIT,
226 reinterpret_cast<sockaddr*
>(&src), &slen);
228 if (errno == EAGAIN || errno == EWOULDBLOCK) {
236 if (m_requirePairing && !m_pairingComplete && !m_fixedPeerConfigured) {
237 if (ProcessPairingPacket(buffer,
static_cast<std::size_t
>(received), src)) {
244 if (m_hasPeer && (src.sin_addr.s_addr != m_initialPeer.sin_addr.s_addr ||
245 src.sin_port != m_initialPeer.sin_port)) {
260 return static_cast<std::size_t
>(received);
274bool UdpPosixAdapter::ProcessPairingPacket(
const uint8_t* buffer, std::size_t length,
const sockaddr_in& src) {
286 m_remoteSchemaHash =
LoadU32(buffer + 4);
288 std::cerr <<
"UDP adapter: Schema mismatch! Local=0x" << std::hex <<
kSchemaHash
289 <<
" Remote=0x" << m_remoteSchemaHash << std::dec << std::endl;
290 m_schemaValidated =
false;
298 m_pairingComplete =
true;
299 m_schemaValidated =
true;
316 if (!m_hasPeer || m_socket < 0) {
325 const auto sent = ::sendto(m_socket, handshake,
sizeof(handshake), 0,
326 reinterpret_cast<sockaddr*
>(&m_lastPeer),
sizeof(m_lastPeer));
327 return sent ==
static_cast<ssize_t
>(
sizeof(handshake));
~UdpPosixAdapter() override
Destructor. Closes the socket.
UdpPosixAdapter(uint16_t listenPort, const char *targetIp=nullptr, uint16_t targetPort=0)
Construct UDP adapter.
std::size_t ReceiveChunk(uint8_t *buffer, std::size_t maxLength) override
Receives a UDP datagram.
void SetPairingToken(uint32_t token)
Sets the expected pairing token for handshake validation.
bool SendHandshake()
Sends the V3 protocol handshake to the current peer.
bool SendBytes(const uint8_t *data, std::size_t length) override
Sends bytes to the current peer via UDP.
void UnlockPeer()
Resets pairing state to allow re-pairing with a new peer.
void SetPeerLockMode(bool locked)
Enables or disables peer locking.
uint32_t LoadU32(const uint8_t *p)
bool EncodeHandshake(uint8_t *out, std::size_t capacity)
Encode handshake with default schema hash.
constexpr std::size_t kHandshakeSize
constexpr std::array< uint8_t, 4 > kHandshakeMagic
constexpr uint32_t kSchemaHash