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