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
mDNSPlatformAddListValidator(mDNSListValidator * lv,mDNSListValidationFunction * lvf,const char * lvfName,void * context)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
validateLists(void)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
mallocL(const char * msg,mDNSu32 size)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
callocL(const char * msg,mDNSu32 size)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
freeL(const char * msg,void * x)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
mDNSPlatformSourceAddrForDest(mDNSAddr * const src,const mDNSAddr * const dst)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
GetConfigOption(char * dst,const char * option,FILE * f)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
ReadDDNSSettingsFromConfFile(mDNS * const m,const char * const filename,domainname * const hostname,domainname * const domain,mDNSBool * DomainDiscoveryDisabled)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
mDNSPlatformWriteDebugMsg(const char * msg)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)
mDNSPlatformWriteLogMsg(const char * ident,const char * buffer,mDNSLogLevel_t loglevel)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
mDNSPosixTCPSocketSetup(int * fd,mDNSAddr_Type addrType,mDNSIPPort * port,mDNSIPPort * outTcpPort)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
mDNSPosixDoTCPListenCallback(int fd,mDNSAddr_Type addressType,TCPSocketFlags socketFlags,TCPAcceptedCallback callback,void * context)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
mDNSPosixTCPListen(int * fd,mDNSAddr_Type addrtype,mDNSIPPort * port,mDNSAddr * addr,mDNSBool reuseAddr,int queueLength)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
mDNSPosixReadTCP(int fd,void * buf,unsigned long buflen,mDNSBool * closed)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
mDNSPosixWriteTCP(int fd,const char * msg,unsigned long len)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
timevalFromPlatformTime(const mDNSs32 platformTimeNow,const mDNSs32 platformTime,struct timeval * const outTv)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
getLocalTimestampFromTimeval(const struct timeval * const tv,char * const outBuffer,const mDNSu32 bufferLen)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
getLocalTimestampFromPlatformTime(const mDNSs32 platformTimeNow,const mDNSs32 platformTime,char * const outBuffer,const mDNSu32 bufferLen)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
getLocalTimestampNow(char * const outBuffer,const mDNSu32 bufferLen)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
getMillisecondsFromTicks(const mDNSs32 ticks)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