Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslogVxWorks64TcpStream.h"
6 : :
7 : : #include <errno.h>
8 : : #include <limits.h>
9 : : #include <stdbool.h>
10 : : #include <stddef.h>
11 : : #include <stdint.h>
12 : :
13 : : #include "vxWorks.h"
14 : :
15 : : #include <netinet/in.h>
16 : : #include <netinet/tcp.h>
17 : : #include <sys/socket.h>
18 : : #include <sys/times.h>
19 : :
20 : : #include "ioLib.h"
21 : : #include "sockLib.h"
22 : :
23 : : #include "SolidSyslogError.h"
24 : : #include "SolidSyslogErrorCategory.h"
25 : : #include "SolidSyslogNullStream.h"
26 : : #include "SolidSyslogStreamCategories.h"
27 : : #include "SolidSyslogStreamDefinition.h"
28 : : #include "SolidSyslogTunables.h"
29 : : #include "SolidSyslogVxWorks64AddressPrivate.h"
30 : : #include "SolidSyslogVxWorks64TcpStreamErrors.h"
31 : : #include "SolidSyslogVxWorks64TcpStreamPrivate.h"
32 : :
33 : : const struct SolidSyslogErrorSource SolidSyslogVxWorks64TcpStreamErrorSource = {
34 : : "VxWorks64TcpStream",
35 : : SolidSyslogTcpStreamErrors_Name
36 : : };
37 : :
38 : : struct SolidSyslogAddress;
39 : :
40 : : enum
41 : : {
42 : : INVALID_SOCKET = -1,
43 : : MILLISECONDS_PER_SECOND = 1000,
44 : : MICROSECONDS_PER_MILLISECOND = 1000
45 : : };
46 : :
47 : : static uint32_t VxWorks64TcpStream_NullConnectTimeoutGetter(void* context);
48 : : static inline bool VxWorks64TcpStream_ConfigProvidesGetter(const struct SolidSyslogVxWorks64TcpStreamConfig* config);
49 : :
50 : : static bool VxWorks64TcpStream_Open(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr);
51 : : static bool VxWorks64TcpStream_TakeSocketAndConnect(
52 : : struct SolidSyslogVxWorks64TcpStream* self,
53 : : const struct sockaddr_in* sin
54 : : );
55 : : static bool VxWorks64TcpStream_Send(struct SolidSyslogStream* base, const void* buffer, size_t size);
56 : : static inline bool VxWorks64TcpStream_FitsTheStack(size_t size);
57 : : static bool VxWorks64TcpStream_PeerIsStillThere(const struct SolidSyslogVxWorks64TcpStream* self);
58 : : static inline bool VxWorks64TcpStream_WouldBlock(int err);
59 : : static inline bool VxWorks64TcpStream_WroteAllBytes(int sent, size_t expected);
60 : : static SolidSyslogSsize VxWorks64TcpStream_Read(struct SolidSyslogStream* base, void* buffer, size_t size);
61 : : static inline int VxWorks64TcpStream_ClampToTheStack(size_t size);
62 : : static void VxWorks64TcpStream_Close(struct SolidSyslogStream* base);
63 : : static uint32_t VxWorks64TcpStream_Version(struct SolidSyslogStream* base);
64 : :
65 : : static inline struct SolidSyslogVxWorks64TcpStream* VxWorks64TcpStream_SelfFromBase(struct SolidSyslogStream* base);
66 : : static void VxWorks64TcpStream_CloseSocket(struct SolidSyslogVxWorks64TcpStream* self);
67 : : static inline bool VxWorks64TcpStream_IsSocketValid(int fd);
68 : : static void VxWorks64TcpStream_ReportConnectFailure(
69 : : struct SolidSyslogVxWorks64TcpStream* self,
70 : : enum SolidSyslogSeverity severity,
71 : : enum SolidSyslogTcpStreamErrors detail
72 : : );
73 : : static void VxWorks64TcpStream_ReportConnectFailureWithErrno(
74 : : struct SolidSyslogVxWorks64TcpStream* self,
75 : : enum SolidSyslogSeverity severity,
76 : : enum SolidSyslogTcpStreamErrors detail,
77 : : int nativeErrno
78 : : );
79 : : static bool VxWorks64TcpStream_ConnectOrCloseOnFailure(
80 : : struct SolidSyslogVxWorks64TcpStream* self,
81 : : const struct sockaddr_in* sin
82 : : );
83 : : static bool VxWorks64TcpStream_Connect(struct SolidSyslogVxWorks64TcpStream* self, const struct sockaddr_in* sin);
84 : : static struct timeval VxWorks64TcpStream_ResolveConnectTimeout(struct SolidSyslogVxWorks64TcpStream* self);
85 : : static inline bool VxWorks64TcpStream_IsRemoteConnectError(int connectErrno);
86 : : static void VxWorks64TcpStream_ApplySocketOptions(int fd);
87 : : static void VxWorks64TcpStream_ReportOptionRefused(void);
88 : :
89 : 121 : void SolidSyslogVxWorks64TcpStream_Initialise(
90 : : struct SolidSyslogStream* base,
91 : : const struct SolidSyslogVxWorks64TcpStreamConfig* config
92 : : )
93 : : {
94 : 121 : struct SolidSyslogVxWorks64TcpStream* self = VxWorks64TcpStream_SelfFromBase(base);
95 : 121 : self->Base.Open = VxWorks64TcpStream_Open;
96 : 121 : self->Base.Send = VxWorks64TcpStream_Send;
97 : 121 : self->Base.Read = VxWorks64TcpStream_Read;
98 : 121 : self->Base.Close = VxWorks64TcpStream_Close;
99 : 121 : self->Base.Version = VxWorks64TcpStream_Version;
100 : 121 : self->Fd = INVALID_SOCKET;
101 : 121 : self->OpenHealth = (struct SolidSyslogStreamOpenHealth) {0};
102 : 121 : self->Config.GetConnectTimeoutMs = VxWorks64TcpStream_NullConnectTimeoutGetter;
103 : 121 : self->Config.ConnectTimeoutContext = NULL;
104 [ + + ]: 121 : if (VxWorks64TcpStream_ConfigProvidesGetter(config) == true)
105 : : {
106 : 3 : self->Config = *config;
107 : : }
108 : 121 : }
109 : :
110 : 121 : static inline bool VxWorks64TcpStream_ConfigProvidesGetter(const struct SolidSyslogVxWorks64TcpStreamConfig* config)
111 : : {
112 [ + + + + ]: 121 : return (config != NULL) && (config->GetConnectTimeoutMs != NULL);
113 : : }
114 : :
115 : : /* Substituted when no getter is installed, so the connect has one code path. */
116 : 54 : static uint32_t VxWorks64TcpStream_NullConnectTimeoutGetter(void* context)
117 : : {
118 : : (void) context;
119 : 54 : return (uint32_t) SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS;
120 : : }
121 : :
122 : 336 : static inline struct SolidSyslogVxWorks64TcpStream* VxWorks64TcpStream_SelfFromBase(struct SolidSyslogStream* base)
123 : : {
124 : 336 : return (struct SolidSyslogVxWorks64TcpStream*) base;
125 : : }
126 : :
127 : 121 : void SolidSyslogVxWorks64TcpStream_Cleanup(struct SolidSyslogStream* base)
128 : : {
129 : 121 : VxWorks64TcpStream_CloseSocket(VxWorks64TcpStream_SelfFromBase(base));
130 : : /* Use-after-destroy lands on the NullStream vtable. */
131 : 121 : *base = *SolidSyslogNullStream_Get();
132 : 121 : }
133 : :
134 : 61 : static bool VxWorks64TcpStream_Open(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr)
135 : : {
136 : 61 : struct SolidSyslogVxWorks64TcpStream* self = VxWorks64TcpStream_SelfFromBase(base);
137 : 61 : bool connected = false;
138 [ + + ]: 61 : if (VxWorks64TcpStream_IsSocketValid(self->Fd))
139 : : {
140 : : /* Refused by contract, not reconnected: what is held stays as it is. */
141 : 2 : VxWorks64TcpStream_Report(
142 : : SOLIDSYSLOG_BAD_ARGUMENT_SEVERITY,
143 : : SOLIDSYSLOG_CAT_BAD_ARGUMENT,
144 : : SOLIDSYSLOG_TCP_STREAM_ERROR_ALREADY_OPEN
145 : : );
146 : : }
147 : : else
148 : : {
149 : 59 : connected = VxWorks64TcpStream_TakeSocketAndConnect(self, SolidSyslogVxWorks64Address_AsConstSockaddrIn(addr));
150 : : }
151 : 61 : return connected;
152 : : }
153 : :
154 : 59 : static bool VxWorks64TcpStream_TakeSocketAndConnect(
155 : : struct SolidSyslogVxWorks64TcpStream* self,
156 : : const struct sockaddr_in* sin
157 : : )
158 : : {
159 : 59 : bool connected = false;
160 : 59 : self->Fd = socket(AF_INET, SOCK_STREAM, 0);
161 : : /* Read errno straight after the call that set it, with nothing between
162 : : * (MISRA 22.10). */
163 [ + + ]: 59 : int socketErrno = VxWorks64TcpStream_IsSocketValid(self->Fd) ? 0 : errno;
164 [ + + ]: 59 : if (VxWorks64TcpStream_IsSocketValid(self->Fd))
165 : : {
166 : 57 : connected = VxWorks64TcpStream_ConnectOrCloseOnFailure(self, sin);
167 : : }
168 : : else
169 : : {
170 : 2 : VxWorks64TcpStream_ReportConnectFailureWithErrno(
171 : : self,
172 : : SOLIDSYSLOG_STREAM_CONNECT_LOCAL_SEVERITY,
173 : : SOLIDSYSLOG_TCP_STREAM_ERROR_ENDPOINT_UNAVAILABLE,
174 : : socketErrno
175 : : );
176 : : }
177 [ + + ]: 59 : if (connected)
178 : : {
179 : 44 : SolidSyslogStreamOpenHealth_Opened(&self->OpenHealth);
180 : : }
181 : 59 : return connected;
182 : : }
183 : :
184 : 151 : static void VxWorks64TcpStream_CloseSocket(struct SolidSyslogVxWorks64TcpStream* self)
185 : : {
186 [ + + ]: 151 : if (VxWorks64TcpStream_IsSocketValid(self->Fd))
187 : : {
188 : 57 : (void) close(self->Fd);
189 : 57 : self->Fd = INVALID_SOCKET;
190 : : }
191 : 151 : }
192 : :
193 : 330 : static inline bool VxWorks64TcpStream_IsSocketValid(int fd)
194 : : {
195 : 330 : return fd >= 0;
196 : : }
197 : :
198 : : /* One category for every connect failure; the detail names the step.
199 : : * Reported once per run of the same detail, as the Open contract requires:
200 : : * the sender retries every service pass. */
201 : 1 : static void VxWorks64TcpStream_ReportConnectFailure(
202 : : struct SolidSyslogVxWorks64TcpStream* self,
203 : : enum SolidSyslogSeverity severity,
204 : : enum SolidSyslogTcpStreamErrors detail
205 : : )
206 : : {
207 : 1 : SolidSyslogStreamOpenHealth_ReportFailure(
208 : : &self->OpenHealth,
209 : : &SolidSyslogVxWorks64TcpStreamErrorSource,
210 : : severity,
211 : : SOLIDSYSLOG_CAT_STREAM_CONNECT_FAILED,
212 : : (int32_t) detail
213 : : );
214 : 1 : }
215 : :
216 : : /* The fault, with the errno behind it when the call left one. */
217 : 15 : static void VxWorks64TcpStream_ReportConnectFailureWithErrno(
218 : : struct SolidSyslogVxWorks64TcpStream* self,
219 : : enum SolidSyslogSeverity severity,
220 : : enum SolidSyslogTcpStreamErrors detail,
221 : : int nativeErrno
222 : : )
223 : : {
224 [ + + ]: 15 : if (nativeErrno != 0)
225 : : {
226 : 14 : SolidSyslogStreamOpenHealth_ReportFailureWithNativeCode(
227 : : &self->OpenHealth,
228 : : &SolidSyslogVxWorks64TcpStreamErrorSource,
229 : : severity,
230 : : SOLIDSYSLOG_CAT_STREAM_CONNECT_FAILED,
231 : : (int32_t) detail,
232 : : (int32_t) nativeErrno
233 : : );
234 : : }
235 : : else
236 : : {
237 : 1 : VxWorks64TcpStream_ReportConnectFailure(self, severity, detail);
238 : : }
239 : 15 : }
240 : :
241 : : /* A failed connect closes its socket, which would otherwise be held until
242 : : * Destroy. */
243 : 57 : static bool VxWorks64TcpStream_ConnectOrCloseOnFailure(
244 : : struct SolidSyslogVxWorks64TcpStream* self,
245 : : const struct sockaddr_in* sin
246 : : )
247 : : {
248 : 57 : bool connected = VxWorks64TcpStream_Connect(self, sin);
249 [ + + ]: 57 : if (connected)
250 : : {
251 : 44 : VxWorks64TcpStream_ApplySocketOptions(self->Fd);
252 : : }
253 : : else
254 : : {
255 : 13 : VxWorks64TcpStream_CloseSocket(self);
256 : : }
257 : 57 : return connected;
258 : : }
259 : :
260 : : /* connectWithTimeout reports the bound expiring as EINPROGRESS. An attempt
261 : : * still under way when it returns dies with the socket. */
262 : 57 : static bool VxWorks64TcpStream_Connect(struct SolidSyslogVxWorks64TcpStream* self, const struct sockaddr_in* sin)
263 : : {
264 : 57 : struct timeval timeout = VxWorks64TcpStream_ResolveConnectTimeout(self);
265 : : /* sockLib takes a non-const address that it only reads (D.006). */
266 : 57 : STATUS status = connectWithTimeout(self->Fd, (struct sockaddr*) sin, (int) sizeof(*sin), &timeout);
267 : : /* Read errno straight after the call that set it, with nothing between
268 : : * (MISRA 22.10). */
269 [ + + ]: 57 : int connectErrno = (status == ERROR) ? errno : 0;
270 : 57 : bool connected = (status == OK);
271 : : /* Each failure carries the errno connectWithTimeout left, the bound
272 : : * expiring included: EINPROGRESS is how it says so. */
273 [ + + ]: 57 : if (connectErrno == EINPROGRESS)
274 : : {
275 : 1 : VxWorks64TcpStream_ReportConnectFailureWithErrno(
276 : : self,
277 : : SOLIDSYSLOG_STREAM_CONNECT_REMOTE_SEVERITY,
278 : : SOLIDSYSLOG_TCP_STREAM_ERROR_CONNECT_TIMED_OUT,
279 : : connectErrno
280 : : );
281 : : }
282 [ + + ]: 56 : else if (VxWorks64TcpStream_IsRemoteConnectError(connectErrno))
283 : : {
284 : 9 : VxWorks64TcpStream_ReportConnectFailureWithErrno(
285 : : self,
286 : : SOLIDSYSLOG_STREAM_CONNECT_REMOTE_SEVERITY,
287 : : SOLIDSYSLOG_TCP_STREAM_ERROR_CONNECT_REFUSED,
288 : : connectErrno
289 : : );
290 : : }
291 [ + + ]: 47 : else if (!connected)
292 : : {
293 : 3 : VxWorks64TcpStream_ReportConnectFailureWithErrno(
294 : : self,
295 : : SOLIDSYSLOG_STREAM_CONNECT_LOCAL_SEVERITY,
296 : : SOLIDSYSLOG_TCP_STREAM_ERROR_CONNECT_NOT_STARTED,
297 : : connectErrno
298 : : );
299 : : }
300 : : else
301 : : {
302 : : /* connected - nothing to report */
303 : : }
304 : 57 : return connected;
305 : : }
306 : :
307 : 57 : static struct timeval VxWorks64TcpStream_ResolveConnectTimeout(struct SolidSyslogVxWorks64TcpStream* self)
308 : : {
309 : 57 : uint32_t requestedMs = self->Config.GetConnectTimeoutMs(self->Config.ConnectTimeoutContext);
310 : 57 : uint32_t ms =
311 : : (requestedMs > SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS_MAX) ? SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS_MAX : requestedMs;
312 : 57 : uint32_t seconds = ms / (uint32_t) MILLISECONDS_PER_SECOND;
313 : 57 : uint32_t microseconds = (ms % (uint32_t) MILLISECONDS_PER_SECOND) * (uint32_t) MICROSECONDS_PER_MILLISECOND;
314 : : struct timeval timeout;
315 : 57 : timeout.tv_sec = (long) seconds;
316 : 57 : timeout.tv_usec = (long) microseconds;
317 : 57 : return timeout;
318 : : }
319 : :
320 : : /* Remote: a SYN was sent and drew a reset or nothing. Any other failure is
321 : : * local, and nothing left this device. */
322 : 56 : static inline bool VxWorks64TcpStream_IsRemoteConnectError(int connectErrno)
323 : : {
324 [ + + + + ]: 56 : return (connectErrno == ECONNREFUSED) || (connectErrno == ETIMEDOUT);
325 : : }
326 : :
327 : : /* NODELAY stops a small record waiting to coalesce; keepalive detects a peer
328 : : * lost while idle. Set after connecting, so a refusal is reported only on a
329 : : * connection that stands. */
330 : 44 : static void VxWorks64TcpStream_ApplySocketOptions(int fd)
331 : : {
332 : 44 : int enable = 1;
333 : 44 : bool allAccepted = (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*) &enable, (int) sizeof(enable)) == OK);
334 : 44 : allAccepted =
335 [ + + + + ]: 44 : (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*) &enable, (int) sizeof(enable)) == OK) && allAccepted;
336 [ + + ]: 44 : if (!allAccepted)
337 : : {
338 : 3 : VxWorks64TcpStream_ReportOptionRefused();
339 : : }
340 : 44 : }
341 : :
342 : : /* One event per attempt, however many options were refused. */
343 : 3 : static void VxWorks64TcpStream_ReportOptionRefused(void)
344 : : {
345 : 3 : VxWorks64TcpStream_Report(
346 : : SOLIDSYSLOG_SEVERITY_WARNING,
347 : : SOLIDSYSLOG_CAT_STREAM_OPTION_REFUSED,
348 : : SOLIDSYSLOG_TCP_STREAM_ERROR_SOCKET_OPTION_REFUSED
349 : : );
350 : 3 : }
351 : :
352 : 17 : static bool VxWorks64TcpStream_Send(struct SolidSyslogStream* base, const void* buffer, size_t size)
353 : : {
354 : 17 : struct SolidSyslogVxWorks64TcpStream* self = VxWorks64TcpStream_SelfFromBase(base);
355 [ + + + + ]: 17 : bool ok = VxWorks64TcpStream_FitsTheStack(size) && VxWorks64TcpStream_PeerIsStillThere(self);
356 [ + + ]: 17 : if (ok)
357 : : {
358 : 13 : int sent = send(self->Fd, (const char*) buffer, (int) size, MSG_DONTWAIT);
359 : 13 : ok = VxWorks64TcpStream_WroteAllBytes(sent, size);
360 : : }
361 [ + + ]: 17 : if (!ok)
362 : : {
363 : 7 : VxWorks64TcpStream_CloseSocket(self);
364 : : }
365 : 17 : return ok;
366 : : }
367 : :
368 : : /* The stack takes a record's length as an int. */
369 : 26 : static inline bool VxWorks64TcpStream_FitsTheStack(size_t size)
370 : : {
371 : 26 : return size <= (size_t) INT_MAX;
372 : : }
373 : :
374 : : /* A peer's FIN leaves the socket writable, so peek first: send would accept a
375 : : * record that cannot arrive. Bytes the peer sent that nothing has read stand in
376 : : * front of its FIN, and the peek sees those instead; a syslog collector sends
377 : : * nothing back, so the queue is empty when the check runs. */
378 : 16 : static bool VxWorks64TcpStream_PeerIsStillThere(const struct SolidSyslogVxWorks64TcpStream* self)
379 : : {
380 : 16 : char discard = 0;
381 : 16 : int peeked = recv(self->Fd, &discard, (int) sizeof(discard), MSG_PEEK | MSG_DONTWAIT);
382 [ + + ]: 16 : int peekErrno = (peeked == ERROR) ? errno : 0;
383 [ + + + + : 16 : return (peeked > 0) || ((peeked == ERROR) && VxWorks64TcpStream_WouldBlock(peekErrno));
+ + ]
384 : : }
385 : :
386 : : /* EWOULDBLOCK and EAGAIN are distinct values in VxWorks 6.4. */
387 : 19 : static inline bool VxWorks64TcpStream_WouldBlock(int err)
388 : : {
389 [ + + - + ]: 19 : return (err == EWOULDBLOCK) || (err == EAGAIN);
390 : : }
391 : :
392 : : /* A short write or any error means the record did not go whole. */
393 : 13 : static inline bool VxWorks64TcpStream_WroteAllBytes(int sent, size_t expected)
394 : : {
395 [ + + + + ]: 13 : return (sent >= 0) && ((size_t) sent == expected);
396 : : }
397 : :
398 : 9 : static SolidSyslogSsize VxWorks64TcpStream_Read(struct SolidSyslogStream* base, void* buffer, size_t size)
399 : : {
400 : 9 : struct SolidSyslogVxWorks64TcpStream* self = VxWorks64TcpStream_SelfFromBase(base);
401 : 9 : int received = recv(self->Fd, (char*) buffer, VxWorks64TcpStream_ClampToTheStack(size), MSG_DONTWAIT);
402 [ + + ]: 9 : int recvErrno = (received == ERROR) ? errno : 0;
403 : 9 : SolidSyslogSsize result = -1;
404 [ + + ]: 9 : if (received > 0)
405 : : {
406 : 1 : result = (SolidSyslogSsize) received;
407 : : }
408 [ + + + + ]: 8 : else if ((received == ERROR) && VxWorks64TcpStream_WouldBlock(recvErrno))
409 : : {
410 : 5 : result = 0;
411 : : }
412 : : else
413 : : {
414 : : /* The peer closed, or the connection failed. Close before reporting
415 : : * it, as the Stream contract asks. */
416 : 3 : VxWorks64TcpStream_CloseSocket(self);
417 : : }
418 : 9 : return result;
419 : : }
420 : :
421 : : /* The stack takes a buffer's length as an int. */
422 : 9 : static inline int VxWorks64TcpStream_ClampToTheStack(size_t size)
423 : : {
424 [ + + ]: 9 : return VxWorks64TcpStream_FitsTheStack(size) ? (int) size : INT_MAX;
425 : : }
426 : :
427 : 7 : static void VxWorks64TcpStream_Close(struct SolidSyslogStream* base)
428 : : {
429 : 7 : VxWorks64TcpStream_CloseSocket(VxWorks64TcpStream_SelfFromBase(base));
430 : 7 : }
431 : :
432 : : /* No runtime-tunable configuration; the destination is versioned by the
433 : : * sender's endpoint. */
434 : 3 : static uint32_t VxWorks64TcpStream_Version(struct SolidSyslogStream* base)
435 : : {
436 : : (void) base;
437 : 3 : return 0U;
438 : : }
|