Home | History | Annotate | Line # | Download | only in mDNSShared
      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