SolidSyslog coverage report
Current view: top level - Platform/LwipRaw/Source - SolidSyslogLwipRawDnsResolver.c (source / functions) Coverage Total Hit
Test: SolidSyslog Lines: 100.0 % 64 64
Test Date: 2026-11-12 23:14:35 Functions: 100.0 % 10 10
Branches: 100.0 % 20 20

             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 "lwip/opt.h"
       6                 :             : 
       7                 :             : /* This component requires lwIP built with DNS. */
       8                 :             : #if LWIP_DNS
       9                 :             : 
      10                 :             : #include <stdbool.h>
      11                 :             : #include <stddef.h>
      12                 :             : #include <stdint.h>
      13                 :             : 
      14                 :             : #include "SolidSyslogError.h"
      15                 :             : #include "SolidSyslogErrorCategory.h"
      16                 :             : #include "SolidSyslogLwipRawAddressPrivate.h"
      17                 :             : #include "SolidSyslogLwipRawDnsResolverErrors.h"
      18                 :             : #include "SolidSyslogLwipRawDnsResolverPrivate.h"
      19                 :             : #include "SolidSyslogLwipRawMarshalPrivate.h"
      20                 :             : #include "SolidSyslogNullResolver.h"
      21                 :             : #include "SolidSyslogPrival.h"
      22                 :             : #include "SolidSyslogResolverCategories.h"
      23                 :             : #include "SolidSyslogResolverDefinition.h"
      24                 :             : #include "SolidSyslogTransport.h"
      25                 :             : #include "SolidSyslogTunables.h"
      26                 :             : #include "lwip/dns.h"
      27                 :             : #include "lwip/err.h"
      28                 :             : #include "lwip/ip_addr.h"
      29                 :             : 
      30                 :             : const struct SolidSyslogErrorSource SolidSyslogLwipRawDnsResolverErrorSource = {
      31                 :             :     "LwipRawDnsResolver",
      32                 :             :     SolidSyslogResolverErrors_Name
      33                 :             : };
      34                 :             : 
      35                 :             : struct SolidSyslogAddress;
      36                 :             : 
      37                 :             : /* Per-resolve parameters carried across the marshal hops. The dns_gethostbyname
      38                 :             :  * call runs on the lwIP-owning thread; its immediate return code comes back in
      39                 :             :  * Err. The async-completion result is published the same way: DoPublishResult
      40                 :             :  * runs on the lwIP thread, copies the resolved address into Destination, and
      41                 :             :  * reports the outcome in Resolved - so the multi-byte ResolvedIp / ResolvedOk
      42                 :             :  * fields the dns_found_callback wrote are read on the thread that wrote them
      43                 :             :  * (race-free, with the marshal providing the cross-thread barrier) rather than
      44                 :             :  * on the caller's thread off the back of the volatile Done flag. An answer in
      45                 :             :  * a family the transports cannot send to is noted in FamilyUnsupported under
      46                 :             :  * the same hop and reported once the caller's thread has it back, so the
      47                 :             :  * error handler is never entered from the lwIP thread. One struct so the
      48                 :             :  * void*-context recovery has a single cast site. */
      49                 :             : struct LwipRawDnsResolverCall
      50                 :             : {
      51                 :             :     struct SolidSyslogLwipRawDnsResolver* Self;
      52                 :             :     const char* Host;
      53                 :             :     struct SolidSyslogLwipRawAddress* Destination;
      54                 :             :     uint16_t Port;
      55                 :             :     err_t Err;
      56                 :             :     bool Resolved;
      57                 :             :     bool FamilyUnsupported;
      58                 :             : };
      59                 :             : 
      60                 :             : static bool LwipRawDnsResolver_Resolve(
      61                 :             :     struct SolidSyslogResolver* base,
      62                 :             :     enum SolidSyslogTransport transport,
      63                 :             :     const char* host,
      64                 :             :     uint16_t port,
      65                 :             :     struct SolidSyslogAddress* result
      66                 :             : );
      67                 :             : 
      68                 :             : static inline struct SolidSyslogLwipRawDnsResolver* LwipRawDnsResolver_SelfFromBase(struct SolidSyslogResolver* base);
      69                 :             : static inline struct LwipRawDnsResolverCall* LwipRawDnsResolverCallFromContext(void* context);
      70                 :             : static void LwipRawDnsResolver_DoGetHostByName(void* context);
      71                 :             : static void LwipRawDnsResolver_Publish(struct LwipRawDnsResolverCall* call, const ip_addr_t* answer);
      72                 :             : static void LwipRawDnsResolver_FoundCallback(const char* name, const ip_addr_t* ipaddr, void* arg);
      73                 :             : static bool LwipRawDnsResolver_WaitForCallback(struct SolidSyslogLwipRawDnsResolver* self);
      74                 :             : static void LwipRawDnsResolver_DoPublishResult(void* context);
      75                 :             : 
      76                 :          38 : void SolidSyslogLwipRawDnsResolver_Initialise(
      77                 :             :     struct SolidSyslogResolver* base,
      78                 :             :     const struct SolidSyslogLwipRawDnsResolverConfig* config
      79                 :             : )
      80                 :             : {
      81                 :          38 :     struct SolidSyslogLwipRawDnsResolver* self = LwipRawDnsResolver_SelfFromBase(base);
      82                 :          38 :     base->Resolve = LwipRawDnsResolver_Resolve;
      83                 :          38 :     self->Config = *config;
      84                 :          38 :     self->Done = false;
      85                 :          38 :     self->ResolvedOk = false;
      86                 :          38 : }
      87                 :             : 
      88                 :          38 : void SolidSyslogLwipRawDnsResolver_Cleanup(struct SolidSyslogResolver* base)
      89                 :             : {
      90                 :             :     /* Overwrite the abstract base with the shared NullResolver vtable so
      91                 :             :      * use-after-destroy resolves cleanly to a failed-lookup error path
      92                 :             :      * rather than a NULL-fn-pointer crash. */
      93                 :          38 :     *base = *SolidSyslogNullResolver_Get();
      94                 :          38 : }
      95                 :             : 
      96                 :          29 : static bool LwipRawDnsResolver_Resolve(
      97                 :             :     struct SolidSyslogResolver* base,
      98                 :             :     enum SolidSyslogTransport transport,
      99                 :             :     const char* host,
     100                 :             :     uint16_t port,
     101                 :             :     struct SolidSyslogAddress* result
     102                 :             : )
     103                 :             : {
     104                 :             :     (void) transport;
     105                 :          29 :     struct SolidSyslogLwipRawDnsResolver* self = LwipRawDnsResolver_SelfFromBase(base);
     106                 :          29 :     struct SolidSyslogLwipRawAddress* destination = SolidSyslogLwipRawAddress_As(result);
     107                 :          29 :     self->Done = false;
     108                 :          29 :     self->ResolvedOk = false;
     109                 :             : 
     110                 :             :     /* dns_gethostbyname touches lwIP core state (unlike the numeric resolver's
     111                 :             :      * pure ipaddr_aton parse), so it must run under the marshal hop. */
     112                 :          29 :     struct LwipRawDnsResolverCall call = {
     113                 :             :         .Self = self,
     114                 :             :         .Host = host,
     115                 :             :         .Destination = destination,
     116                 :             :         .Port = port,
     117                 :             :         .Err = ERR_OK,
     118                 :             :         .Resolved = false,
     119                 :             :         .FamilyUnsupported = false,
     120                 :             :     };
     121                 :          29 :     SolidSyslogLwipRaw_Marshal(LwipRawDnsResolver_DoGetHostByName, &call);
     122                 :             : 
     123         [ +  + ]:          29 :     if (call.Err == ERR_OK)
     124                 :             :     {
     125                 :             :         /* Synchronous hit: numeric literal, DNS cache, or local hostlist -
     126                 :             :          * dns_gethostbyname wrote ResolvedIp under the marshal hop above and the
     127                 :             :          * found_callback never fired, so reading it here (ordered after the
     128                 :             :          * marshal returned) is safe and on a single thread. */
     129                 :          14 :         LwipRawDnsResolver_Publish(&call, &self->ResolvedIp);
     130                 :             :     }
     131         [ +  + ]:          15 :     else if (call.Err == ERR_INPROGRESS)
     132                 :             :     {
     133                 :             :         /* Query queued: bound-spin on the caller's thread until the
     134                 :             :          * dns_found_callback signals completion via the volatile Done flag (or
     135                 :             :          * the deadline passes). The callback wrote the multi-byte ResolvedIp /
     136                 :             :          * ResolvedOk on the lwIP thread, so the authoritative read + publish runs
     137                 :             :          * back on that thread via DoPublishResult - never off the volatile flag
     138                 :             :          * on the caller's thread, which would be an unsynchronised data race. */
     139         [ +  + ]:          13 :         if (LwipRawDnsResolver_WaitForCallback(self))
     140                 :             :         {
     141                 :           9 :             SolidSyslogLwipRaw_Marshal(LwipRawDnsResolver_DoPublishResult, &call);
     142                 :             :         }
     143                 :             :     }
     144                 :             :     else
     145                 :             :     {
     146                 :             :         /* ERR_ARG / any other immediate rejection - Resolved stays false.
     147                 :             :          * Terminating else per MISRA 15.7. */
     148                 :             :     }
     149                 :             : 
     150         [ +  + ]:          29 :     if (call.FamilyUnsupported)
     151                 :             :     {
     152                 :           6 :         LwipRawDnsResolver_Report(
     153                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     154                 :             :             SOLIDSYSLOG_CAT_RESOLVER_RESOLVE_FAILED,
     155                 :             :             SOLIDSYSLOG_RESOLVER_ERROR_ADDRESS_FAMILY_UNSUPPORTED
     156                 :             :         );
     157                 :             :     }
     158                 :          29 :     return call.Resolved;
     159                 :             : }
     160                 :             : 
     161                 :             : /* IPv4 only, which is what the datagram's and stream's pcbs send to. A stack
     162                 :             :  * built with LWIP_IPV6 can answer a name with an IPv6 address, and storing it
     163                 :             :  * would give the transports a destination they fail every send to. */
     164                 :          22 : static void LwipRawDnsResolver_Publish(struct LwipRawDnsResolverCall* call, const ip_addr_t* answer)
     165                 :             : {
     166         [ +  + ]:          22 :     if (LwipRawAddress_IsIpv4(answer))
     167                 :             :     {
     168                 :          16 :         call->Destination->Ip = *answer;
     169                 :          16 :         call->Destination->Port = call->Port;
     170                 :          16 :         call->Resolved = true;
     171                 :             :     }
     172                 :             :     else
     173                 :             :     {
     174                 :           6 :         call->FamilyUnsupported = true;
     175                 :             :     }
     176                 :          22 : }
     177                 :             : 
     178                 :          29 : static void LwipRawDnsResolver_DoGetHostByName(void* context)
     179                 :             : {
     180                 :          29 :     struct LwipRawDnsResolverCall* call = LwipRawDnsResolverCallFromContext(context);
     181                 :          29 :     struct SolidSyslogLwipRawDnsResolver* self = call->Self;
     182                 :             :     /* Asked for lwIP's default family, not IPv4 alone: told IPv4 only, lwIP
     183                 :             :      * keeps an IPv6 literal or entry to itself and queries the name afresh,
     184                 :             :      * so the lookup fails later as a plain miss. Asked for either, it hands
     185                 :             :      * the IPv6 answer over, and Publish refuses it with the family named. */
     186                 :          29 :     call->Err = dns_gethostbyname(call->Host, &self->ResolvedIp, LwipRawDnsResolver_FoundCallback, self);
     187                 :          29 : }
     188                 :             : 
     189                 :             : /* lwIP fires this on the tcpip thread when an async lookup completes. ipaddr is
     190                 :             :  * the resolved address, or NULL on failure. Touches no lwIP API, so it needs no
     191                 :             :  * marshalling; sets the flags the bounded spin (WaitForCallback) polls. */
     192                 :           9 : static void LwipRawDnsResolver_FoundCallback(const char* name, const ip_addr_t* ipaddr, void* arg)
     193                 :             : {
     194                 :             :     (void) name;
     195                 :           9 :     struct SolidSyslogLwipRawDnsResolver* self = (struct SolidSyslogLwipRawDnsResolver*) arg;
     196         [ +  + ]:           9 :     if (ipaddr != NULL)
     197                 :             :     {
     198                 :           8 :         self->ResolvedIp = *ipaddr;
     199                 :           8 :         self->ResolvedOk = true;
     200                 :             :     }
     201                 :           9 :     self->Done = true;
     202                 :           9 : }
     203                 :             : 
     204                 :             : /* Bounded async-resolve spin: each iteration sleeps via the integrator-injected
     205                 :             :  * Sleep so lwIP's DNS timer / RX paths get cycles to advance the query. Runs on
     206                 :             :  * the caller's thread - never the lwIP thread. Exits when the callback has set
     207                 :             :  * the volatile Done flag (success or NULL-delivery failure) or the deadline
     208                 :             :  * elapses (timeout). Returns whether completion was observed; the authoritative
     209                 :             :  * success/address read happens under the marshal in DoPublishResult, so this
     210                 :             :  * deliberately does NOT read the non-volatile ResolvedOk / ResolvedIp here. */
     211                 :          13 : static bool LwipRawDnsResolver_WaitForCallback(struct SolidSyslogLwipRawDnsResolver* self)
     212                 :             : {
     213                 :          13 :     const uint32_t pollMs = (uint32_t) SOLIDSYSLOG_LWIP_RAW_DNS_RESOLVE_POLL_MS;
     214                 :          13 :     const uint32_t deadlineMs = (uint32_t) SOLIDSYSLOG_DNS_RESOLVE_TIMEOUT_MS;
     215                 :          13 :     uint32_t elapsedMs = 0U;
     216   [ +  +  +  + ]:        2022 :     while (!self->Done && (elapsedMs < deadlineMs))
     217                 :             :     {
     218                 :        2009 :         self->Config.Sleep(pollMs);
     219                 :        2009 :         elapsedMs += pollMs;
     220                 :             :     }
     221         [ +  + ]:          13 :     if (!self->Done)
     222                 :             :     {
     223                 :           4 :         LwipRawDnsResolver_Report(
     224                 :             :             SOLIDSYSLOG_SEVERITY_WARNING,
     225                 :             :             SOLIDSYSLOG_CAT_RESOLVER_RESOLVE_FAILED,
     226                 :             :             SOLIDSYSLOG_RESOLVER_ERROR_RESOLVE_TIMEOUT
     227                 :             :         );
     228                 :             :     }
     229                 :          13 :     return self->Done;
     230                 :             : }
     231                 :             : 
     232                 :             : /* Marshalled result read for the async path: runs on the lwIP thread (the same
     233                 :             :  * thread the dns_found_callback wrote ResolvedOk / ResolvedIp on), so the read
     234                 :             :  * is race-free and the marshal hop carries the cross-thread happens-before that
     235                 :             :  * the volatile Done flag alone cannot. Publishes into the caller-owned
     236                 :             :  * Destination and reports the outcome in Resolved (read after the marshal
     237                 :             :  * returns, ordered by it). */
     238                 :           9 : static void LwipRawDnsResolver_DoPublishResult(void* context)
     239                 :             : {
     240                 :           9 :     struct LwipRawDnsResolverCall* call = LwipRawDnsResolverCallFromContext(context);
     241                 :           9 :     struct SolidSyslogLwipRawDnsResolver* self = call->Self;
     242         [ +  + ]:           9 :     if (self->ResolvedOk)
     243                 :             :     {
     244                 :           8 :         LwipRawDnsResolver_Publish(call, &self->ResolvedIp);
     245                 :             :     }
     246                 :           9 : }
     247                 :             : 
     248                 :          67 : static inline struct SolidSyslogLwipRawDnsResolver* LwipRawDnsResolver_SelfFromBase(struct SolidSyslogResolver* base)
     249                 :             : {
     250                 :          67 :     return (struct SolidSyslogLwipRawDnsResolver*) base;
     251                 :             : }
     252                 :             : 
     253                 :          38 : static inline struct LwipRawDnsResolverCall* LwipRawDnsResolverCallFromContext(void* context)
     254                 :             : {
     255                 :          38 :     return (struct LwipRawDnsResolverCall*) context;
     256                 :             : }
     257                 :             : 
     258                 :             : #else
     259                 :             : 
     260                 :             : /* ISO C forbids an empty translation unit. */
     261                 :             : typedef int LwipRawDnsResolver_EmptyTranslationUnit;
     262                 :             : 
     263                 :             : #endif /* LWIP_DNS */
        

Generated by: LCOV version 2.0-1