SafeAPI Backend POSIX
POSIX/Linux OAL backend for safeAPIFreamwork
Loading...
Searching...
No Matches
sapi_posix_backend_channel_service.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
2
4
6
7#include <arpa/inet.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <netdb.h>
11#include <netinet/in.h>
12#include <poll.h>
13#include <stdio.h>
14#include <sys/socket.h>
15#include <sys/types.h>
16#include <unistd.h>
17
18#include "safeapi/oal/memory/sapi_mem_util.h"
19
20typedef struct
21{
22 int fd;
23 sapi_netlink_config_t config;
24 struct sockaddr_in peer_addr;
25 socklen_t peer_addr_len;
26 bool has_peer_addr;
27 bool initialized;
29
30typedef char sapi_posix_channel_state_fits_[(sizeof(sapi_posix_channel_state_t) <=
31 sizeof(sapi_channel_service_storage_t)) ? 1 : -1];
32
33static sapi_posix_channel_resolve_fn s_resolver;
34static void *s_resolver_context;
35
36static sapi_posix_channel_state_t *channel_state(sapi_channel_service_storage_t *storage)
37{
38 return (sapi_posix_channel_state_t *)(void *)storage;
39}
40
41static sapi_status_t set_nonblocking(int fd)
42{
43 int flags = fcntl(fd, F_GETFL, 0);
44 if (flags < 0)
45 {
46 return SAPI_STATUS_INTERNAL_ERROR;
47 }
48 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
49 {
50 return SAPI_STATUS_INTERNAL_ERROR;
51 }
52 return SAPI_STATUS_OK;
53}
54
55static sapi_status_t init_listen_socket(sapi_posix_channel_state_t *state)
56{
57 int fd;
58 int reuse = 1;
59 struct sockaddr_in bind_addr;
60
61 if (state->fd >= 0)
62 {
63 return SAPI_STATUS_OK;
64 }
65
66 fd = socket(AF_INET, SOCK_DGRAM, 0);
67 if (fd < 0)
68 {
69 return SAPI_STATUS_INTERNAL_ERROR;
70 }
71 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
72#ifdef SO_REUSEPORT
73 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
74#endif
75
76 sapi_mem_set(&bind_addr, 0, sizeof(bind_addr));
77 bind_addr.sin_family = AF_INET;
78 bind_addr.sin_port = htons(state->config.port);
79 if (state->config.host == NULL)
80 {
81 bind_addr.sin_addr.s_addr = INADDR_ANY;
82 }
83 else if (inet_pton(AF_INET, state->config.host, &bind_addr.sin_addr) != 1)
84 {
85 (void)close(fd);
86 return SAPI_STATUS_INVALID_PARAM;
87 }
88
89 if (bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0)
90 {
91 (void)close(fd);
92 return SAPI_STATUS_INTERNAL_ERROR;
93 }
94 if (set_nonblocking(fd) != SAPI_STATUS_OK)
95 {
96 (void)close(fd);
97 return SAPI_STATUS_INTERNAL_ERROR;
98 }
99 state->fd = fd;
100 return SAPI_STATUS_OK;
101}
102
103static sapi_status_t init_connect_socket(sapi_posix_channel_state_t *state)
104{
105 struct addrinfo hints;
106 struct addrinfo *resolved = NULL;
107 char port_str[8];
108 int fd;
109
110 if (state->fd >= 0)
111 {
112 return SAPI_STATUS_OK;
113 }
114
115 (void)snprintf(port_str, sizeof(port_str), "%u", (unsigned int)state->config.port);
116 sapi_mem_set(&hints, 0, sizeof(hints));
117 hints.ai_family = AF_INET;
118 hints.ai_socktype = SOCK_DGRAM;
119
120 if (getaddrinfo(state->config.host, port_str, &hints, &resolved) != 0)
121 {
122 return SAPI_STATUS_INVALID_PARAM;
123 }
124
125 fd = socket(resolved->ai_family, resolved->ai_socktype, resolved->ai_protocol);
126 if (fd < 0)
127 {
128 perror("init_connect_socket: socket");
129 freeaddrinfo(resolved);
130 return SAPI_STATUS_INTERNAL_ERROR;
131 }
132 if (set_nonblocking(fd) != SAPI_STATUS_OK)
133 {
134 perror("init_connect_socket: set_nonblocking");
135 (void)close(fd);
136 freeaddrinfo(resolved);
137 return SAPI_STATUS_INTERNAL_ERROR;
138 }
139
140 if (connect(fd, resolved->ai_addr, resolved->ai_addrlen) != 0)
141 {
142 perror("init_connect_socket: connect");
143 (void)close(fd);
144 freeaddrinfo(resolved);
145 return SAPI_STATUS_INTERNAL_ERROR;
146 }
147 freeaddrinfo(resolved);
148 state->fd = fd;
149 {
150 uint8_t hello = 0xA5U;
151 (void)send(state->fd, &hello, 1, 0);
152 }
153 return SAPI_STATUS_OK;
154}
155
156static sapi_status_t backend_setup(sapi_channel_service_storage_t *storage, const char *channel_name)
157{
159 sapi_status_t status;
160
161 if ((storage == NULL) || (channel_name == NULL) || (s_resolver == NULL))
162 {
163 return SAPI_STATUS_INVALID_PARAM;
164 }
165 state = channel_state(storage);
166 sapi_mem_set(state, 0, sizeof(*state));
167 state->fd = -1;
168 status = s_resolver(channel_name, &state->config, s_resolver_context);
169 if (status != SAPI_STATUS_OK)
170 {
171 return status;
172 }
173 state->initialized = true;
174 if (state->config.role == SAPI_NETLINK_ROLE_LISTEN)
175 {
176 return init_listen_socket(state);
177 }
178 return init_connect_socket(state);
179}
180
181static sapi_status_t backend_read(sapi_channel_service_storage_t *storage, void *data, size_t data_size,
182 sapi_duration_ms_t timeout_ms)
183{
185 ssize_t n;
186
187 if ((storage == NULL) || (data == NULL) || (data_size == 0U))
188 {
189 return SAPI_STATUS_INVALID_PARAM;
190 }
191 state = channel_state(storage);
192 if (!state->initialized)
193 {
194 return SAPI_STATUS_NOT_INITIALIZED;
195 }
196 if (state->fd < 0)
197 {
198 if (state->config.role == SAPI_NETLINK_ROLE_LISTEN)
199 {
200 if (init_listen_socket(state) != SAPI_STATUS_OK)
201 {
202 return SAPI_STATUS_TIMEOUT;
203 }
204 }
205 else
206 {
207 if (init_connect_socket(state) != SAPI_STATUS_OK)
208 {
209 return SAPI_STATUS_TIMEOUT;
210 }
211 }
212 }
213
214 if (timeout_ms > 0U)
215 {
216 struct pollfd pfd;
217 int rc;
218
219 pfd.fd = state->fd;
220 pfd.events = POLLIN;
221 pfd.revents = 0;
222 rc = poll(&pfd, 1, (int)timeout_ms);
223 if (rc == 0)
224 {
225 return SAPI_STATUS_TIMEOUT;
226 }
227 if (rc < 0)
228 {
229 if (errno == EINTR)
230 {
231 return SAPI_STATUS_TIMEOUT;
232 }
233 return SAPI_STATUS_INTERNAL_ERROR;
234 }
235 }
236
237 if (state->config.role == SAPI_NETLINK_ROLE_LISTEN)
238 {
239 while (1)
240 {
241 struct sockaddr_in src_addr;
242 socklen_t addr_len = (socklen_t)sizeof(src_addr);
243 sapi_mem_set(&src_addr, 0, sizeof(src_addr));
244
245 n = recvfrom(state->fd, data, data_size, 0, (struct sockaddr *)&src_addr, &addr_len);
246 if (n > 0)
247 {
248 state->peer_addr = src_addr;
249 state->peer_addr_len = addr_len;
250 state->has_peer_addr = true;
251
252 if ((n == 1) && (((const uint8_t *)data)[0] == 0xA5U))
253 {
254 uint8_t ack = 0x5AU;
255 (void)sendto(state->fd, &ack, 1, 0, (const struct sockaddr *)&src_addr, addr_len);
256 continue;
257 }
258 }
259 break;
260 }
261 }
262 else
263 {
264 while (1)
265 {
266 if (!state->has_peer_addr)
267 {
268 uint8_t hello = 0xA5U;
269 (void)send(state->fd, &hello, 1, 0);
270 }
271 n = recv(state->fd, data, data_size, 0);
272 if (n > 0)
273 {
274 if ((n == 1) && (((const uint8_t *)data)[0] == 0x5AU))
275 {
276 state->has_peer_addr = true;
277 continue;
278 }
279 if ((n == 1) && (((const uint8_t *)data)[0] == 0xA5U))
280 {
281 uint8_t ack = 0x5AU;
282 (void)send(state->fd, &ack, 1, 0);
283 state->has_peer_addr = true;
284 continue;
285 }
286 }
287 break;
288 }
289 }
290
291 if (n < 0)
292 {
293 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ECONNREFUSED) ||
294 (errno == ENOTCONN) || (errno == EHOSTUNREACH) || (errno == EPIPE))
295 {
296 return SAPI_STATUS_TIMEOUT;
297 }
298 return SAPI_STATUS_INTERNAL_ERROR;
299 }
300 if (n == 0)
301 {
302 return SAPI_STATUS_TIMEOUT;
303 }
304 return SAPI_STATUS_OK;
305}
306
307static sapi_status_t backend_send(sapi_channel_service_storage_t *storage, const void *data, size_t data_size,
308 sapi_duration_ms_t timeout_ms)
309{
311 ssize_t n;
312
313 if ((storage == NULL) || (data == NULL) || (data_size == 0U))
314 {
315 return SAPI_STATUS_INVALID_PARAM;
316 }
317 state = channel_state(storage);
318 if (!state->initialized)
319 {
320 return SAPI_STATUS_NOT_INITIALIZED;
321 }
322 if (state->fd < 0)
323 {
324 if (state->config.role == SAPI_NETLINK_ROLE_LISTEN)
325 {
326 if (init_listen_socket(state) != SAPI_STATUS_OK)
327 {
328 return SAPI_STATUS_TIMEOUT;
329 }
330 }
331 else
332 {
333 if (init_connect_socket(state) != SAPI_STATUS_OK)
334 {
335 return SAPI_STATUS_TIMEOUT;
336 }
337 }
338 }
339
340 if (timeout_ms > 0U)
341 {
342 struct pollfd pfd;
343 int rc;
344
345 pfd.fd = state->fd;
346 pfd.events = POLLOUT;
347 pfd.revents = 0;
348 rc = poll(&pfd, 1, (int)timeout_ms);
349 if (rc == 0)
350 {
351 return SAPI_STATUS_TIMEOUT;
352 }
353 if (rc < 0)
354 {
355 if (errno == EINTR)
356 {
357 return SAPI_STATUS_TIMEOUT;
358 }
359 return SAPI_STATUS_INTERNAL_ERROR;
360 }
361 }
362
363 if (state->config.role == SAPI_NETLINK_ROLE_LISTEN)
364 {
365 if (!state->has_peer_addr)
366 {
367 /* If no peer has spoken to this listen socket yet, drop outbound gracefully */
368 return SAPI_STATUS_OK;
369 }
370 n = sendto(state->fd, data, data_size, 0, (const struct sockaddr *)&state->peer_addr, state->peer_addr_len);
371 }
372 else
373 {
374 n = send(state->fd, data, data_size, 0);
375 }
376
377 if (n < 0)
378 {
379 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ECONNREFUSED) ||
380 (errno == ENOTCONN) || (errno == EHOSTUNREACH) || (errno == EADDRNOTAVAIL) || (errno == EPIPE))
381 {
382 return SAPI_STATUS_TIMEOUT;
383 }
384 return SAPI_STATUS_INTERNAL_ERROR;
385 }
386 return SAPI_STATUS_OK;
387}
388
389static sapi_status_t backend_close(sapi_channel_service_storage_t *storage)
390{
392
393 if (storage == NULL)
394 {
395 return SAPI_STATUS_OK;
396 }
397 state = channel_state(storage);
398 if (!state->initialized)
399 {
400 return SAPI_STATUS_OK;
401 }
402 state->initialized = false;
403 if (state->fd >= 0)
404 {
405 (void)close(state->fd);
406 state->fd = -1;
407 }
408 state->has_peer_addr = false;
409 return SAPI_STATUS_OK;
410}
411
412static const sapi_channel_service_backend_t s_backend = {
413 backend_setup,
414 backend_read,
415 backend_send,
416 backend_close
417};
418
419sapi_status_t sapi_posix_backend_channel_service_register_resolver(sapi_posix_channel_resolve_fn resolver, void *context)
420{
421 if (resolver == NULL)
422 {
423 return SAPI_STATUS_INVALID_PARAM;
424 }
425 s_resolver = resolver;
426 s_resolver_context = context;
427 return sapi_channel_service_register_backend(&s_backend);
428}
429
430const sapi_channel_service_backend_t *sapi_posix_backend_channel_service(void)
431{
432 return &s_backend;
433}
POSIX named-channel adapter.