xref: /illumos-gate/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.c (revision ed093b41a93e8563e6e1e5dae0768dda2a7bcc27)
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-2019 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  *     http://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 <assert.h>
33 
34 #if APPLE_OSX_mDNSResponder
35 #include <os/log.h>
36 #endif
37 
38 #include "mDNSEmbeddedAPI.h"    // Defines the interface provided to the client layer above
39 #include "DNSCommon.h"
40 #include "PlatformCommon.h"
41 
42 #ifdef NOT_HAVE_SOCKLEN_T
43 typedef unsigned int socklen_t;
44 #endif
45 
46 #if MDNS_MALLOC_DEBUGGING
47 // We ONLY want this for malloc debugging--on a running production system we want to deal with
48 // malloc failures, not just die.   There is a small performance penalty for enabling these options
49 // as well, so they are all only appropriate for debugging.   The flags mean:
50 //
51 // A = warnings are errors
52 // X = abort on failure
53 // Z = sets J & R
54 // J = allocated memory is initialized to a pattern
55 // R causes realloc to always reallocate even if not needed
56 
57 char _malloc_options[] = "AXZ";
58 
59 mDNSlocal mDNSListValidator *listValidators;
60 
61 mDNSexport void mDNSPlatformAddListValidator(mDNSListValidator *lv, mDNSListValidationFunction *lvf,
62                                              const char *lvfName, void *context)
63 {
64     mDNSPlatformMemZero(lv, sizeof *lv);
65     lv->validator = lvf;
66     lv->validationFunctionName = lvfName;
67     lv->context = context;
68     lv->next = listValidators;
69     listValidators = lv;
70 }
71 
72 mDNSlocal void validateLists(void)
73 {
74     mDNSListValidator *vfp;
75     // Check Unix Domain Socket client lists (uds_daemon.c)
76     for (vfp = listValidators; vfp; vfp = vfp->next)
77     {
78         vfp->validator(vfp->context);
79     }
80 
81     mDNSPlatformValidateLists();
82 }
83 
84 #define kAllocMagic     0xDEAD1234
85 #define kGuardMagic     0xDEAD1234
86 #define kFreeMagic      0xDEADDEAD
87 #define kAllocLargeSize 32768
88 
89 mDNSexport void *mallocL(const char *msg, mDNSu32 size)
90 {
91     // Allocate space for two words of sanity checking data before the requested block and two words after.
92     // Adjust the length for alignment.
93     mDNSu32 *mem = malloc(sizeof(mDNSu32) * 4 + size);
94     mDNSu32 guard[2];
95     if (!mem)
96     { LogMsg("malloc( %s : %u ) failed", msg, size); return(NULL); }
97     else
98     {
99         mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)(mem + 2) + size);
100         if      (size > kAllocLargeSize)      LogMsg("malloc( %s : %lu ) @ %p suspiciously large", msg, size, &mem[2]);
101         else if (MDNS_MALLOC_DEBUGGING >= 2)  LogMsg("malloc( %s : %lu ) @ %p",                    msg, size, &mem[2]);
102         mem[  0] = kAllocMagic;
103         guard[0] = kGuardMagic;
104         mem[  1] = size;
105         guard[1] = size;
106         memcpy(after, &guard, sizeof guard);
107         memset(&mem[2], 0xFF, size);
108         validateLists();
109         return(&mem[2]);
110     }
111 }
112 
113 mDNSexport void *callocL(const char *msg, mDNSu32 size)
114 {
115     mDNSu32 guard[2];
116     const mDNSu32 headerSize = 4 * sizeof(mDNSu32);
117 
118     // Allocate space for two words of sanity checking data before the requested block and two words after.
119     // Adjust the length for alignment.
120     mDNSu32 *mem = (mDNSu32 *)calloc(1, headerSize + size);
121     if (!mem)
122     { LogMsg("calloc( %s : %u ) failed", msg, size); return(NULL); }
123     else
124     {
125         mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)(mem + 2) + size);
126         if      (size > kAllocLargeSize)     LogMsg("calloc( %s : %lu ) @ %p suspiciously large", msg, size, &mem[2]);
127         else if (MDNS_MALLOC_DEBUGGING >= 2) LogMsg("calloc( %s : %lu ) @ %p",                    msg, size, &mem[2]);
128         mem[  0] = kAllocMagic;
129         guard[0] = kGuardMagic;
130         mem[  1] = size;
131         guard[1] = size;
132         memcpy(after, guard, sizeof guard);
133         validateLists();
134         return(&mem[2]);
135     }
136 }
137 
138 mDNSexport void freeL(const char *msg, void *x)
139 {
140     if (!x)
141         LogMsg("free( %s @ NULL )!", msg);
142     else
143     {
144         mDNSu32 *mem = ((mDNSu32 *)x) - 2;
145         if      (mem[0] == kFreeMagic)  { LogMemCorruption("free( %s : %lu @ %p ) !!!! ALREADY DISPOSED !!!!", msg, mem[1], &mem[2]); return; }
146         if      (mem[0] != kAllocMagic) { LogMemCorruption("free( %s : %lu @ %p ) !!!! NEVER ALLOCATED !!!!",  msg, mem[1], &mem[2]); return; }
147         if      (mem[1] > kAllocLargeSize)          LogMsg("free( %s : %lu @ %p) suspiciously large",          msg, mem[1], &mem[2]);
148         else if (MDNS_MALLOC_DEBUGGING >= 2)        LogMsg("free( %s : %ld @ %p)",                             msg, mem[1], &mem[2]);
149         mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)x + mem[1]);
150         mDNSu32 guard[2];
151 
152         memcpy(guard, after, sizeof guard);
153         if (guard[0] != kGuardMagic)    { LogMemCorruption("free( %s : %lu @ %p ) !!!! END GUARD OVERWRITE !!!!",
154                                                            msg, mem[1], &mem[2]); return; }
155         if (guard[1] != mem[1])         { LogMemCorruption("free( %s : %lu @ %p ) !!!! LENGTH MISMATCH !!!!",
156                                                            msg, mem[1], &mem[2]); return; }
157         mem[0] = kFreeMagic;
158         memset(mem + 2, 0xFF, mem[1] + 2 * sizeof(mDNSu32));
159         validateLists();
160         free(mem);
161     }
162 }
163 
164 #endif
165 
166 // Bind a UDP socket to find the source address to a destination
167 mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
168 {
169     union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
170     socklen_t len = sizeof(addr);
171     socklen_t inner_len = 0;
172     int sock = socket(AF_INET, SOCK_DGRAM, 0);
173     src->type = mDNSAddrType_None;
174     if (sock == -1) return;
175     if (dst->type == mDNSAddrType_IPv4)
176     {
177         inner_len = sizeof(addr.a4);
178         #ifndef NOT_HAVE_SA_LEN
179         addr.a4.sin_len         = inner_len;
180         #endif
181         addr.a4.sin_family      = AF_INET;
182         addr.a4.sin_port        = 1;    // Not important, any port will do
183         addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
184     }
185     else if (dst->type == mDNSAddrType_IPv6)
186     {
187         inner_len = sizeof(addr.a6);
188         #ifndef NOT_HAVE_SA_LEN
189         addr.a6.sin6_len      = inner_len;
190         #endif
191         addr.a6.sin6_family   = AF_INET6;
192         addr.a6.sin6_flowinfo = 0;
193         addr.a6.sin6_port     = 1;  // Not important, any port will do
194         addr.a6.sin6_addr     = *(struct in6_addr*)&dst->ip.v6;
195         addr.a6.sin6_scope_id = 0;
196     }
197     else return;
198 
199     if ((connect(sock, &addr.s, inner_len)) < 0) {
200 	if (errno != ENETUNREACH)
201 		LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno,
202 		    strerror(errno));
203 	goto exit;
204     }
205 
206     if ((getsockname(sock, &addr.s, &len)) < 0)
207     { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; }
208 
209     src->type = dst->type;
210     if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr;
211     else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
212 exit:
213     close(sock);
214 }
215 
216 // dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
217 mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
218 {
219     char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value
220     size_t len = strlen(option);
221     if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; }
222     fseek(f, 0, SEEK_SET);  // set position to beginning of stream
223     while (fgets(buf, sizeof(buf), f))      // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator
224     {
225         if (!strncmp(buf, option, len))
226         {
227             strncpy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1);
228             if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0';
229             len = strlen(dst);
230             if (len && dst[len-1] == '\n') dst[len-1] = '\0';  // chop newline
231             return mDNStrue;
232         }
233     }
234     debugf("Option %s not set", option);
235     return mDNSfalse;
236 }
237 
238 mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
239 {
240     char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
241     mStatus err;
242     FILE *f = fopen(filename, "r");
243 
244     if (hostname) hostname->c[0] = 0;
245     if (domain) domain->c[0] = 0;
246     if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse;
247 
248     if (f)
249     {
250         if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue;
251         if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf;
252         if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf;
253         buf[0] = 0;
254         GetConfigOption(buf, "secret-64", f);  // failure means no authentication
255         fclose(f);
256         f = NULL;
257     }
258     else
259     {
260         if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened.");
261         return;
262     }
263 
264     if (domain && domain->c[0] && buf[0])
265     {
266         DomainAuthInfo *info = (DomainAuthInfo*) mDNSPlatformMemAllocateClear(sizeof(*info));
267         // for now we assume keyname = service reg domain and we use same key for service and hostname registration
268         err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0);
269         if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
270     }
271 
272     return;
273 
274 badf:
275     LogMsg("ERROR: malformatted config file");
276     if (f) fclose(f);
277 }
278 
279 #if MDNS_DEBUGMSGS
280 mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
281 {
282     fprintf(stderr,"%s\n", msg);
283     fflush(stderr);
284 }
285 #endif
286 
287 #if !MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
288 mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel)
289 {
290 #if APPLE_OSX_mDNSResponder && LogTimeStamps
291     extern mDNS mDNSStorage;
292     extern mDNSu32 mDNSPlatformClockDivisor;
293     mDNSs32 t = mDNSStorage.timenow ? mDNSStorage.timenow : mDNSPlatformClockDivisor ? mDNS_TimeNow_NoLock(&mDNSStorage) : 0;
294     int ms = ((t < 0) ? -t : t) % 1000;
295 #endif
296 
297     if (mDNS_DebugMode) // In debug mode we write to stderr
298     {
299 #if APPLE_OSX_mDNSResponder && LogTimeStamps
300         if (ident && ident[0] && mDNSPlatformClockDivisor)
301             fprintf(stderr,"%8d.%03d: %s\n", (int)(t/1000), ms, buffer);
302         else
303 #endif
304         fprintf(stderr,"%s\n", buffer);
305         fflush(stderr);
306     }
307     else                // else, in production mode, we write to syslog
308     {
309         static int log_inited = 0;
310 
311         int syslog_level;
312         switch (loglevel)
313         {
314             case MDNS_LOG_FAULT:     syslog_level = LOG_ERR;     break;
315             case MDNS_LOG_ERROR:     syslog_level = LOG_ERR;     break;
316             case MDNS_LOG_WARNING:   syslog_level = LOG_WARNING; break;
317             case MDNS_LOG_DEFAULT:   syslog_level = LOG_NOTICE;  break;
318             case MDNS_LOG_INFO:      syslog_level = LOG_INFO;    break;
319             case MDNS_LOG_DEBUG:     syslog_level = LOG_DEBUG;   break;
320             default:                 syslog_level = LOG_NOTICE;  break;
321         }
322 
323         if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; }
324 
325 #if APPLE_OSX_mDNSResponder && LogTimeStamps
326         if (ident && ident[0] && mDNSPlatformClockDivisor)
327             syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer);
328         else
329 #endif
330         {
331             syslog(syslog_level, "%s", buffer);
332         }
333     }
334 }
335 #endif // !MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
336 
337 mDNSexport mDNSBool mDNSPosixTCPSocketSetup(int *fd, mDNSAddr_Type addrType, mDNSIPPort *port, mDNSIPPort *outTcpPort)
338 {
339     int sa_family = (addrType == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
340     int err;
341     int sock;
342     mDNSu32 lowWater = 15384;
343 
344     sock = socket(sa_family, SOCK_STREAM, IPPROTO_TCP);
345     if (sock < 3)
346     {
347         if (errno != EAFNOSUPPORT)
348         {
349             LogMsg("mDNSPosixTCPSocketSetup: socket error %d errno %d (%s)", sock, errno, strerror(errno));
350         }
351         return mDNStrue;
352     }
353     *fd = sock;
354 
355     union
356     {
357         struct sockaddr sa;
358         struct sockaddr_in sin;
359         struct sockaddr_in6 sin6;
360     } addr;
361     // If port is not NULL, bind to it.
362     if (port != NULL)
363     {
364         socklen_t len = (sa_family == AF_INET) ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
365         mDNSPlatformMemZero(&addr, sizeof addr);
366 
367         addr.sa.sa_family = sa_family;
368 #ifndef NOT_HAVE_SA_LEN
369 	addr.sa.sa_len = len;
370 #endif
371         if (sa_family == AF_INET6)
372         {
373             addr.sin6.sin6_port = port->NotAnInteger;
374         }
375         else
376         {
377             addr.sin.sin_port = port->NotAnInteger;
378         }
379         err = bind(sock, &addr.sa, len);
380         if (err < 0)
381         {
382             LogMsg("mDNSPosixTCPSocketSetup getsockname: %s", strerror(errno));
383             return mDNSfalse;
384         }
385     }
386 
387     socklen_t addrlen = sizeof addr;
388     err = getsockname(sock, (struct sockaddr *)&addr, &addrlen);
389     if (err < 0)
390     {
391         LogMsg("mDNSPosixTCPSocketSetup getsockname: %s", strerror(errno));
392         return mDNSfalse;
393     }
394     if (sa_family == AF_INET6)
395     {
396         outTcpPort->NotAnInteger = addr.sin6.sin6_port;
397 
398     } else
399     {
400         outTcpPort->NotAnInteger = addr.sin.sin_port;
401     }
402     if (port)
403         port->NotAnInteger = outTcpPort->NotAnInteger;
404 
405 #ifdef TCP_NOTSENT_LOWAT
406     err = setsockopt(sock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &lowWater, sizeof lowWater);
407     if (err < 0)
408     {
409         LogMsg("mDNSPosixTCPSocketSetup: TCP_NOTSENT_LOWAT failed: %s", strerror(errno));
410         return mDNSfalse;
411     }
412 #endif
413 
414     return mDNStrue;
415 }
416 
417 mDNSexport TCPSocket *mDNSPosixDoTCPListenCallback(int fd, mDNSAddr_Type addressType, TCPSocketFlags socketFlags,
418                                              TCPAcceptedCallback callback, void *context)
419 {
420     union
421     {
422         struct sockaddr_in6 sin6;
423         struct sockaddr_in sin;
424         struct sockaddr sa;
425     } address;
426 
427     socklen_t slen = sizeof address;
428     int remoteSock;
429     mDNSAddr addr;
430     mDNSIPPort port;
431     TCPSocket *sock = mDNSNULL;
432     int failed;
433     char *nbp;
434     int i;
435     mDNSu32 lowWater = 16384;
436     // When we remember our connection, we remember a name that we can print for logging.   But
437     // since we are the listener in this case, we don't /have/ a name for it.   This buffer
438     // is used to print the IP address into a human readable string which will serve that purpose
439     // for this case.
440     char namebuf[INET6_ADDRSTRLEN + 1 + 5 + 1];
441 
442     remoteSock = accept(fd, &address.sa, &slen);
443     if (remoteSock < 0)
444     {
445         LogMsg("mDNSPosixDoTCPListenCallback: accept returned %d", remoteSock);
446         goto out;
447     }
448 
449     failed = fcntl(remoteSock, F_SETFL, O_NONBLOCK);
450     if (failed < 0)
451     {
452         close(remoteSock);
453         LogMsg("mDNSPosixDoTCPListenCallback: fcntl returned %d", errno);
454         goto out;
455     }
456 
457 #ifdef TCP_NOTSENT_LOWAT
458     failed = setsockopt(remoteSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT,
459                         &lowWater, sizeof lowWater);
460     if (failed < 0)
461     {
462         close(remoteSock);
463         LogMsg("mDNSPosixDoTCPListenCallback: TCP_NOTSENT_LOWAT returned %d", errno);
464         goto out;
465     }
466 #endif
467 
468     if (address.sa.sa_family == AF_INET6)
469     {
470         // If we are listening on an IPv4/IPv6 socket, the incoming address might be an IPv4-in-IPv6 address
471         for (i = 0; i < 10; i++)
472         {
473             if (address.sin6.sin6_addr.s6_addr[i] != 0)
474             {
475                 addr.type = mDNSAddrType_IPv6;
476                 goto nope;
477             }
478         }
479 
480         // a legit IPv4 address would be ::ffff:a.b.c.d; if there's no ::ffff bit, then it's an IPv6
481         // address with a really weird prefix.
482         if (address.sin6.sin6_addr.s6_addr[10] != 0xFF || address.sin6.sin6_addr.s6_addr[11] != 0xFF)
483         {
484             addr.type = mDNSAddrType_IPv6;
485         } else if (addressType != mDNSAddrType_None)
486         {
487             if (inet_ntop(AF_INET, &address.sin6.sin6_addr.s6_addr[12], namebuf, INET6_ADDRSTRLEN + 1) == NULL)
488             {
489                 strcpy(namebuf, ":unknown:");
490             }
491             LogMsg("mDNSPosixDoTCPListenCallback received an IPv4 connection from %s on an IPv6-only socket.",
492                    namebuf);
493             close(remoteSock);
494             goto out;
495         }
496         else
497         {
498             addr.type = mDNSAddrType_IPv4;
499         }
500     nope:
501         if (addr.type == mDNSAddrType_IPv6)
502         {
503             if (inet_ntop(address.sin6.sin6_family, &address.sin6.sin6_addr, namebuf, INET6_ADDRSTRLEN + 1) == NULL)
504             {
505                 strcpy(namebuf, ":unknown:");
506             }
507             memcpy(&addr.ip.v6, &address.sin6.sin6_addr, sizeof addr.ip.v6);
508         }
509         else
510         {
511             if (inet_ntop(AF_INET, &address.sin6.sin6_addr.s6_addr[12], namebuf, INET6_ADDRSTRLEN + 1) == NULL)
512             {
513                 strcpy(namebuf, ":unknown:");
514             }
515             memcpy(&addr.ip.v4, &address.sin6.sin6_addr.s6_addr[12], sizeof addr.ip.v4);
516         }
517         port.NotAnInteger = address.sin6.sin6_port;
518     }
519     else if (address.sa.sa_family == AF_INET)
520     {
521         addr.type = mDNSAddrType_IPv4;
522         memcpy(&addr.ip.v4, &address.sin.sin_addr, sizeof addr.ip.v4);
523         port.NotAnInteger = address.sin.sin_port;
524         if (inet_ntop(AF_INET, &address.sin.sin_addr, namebuf, INET6_ADDRSTRLEN + 1) == NULL)
525         {
526             strcpy(namebuf, ":unknown:");
527         }
528     } else {
529         LogMsg("mDNSPosixDoTCPListenCallback: connection from unknown address family %d", address.sa.sa_family);
530         close(remoteSock);
531         goto out;
532     }
533     nbp = namebuf + strlen(namebuf);
534     *nbp++ = '%';
535     snprintf(nbp, 6, "%u", ntohs(port.NotAnInteger));
536 
537     sock = mDNSPlatformTCPAccept(socketFlags, remoteSock);
538     if (sock == NULL)
539     {
540         LogMsg("mDNSPosixDoTCPListenCallback: mDNSPlatformTCPAccept returned NULL; dropping connection from %s",
541                namebuf);
542         close(remoteSock);
543         goto out;
544     }
545     callback(sock, &addr, &port, namebuf, context);
546 out:
547     return sock;
548 }
549 
550 mDNSexport mDNSBool mDNSPosixTCPListen(int *fd, mDNSAddr_Type addrtype, mDNSIPPort *port, mDNSAddr *addr,
551                                        mDNSBool reuseAddr, int queueLength)
552 
553 {
554     union
555     {
556         struct sockaddr_in6 sin6;
557         struct sockaddr_in sin;
558         struct sockaddr sa;
559     } address;
560 
561     int failed;
562     int sock;
563     int one = 1;
564     socklen_t sock_len;
565 
566     // We require an addrtype parameter because addr is allowed to be null, but they have to agree.
567     if (addr != mDNSNULL && addr->type != addrtype)
568     {
569         LogMsg("mDNSPlatformTCPListen: address type conflict: %d:%d", addr->type, addrtype);
570         return mDNSfalse;
571     }
572     if (port == mDNSNULL)
573     {
574         LogMsg("mDNSPlatformTCPListen: port must not be NULL");
575         return mDNSfalse;
576     }
577 
578     mDNSPlatformMemZero(&address, sizeof address);
579     if (addrtype == mDNSAddrType_None || addrtype == mDNSAddrType_IPv6)
580     {
581         // Set up DNS listener socket
582         if (addr != mDNSNULL)
583         {
584             memcpy(&address.sin6.sin6_addr.s6_addr, &addr->ip, sizeof address.sin6.sin6_addr.s6_addr);
585         }
586         address.sin6.sin6_port = port->NotAnInteger;
587 
588         sock_len = sizeof address.sin6;
589         address.sin6.sin6_family = AF_INET6;
590     }
591     else if (addrtype == mDNSAddrType_IPv4)
592     {
593         if (addr != mDNSNULL)
594         {
595             memcpy(&address.sin.sin_addr.s_addr, &addr->ip, sizeof address.sin.sin_addr.s_addr);
596         }
597         address.sin.sin_port = port->NotAnInteger;
598         sock_len = sizeof address.sin;
599         address.sin.sin_family = AF_INET;
600     }
601     else
602     {
603         LogMsg("mDNSPlatformTCPListen: invalid address type: %d", addrtype);
604         return mDNSfalse;
605     }
606 #ifndef NOT_HAVE_SA_LEN
607     address.sa.sa_len = sock_len;
608 #endif
609     sock = socket(address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
610 
611     if (sock < 0)
612     {
613         LogMsg("mDNSPlatformTCPListen: socket call failed: %s", strerror(errno));
614         return mDNSfalse;
615     }
616     *fd = sock;
617 
618     // The reuseAddr flag is used to indicate that we want to listen on this port even if
619     // there are still lingering sockets.   We will still fail if there is another listener.
620     // Note that this requires SO_REUSEADDR, not SO_REUSEPORT, which does not have special
621     // handling for lingering sockets.
622     if (reuseAddr)
623     {
624         failed = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
625         if (failed < 0)
626         {
627             LogMsg("mDNSPlatformTCPListen: SO_REUSEADDR failed %s", strerror(errno));
628             return mDNSfalse;
629         }
630     }
631 
632     // Bind to the port and (if provided) address
633     failed = bind(sock, &address.sa, sock_len);
634     if (failed < 0)
635     {
636         LogMsg("mDNSPlatformTCPListen: bind failed %s", strerror(errno));
637         return mDNSfalse;
638     }
639 
640     // If there was no specified listen port, we need to know what port we got.
641     if (port->NotAnInteger == 0)
642     {
643         mDNSPlatformMemZero(&address, sizeof address);
644         failed = getsockname(sock, &address.sa, &sock_len);
645         if (failed < 0)
646         {
647             LogMsg("mDNSRelay: getsockname failed: %s", strerror(errno));
648             return mDNSfalse;
649         }
650         if (address.sa.sa_family == AF_INET)
651         {
652             port->NotAnInteger = address.sin.sin_port;
653         }
654         else
655         {
656             port->NotAnInteger = address.sin6.sin6_port;
657         }
658     }
659 
660     failed = listen(sock, queueLength);
661     if (failed < 0)
662     {
663         LogMsg("mDNSPlatformTCPListen: listen failed: %s", strerror(errno));
664         return mDNSfalse;
665     }
666     return mDNStrue;
667 }
668 
669 mDNSexport long mDNSPosixReadTCP(int fd, void *buf, unsigned long buflen, mDNSBool *closed)
670 {
671     static int CLOSEDcount = 0;
672     static int EAGAINcount = 0;
673     ssize_t nread = recv(fd, buf, buflen, 0);
674 
675     if (nread > 0)
676     {
677         CLOSEDcount = 0;
678         EAGAINcount = 0;
679     } // On success, clear our error counters
680     else if (nread == 0)
681     {
682         *closed = mDNStrue;
683         if ((++CLOSEDcount % 20) == 0)
684         {
685             LogMsg("ERROR: mDNSPosixReadFromSocket - recv %d got CLOSED %d times", fd, CLOSEDcount);
686             assert(CLOSEDcount < 1000);
687             // Recovery Mechanism to bail mDNSResponder out of trouble: Instead of logging the same error
688             // msg multiple times, crash mDNSResponder using assert() and restart fresh. See advantages
689             // below:
690             // 1.Better User Experience
691             // 2.CrashLogs frequency can be monitored
692             // 3.StackTrace can be used for more info
693         }
694     }
695     // else nread is negative -- see what kind of error we got
696     else if (errno == ECONNRESET)
697     {
698         nread = 0; *closed = mDNStrue;
699     }
700     else if (errno != EAGAIN)
701     {
702         LogMsg("ERROR: mDNSPosixReadFromSocket - recv: %d (%s)", errno, strerror(errno));
703         nread = -1;
704     }
705     else
706     { // errno is EAGAIN (EWOULDBLOCK) -- no data available
707         nread = 0;
708         if ((++EAGAINcount % 1000) == 0)
709         {
710             LogMsg("ERROR: mDNSPosixReadFromSocket - recv %d got EAGAIN %d times", fd, EAGAINcount);
711             sleep(1);
712         }
713     }
714     return nread;
715 }
716 
717 mDNSexport long mDNSPosixWriteTCP(int fd, const char *msg, unsigned long len)
718 {
719     ssize_t result;
720     long nsent;
721 
722     result = write(fd, msg, len);
723     if (result < 0)
724     {
725         if (errno == EAGAIN)
726         {
727             nsent = 0;
728         }
729         else
730         {
731             LogMsg("ERROR: mDNSPosixWriteTCP - send %s", strerror(errno)); nsent = -1;
732         }
733     }
734     else
735     {
736         nsent = (long)result;
737     }
738     return nsent;
739 }
740