CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
host_api_socket.cpp
Go to the documentation of this file.
1
16
20#include "cdc_log.h"
21
22#include "lwip/sockets.h"
23#include "lwip/netdb.h"
24
25#include <cerrno>
26#include <cstdio>
27#include <fcntl.h>
28#include <unistd.h>
29
30extern "C" void* plg_get_active_plugin(void);
31
32namespace {
33
34static constexpr const char* TAG = "SOCK";
35
36constexpr size_t MAX_SOCKET_SLOTS = 4;
37constexpr uint32_t kDefaultTimeoutMs = 5000;
38
39struct SocketSlot {
40 bool used = false;
41 void* owner = nullptr; // plugin that opened the socket
42 int fd = -1;
43 uint8_t proto = HOST_SOCK_TCP;
44};
45
47
48bool socketAllowed()
49{
50 auto* p = static_cast<cdc::plugin_manager::Plugin*>(plg_get_active_plugin());
51 return p && p->manifest().capabilities.socket;
52}
53
54SocketSlot* slotFor(int handle) { return s_slots.lookup(handle); }
55
56void closeSlot(SocketSlot& slot)
57{
58 if (slot.fd >= 0) ::close(slot.fd);
59 slot = SocketSlot{};
60}
61
62// Apply a per-call send or receive timeout to a socket fd.
63void applyTimeout(int fd, int optname, uint32_t to_ms)
64{
65 struct timeval tv;
66 tv.tv_sec = static_cast<time_t>(to_ms / 1000);
67 tv.tv_usec = static_cast<suseconds_t>((to_ms % 1000) * 1000);
68 ::setsockopt(fd, SOL_SOCKET, optname, &tv, sizeof(tv));
69}
70
71// TCP: bound the connect with a non-blocking handshake + select(); UDP:
72// connect() only fixes the default peer and returns immediately.
73int connectEndpoint(int fd, const struct sockaddr* addr, socklen_t addrlen,
74 uint8_t proto, uint32_t to_ms)
75{
76 if (proto == HOST_SOCK_UDP) {
77 return ::connect(fd, addr, addrlen) == 0 ? HOST_OK : HOST_ERR_GENERIC;
78 }
79
80 int flags = ::fcntl(fd, F_GETFL, 0);
81 if (flags < 0) return HOST_ERR_GENERIC;
82 ::fcntl(fd, F_SETFL, flags | O_NONBLOCK);
83
84 int rc = ::connect(fd, addr, addrlen);
85 if (rc != 0 && errno != EINPROGRESS) return HOST_ERR_GENERIC;
86
87 if (rc != 0) {
88 fd_set wset;
89 FD_ZERO(&wset);
90 FD_SET(fd, &wset);
91 struct timeval tv;
92 tv.tv_sec = static_cast<time_t>(to_ms / 1000);
93 tv.tv_usec = static_cast<suseconds_t>((to_ms % 1000) * 1000);
94 int sel = ::select(fd + 1, nullptr, &wset, nullptr, &tv);
95 if (sel == 0) return HOST_ERR_TIMEOUT;
96 if (sel < 0) return HOST_ERR_GENERIC;
97
98 int soerr = 0;
99 socklen_t len = sizeof(soerr);
100 if (::getsockopt(fd, SOL_SOCKET, SO_ERROR, &soerr, &len) != 0 || soerr != 0) {
101 return HOST_ERR_GENERIC;
102 }
103 }
104
105 ::fcntl(fd, F_SETFL, flags);
106 return HOST_OK;
107}
108
109} // namespace
110
111extern "C" {
112
113int host_socket_open(uint8_t proto, const char* host, uint16_t port, uint32_t timeout_ms)
114{
115 if (!socketAllowed()) return HOST_ERR_NO_CAPABILITY;
116 if (!host || !*host) return HOST_ERR_INVALID_ARG;
117 if (proto != HOST_SOCK_TCP && proto != HOST_SOCK_UDP) return HOST_ERR_INVALID_ARG;
118
119 const int socktype = (proto == HOST_SOCK_UDP) ? SOCK_DGRAM : SOCK_STREAM;
120 const uint32_t to_ms = timeout_ms ? timeout_ms : kDefaultTimeoutMs;
121
122 int slot_id = 0;
123 SocketSlot* slot = s_slots.allocate(slot_id);
124 if (!slot) return HOST_ERR_NO_MEMORY;
125 *slot = SocketSlot{};
126 slot->used = true;
127 slot->owner = plg_get_active_plugin();
128
129 char port_str[6];
130 std::snprintf(port_str, sizeof(port_str), "%u", static_cast<unsigned>(port));
131
132 struct addrinfo hints{};
133 hints.ai_family = AF_UNSPEC;
134 hints.ai_socktype = socktype;
135 struct addrinfo* res = nullptr;
136 if (::getaddrinfo(host, port_str, &hints, &res) != 0 || !res) {
137 LOG_W(TAG, "resolve failed for '%s'", host);
138 *slot = SocketSlot{};
139 return HOST_ERR_NOT_FOUND;
140 }
141
142 int fd = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
143 if (fd < 0) {
144 ::freeaddrinfo(res);
145 *slot = SocketSlot{};
146 return HOST_ERR_GENERIC;
147 }
148
149 int rc = connectEndpoint(fd, res->ai_addr, res->ai_addrlen, proto, to_ms);
150 ::freeaddrinfo(res);
151 if (rc != HOST_OK) {
152 ::close(fd);
153 *slot = SocketSlot{};
154 return rc;
155 }
156
157 slot->fd = fd;
158 slot->proto = proto;
159 return slot_id;
160}
161
162int host_socket_write(int handle, const uint8_t* data, size_t len, uint32_t timeout_ms)
163{
164 if (!socketAllowed()) return HOST_ERR_NO_CAPABILITY;
165 auto* slot = slotFor(handle);
166 if (!slot || slot->fd < 0) return HOST_ERR_INVALID_ARG;
167 if (len == 0) return 0;
168 if (!data) return HOST_ERR_INVALID_ARG;
169
170 applyTimeout(slot->fd, SO_SNDTIMEO, timeout_ms ? timeout_ms : kDefaultTimeoutMs);
171 int n = static_cast<int>(::send(slot->fd, data, len, 0));
172 if (n < 0) {
173 return (errno == EAGAIN || errno == EWOULDBLOCK) ? HOST_ERR_TIMEOUT
175 }
176 return n;
177}
178
179int host_socket_read(int handle, uint8_t* out, size_t cap, uint32_t timeout_ms)
180{
181 if (!socketAllowed()) return HOST_ERR_NO_CAPABILITY;
182 auto* slot = slotFor(handle);
183 if (!slot || slot->fd < 0) return HOST_ERR_INVALID_ARG;
184 if (cap == 0) return 0;
185 if (!out) return HOST_ERR_INVALID_ARG;
186
187 applyTimeout(slot->fd, SO_RCVTIMEO, timeout_ms ? timeout_ms : kDefaultTimeoutMs);
188 int n = static_cast<int>(::recv(slot->fd, out, cap, 0));
189 if (n < 0) {
190 return (errno == EAGAIN || errno == EWOULDBLOCK) ? HOST_ERR_TIMEOUT
192 }
193 return n; // 0 == peer closed the connection (TCP EOF)
194}
195
196int host_socket_close(int handle)
197{
198 auto* slot = slotFor(handle);
199 if (!slot) return HOST_ERR_INVALID_ARG;
200 closeSlot(*slot);
201 return HOST_OK;
202}
203
204void plg_socket_on_unload(void* plugin)
205{
206 if (!plugin) return;
207 for (size_t i = 0; i < MAX_SOCKET_SLOTS; ++i) {
208 SocketSlot& slot = s_slots.slots[i];
209 if (!slot.used || slot.owner != plugin) continue;
210 LOG_W(TAG, "force-closing leaked socket slot %u", static_cast<unsigned>(i + 1));
211 closeSlot(slot);
212 }
213}
214
215} // extern "C"
static const char * TAG
Owned WAMR module instance + per-plugin state.
Fixed-capacity, 1-based slot table for host-API resources.
uint8_t flags
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
const PluginManifest & manifest() const noexcept
Definition Plugin.h:88
#define HOST_SOCK_UDP
Definition host_api.h:381
int host_socket_read(int handle, uint8_t *out, size_t cap, uint32_t timeout_ms)
Read bytes from the stream / receive a datagram from the connected peer.
#define HOST_SOCK_TCP
Protocol selector for host_socket_open.
Definition host_api.h:380
int host_socket_close(int handle)
Close a socket handle.
int host_socket_write(int handle, const uint8_t *data, size_t len, uint32_t timeout_ms)
Write bytes to the stream / send a datagram to the connected peer.
int host_socket_open(uint8_t proto, const char *host, uint16_t port, uint32_t timeout_ms)
Open an outbound connection to a single remote endpoint.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_NO_CAPABILITY
Definition host_api.h:40
#define HOST_OK
Definition host_api.h:37
#define HOST_ERR_INVALID_ARG
Definition host_api.h:39
#define HOST_ERR_NO_MEMORY
Definition host_api.h:43
#define HOST_ERR_TIMEOUT
Definition host_api.h:42
#define HOST_ERR_NOT_FOUND
Definition host_api.h:41
#define HOST_ERR_GENERIC
Definition host_api.h:38
void * plg_get_active_plugin(void)
void plg_socket_on_unload(void *plugin)
T * lookup(int id)
1-based lookup. Returns nullptr if id is out of range or slot unused.
Definition SlotTable.h:26
std::array< T, N > slots
Definition SlotTable.h:21
T * allocate(int &out_id)
Definition SlotTable.h:37