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 <stdbool.h>
6 : : #include <stddef.h>
7 : : #include <stdint.h>
8 : :
9 : : #include "SolidSyslogEndpoint.h"
10 : : #include "SolidSyslogEndpointHostPrivate.h"
11 : : #include "SolidSyslogError.h"
12 : : #include "SolidSyslogFormatter.h"
13 : : #include "SolidSyslogNullSender.h"
14 : : #include "SolidSyslogResolver.h"
15 : : #include "SolidSyslogSenderDefinition.h"
16 : : #include "SolidSyslogSenderHealth.h"
17 : : #include "SolidSyslogStream.h"
18 : : #include "SolidSyslogStreamSender.h"
19 : : #include "SolidSyslogStreamSenderErrors.h"
20 : : #include "SolidSyslogStreamSenderPrivate.h"
21 : : #include "SolidSyslogTransport.h"
22 : :
23 : : const struct SolidSyslogErrorSource SolidSyslogStreamSenderErrorSource = {
24 : : "StreamSender",
25 : : SolidSyslogStreamSenderErrors_Name
26 : : };
27 : :
28 : : struct SolidSyslogAddress;
29 : : struct SolidSyslogFormatter;
30 : :
31 : : enum
32 : : {
33 : : UINT32_MAX_DECIMAL_DIGITS = 10,
34 : : OCTET_COUNTING_SEPARATOR = 1,
35 : : OCTET_COUNTING_NULL_TERMINATOR = 1,
36 : : OCTET_COUNTING_PREFIX_CAPACITY =
37 : : UINT32_MAX_DECIMAL_DIGITS + OCTET_COUNTING_SEPARATOR + OCTET_COUNTING_NULL_TERMINATOR
38 : : };
39 : :
40 : : static bool StreamSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size);
41 : : static void StreamSender_Disconnect(struct SolidSyslogSender* base);
42 : :
43 : : static inline void StreamSender_UpdateDeliveryHealth(struct SolidSyslogStreamSender* self, bool delivered);
44 : : static inline struct SolidSyslogStreamSender* StreamSender_SelfFromBase(struct SolidSyslogSender* base);
45 : :
46 : : static inline bool StreamSender_Reconcile(struct SolidSyslogStreamSender* self);
47 : : static inline void StreamSender_DisconnectIfStale(struct SolidSyslogStreamSender* self);
48 : : static inline bool StreamSender_ConfigurationMoved(
49 : : const struct SolidSyslogStreamSender* self,
50 : : uint32_t endpointVersion,
51 : : uint32_t streamVersion
52 : : );
53 : : static inline bool StreamSender_EnsureConnected(struct SolidSyslogStreamSender* self);
54 : : static inline bool StreamSender_Connected(const struct SolidSyslogStreamSender* self);
55 : : static bool StreamSender_Connect(struct SolidSyslogStreamSender* self);
56 : : static bool StreamSender_ResolveDestination(struct SolidSyslogStreamSender* self, struct SolidSyslogAddress* addr);
57 : : static inline void StreamSender_CloseStream(struct SolidSyslogStreamSender* self);
58 : : static bool StreamSender_TransmitFramed(struct SolidSyslogStreamSender* self, const void* buffer, size_t size);
59 : : static struct SolidSyslogFormatter* StreamSender_FormatOctetCountingPrefix(
60 : : SolidSyslogFormatterStorage* storage,
61 : : size_t messageSize
62 : : );
63 : : static bool StreamSender_SendBytes(struct SolidSyslogStreamSender* self, const void* data, size_t len);
64 : : static void StreamSender_NilEndpoint(struct SolidSyslogEndpoint* endpoint, void* context);
65 : : static uint32_t StreamSender_NilEndpointVersion(void* context);
66 : :
67 : 219 : void SolidSyslogStreamSender_Initialise(
68 : : struct SolidSyslogSender* base,
69 : : const struct SolidSyslogStreamSenderConfig* config
70 : : )
71 : : {
72 : 219 : struct SolidSyslogStreamSender* self = StreamSender_SelfFromBase(base);
73 : 219 : self->Base.Send = StreamSender_Send;
74 : 219 : self->Base.Disconnect = StreamSender_Disconnect;
75 : 219 : self->Config.Resolver = config->Resolver;
76 : 219 : self->Config.Stream = config->Stream;
77 : 219 : self->Config.Address = config->Address;
78 [ + + ]: 219 : self->Config.Endpoint = (config->Endpoint != NULL) ? config->Endpoint : StreamSender_NilEndpoint;
79 : 219 : self->Config.EndpointVersion =
80 [ + + ]: 219 : (config->EndpointVersion != NULL) ? config->EndpointVersion : StreamSender_NilEndpointVersion;
81 : 219 : self->Config.EndpointContext = config->EndpointContext;
82 : 219 : self->Connected = false;
83 : 219 : self->DeliveryHealthy = true;
84 : 219 : self->LastEndpointVersion = 0;
85 : 219 : self->LastStreamVersion = 0;
86 : 219 : }
87 : :
88 : 219 : void SolidSyslogStreamSender_Cleanup(struct SolidSyslogSender* base)
89 : : {
90 : : /* Disconnect first so the live Config.Stream is still reachable; then overwrite the
91 : : * abstract base with the shared NullSender vtable so use-after-destroy is a safe
92 : : * no-op rather than a NULL-fn-pointer crash. Derived fields are private to this TU
93 : : * so the next SolidSyslogStreamSender_Initialise overwrites them; no need to wipe here. */
94 : 219 : StreamSender_Disconnect(base);
95 : 219 : *base = *SolidSyslogNullSender_Get();
96 : 219 : }
97 : :
98 : 3457 : static bool StreamSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size)
99 : : {
100 : 3457 : struct SolidSyslogStreamSender* self = StreamSender_SelfFromBase(base);
101 [ + + + + ]: 3457 : bool delivered = StreamSender_Reconcile(self) && StreamSender_TransmitFramed(self, buffer, size);
102 : 3457 : StreamSender_UpdateDeliveryHealth(self, delivered);
103 : 3457 : return delivered;
104 : : }
105 : :
106 : 3457 : static inline void StreamSender_UpdateDeliveryHealth(struct SolidSyslogStreamSender* self, bool delivered)
107 : : {
108 : : static const struct SolidSyslogSenderHealthReporter reporter = {
109 : : .Source = &SolidSyslogStreamSenderErrorSource,
110 : : .FailedDetail = (int32_t) SOLIDSYSLOG_STREAM_SENDER_ERROR_DELIVERY_FAILED,
111 : : .RestoredDetail = (int32_t) SOLIDSYSLOG_STREAM_SENDER_ERROR_DELIVERY_RESTORED
112 : : };
113 : 3457 : SolidSyslogSenderHealth_Update(&self->DeliveryHealthy, delivered, &reporter);
114 : 3457 : }
115 : :
116 : 3457 : static inline bool StreamSender_Reconcile(struct SolidSyslogStreamSender* self)
117 : : {
118 : 3457 : StreamSender_DisconnectIfStale(self);
119 : 3457 : return StreamSender_EnsureConnected(self);
120 : : }
121 : :
122 : 3457 : static inline void StreamSender_DisconnectIfStale(struct SolidSyslogStreamSender* self)
123 : : {
124 : 3457 : uint32_t endpointVersion = self->Config.EndpointVersion(self->Config.EndpointContext);
125 : 3457 : uint32_t streamVersion = SolidSyslogStream_Version(self->Config.Stream);
126 : :
127 [ + + ]: 3457 : if (StreamSender_ConfigurationMoved(self, endpointVersion, streamVersion))
128 : : {
129 : 114 : StreamSender_Disconnect(&self->Base);
130 : 114 : self->LastEndpointVersion = endpointVersion;
131 : 114 : self->LastStreamVersion = streamVersion;
132 : : }
133 : 3457 : }
134 : :
135 : 3457 : static inline bool StreamSender_ConfigurationMoved(
136 : : const struct SolidSyslogStreamSender* self,
137 : : uint32_t endpointVersion,
138 : : uint32_t streamVersion
139 : : )
140 : : {
141 [ + + + + ]: 3457 : return (endpointVersion != self->LastEndpointVersion) || (streamVersion != self->LastStreamVersion);
142 : : }
143 : :
144 : 3457 : static inline bool StreamSender_EnsureConnected(struct SolidSyslogStreamSender* self)
145 : : {
146 [ + + + + ]: 3457 : return StreamSender_Connected(self) || StreamSender_Connect(self);
147 : : }
148 : :
149 : 5494 : static inline bool StreamSender_Connected(const struct SolidSyslogStreamSender* self)
150 : : {
151 : 5494 : return self->Connected;
152 : : }
153 : :
154 : 1668 : static bool StreamSender_Connect(struct SolidSyslogStreamSender* self)
155 : : {
156 : 1668 : struct SolidSyslogAddress* addr = self->Config.Address;
157 : :
158 [ + + ]: 1668 : if (StreamSender_ResolveDestination(self, addr))
159 : : {
160 : 1665 : self->Connected = SolidSyslogStream_Open(self->Config.Stream, addr);
161 : : }
162 : :
163 : 1668 : return StreamSender_Connected(self);
164 : : }
165 : :
166 : 1668 : static bool StreamSender_ResolveDestination(struct SolidSyslogStreamSender* self, struct SolidSyslogAddress* addr)
167 : : {
168 : : SolidSyslogFormatterStorage hostStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(SOLIDSYSLOG_MAX_HOST_SIZE)];
169 : 1668 : struct SolidSyslogFormatter* hostFormatter = SolidSyslogFormatter_Create(hostStorage, SOLIDSYSLOG_MAX_HOST_SIZE);
170 : : struct SolidSyslogEndpointHost hostSink;
171 : 1668 : SolidSyslogEndpointHost_FromFormatter(&hostSink, hostFormatter);
172 : 1668 : struct SolidSyslogEndpoint endpoint = {.Host = &hostSink, .Port = 0};
173 : :
174 : 1668 : self->Config.Endpoint(&endpoint, self->Config.EndpointContext);
175 : :
176 : 1668 : return SolidSyslogResolver_Resolve(
177 : : self->Config.Resolver,
178 : : SOLIDSYSLOG_TRANSPORT_TCP,
179 : : SolidSyslogFormatter_AsFormattedBuffer(hostFormatter),
180 : 1668 : endpoint.Port,
181 : : addr
182 : : );
183 : : }
184 : :
185 : 369 : static void StreamSender_Disconnect(struct SolidSyslogSender* base)
186 : : {
187 : 369 : struct SolidSyslogStreamSender* self = StreamSender_SelfFromBase(base);
188 : :
189 [ + + ]: 369 : if (StreamSender_Connected(self))
190 : : {
191 : 183 : StreamSender_CloseStream(self);
192 : : }
193 : 369 : }
194 : :
195 : 4045 : static inline struct SolidSyslogStreamSender* StreamSender_SelfFromBase(struct SolidSyslogSender* base)
196 : : {
197 : 4045 : return (struct SolidSyslogStreamSender*) base;
198 : : }
199 : :
200 : 1267 : static inline void StreamSender_CloseStream(struct SolidSyslogStreamSender* self)
201 : : {
202 : 1267 : SolidSyslogStream_Close(self->Config.Stream);
203 : 1267 : self->Connected = false;
204 : 1267 : }
205 : :
206 : 3056 : static bool StreamSender_TransmitFramed(struct SolidSyslogStreamSender* self, const void* buffer, size_t size)
207 : : {
208 : : SolidSyslogFormatterStorage prefixStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(OCTET_COUNTING_PREFIX_CAPACITY)];
209 : 3056 : struct SolidSyslogFormatter* prefix = StreamSender_FormatOctetCountingPrefix(prefixStorage, size);
210 : :
211 : 6112 : return StreamSender_SendBytes(
212 : : self,
213 : 3056 : SolidSyslogFormatter_AsFormattedBuffer(prefix),
214 : : SolidSyslogFormatter_Length(prefix)
215 [ + + + + ]: 5030 : ) &&
216 : 1974 : StreamSender_SendBytes(self, buffer, size);
217 : : }
218 : :
219 : 3056 : static struct SolidSyslogFormatter* StreamSender_FormatOctetCountingPrefix(
220 : : SolidSyslogFormatterStorage* storage,
221 : : size_t messageSize
222 : : )
223 : : {
224 : 3056 : struct SolidSyslogFormatter* f = SolidSyslogFormatter_Create(storage, OCTET_COUNTING_PREFIX_CAPACITY);
225 : 3056 : SolidSyslogFormatter_Uint32(f, (uint32_t) messageSize);
226 : 3056 : SolidSyslogFormatter_AsciiCharacter(f, ' ');
227 : 3056 : return f;
228 : : }
229 : :
230 : 5030 : static bool StreamSender_SendBytes(struct SolidSyslogStreamSender* self, const void* data, size_t len)
231 : : {
232 : 5030 : bool sent = SolidSyslogStream_Send(self->Config.Stream, data, len);
233 : :
234 [ + + ]: 5030 : if (!sent)
235 : : {
236 : 1084 : StreamSender_CloseStream(self);
237 : : }
238 : :
239 : 5030 : return sent;
240 : : }
241 : :
242 : 1598 : static void StreamSender_NilEndpoint(struct SolidSyslogEndpoint* endpoint, void* context)
243 : : {
244 : : (void) context;
245 : 1598 : SolidSyslogEndpointHost_String(endpoint->Host, "", 0);
246 : 1598 : endpoint->Port = 0;
247 : 1598 : }
248 : :
249 : 3377 : static uint32_t StreamSender_NilEndpointVersion(void* context)
250 : : {
251 : : (void) context;
252 : 3377 : return 0U;
253 : : }
|