xref: /netbsd-src/external/apache2/mDNSResponder/dist/mDNSShared/PlatformCommon.c (revision 450dee115da5565aafca4d92dc3ba1c9d6b0cd2a)
1 /* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (c) 2004-2024 Apple Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     https://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * This file defines functions that are common to platforms with Posix APIs.
18  * Current examples are mDNSMacOSX and mDNSPosix.
19  */
20 
21 #include <stdio.h>              // Needed for fopen() etc.
22 #include <unistd.h>             // Needed for close()
23 #include <stdlib.h>             // Needed for malloc()
24 #include <string.h>             // Needed for strlen() etc.
25 #include <errno.h>              // Needed for errno etc.
26 #include <sys/socket.h>         // Needed for socket() etc.
27 #include <netinet/in.h>         // Needed for sockaddr_in
28 #include <syslog.h>
29 #include <sys/fcntl.h>
30 #include <netinet/tcp.h>
31 #include <arpa/inet.h>
32 #include <time.h>
33 #include <sys/time.h>           // Needed for #include <sys/time.h>().
34 #include <assert.h>
35 #include <limits.h>
36 
37 
38 #include "mDNSEmbeddedAPI.h"    // Defines the interface provided to the client layer above
39 #include "DNSCommon.h"
40 #include "PlatformCommon.h"
41 #include "mdns_strict.h"
42 
43 #ifdef NOT_HAVE_SOCKLEN_T
44 typedef unsigned int socklen_t;
45 #endif
46 
47 #if MDNS_MALLOC_DEBUGGING
48 // We ONLY want this for malloc debugging--on a running production system we want to deal with
49 // malloc failures, not just die.   There is a small performance penalty for enabling these options
50 // as well, so they are all only appropriate for debugging.   The flags mean:
51 //
52 // A = warnings are errors
53 // X = abort on failure
54 // Z = sets J & R
55 // J = allocated memory is initialized to a pattern
56 // R causes realloc to always reallocate even if not needed
57 
58 char _malloc_options[] = "AXZ";
59 
60 mDNSlocal mDNSListValidator *listValidators;
61 
62 mDNSexport void mDNSPlatformAddListValidator(mDNSListValidator *lv, mDNSListValidationFunction *lvf,
63                                              const char *lvfName, void *context)
64 {
65     mDNSPlatformMemZero(lv, sizeof *lv);
66     lv->validator = lvf;
67     lv->validationFunctionName = lvfName;
68     lv->context = context;
69     lv->next = listValidators;
70     listValidators = lv;
71 }
72 
73 mDNSlocal void validateLists(void)
74 {
75     mDNSListValidator *vfp;
76     // Check Unix Domain Socket client lists (uds_daemon.c)
77     for (vfp = listValidators; vfp; vfp = vfp->next)
78     {
79         vfp->validator(vfp->context);
80     }
81 
82     mDNSPlatformValidateLists();
83 }
84 
85 #define kAllocMagic     0xDEAD1234
86 #define kGuardMagic     0xDEAD1234
87 #define kFreeMagic      0xDEADDEAD
88 #define kAllocLargeSize 32768
89 
90 mDNSexport void *mallocL(const char *msg, mDNSu32 size)
91 {
92     // Allocate space for two words of sanity checking data before the requested block and two words after.
93     // Adjust the length for alignment.
94     mDNSu32 *mem = malloc(sizeof(mDNSu32) * 4 + size);
95     mDNSu32 guard[2];
96     if (!mem)
97     { LogMsg("malloc( %s : %u ) failed", msg, size); return(NULL); }
98     else
99     {
100         mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)(mem + 2) + size);
101         if      (size > kAllocLargeSize)      LogMsg("malloc( %s : %lu ) @ %p suspiciously large", msg, size, &mem[2]);
102         else if (MDNS_MALLOC_DEBUGGING >= 2)  LogMsg("malloc( %s : %lu ) @ %p",                    msg, size, &mem[2]);
103         mem[  0] = kAllocMagic;
104         guard[0] = kGuardMagic;
105         mem[  1] = size;
106         guard[1] = size;
107         memcpy(after, &guard, sizeof guard);
108         memset(&mem[2], 0xFF, size);
109         validateLists();
110         return(&mem[2]);
111     }
112 }
113 
114 mDNSexport void *callocL(const char *msg, mDNSu32 size)
115 {
116     mDNSu32 guard[2];
117     const mDNSu32 headerSize = 4 * sizeof(mDNSu32);
118 
119     // Allocate space for two words of sanity checking data before the requested block and two words after.
120     // Adjust the length for alignment.
121     mDNSu32 *mem = (mDNSu32 *)calloc(1, headerSize + size);
122     if (!mem)
123     { LogMsg("calloc( %s : %u ) failed", msg, size); return(NULL); }
124     else
125     {
126         mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)(mem + 2) + size);
127         if      (size > kAllocLargeSize)     LogMsg("calloc( %s : %lu ) @ %p suspiciously large", msg, size, &mem[2]);
128         else if (MDNS_MALLOC_DEBUGGING >= 2) LogMsg("calloc( %s : %lu ) @ %p",                    msg, size, &mem[2]);
129         mem[  0] = kAllocMagic;
130         guard[0] = kGuardMagic;
131         mem[  1] = size;
132         guard[1] = size;
133         memcpy(after, guard, sizeof guard);
134         validateLists();
135         return(&mem[2]);
136     }
137 }
138 
139 mDNSexport void freeL(const char *msg, void *x)
140 {
141     if (!x)
142         LogMsg("free( %s @ NULL )!", msg);
143     else
144     {
145         mDNSu32 *mem = ((mDNSu32 *)x) - 2;
146         if      (mem[0] == kFreeMagic)  { LogMemCorruption("free( %s : %lu @ %p ) !!!! ALREADY DISPOSED !!!!", msg, mem[1], &mem[2]); return; }
147         if      (mem[0] != kAllocMagic) { LogMemCorruption("free( %s : %lu @ %p ) !!!! NEVER ALLOCATED !!!!",  msg, mem[1], &mem[2]); return; }
148         if      (mem[1] > kAllocLargeSize)          LogMsg("free( %s : %lu @ %p) suspiciously large",          msg, mem[1], &mem[2]);
149         else if (MDNS_MALLOC_DEBUGGING >= 2)        LogMsg("free( %s : %ld @ %p)",                             msg, mem[1], &mem[2]);
150         mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)x + mem[1]);
151         mDNSu32 guard[2];
152 
153         memcpy(guard, after, sizeof guard);
154         if (guard[0] != kGuardMagic)    { LogMemCorruption("free( %s : %lu @ %p ) !!!! END GUARD OVERWRITE !!!!",
155                                                            msg, mem[1], &mem[2]); return; }
156         if (guard[1] != mem[1])         { LogMemCorruption("free( %s : %lu @ %p ) !!!! LENGTH MISMATCH !!!!",
157                                                            msg, mem[1], &mem[2]); return; }
158         mem[0] = kFreeMagic;
159         memset(mem + 2, 0xFF, mem[1] + 2 * sizeof(mDNSu32));
160         validateLists();
161         free(mem);
162     }
163 }
164 
165 #endif
166 
167 // Bind a UDP socket to find the source address to a destination
168 mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
169 {
170     union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
171     socklen_t len = sizeof(addr);
172     socklen_t inner_len = 0;
173     int sock = socket(AF_INET, SOCK_DGRAM, 0);
174     src->type = mDNSAddrType_None;
175     if (sock == -1) return;
176     memset(&addr, 0, sizeof(addr));
177     if (dst->type == mDNSAddrType_IPv4)
178     {
179         inner_len = sizeof(addr.a4);
180         #ifndef NOT_HAVE_SA_LEN
181         addr.a4.sin_len         = (unsigned char)inner_len;
182         #endif
183         addr.a4.sin_family      = AF_INET;
184         addr.a4.sin_port        = 7;    // Not important, any port will do
185         addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
186     }
187     else if (dst->type == mDNSAddrType_IPv6)
188     {
189         inner_len = sizeof(addr.a6);
190         #ifndef NOT_HAVE_SA_LEN
191         addr.a6.sin6_len      = (unsigned char)inner_len;
192         #endif
193         addr.a6.sin6_family   = AF_INET6;
194         addr.a6.sin6_flowinfo = 0;
195         addr.a6.sin6_port     = 1;  // Not important, any port will do
196         addr.a6.sin6_addr     = *(const struct in6_addr*)&dst->ip.v6;
197         addr.a6.sin6_scope_id = 0;
198     }
199     else return;
200 
201     if ((connect(sock, &addr.s, inner_len)) < 0)
202     {
203         static mDNSv4Addr dummy = { 198, 51, 100, 42 };
204 
205         // Don't spam if we can't connect to 198.51.100.42 to the console.
206         // That is our test address to out which interfaces/address should be primary and is also
207         // configured in mDNSPosix/PosixDaemon.c:Reconfigure()
208         // Failing to connect to it with EADDRNOTAVAIL is a common situation, especially on boot up.
209         if (dst->type == mDNSAddrType_IPv4 && dst->ip.v4.NotAnInteger == dummy.NotAnInteger && errno == EADDRNOTAVAIL)
210             LogInfo("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno));
211         else
212             LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno));
213         goto exit;
214     }
215 
216     if ((getsockname(sock, &addr.s, &len)) < 0)
217     { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; }
218 
219     src->type = dst->type;
220     if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr;
221     else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
222 exit:
223     close(sock);
224 }
225 
226 // dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
227 mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
228 {
229     char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value
230     size_t len = strlen(option);
231     if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; }
232     fseek(f, 0, SEEK_SET);  // set position to beginning of stream
233     while (fgets(buf, sizeof(buf), f))      // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator
234     {
235         if (!strncmp(buf, option, len))
236         {
237             mDNSPlatformStrLCopy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1);
238             if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0';
239             len = strlen(dst);
240             if (len && dst[len-1] == '\n') dst[len-1] = '\0';  // chop newline
241             return mDNStrue;
242         }
243     }
244     debugf("Option %s not set", option);
245     return mDNSfalse;
246 }
247 
248 mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
249 {
250     char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
251     mStatus err;
252     FILE *f = fopen(filename, "r");
253 
254     if (hostname) hostname->c[0] = 0;
255     if (domain) domain->c[0] = 0;
256     if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse;
257 
258     if (f)
259     {
260         if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue;
261         if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf;
262         if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf;
263         buf[0] = 0;
264         GetConfigOption(buf, "secret-64", f);  // failure means no authentication
265         fclose(f);
266         f = NULL;
267     }
268     else
269     {
270         if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened.");
271         return;
272     }
273 
274     if (domain && domain->c[0] && buf[0])
275     {
276         DomainAuthInfo *info = (DomainAuthInfo*) mDNSPlatformMemAllocateClear(sizeof(*info));
277         // for now we assume keyname = service reg domain and we use same key for service and hostname registration
278         err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0);
279         if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
280     }
281 
282     return;
283 
284 badf:
285     LogMsg("ERROR: malformatted config file");
286     if (f) fclose(f);
287 }
288 
289 #if MDNS_DEBUGMSGS
290 mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
291 {
292     fprintf(stderr,"%s\n", msg);
293     fflush(stderr);
294 }
295 #endif
296 
297 #if !MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
298 mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel)
299 {
300 
301     if (mDNS_DebugMode) // In debug mode we write to stderr
302     {
303         fprintf(stderr,"%s\n", buffer);
304         fflush(stderr);
305     }
306     else                // else, in production mode, we write to syslog
307     {
308         static int log_inited = 0;
309 
310         int syslog_level;
311         switch (loglevel)
312         {
313             case MDNS_LOG_FAULT:     syslog_level = LOG_ERR;     break;
314             case MDNS_LOG_ERROR:     syslog_level = LOG_ERR;     break;
315             case MDNS_LOG_WARNING:   syslog_level = LOG_WARNING; break;
316             case MDNS_LOG_DEFAULT:   syslog_level = LOG_NOTICE;  break;
317             case MDNS_LOG_INFO:      syslog_level = LOG_NOTICE;  break;
318             case MDNS_LOG_DEBUG:
319             {
320                 syslog_level = (mDNS_DebugLoggingEnabled ? LOG_NOTICE : LOG_DEBUG);
321                 break;
322             }
323             default:                 syslog_level = LOG_NOTICE;  break;
324         }
325 
326         if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; }
327 
328         {
329             syslog(syslog_level, "%s", buffer);
330         }
331     }
332 }
333 #endif // !MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
334 
335 mDNSexport mDNSBool mDNSPosixTCPSocketSetup(int *fd, mDNSAddr_Type addrType, mDNSIPPort *port, mDNSIPPort *outTcpPort)
336 {
337     const sa_family_t sa_family = (addrType == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
338     int err;
339     int sock;
340     mDNSu32 lowWater = 15384;
341 
342     sock = socket(sa_family, SOCK_STREAM, IPPROTO_TCP);
343     if (sock < 3)
344     {
345         if (errno != EAFNOSUPPORT)
346         {
347             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNSPosixTCPSocketSetup: socket error %d errno %d (" PUB_S ")", sock, errno, strerror(errno));
348         }
349         return mDNStrue;
350     }
351     *fd = sock;
352 
353     union
354     {
355         struct sockaddr sa;
356         struct sockaddr_in sin;
357         struct sockaddr_in6 sin6;
358     } addr;
359     // If port is not NULL, bind to it.
360     if (port != NULL)
361     {
362         socklen_t len = (sa_family == AF_INET) ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
363         mDNSPlatformMemZero(&addr, sizeof addr);
364 
365         addr.sa.sa_family = sa_family;
366 #ifndef NOT_HAVE_SA_LEN
367         addr.sa.sa_len = (unsigned char)len;
368 #endif
369         if (sa_family == AF_INET6)
370         {
371             addr.sin6.sin6_port = port->NotAnInteger;
372         }
373         else
374         {
375             addr.sin.sin_port = port->NotAnInteger;
376         }
377         err = bind(sock, &addr.sa, len);
378         if (err < 0)
379         {
380             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNSPosixTCPSocketSetup getsockname: " PUB_S, strerror(errno));
381             return mDNSfalse;
382         }
383     }
384 
385     socklen_t addrlen = sizeof addr;
386     err = getsockname(sock, (struct sockaddr *)&addr, &addrlen);
387     if (err < 0)
388     {
389         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNSPosixTCPSocketSetup getsockname: " PUB_S, strerror(errno));
390         return mDNSfalse;
391     }
392     if (sa_family == AF_INET6)
393     {
394         outTcpPort->NotAnInteger = addr.sin6.sin6_port;
395 
396     } else
397     {
398         outTcpPort->NotAnInteger = addr.sin.sin_port;
399     }
400     if (port)
401         port->NotAnInteger = outTcpPort->NotAnInteger;
402 
403 #ifdef TCP_NOTSENT_LOWAT
404     err = setsockopt(sock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &lowWater, sizeof lowWater);
405     if (err < 0)
406     {
407         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNSPosixTCPSocketSetup: TCP_NOTSENT_LOWAT failed: " PUB_S, strerror(errno));
408         return mDNSfalse;
409     }
410 #endif // TCP_NOTSENT_LOWAT
411 
412     return mDNStrue;
413 }
414 
415 mDNSexport TCPSocket *mDNSPosixDoTCPListenCallback(int fd, mDNSAddr_Type addressType, TCPSocketFlags socketFlags,
416                                              TCPAcceptedCallback callback, void *context)
417 {
418     union
419     {
420         struct sockaddr_in6 sin6;
421         struct sockaddr_in sin;
422         struct sockaddr sa;
423     } address;
424 
425     socklen_t slen = sizeof address;
426     int remoteSock;
427     mDNSAddr addr;
428     mDNSIPPort port;
429     TCPSocket *sock = mDNSNULL;
430     int failed;
431     char *nbp;
432     int i;
433     mDNSu32 lowWater = 16384;
434     // When we remember our connection, we remember a name that we can print for logging.   But
435     // since we are the listener in this case, we don't /have/ a name for it.   This buffer
436     // is used to print the IP address into a human readable string which will serve that purpose
437     // for this case.
438     char namebuf[INET6_ADDRSTRLEN + 1 + 5 + 1];
439 
440     remoteSock = accept(fd, &address.sa, &slen);
441     if (remoteSock < 0)
442     {
443         LogMsg("mDNSPosixDoTCPListenCallback: accept returned %d", remoteSock);
444         goto out;
445     }
446 
447     failed = fcntl(remoteSock, F_SETFL, O_NONBLOCK);
448     if (failed < 0)
449     {
450         close(remoteSock);
451         LogMsg("mDNSPosixDoTCPListenCallback: fcntl returned %d", errno);
452         goto out;
453     }
454 
455 #ifdef TCP_NOTSENT_LOWAT
456     failed = setsockopt(remoteSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT,
457                         &lowWater, sizeof lowWater);
458     if (failed < 0)
459     {
460         close(remoteSock);
461         LogMsg("mDNSPosixDoTCPListenCallback: TCP_NOTSENT_LOWAT returned %d", errno);
462         goto out;
463     }
464 #endif // TCP_NOTSENT_LOWAT
465 
466     if (address.sa.sa_family == AF_INET6)
467     {
468         // If we are listening on an IPv4/IPv6 socket, the incoming address might be an IPv4-in-IPv6 address
469         for (i = 0; i < 10; i++)
470         {
471             if (address.sin6.sin6_addr.s6_addr[i] != 0)
472             {
473                 addr.type = mDNSAddrType_IPv6;
474                 goto nope;
475             }
476         }
477 
478         // a legit IPv4 address would be ::ffff:a.b.c.d; if there's no ::ffff bit, then it's an IPv6
479         // address with a really weird prefix.
480         if (address.sin6.sin6_addr.s6_addr[10] != 0xFF || address.sin6.sin6_addr.s6_addr[11] != 0xFF)
481         {
482             addr.type = mDNSAddrType_IPv6;
483         } else if (addressType != mDNSAddrType_None)
484         {
485             if (inet_ntop(AF_INET, &address.sin6.sin6_addr.s6_addr[12], namebuf, INET6_ADDRSTRLEN + 1) == NULL)
486             {
487                 strcpy(namebuf, ":unknown:");
488             }
489             LogMsg("mDNSPosixDoTCPListenCallback received an IPv4 connection from %s on an IPv6-only socket.",
490                    namebuf);
491             close(remoteSock);
492             goto out;
493         }
494         else
495         {
496             addr.type = mDNSAddrType_IPv4;
497         }
498     nope:
499         if (addr.type == mDNSAddrType_IPv6)
500         {
501             if (inet_ntop(address.sin6.sin6_family, &address.sin6.sin6_addr, namebuf, INET6_ADDRSTRLEN + 1) == NULL)
502             {
503                 strcpy(namebuf, ":unknown:");
504             }
505             memcpy(&addr.ip.v6, &address.sin6.sin6_addr, sizeof addr.ip.v6);
506         }
507         else
508         {
509             if (inet_ntop(AF_INET, &address.sin6.sin6_addr.s6_addr[12], namebuf, INET6_ADDRSTRLEN + 1) == NULL)
510             {
511                 strcpy(namebuf, ":unknown:");
512             }
513             memcpy(&addr.ip.v4, &address.sin6.sin6_addr.s6_addr[12], sizeof addr.ip.v4);
514         }
515         port.NotAnInteger = address.sin6.sin6_port;
516     }
517     else if (address.sa.sa_family == AF_INET)
518     {
519         addr.type = mDNSAddrType_IPv4;
520         memcpy(&addr.ip.v4, &address.sin.sin_addr, sizeof addr.ip.v4);
521         port.NotAnInteger = address.sin.sin_port;
522         if (inet_ntop(AF_INET, &address.sin.sin_addr, namebuf, INET6_ADDRSTRLEN + 1) == NULL)
523         {
524             strcpy(namebuf, ":unknown:");
525         }
526     } else {
527         LogMsg("mDNSPosixDoTCPListenCallback: connection from unknown address family %d", address.sa.sa_family);
528         close(remoteSock);
529         goto out;
530     }
531     nbp = namebuf + strlen(namebuf);
532     *nbp++ = '%';
533     snprintf(nbp, 6, "%u", ntohs(port.NotAnInteger));
534 
535     sock = mDNSPlatformTCPAccept(socketFlags, remoteSock);
536     if (sock == NULL)
537     {
538         LogMsg("mDNSPosixDoTCPListenCallback: mDNSPlatformTCPAccept returned NULL; dropping connection from %s",
539                namebuf);
540         close(remoteSock);
541         goto out;
542     }
543     callback(sock, &addr, &port, namebuf, context);
544 out:
545     return sock;
546 }
547 
548 mDNSexport mDNSBool mDNSPosixTCPListen(int *fd, mDNSAddr_Type addrtype, mDNSIPPort *port, mDNSAddr *addr,
549                                        mDNSBool reuseAddr, int queueLength)
550 
551 {
552     union
553     {
554         struct sockaddr_in6 sin6;
555         struct sockaddr_in sin;
556         struct sockaddr sa;
557     } address;
558 
559     int failed;
560     int sock;
561     int one = 1;
562     socklen_t sock_len;
563 
564     // We require an addrtype parameter because addr is allowed to be null, but they have to agree.
565     if (addr != mDNSNULL && addr->type != addrtype)
566     {
567         LogMsg("mDNSPlatformTCPListen: address type conflict: %d:%d", addr->type, addrtype);
568         return mDNSfalse;
569     }
570     if (port == mDNSNULL)
571     {
572         LogMsg("mDNSPlatformTCPListen: port must not be NULL");
573         return mDNSfalse;
574     }
575 
576     mDNSPlatformMemZero(&address, sizeof address);
577     if (addrtype == mDNSAddrType_None || addrtype == mDNSAddrType_IPv6)
578     {
579         // Set up DNS listener socket
580         if (addr != mDNSNULL)
581         {
582             memcpy(&address.sin6.sin6_addr.s6_addr, &addr->ip, sizeof address.sin6.sin6_addr.s6_addr);
583         }
584         address.sin6.sin6_port = port->NotAnInteger;
585 
586         sock_len = sizeof address.sin6;
587         address.sin6.sin6_family = AF_INET6;
588     }
589     else if (addrtype == mDNSAddrType_IPv4)
590     {
591         if (addr != mDNSNULL)
592         {
593             memcpy(&address.sin.sin_addr.s_addr, &addr->ip, sizeof address.sin.sin_addr.s_addr);
594         }
595         address.sin.sin_port = port->NotAnInteger;
596         sock_len = sizeof address.sin;
597         address.sin.sin_family = AF_INET;
598     }
599     else
600     {
601         LogMsg("mDNSPlatformTCPListen: invalid address type: %d", addrtype);
602         return mDNSfalse;
603     }
604 #ifndef NOT_HAVE_SA_LEN
605     address.sa.sa_len = (unsigned char)sock_len;
606 #endif
607     sock = socket(address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
608 
609     if (sock < 0)
610     {
611         LogMsg("mDNSPlatformTCPListen: socket call failed: %s", strerror(errno));
612         return mDNSfalse;
613     }
614     *fd = sock;
615 
616     // The reuseAddr flag is used to indicate that we want to listen on this port even if
617     // there are still lingering sockets.   We will still fail if there is another listener.
618     // Note that this requires SO_REUSEADDR, not SO_REUSEPORT, which does not have special
619     // handling for lingering sockets.
620     if (reuseAddr)
621     {
622         failed = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
623         if (failed < 0)
624         {
625             LogMsg("mDNSPlatformTCPListen: SO_REUSEADDR failed %s", strerror(errno));
626             return mDNSfalse;
627         }
628     }
629 
630     // Bind to the port and (if provided) address
631     failed = bind(sock, &address.sa, sock_len);
632     if (failed < 0)
633     {
634         LogMsg("mDNSPlatformTCPListen: bind failed %s", strerror(errno));
635         return mDNSfalse;
636     }
637 
638     // If there was no specified listen port, we need to know what port we got.
639     if (port->NotAnInteger == 0)
640     {
641         mDNSPlatformMemZero(&address, sizeof address);
642         failed = getsockname(sock, &address.sa, &sock_len);
643         if (failed < 0)
644         {
645             LogMsg("mDNSRelay: getsockname failed: %s", strerror(errno));
646             return mDNSfalse;
647         }
648         if (address.sa.sa_family == AF_INET)
649         {
650             port->NotAnInteger = address.sin.sin_port;
651         }
652         else
653         {
654             port->NotAnInteger = address.sin6.sin6_port;
655         }
656     }
657 
658     failed = listen(sock, queueLength);
659     if (failed < 0)
660     {
661         LogMsg("mDNSPlatformTCPListen: listen failed: %s", strerror(errno));
662         return mDNSfalse;
663     }
664     return mDNStrue;
665 }
666 
667 mDNSexport long mDNSPosixReadTCP(int fd, void *buf, unsigned long buflen, mDNSBool *closed)
668 {
669     static int CLOSEDcount = 0;
670     static int EAGAINcount = 0;
671 #ifndef FUZZING
672     ssize_t nread = recv(fd, buf, buflen, 0);
673 #else
674     ssize_t nread = read(fd, buf, buflen);
675 #endif
676 
677     if (nread > 0)
678     {
679         CLOSEDcount = 0;
680         EAGAINcount = 0;
681     } // On success, clear our error counters
682     else if (nread == 0)
683     {
684         *closed = mDNStrue;
685         if ((++CLOSEDcount % 20) == 0)
686         {
687             LogMsg("ERROR: mDNSPosixReadFromSocket - recv %d got CLOSED %d times", fd, CLOSEDcount);
688             assert(CLOSEDcount < 1000);
689             // Recovery Mechanism to bail mDNSResponder out of trouble: Instead of logging the same error
690             // msg multiple times, crash mDNSResponder using assert() and restart fresh. See advantages
691             // below:
692             // 1.Better User Experience
693             // 2.CrashLogs frequency can be monitored
694             // 3.StackTrace can be used for more info
695         }
696     }
697     // else nread is negative -- see what kind of error we got
698     else if (errno == ECONNRESET)
699     {
700         nread = 0; *closed = mDNStrue;
701     }
702     else if (errno != EAGAIN)
703     {
704         LogMsg("ERROR: mDNSPosixReadFromSocket - recv: %d (%s)", errno, strerror(errno));
705         nread = -1;
706     }
707     else
708     { // errno is EAGAIN (EWOULDBLOCK) -- no data available
709         nread = 0;
710         if ((++EAGAINcount % 1000) == 0)
711         {
712             LogMsg("ERROR: mDNSPosixReadFromSocket - recv %d got EAGAIN %d times", fd, EAGAINcount);
713             sleep(1);
714         }
715     }
716     return nread;
717 }
718 
719 mDNSexport long mDNSPosixWriteTCP(int fd, const char *msg, unsigned long len)
720 {
721     ssize_t result;
722     long nsent;
723 
724     result = write(fd, msg, len);
725     if (result < 0)
726     {
727         if (errno == EAGAIN)
728         {
729             nsent = 0;
730         }
731         else
732         {
733             LogMsg("ERROR: mDNSPosixWriteTCP - send %s", strerror(errno)); nsent = -1;
734         }
735     }
736     else
737     {
738         nsent = (long)result;
739     }
740     return nsent;
741 }
742 
743 mDNSlocal void timevalFromPlatformTime(const mDNSs32 platformTimeNow, const mDNSs32 platformTime,
744                                        struct timeval *const outTv)
745 {
746     struct timeval now;
747     gettimeofday(&now, mDNSNULL);
748 
749     // Ensure that mDNSPlatformOneSecond * USEC_PER_SEC does not overflow for mDNSs32.
750     if (INT32_MAX / (mDNSs32)USEC_PER_SEC < mDNSPlatformOneSecond)
751     {
752         outTv->tv_sec = now.tv_sec;
753         outTv->tv_usec = now.tv_usec;
754         return;
755     }
756 
757     if (platformTime - platformTimeNow > 0)
758     {
759         // The time is in the future.
760         const mDNSs32 remainingTime = platformTime - platformTimeNow;
761         const mDNSs32 remainingSeconds = remainingTime / mDNSPlatformOneSecond;
762         const mDNSs32 remainingMicroSeconds = (remainingTime % mDNSPlatformOneSecond) * (mDNSs32)USEC_PER_SEC / mDNSPlatformOneSecond;
763 
764         outTv->tv_sec = now.tv_sec + remainingSeconds + ((now.tv_usec + remainingMicroSeconds) / (mDNSs32)USEC_PER_SEC);
765         outTv->tv_usec = ((now.tv_usec + remainingMicroSeconds) % (mDNSs32)USEC_PER_SEC);
766     }
767     else
768     {
769         // The time is in the past.
770         const mDNSs32 passedTime = platformTimeNow - platformTime;
771         const mDNSs32 passedSeconds = passedTime / mDNSPlatformOneSecond;
772         const mDNSs32 passedMicroSeconds = (passedTime % mDNSPlatformOneSecond) * (mDNSs32)USEC_PER_SEC / mDNSPlatformOneSecond;
773 
774         outTv->tv_sec = now.tv_sec - passedSeconds - (passedMicroSeconds > now.tv_usec ? 1 : 0);
775         outTv->tv_usec = (passedMicroSeconds > now.tv_usec ? (mDNSs32)USEC_PER_SEC : 0) + now.tv_usec - passedMicroSeconds;
776     }
777 }
778 
779 mDNSlocal void getLocalTimestampFromTimeval(const struct timeval *const tv,
780                                             char *const outBuffer, const mDNSu32 bufferLen)
781 {
782     struct tm localTime;
783     char dateTimeStr[20]; // 1900-01-01 00:00:00\0
784     char timeZoneStr[6]; // -0000\0
785 
786     localtime_r(&tv->tv_sec, &localTime);
787 
788     // Get formatted date and time.
789     strftime(dateTimeStr, sizeof(dateTimeStr), "%F %T", &localTime);
790     // Get formatted time zone offset.
791     strftime(timeZoneStr, sizeof(timeZoneStr), "%z", &localTime);
792 
793     // Construct the final timestamp with the milliseconds.
794     snprintf(outBuffer, bufferLen, "%s.%03u%s", dateTimeStr, (mDNSs32)tv->tv_usec / (mDNSs32)USEC_PER_MSEC,
795              timeZoneStr);
796 }
797 
798 mDNSexport void getLocalTimestampFromPlatformTime(const mDNSs32 platformTimeNow, const mDNSs32 platformTime,
799                                                   char *const outBuffer, const mDNSu32 bufferLen)
800 {
801     struct timeval tv;
802     timevalFromPlatformTime(platformTimeNow, platformTime, &tv);
803     getLocalTimestampFromTimeval(&tv, outBuffer, bufferLen);
804 }
805 
806 mDNSexport void getLocalTimestampNow(char *const outBuffer, const mDNSu32 bufferLen)
807 {
808     struct timeval now;
809     gettimeofday(&now, mDNSNULL);
810     getLocalTimestampFromTimeval(&now, outBuffer, bufferLen);
811 }
812 
813 mDNSexport mDNSu32 getMillisecondsFromTicks(const mDNSs32 ticks)
814 {
815     if (ticks <= 0)
816     {
817         return 0;
818     }
819     mDNSu32 adjusted_ticks = ((mDNSu32)ticks);
820 
821     const mDNSu32 maxMilliseconds = UINT_MAX;
822     // Number of whole seconds in 2^32 - 1 milliseconds.
823     const mDNSu32 maxWholeSecs = maxMilliseconds / MSEC_PER_SEC;
824     // Number of remaining milliseconds in 2^32 - 1 milliseconds.
825     const mDNSu32 maxReminderMs = maxMilliseconds % MSEC_PER_SEC;
826 
827     // Max number of seconds that can be represented by the max ticks argument (2^31 - 1)
828     const mDNSu32 maxWholeSecsFromMaxTicks = (INT_MAX / (mDNSu32)mDNSPlatformOneSecond);
829     // If maxWholeSecs is less than the number of seconds that can be represented by the max ticks argument (2^31 - 1),
830     // then there's possibility of overflow, in which case, we need to clamp the number ticks that can be converted to
831     // milliseconds.
832     if (maxWholeSecs <= maxWholeSecsFromMaxTicks)
833     {
834         // Calculate the maximum number of ticks that will not exceed 2^32 - 1 milliseconds.
835         const mDNSu32 maxWholeSecsTicks = maxWholeSecs * ((mDNSu32)mDNSPlatformOneSecond);
836         const mDNSu32 maxReminderMsTicks = (maxReminderMs * ((mDNSu32)mDNSPlatformOneSecond)) / MSEC_PER_SEC;
837         const mDNSu32 maxTicks = maxWholeSecsTicks + maxReminderMsTicks;
838         // If the ticks argument is greater than the maximum value above, clamp it.
839         if (adjusted_ticks > maxTicks)
840         {
841             adjusted_ticks = maxTicks;
842         }
843     }
844 
845     const mDNSu32 wholeSeconds = (adjusted_ticks / ((mDNSu32)mDNSPlatformOneSecond));
846     const mDNSu32 reminderTicks = (adjusted_ticks % ((mDNSu32)mDNSPlatformOneSecond));
847     const mDNSu32 reminderMs = (reminderTicks * ((mDNSs32)MSEC_PER_SEC)) / ((mDNSu32)mDNSPlatformOneSecond);
848     const mDNSu32 milliseconds = (wholeSeconds * MSEC_PER_SEC) + reminderMs;
849 
850     return milliseconds;
851 }
852