]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / daemon.c
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) 2002-2020 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
18 #include <mach/mach.h>
19 #include <mach/mach_error.h>
20 #include <sys/types.h>
21 #include <errno.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include <paths.h>
25 #include <fcntl.h>
26 #include <launch.h>
27 #include <launch_priv.h> // for launch_socket_service_check_in()
28 #include <pwd.h>
29 #include <sys/event.h>
30 #include <pthread.h>
31 #include <sandbox.h>
32 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
33 #include <err.h>
34 #include <sysexits.h>
35 #include <TargetConditionals.h>
36
37 #include "uDNS.h"
38 #include "DNSCommon.h"
39 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
40
41 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
42 #include "xpc_services.h"
43 #include "xpc_service_dns_proxy.h"
44 #include "xpc_service_log_utility.h"
45 #include "helper.h"
46 #include "posix_utilities.h" // for getLocalTimestamp()
47
48 #if MDNSRESPONDER_SUPPORTS(APPLE, METRICS)
49 #include "Metrics.h"
50 #endif
51
52 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSD_XPC_SERVICE)
53 #include "dnssd_server.h"
54 #endif
55
56 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
57 #include "mdns_managed_defaults.h"
58 #include "QuerierSupport.h"
59 #endif
60
61 // Used on OSX(10.11.x onwards) for manipulating mDNSResponder program arguments
62 #if APPLE_OSX_mDNSResponder
63 // plist file to read the user's preferences
64 #define kProgramArguments CFSTR("com.apple.mDNSResponder")
65 // possible arguments for external customers
66 #define kPreferencesKey_DebugLogging CFSTR("DebugLogging")
67 #define kPreferencesKey_UnicastPacketLogging CFSTR("UnicastPacketLogging")
68 #define kPreferencesKey_AlwaysAppendSearchDomains CFSTR("AlwaysAppendSearchDomains")
69 #define kPreferencesKey_EnableAllowExpired CFSTR("EnableAllowExpired")
70 #define kPreferencesKey_NoMulticastAdvertisements CFSTR("NoMulticastAdvertisements")
71 #define kPreferencesKey_StrictUnicastOrdering CFSTR("StrictUnicastOrdering")
72 #define kPreferencesKey_OfferSleepProxyService CFSTR("OfferSleepProxyService")
73 #define kPreferencesKey_UseInternalSleepProxy CFSTR("UseInternalSleepProxy")
74
75 #if ENABLE_BLE_TRIGGERED_BONJOUR
76 #define kPreferencesKey_EnableBLEBasedDiscovery CFSTR("EnableBLEBasedDiscovery")
77 #define kPreferencesKey_DefaultToBLETriggered CFSTR("DefaultToBLETriggered")
78 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
79
80 #if MDNSRESPONDER_SUPPORTS(APPLE, PREALLOCATED_CACHE)
81 #define kPreferencesKey_PreallocateCacheMemory CFSTR("PreallocateCacheMemory")
82 #endif
83 #define kPreferencesKey_PQWorkaroundThreshold CFSTR("PQWorkaroundThreshold")
84 #endif
85
86 //*************************************************************************************************************
87 #if COMPILER_LIKES_PRAGMA_MARK
88 #pragma mark - Globals
89 #endif
90
91 static mDNS_PlatformSupport PlatformStorage;
92
93 // Start off with a default cache of 32K (136 records of 240 bytes each)
94 // Each time we grow the cache we add another 136 records
95 // 136 * 240 = 32640 bytes.
96 // This fits in eight 4kB pages, with 128 bytes spare for memory block headers and similar overhead
97 #define RR_CACHE_SIZE ((32*1024) / sizeof(CacheRecord))
98 static CacheEntity rrcachestorage[RR_CACHE_SIZE];
99 struct CompileTimeAssertionChecks_RR_CACHE_SIZE { char a[(RR_CACHE_SIZE >= 136) ? 1 : -1]; };
100 #define kRRCacheGrowSize (sizeof(CacheEntity) * RR_CACHE_SIZE)
101
102
103 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
104 mDNSlocal void PrepareForIdle(void *m_param);
105 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
106 static mach_port_t signal_port = MACH_PORT_NULL;
107 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
108
109 static dnssd_sock_t *launchd_fds = mDNSNULL;
110 static size_t launchd_fds_count = 0;
111
112 static mDNSBool NoMulticastAdvertisements = mDNSfalse; // By default, advertise addresses (& other records) via multicast
113
114 extern mDNSBool StrictUnicastOrdering;
115 extern mDNSBool AlwaysAppendSearchDomains;
116 extern mDNSBool EnableAllowExpired;
117 mDNSexport void INFOCallback(void);
118 mDNSexport void dump_state_to_fd(int fd);
119
120 #if ENABLE_BLE_TRIGGERED_BONJOUR
121 extern mDNSBool EnableBLEBasedDiscovery;
122 extern mDNSBool DefaultToBLETriggered;
123 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
124
125 #if MDNSRESPONDER_SUPPORTS(APPLE, PREALLOCATED_CACHE)
126 static mDNSBool PreallocateCacheMemory = mDNSfalse;
127 #endif
128
129 #if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_MEM_LIMIT)
130 #define kRRCacheMemoryLimit 1000000 // For now, we limit the cache to at most 1MB on iOS devices.
131 #endif
132
133 // We keep a list of client-supplied event sources in KQSocketEventSource records
134 typedef struct KQSocketEventSource
135 {
136 struct KQSocketEventSource *next;
137 int fd;
138 KQueueEntry kqs;
139 udsEventCallback callback;
140 void *context;
141 } KQSocketEventSource;
142
143 static KQSocketEventSource *gEventSources;
144
145 //*************************************************************************************************************
146 #if COMPILER_LIKES_PRAGMA_MARK
147 #pragma mark -
148 #pragma mark - General Utility Functions
149 #endif
150
151 #if MDNS_MALLOC_DEBUGGING
152 void mDNSPlatformValidateLists()
153 {
154 mDNS *const m = &mDNSStorage;
155
156 KQSocketEventSource *k;
157 for (k = gEventSources; k; k=k->next)
158 if (k->next == (KQSocketEventSource *)~0 || k->fd < 0)
159 LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd);
160
161 // Check platform-layer lists
162 NetworkInterfaceInfoOSX *i;
163 for (i = m->p->InterfaceList; i; i = i->next)
164 if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0)
165 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname);
166
167 ClientTunnel *t;
168 for (t = m->TunnelClients; t; t=t->next)
169 if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63)
170 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]);
171 }
172 #endif // MDNS_MALLOC_DEBUGGING
173
174 //*************************************************************************************************************
175 // Registration
176
177 mDNSexport void RecordUpdatedNiceLabel(mDNSs32 delay)
178 {
179 mDNSStorage.p->NotifyUser = NonZeroTime(mDNSStorage.timenow + delay);
180 }
181
182 mDNSlocal void mDNSPreferencesSetNames(int key, domainlabel *old, domainlabel *new)
183 {
184 mDNS *const m = &mDNSStorage;
185 domainlabel *prevold, *prevnew;
186 switch (key)
187 {
188 case kmDNSComputerName:
189 case kmDNSLocalHostName:
190 if (key == kmDNSComputerName)
191 {
192 prevold = &m->p->prevoldnicelabel;
193 prevnew = &m->p->prevnewnicelabel;
194 }
195 else
196 {
197 prevold = &m->p->prevoldhostlabel;
198 prevnew = &m->p->prevnewhostlabel;
199 }
200 // There are a few cases where we need to invoke the helper.
201 //
202 // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need
203 // to invoke the helper so that it pops up a dialogue to inform the user about the
204 // conflict
205 //
206 // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label
207 // through the preferences pane. We may have to inform the helper as it may have popped up
208 // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking
209 // the helper in this case if the previous values (old and new) that we told helper last time
210 // are same. If the previous old and new values are same, helper does not care.
211 //
212 // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old"
213 // is not called with NULL today, but this makes it future proof.
214 if (!old || !new || !SameDomainLabelCS(old->c, new->c) ||
215 !SameDomainLabelCS(old->c, prevold->c) ||
216 !SameDomainLabelCS(new->c, prevnew->c))
217 {
218 // Work around bug radar:21397654
219 #ifndef __clang_analyzer__
220 if (old)
221 *prevold = *old;
222 else
223 prevold->c[0] = 0;
224 if (new)
225 *prevnew = *new;
226 else
227 prevnew->c[0] = 0;
228 #endif
229 mDNSPreferencesSetName(key, old, new);
230 }
231 else
232 {
233 LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s",
234 (key == kmDNSComputerName ? "prevoldnicelabel" : "prevoldhostlabel"), prevold->c,
235 (key == kmDNSComputerName ? "prevnewnicelabel" : "prevnewhostlabel"), prevnew->c,
236 old->c, new->c);
237 }
238 break;
239 default:
240 LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key);
241 return;
242 }
243 }
244
245 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
246 {
247 if (result == mStatus_NoError)
248 {
249 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
250 LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
251 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
252 RecordUpdatedNiceLabel(mDNSPlatformOneSecond);
253 }
254 else if (result == mStatus_NameConflict)
255 {
256 LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c);
257 if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow);
258 else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond)
259 {
260 // Tell the helper we've given up
261 mDNSPreferencesSetNames(kmDNSLocalHostName, &m->p->userhostlabel, NULL);
262 }
263 }
264 else if (result == mStatus_GrowCache)
265 {
266 // Allocate another chunk of cache storage
267 static unsigned int allocated = 0;
268 #if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_MEM_LIMIT)
269 if (allocated >= kRRCacheMemoryLimit) return; // For now we limit the cache to at most 1MB on iOS devices
270 #endif
271 allocated += kRRCacheGrowSize;
272 // LogMsg("GrowCache %d * %d = %d; total so far %6u", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE, allocated);
273 CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE);
274 //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
275 if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
276 }
277 else if (result == mStatus_ConfigChanged)
278 {
279 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
280 mDNSPreferencesSetNames(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
281 mDNSPreferencesSetNames(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
282
283 // Then we call into the UDS daemon code, to let it do the same
284 udsserver_handle_configchange(m);
285 }
286 }
287
288
289 //*************************************************************************************************************
290 #if COMPILER_LIKES_PRAGMA_MARK
291 #pragma mark -
292 #pragma mark - Startup, shutdown, and supporting code
293 #endif
294
295 mDNSlocal void ExitCallback(int sig)
296 {
297 (void)sig; // Unused
298 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, PUB_S " stopping", mDNSResponderVersionString);
299
300 if (udsserver_exit() < 0)
301 {
302 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "ExitCallback: udsserver_exit failed");
303 }
304
305 debugf("ExitCallback: mDNS_StartExit");
306 mDNS_StartExit(&mDNSStorage);
307 }
308
309 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
310
311 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
312 mDNSlocal void HandleSIG(int sig)
313 {
314 kern_return_t status;
315 mach_msg_header_t header;
316
317 // WARNING: can't call syslog or fprintf from signal handler
318 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
319 header.msgh_remote_port = signal_port;
320 header.msgh_local_port = MACH_PORT_NULL;
321 header.msgh_size = sizeof(header);
322 header.msgh_id = sig;
323
324 status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size,
325 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
326
327 if (status != MACH_MSG_SUCCESS)
328 {
329 if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
330 if (sig == SIGTERM || sig == SIGINT) exit(-1);
331 }
332 }
333
334 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
335
336 mDNSexport void dump_state_to_fd(int fd)
337 {
338 #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
339 mDNS *const m = &mDNSStorage;
340 #endif
341 char buffer[1024];
342 buffer[0] = '\0';
343
344 mDNSs32 utc = mDNSPlatformUTC();
345 const mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
346 NetworkInterfaceInfoOSX *i;
347 #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
348 DNSServer *s;
349 #endif
350 McastResolver *mr;
351 char timestamp[64]; // 64 is enough to store the UTC timestmp
352
353 LogToFD(fd, "---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
354 getLocalTimestamp(timestamp, sizeof(timestamp));
355 LogToFD(fd, "Date: %s", timestamp);
356 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "---- BEGIN STATE LOG ---- (" PUB_S ")", timestamp);
357
358 udsserver_info_dump_to_fd(fd);
359
360 LogToFD(fd, "----- Platform Timers -----");
361 LogTimerToFD(fd, "m->NextCacheCheck ", mDNSStorage.NextCacheCheck);
362 LogTimerToFD(fd, "m->NetworkChanged ", mDNSStorage.NetworkChanged);
363 LogTimerToFD(fd, "m->p->NotifyUser ", mDNSStorage.p->NotifyUser);
364 LogTimerToFD(fd, "m->p->HostNameConflict ", mDNSStorage.p->HostNameConflict);
365 LogTimerToFD(fd, "m->p->KeyChainTimer ", mDNSStorage.p->KeyChainTimer);
366
367 log_dnsproxy_info_to_fd(fd, &mDNSStorage);
368
369 LogToFD(fd, "----- KQSocketEventSources -----");
370 if (!gEventSources) LogToFD(fd, "<None>");
371 else
372 {
373 KQSocketEventSource *k;
374 for (k = gEventSources; k; k=k->next)
375 LogToFD(fd, "%3d %s %s", k->fd, k->kqs.KQtask, k->fd == mDNSStorage.uds_listener_skt ? "Listener for incoming UDS clients" : " ");
376 }
377
378 LogToFD(fd, "------ Network Interfaces ------");
379 if (!mDNSStorage.p->InterfaceList) LogToFD(fd, "<None>");
380 else
381 {
382 LogToFD(fd, " Struct addr Registered MAC BSSID Interface Address");
383 for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
384 {
385 // Allow six characters for interface name, for names like "vmnet8"
386 if (!i->Exists)
387 LogToFD(fd, "%p %2ld, %p, %s %-6s %.6a %.6a %#-14a dormant for %d seconds",
388 i, i->ifinfo.InterfaceID, i->Registered,
389 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, &i->ifinfo.MAC, &i->BSSID,
390 &i->ifinfo.ip, utc - i->LastSeen);
391 else
392 {
393 const CacheRecord *sps[3];
394 FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps);
395 LogToFD(fd, "%p %2ld, %p, %s %-6s %.6a %.6a %s %s %s %s %s %s %#a",
396 i, i->ifinfo.InterfaceID, i->Registered,
397 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, &i->ifinfo.MAC, &i->BSSID,
398 i->ifinfo.InterfaceActive ? "Active" : " ",
399 i->ifinfo.IPv4Available ? "v4" : " ",
400 i->ifinfo.IPv6Available ? "v6" : " ",
401 i->ifinfo.Advertise ? "A" : " ",
402 i->ifinfo.McastTxRx ? "M" : " ",
403 !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "p" : "P",
404 &i->ifinfo.ip);
405
406 // Only print the discovered sleep proxies once for the lead/active interface of an interface set.
407 if (i == i->Registered && (sps[0] || sps[1] || sps[2]))
408 {
409 LogToFD(fd, " Sleep Proxy Metric Name");
410 if (sps[0]) LogToFD(fd, " %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c);
411 if (sps[1]) LogToFD(fd, " %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c);
412 if (sps[2]) LogToFD(fd, " %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c);
413 }
414 }
415 }
416 }
417
418 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
419 LogToFD(fd, "----------- DNS Services -----------");
420 {
421 const mdns_dns_service_manager_t manager = Querier_GetDNSServiceManager();
422 if (manager)
423 {
424 mdns_dns_service_manager_iterate(manager,
425 ^ bool (const mdns_dns_service_t service)
426 {
427 char *const desc = mdns_copy_description(service);
428 LogToFD(fd, "%s", desc ? desc : "<missing description>");
429 free(desc);
430 return false;
431 });
432 }
433 }
434 #else
435 LogToFD(fd, "--------- DNS Servers(%d) ----------", CountOfUnicastDNSServers(&mDNSStorage));
436 if (!mDNSStorage.DNSServers) LogToFD(fd, "<None>");
437 else
438 {
439 for (s = mDNSStorage.DNSServers; s; s = s->next)
440 {
441 NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(s->interface);
442 LogToFD(fd, "DNS Server %##s %s%s%#a:%d %d %s %d %d %sv4 %sv6 %scell %sexp %sconstrained %sCLAT46",
443 s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
444 s->penaltyTime ? (s->penaltyTime - mDNS_TimeNow(&mDNSStorage)) : 0, DNSScopeToString(s->scopeType),
445 s->timeout, s->resGroupID,
446 s->usableA ? "" : "!",
447 s->usableAAAA ? "" : "!",
448 s->isCell ? "" : "!",
449 s->isExpensive ? "" : "!",
450 s->isConstrained ? "" : "!",
451 s->isCLAT46 ? "" : "!");
452 }
453 }
454 #endif // MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
455
456 LogToFD(fd, "v4answers %d", mDNSStorage.p->v4answers);
457 LogToFD(fd, "v6answers %d", mDNSStorage.p->v6answers);
458 LogToFD(fd, "Last DNS Trigger: %d ms ago", (now - mDNSStorage.p->DNSTrigger));
459
460 #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
461 LogToFD(fd, "-------- Interface Monitors --------");
462 const CFIndex n = m->p->InterfaceMonitors ? CFArrayGetCount(m->p->InterfaceMonitors) : 0;
463 if (n > 0)
464 {
465 for (CFIndex j = 0; j < n; j++)
466 {
467 mdns_interface_monitor_t monitor = (mdns_interface_monitor_t) CFArrayGetValueAtIndex(m->p->InterfaceMonitors, j);
468 char *description = mdns_copy_description(monitor);
469 if (description)
470 {
471 LogToFD(fd, "%s", description);
472 free(description);
473 }
474 else
475 {
476 LogToFD(fd, "monitor %p (no description)", monitor);
477 }
478 }
479 }
480 else
481 {
482 LogToFD(fd, "No interface monitors");
483 }
484 #endif
485
486 LogToFD(fd, "--------- Mcast Resolvers ----------");
487 if (!mDNSStorage.McastResolvers) LogToFD(fd, "<None>");
488 else
489 {
490 for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next)
491 LogToFD(fd, "Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout);
492 }
493
494 LogToFD(fd, "------------ Hostnames -------------");
495 if (!mDNSStorage.Hostnames) LogToFD(fd, "<None>");
496 else
497 {
498 HostnameInfo *hi;
499 for (hi = mDNSStorage.Hostnames; hi; hi = hi->next)
500 {
501 LogToFD(fd, "%##s v4 %d %s", hi->fqdn.c, hi->arv4.state, ARDisplayString(&mDNSStorage, &hi->arv4));
502 LogToFD(fd, "%##s v6 %d %s", hi->fqdn.c, hi->arv6.state, ARDisplayString(&mDNSStorage, &hi->arv6));
503 }
504 }
505
506 LogToFD(fd, "--------------- FQDN ---------------");
507 if (!mDNSStorage.FQDN.c[0]) LogToFD(fd, "<None>");
508 else
509 {
510 LogToFD(fd, "%##s", mDNSStorage.FQDN.c);
511 }
512
513 #if MDNSRESPONDER_SUPPORTS(APPLE, METRICS)
514 LogMetricsToFD(fd);
515 #endif
516
517 // getLocalTimestamp(timestamp, sizeof(timestamp));
518 LogToFD(fd, "Date: %s", timestamp);
519 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "---- END STATE LOG ---- (" PUB_S ")", timestamp);
520 LogToFD(fd, "---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
521 }
522
523 mDNSexport void INFOCallback(void)
524 {
525 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING,
526 "Sending SIGINFO to mDNSResponder daemon is deprecated. To trigger state dump, please use 'dns-sd -O', enter 'dns-sd -h' for more information");
527 }
528
529 // Writes the state out to the dynamic store and also affects the ASL filter level
530 mDNSexport void UpdateDebugState()
531 {
532 mDNSu32 one = 1;
533 mDNSu32 zero = 0;
534
535 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
536 if (!dict)
537 {
538 LogMsg("UpdateDebugState: Could not create dict");
539 return;
540 }
541
542 CFNumberRef numOne = CFNumberCreate(NULL, kCFNumberSInt32Type, &one);
543 if (numOne == NULL)
544 {
545 LogMsg("UpdateDebugState: Could not create CFNumber one");
546 return;
547 }
548 CFNumberRef numZero = CFNumberCreate(NULL, kCFNumberSInt32Type, &zero);
549 if (numZero == NULL)
550 {
551 LogMsg("UpdateDebugState: Could not create CFNumber zero");
552 CFRelease(numOne);
553 return;
554 }
555
556 if (mDNS_LoggingEnabled)
557 CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numOne);
558 else
559 CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numZero);
560
561 if (mDNS_PacketLoggingEnabled)
562 CFDictionarySetValue(dict, CFSTR("PacketLogging"), numOne);
563 else
564 CFDictionarySetValue(dict, CFSTR("PacketLogging"), numZero);
565
566 if (mDNS_McastLoggingEnabled)
567 CFDictionarySetValue(dict, CFSTR("McastLogging"), numOne);
568 else
569 CFDictionarySetValue(dict, CFSTR("McastLogging"), numZero);
570
571 if (mDNS_McastTracingEnabled)
572 CFDictionarySetValue(dict, CFSTR("McastTracing"), numOne);
573 else
574 CFDictionarySetValue(dict, CFSTR("McastTracing"), numZero);
575
576 CFRelease(numOne);
577 CFRelease(numZero);
578 mDNSDynamicStoreSetConfig(kmDNSDebugState, mDNSNULL, dict);
579 CFRelease(dict);
580
581 }
582
583
584 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
585
586 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
587 {
588 (void)port; // Unused
589 (void)size; // Unused
590 (void)info; // Unused
591 mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
592 mDNS *const m = &mDNSStorage;
593
594 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
595 KQueueLock();
596 switch(msg_header->msgh_id)
597 {
598 case SIGHUP: {
599 mDNSu32 slot;
600 CacheGroup *cg;
601 CacheRecord *rr;
602 LogMsg("SIGHUP: Purge cache");
603 mDNS_Lock(m);
604 FORALL_CACHERECORDS(slot, cg, rr)
605 {
606 mDNS_PurgeCacheResourceRecord(m, rr);
607 }
608 // Restart unicast and multicast queries
609 mDNSCoreRestartQueries(m);
610 mDNS_Unlock(m);
611 } break;
612 case SIGINT:
613 case SIGTERM: ExitCallback(msg_header->msgh_id); break;
614 case SIGINFO: INFOCallback(); break;
615 case SIGUSR1:
616 #if APPLE_OSX_mDNSResponder
617 mDNS_LoggingEnabled = 1;
618 LogMsg("SIGUSR1: Logging %s on Apple Platforms", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
619 #else
620 mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
621 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
622 #endif
623 WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
624 UpdateDebugState();
625 LogInfo("USR1 Logging Enabled");
626 break;
627 case SIGUSR2:
628 #if APPLE_OSX_mDNSResponder
629 mDNS_PacketLoggingEnabled = 1;
630 LogMsg("SIGUSR2: Packet Logging %s on Apple Platforms", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
631 #else
632 mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
633 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
634 #endif
635 mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse;
636 LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled");
637 UpdateDebugState();
638 break;
639 case SIGPROF: mDNS_McastLoggingEnabled = mDNS_McastLoggingEnabled ? mDNSfalse : mDNStrue;
640 LogMsg("SIGPROF: Multicast Logging %s", mDNS_McastLoggingEnabled ? "Enabled" : "Disabled");
641 LogMcastStateInfo(mDNSfalse, mDNStrue, mDNStrue);
642 mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse;
643 LogMsg("SIGPROF: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled");
644 UpdateDebugState();
645 break;
646 case SIGTSTP: mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = mDNS_McastLoggingEnabled = mDNS_McastTracingEnabled = mDNSfalse;
647 LogMsg("All mDNSResponder Debug Logging/Tracing Disabled (USR1/USR2/PROF)");
648 UpdateDebugState();
649 break;
650
651 default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
652 }
653 KQueueUnlock("Unix Signal");
654 }
655
656 // MachServerName is com.apple.mDNSResponder (Supported only till 10.9.x)
657 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
658 {
659 mStatus err;
660
661 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
662 rrcachestorage, RR_CACHE_SIZE,
663 !NoMulticastAdvertisements,
664 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
665
666 if (err)
667 {
668 LogMsg("Daemon start: mDNS_Init failed %d", err);
669 return(err);
670 }
671
672 #if MDNSRESPONDER_SUPPORTS(APPLE, PREALLOCATED_CACHE)
673 if (PreallocateCacheMemory)
674 {
675 const int growCount = (kRRCacheMemoryLimit + kRRCacheGrowSize - 1) / kRRCacheGrowSize;
676 int i;
677
678 for (i = 0; i < growCount; ++i)
679 {
680 mDNS_StatusCallback(&mDNSStorage, mStatus_GrowCache);
681 }
682 }
683 #endif
684
685 CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL);
686 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
687 signal_port = CFMachPortGetPort(i_port);
688 CFRunLoopAddSource(CFRunLoopGetMain(), i_rls, kCFRunLoopDefaultMode);
689 CFRelease(i_rls);
690
691 return(err);
692 }
693
694 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
695
696 // SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
697 // The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
698 // The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
699 // of code from above. Alternatively we could remove the duplicated source code by having
700 // single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM"
701
702 mDNSlocal void SignalDispatch(dispatch_source_t source)
703 {
704 int sig = (int)dispatch_source_get_handle(source);
705 mDNS *const m = &mDNSStorage;
706 KQueueLock();
707 switch(sig)
708 {
709 case SIGHUP: {
710 mDNSu32 slot;
711 CacheGroup *cg;
712 CacheRecord *rr;
713 LogMsg("SIGHUP: Purge cache");
714 mDNS_Lock(m);
715 FORALL_CACHERECORDS(slot, cg, rr)
716 {
717 mDNS_PurgeCacheResourceRecord(m, rr);
718 }
719 // Restart unicast and multicast queries
720 mDNSCoreRestartQueries(m);
721 mDNS_Unlock(m);
722 } break;
723 case SIGINT:
724 case SIGTERM: ExitCallback(sig); break;
725 case SIGINFO: INFOCallback(); break;
726 case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
727 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
728 WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
729 UpdateDebugState();
730 break;
731 case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
732 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
733 UpdateDebugState();
734 break;
735 default: LogMsg("SignalCallback: Unknown signal %d", sig); break;
736 }
737 KQueueUnlock("Unix Signal");
738 }
739
740 mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig)
741 {
742 signal(sig, SIG_IGN);
743 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, queue);
744
745 if (source)
746 {
747 dispatch_source_set_event_handler(source, ^{SignalDispatch(source);});
748 // Start processing signals
749 dispatch_resume(source);
750 }
751 else
752 {
753 LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig);
754 }
755 }
756
757 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
758 {
759 mStatus err;
760 dispatch_queue_t queue = dispatch_get_main_queue();
761
762 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
763 rrcachestorage, RR_CACHE_SIZE,
764 !NoMulticastAdvertisements,
765 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
766
767 if (err)
768 {
769 LogMsg("Daemon start: mDNS_Init failed %d", err);
770 return(err);
771 }
772
773 mDNSSetupSignal(queue, SIGHUP);
774 mDNSSetupSignal(queue, SIGINT);
775 mDNSSetupSignal(queue, SIGTERM);
776 mDNSSetupSignal(queue, SIGINFO);
777 mDNSSetupSignal(queue, SIGUSR1);
778 mDNSSetupSignal(queue, SIGUSR2);
779
780 // Create a custom handler for doing the housekeeping work. This is either triggered
781 // by the timer or an event source
782 PlatformStorage.custom = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
783 if (PlatformStorage.custom == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;}
784 dispatch_source_set_event_handler(PlatformStorage.custom, ^{PrepareForIdle(&mDNSStorage);});
785 dispatch_resume(PlatformStorage.custom);
786
787 // Create a timer source to trigger housekeeping work. The houskeeping work itself
788 // is done in the custom handler that we set below.
789
790 PlatformStorage.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
791 if (PlatformStorage.timer == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;}
792
793 // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we
794 // always reset the time to the new time computed. In effect, we ignore the interval
795 dispatch_source_set_timer(PlatformStorage.timer, DISPATCH_TIME_NOW, 1000ull * 1000000000, 0);
796 dispatch_source_set_event_handler(PlatformStorage.timer, ^{
797 dispatch_source_merge_data(PlatformStorage.custom, 1);
798 });
799 dispatch_resume(PlatformStorage.timer);
800
801 LogMsg("DaemonIntialize done successfully");
802
803 return(err);
804 }
805
806 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
807
808 mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
809 {
810 mDNSs32 now = mDNS_TimeNow(m);
811
812 // 1. If we need to set domain secrets, do so before handling the network change
813 // Detailed reason:
814 // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list,
815 // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens.
816 if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0)
817 {
818 m->p->KeyChainTimer = 0;
819 mDNS_Lock(m);
820 SetDomainSecrets(m);
821 mDNS_Unlock(m);
822 }
823
824 // 2. If we have network change events to handle, do them before calling mDNS_Execute()
825 // Detailed reason:
826 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
827 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
828 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
829 // we then systematically lose our own looped-back packets.
830 if (m->NetworkChanged && now - m->NetworkChanged >= 0) mDNSMacOSXNetworkChanged();
831
832 if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0)
833 {
834 m->p->RequestReSleep = 0;
835 mDNSPowerRequest(0, 0);
836 }
837
838 // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do
839 mDNSs32 nextevent = mDNS_Execute(m);
840
841 if (m->NetworkChanged)
842 if (nextevent - m->NetworkChanged > 0)
843 nextevent = m->NetworkChanged;
844
845 if (m->p->KeyChainTimer)
846 if (nextevent - m->p->KeyChainTimer > 0)
847 nextevent = m->p->KeyChainTimer;
848
849 if (m->p->RequestReSleep)
850 if (nextevent - m->p->RequestReSleep > 0)
851 nextevent = m->p->RequestReSleep;
852
853
854 if (m->p->NotifyUser)
855 {
856 if (m->p->NotifyUser - now < 0)
857 {
858 if (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c))
859 {
860 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c);
861 mDNSPreferencesSetNames(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
862 m->p->usernicelabel = m->nicelabel;
863 }
864 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
865 {
866 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
867 mDNSPreferencesSetNames(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
868 m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful
869 m->p->userhostlabel = m->hostlabel;
870 }
871 m->p->NotifyUser = 0;
872 }
873 else
874 if (nextevent - m->p->NotifyUser > 0)
875 nextevent = m->p->NotifyUser;
876 }
877
878 return(nextevent);
879 }
880
881 // Right now we consider *ALL* of our DHCP leases
882 // It might make sense to be a bit more selective and only consider the leases on interfaces
883 // (a) that are capable and enabled for wake-on-LAN, and
884 // (b) where we have found (and successfully registered with) a Sleep Proxy
885 // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
886 mDNSlocal mDNSu32 DHCPWakeTime(void)
887 {
888 mDNSu32 e = 24 * 3600; // Maximum maintenance wake interval is 24 hours
889 const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
890 if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
891 else
892 {
893 CFIndex ic, j;
894
895 const void *pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetDHCP);
896 if (!pattern)
897 {
898 LogMsg("DHCPWakeTime: SCDynamicStoreKeyCreateNetworkServiceEntity failed\n");
899 return e;
900 }
901 CFArrayRef dhcpinfo = CFArrayCreate(NULL, (const void **)&pattern, 1, &kCFTypeArrayCallBacks);
902 CFRelease(pattern);
903 if (dhcpinfo)
904 {
905 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("DHCP-LEASES"), NULL, NULL);
906 if (store)
907 {
908 CFDictionaryRef dict = SCDynamicStoreCopyMultiple(store, NULL, dhcpinfo);
909 if (dict)
910 {
911 ic = CFDictionaryGetCount(dict);
912 const void *vals[ic];
913 CFDictionaryGetKeysAndValues(dict, NULL, vals);
914
915 for (j = 0; j < ic; j++)
916 {
917 const CFDictionaryRef dhcp = (CFDictionaryRef)vals[j];
918 if (dhcp)
919 {
920 const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp);
921 const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51); // Option 51 = IP Address Lease Time
922 if (!start || !lease || CFDataGetLength(lease) < 4)
923 LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
924 "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
925 j, start, lease, lease ? CFDataGetLength(lease) : 0);
926 else
927 {
928 const UInt8 *d = CFDataGetBytePtr(lease);
929 if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %ld failed", (long)j);
930 else
931 {
932 const mDNSu32 elapsed = now - CFDateGetAbsoluteTime(start);
933 const mDNSu32 lifetime = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]);
934 const mDNSu32 remaining = lifetime - elapsed;
935 const mDNSu32 wake = remaining > 60 ? remaining - remaining/10 : 54; // Wake at 90% of the lease time
936 LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake);
937 if (e > wake) e = wake;
938 }
939 }
940 }
941 }
942 CFRelease(dict);
943 }
944 CFRelease(store);
945 }
946 CFRelease(dhcpinfo);
947 }
948 }
949 return(e);
950 }
951
952 // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
953 // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
954 // If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
955 // for a few seconds and then waking again is silly and annoying.
956 // If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
957 // Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
958 // allowing us an adequate safety margin to renew our lease before we lose it.
959
960 mDNSlocal mDNSBool AllowSleepNow(mDNSs32 now)
961 {
962 mDNS *const m = &mDNSStorage;
963 mDNSBool ready = mDNSCoreReadyForSleep(m, now);
964 if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse);
965
966 m->p->WakeAtUTC = 0;
967 int result = kIOReturnSuccess;
968 CFDictionaryRef opts = NULL;
969
970 // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
971 // do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
972 if (!m->SleepState)
973 LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now);
974 else
975 {
976 if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m))
977 LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
978 m->SystemWakeOnLANEnabled ? "is" : "not",
979 mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no");
980 else
981 {
982 mDNSs32 dhcp = DHCPWakeTime();
983 LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp);
984 mDNSNextWakeReason reason = mDNSNextWakeReason_Null;
985 mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now, &reason) / mDNSPlatformOneSecond;
986 if (interval > dhcp)
987 {
988 interval = dhcp;
989 reason = mDNSNextWakeReason_DHCPLeaseRenewal;
990 }
991 // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
992 // transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
993 // a single SPS failure could result in a remote machine falling permanently asleep, requiring
994 // someone to go to the machine in person to wake it up again, which would be unacceptable.
995 if (!ready && interval > 3600)
996 {
997 interval = 3600;
998 reason = mDNSNextWakeReason_SleepProxyRegistrationRetry;
999 }
1000 //interval = 48; // For testing
1001
1002 #if TARGET_OS_OSX && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
1003 if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that
1004 {
1005 CFStringRef reasonStr;
1006 switch (reason)
1007 {
1008 case mDNSNextWakeReason_NATPortMappingRenewal:
1009 reasonStr = CFSTR("NAT port mapping renewal");
1010 break;
1011
1012 case mDNSNextWakeReason_RecordRegistrationRenewal:
1013 reasonStr = CFSTR("record registration renewal");
1014 break;
1015
1016 case mDNSNextWakeReason_UpkeepWake:
1017 reasonStr = CFSTR("upkeep wake");
1018 break;
1019
1020 case mDNSNextWakeReason_DHCPLeaseRenewal:
1021 reasonStr = CFSTR("DHCP lease renewal");
1022 break;
1023
1024 case mDNSNextWakeReason_SleepProxyRegistrationRetry:
1025 reasonStr = CFSTR("sleep proxy registration retry");
1026 break;
1027
1028 case mDNSNextWakeReason_Null:
1029 default:
1030 reasonStr = CFSTR("unspecified");
1031 break;
1032 }
1033 const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval);
1034 if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed");
1035 else
1036 {
1037 const mDNSs32 reqs = kIOPMSystemPowerStateCapabilityNetwork;
1038 const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs);
1039 if (Requirements == NULL) LogMsg("ScheduleNextWake: CFNumberCreate failed");
1040 else
1041 {
1042 const void *OptionKeys[3] = { kIOPMAckDHCPRenewWakeDate, kIOPMAckSystemCapabilityRequirements, kIOPMAckClientInfoKey };
1043 const void *OptionVals[3] = { WakeDate, Requirements, reasonStr };
1044 opts = CFDictionaryCreate(NULL, OptionKeys, OptionVals, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1045 if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
1046 CFRelease(Requirements);
1047 }
1048 CFRelease(WakeDate);
1049 }
1050 LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval);
1051 }
1052 else // else schedule the wakeup using the old API instead to
1053 #endif
1054 {
1055 // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
1056 // so we should put it back to sleep. To avoid frustrating the user, we always request at least
1057 // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
1058 // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
1059 if (interval < 60)
1060 interval = 60;
1061
1062 result = mDNSPowerRequest(1, interval);
1063
1064 if (result == kIOReturnNotReady)
1065 {
1066 int r;
1067 LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
1068 // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
1069 // requested wake time is "too soon", but there's no API to find out what constitutes
1070 // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
1071 // we just have to iterate with successively longer intervals until it doesn't fail.
1072 // We preserve the value of "result" because if our original power request was deemed "too soon"
1073 // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request,
1074 // since the implication is that the system won't manage to be awake again at the time we need it.
1075 do
1076 {
1077 interval += (interval < 20) ? 1 : ((interval+3) / 4);
1078 r = mDNSPowerRequest(1, interval);
1079 }
1080 while (r == kIOReturnNotReady);
1081 if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r);
1082 else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval);
1083 }
1084 else
1085 {
1086 if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
1087 else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
1088 }
1089 m->p->WakeAtUTC = mDNSPlatformUTC() + interval;
1090 }
1091 }
1092
1093 m->SleepState = SleepState_Sleeping;
1094 // Clear our interface list to empty state, ready to go to sleep
1095 // As a side effect of doing this, we'll also cancel any outstanding SPS Resolve calls that didn't complete
1096 mDNSMacOSXNetworkChanged();
1097 }
1098
1099 LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
1100 #if TARGET_OS_OSX && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
1101 (m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" :
1102 #endif
1103 (result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange",
1104 m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now);
1105
1106 m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above
1107 m->TimeSlept = mDNSPlatformUTC();
1108
1109 #if TARGET_OS_OSX && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
1110 if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, (IOPMConnectionMessageToken)m->p->SleepCookie, opts);
1111 else
1112 #endif
1113 if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie);
1114 else IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie);
1115
1116 if (opts) CFRelease(opts);
1117 return(mDNStrue);
1118 }
1119
1120 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1121
1122 mDNSexport void TriggerEventCompletion()
1123 {
1124 debugf("TriggerEventCompletion: Merge data");
1125 dispatch_source_merge_data(PlatformStorage.custom, 1);
1126 }
1127
1128 mDNSlocal void PrepareForIdle(void *m_param)
1129 {
1130 mDNS *m = m_param;
1131 int64_t time_offset;
1132 dispatch_time_t dtime;
1133
1134 const int multiplier = 1000000000 / mDNSPlatformOneSecond;
1135
1136 // This is the main work loop:
1137 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
1138 // (2) Then we make sure we've delivered all waiting browse messages to our clients
1139 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
1140
1141 debugf("PrepareForIdle: called");
1142 // Run mDNS_Execute to find out the time we next need to wake up
1143 mDNSs32 start = mDNSPlatformRawTime();
1144 mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
1145 #if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH)
1146 if (m->DNSPushServers != mDNSNULL)
1147 {
1148 nextTimerEvent = dso_idle(m, nextTimerEvent);
1149 }
1150 #endif
1151 mDNSs32 end = mDNSPlatformRawTime();
1152 if (end - start >= WatchDogReportingThreshold)
1153 {
1154 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING,
1155 "CustomSourceHandler: WARNING: Idle task took %d ms to complete", end - start);
1156 }
1157
1158 mDNSs32 now = mDNS_TimeNow(m);
1159
1160 if (m->ShutdownTime)
1161 {
1162 if (mDNSStorage.ResourceRecords)
1163 {
1164 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords));
1165 if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
1166 }
1167 if (mDNS_ExitNow(m, now))
1168 {
1169 LogInfo("IdleLoop: mDNS_FinalExit");
1170 mDNS_FinalExit(&mDNSStorage);
1171 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
1172 exit(0);
1173 }
1174 if (nextTimerEvent - m->ShutdownTime >= 0)
1175 nextTimerEvent = m->ShutdownTime;
1176 }
1177
1178 if (m->SleepLimit)
1179 if (!AllowSleepNow(now))
1180 if (nextTimerEvent - m->SleepLimit >= 0)
1181 nextTimerEvent = m->SleepLimit;
1182
1183 // Convert absolute wakeup time to a relative time from now
1184 mDNSs32 ticks = nextTimerEvent - now;
1185 if (ticks < 1) ticks = 1;
1186
1187 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
1188 if (ticks > 1)
1189 RepeatedBusy = 0;
1190 else
1191 {
1192 ticks = 1;
1193 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
1194 }
1195
1196 time_offset = ((mDNSu32)ticks / mDNSPlatformOneSecond) * 1000000000 + (ticks % mDNSPlatformOneSecond) * multiplier;
1197 dtime = dispatch_time(DISPATCH_TIME_NOW, time_offset);
1198 dispatch_source_set_timer(PlatformStorage.timer, dtime, 1000ull*1000000000, 0);
1199 debugf("PrepareForIdle: scheduling timer with ticks %d", ticks);
1200 return;
1201 }
1202
1203 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1204
1205 mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context, __unused mDNSBool encounteredEOF)
1206 {
1207 // Read all of the bytes so we won't wake again.
1208 char buffer[100];
1209 while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue;
1210 }
1211
1212 mDNSlocal void SetLowWater(const KQSocketSet *const k, const int r)
1213 {
1214 if (k->sktv4 >=0 && setsockopt(k->sktv4, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0)
1215 LogMsg("SO_RCVLOWAT IPv4 %d error %d errno %d (%s)", k->sktv4, r, errno, strerror(errno));
1216 if (k->sktv6 >=0 && setsockopt(k->sktv6, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0)
1217 LogMsg("SO_RCVLOWAT IPv6 %d error %d errno %d (%s)", k->sktv6, r, errno, strerror(errno));
1218 }
1219
1220 mDNSlocal void * KQueueLoop(void *m_param)
1221 {
1222 mDNS *m = m_param;
1223 int numevents = 0;
1224
1225 #if USE_SELECT_WITH_KQUEUEFD
1226 fd_set readfds;
1227 FD_ZERO(&readfds);
1228 const int multiplier = 1000000 / mDNSPlatformOneSecond;
1229 #else
1230 const int multiplier = 1000000000 / mDNSPlatformOneSecond;
1231 #endif
1232
1233 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSD_XPC_SERVICE)
1234 dnssd_server_init();
1235 #endif
1236 pthread_mutex_lock(&PlatformStorage.BigMutex);
1237 LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last);
1238
1239 // This is the main work loop:
1240 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
1241 // (2) Then we make sure we've delivered all waiting browse messages to our clients
1242 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
1243 // (4) On wakeup we first process *all* events
1244 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
1245 for ( ; ; )
1246 {
1247 #define kEventsToReadAtOnce 1
1248 struct kevent new_events[kEventsToReadAtOnce];
1249
1250 // Run mDNS_Execute to find out the time we next need to wake up
1251 mDNSs32 start = mDNSPlatformRawTime();
1252 mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
1253 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSD_XPC_SERVICE)
1254 dnssd_server_idle();
1255 #endif
1256 #if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH)
1257 if (m->DNSPushServers != mDNSNULL)
1258 {
1259 mDNS_Lock(m);
1260 nextTimerEvent = dso_idle(m, m->timenow, nextTimerEvent);
1261 mDNS_Unlock(m);
1262 }
1263 #endif
1264 mDNSs32 end = mDNSPlatformRawTime();
1265 if (end - start >= WatchDogReportingThreshold)
1266 {
1267 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "WARNING: Idle task took %d ms to complete", end - start);
1268 }
1269
1270 #if MDNS_MALLOC_DEBUGGING >= 1
1271 mDNSPlatformValidateLists();
1272 #endif
1273
1274 mDNSs32 now = mDNS_TimeNow(m);
1275
1276 if (m->ShutdownTime)
1277 {
1278 if (mDNSStorage.ResourceRecords)
1279 {
1280 AuthRecord *rr;
1281 for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
1282 {
1283 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, rr));
1284 if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
1285 }
1286 }
1287 if (mDNS_ExitNow(m, now))
1288 {
1289 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_FinalExit");
1290 mDNS_FinalExit(&mDNSStorage);
1291 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
1292 exit(0);
1293 }
1294 if (nextTimerEvent - m->ShutdownTime >= 0)
1295 nextTimerEvent = m->ShutdownTime;
1296 }
1297
1298 if (m->SleepLimit)
1299 if (!AllowSleepNow(now))
1300 if (nextTimerEvent - m->SleepLimit >= 0)
1301 nextTimerEvent = m->SleepLimit;
1302
1303 // Convert absolute wakeup time to a relative time from now
1304 mDNSs32 ticks = nextTimerEvent - now;
1305 if (ticks < 1) ticks = 1;
1306
1307 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
1308 if (ticks > 1)
1309 RepeatedBusy = 0;
1310 else
1311 {
1312 ticks = 1;
1313 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
1314 }
1315
1316 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks);
1317 numevents = 0;
1318
1319 // Release the lock, and sleep until:
1320 // 1. Something interesting happens like a packet arriving, or
1321 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
1322 // 3. The timeout expires
1323 pthread_mutex_unlock(&PlatformStorage.BigMutex);
1324
1325 // If we woke up to receive a multicast, set low-water mark to dampen excessive wakeup rate
1326 if (m->p->num_mcasts)
1327 {
1328 SetLowWater(&m->p->permanentsockets, 0x10000);
1329 if (ticks > mDNSPlatformOneSecond / 8) ticks = mDNSPlatformOneSecond / 8;
1330 }
1331
1332 #if USE_SELECT_WITH_KQUEUEFD
1333 struct timeval timeout;
1334 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
1335 timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier;
1336 FD_SET(KQueueFD, &readfds);
1337 if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0)
1338 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
1339 #else
1340 struct timespec timeout;
1341 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
1342 timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier;
1343 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
1344 // and have it work similarly to the way it does with nevents non-zero --
1345 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
1346 // In fact, what happens if you do this is that it just returns immediately. So, we have
1347 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
1348 if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0)
1349 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
1350 #endif
1351
1352 pthread_mutex_lock(&PlatformStorage.BigMutex);
1353 // We have to ignore the event we may have been told about above, because that
1354 // was done without holding the lock, and between the time we woke up and the
1355 // time we reclaimed the lock the other thread could have done something that
1356 // makes the event no longer valid. Now we have the lock, we call kevent again
1357 // and this time we can safely process the events it tells us about.
1358
1359 // If we changed UDP socket low-water mark, restore it, so we will be told about every packet
1360 if (m->p->num_mcasts)
1361 {
1362 SetLowWater(&m->p->permanentsockets, 1);
1363 m->p->num_mcasts = 0;
1364 }
1365
1366 static const struct timespec zero_timeout = { 0, 0 };
1367 int events_found;
1368 while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0)
1369 {
1370 if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR))
1371 {
1372 const int kevent_errno = errno;
1373 // Not sure what to do here, our kqueue has failed us - this isn't ideal
1374 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", kevent_errno, strerror(kevent_errno));
1375 exit(kevent_errno);
1376 }
1377
1378 numevents += events_found;
1379
1380 int i;
1381 for (i = 0; i < events_found; i++)
1382 {
1383 const KQueueEntry *const kqentry = new_events[i].udata;
1384 mDNSs32 stime = mDNSPlatformRawTime();
1385 const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task
1386 kqentry->KQcallback((int)new_events[i].ident, new_events[i].filter, kqentry->KQcontext, (new_events[i].flags & EV_EOF) != 0);
1387 mDNSs32 etime = mDNSPlatformRawTime();
1388 if (etime - stime >= WatchDogReportingThreshold)
1389 {
1390 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING,
1391 "WARNING: " PUB_S " took %d ms to complete", KQtask, etime - stime);
1392 }
1393 }
1394 }
1395 }
1396
1397 return NULL;
1398 }
1399
1400 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1401
1402 mDNSlocal size_t LaunchdCheckin(void)
1403 {
1404 // Ask launchd for our socket
1405 int result = launch_activate_socket("Listeners", &launchd_fds, &launchd_fds_count);
1406 if (result != 0) { LogMsg("launch_activate_socket() failed error %d (%s)", result, strerror(result)); }
1407 return launchd_fds_count;
1408 }
1409
1410
1411 extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));
1412
1413 #if APPLE_OSX_mDNSResponder
1414 mDNSlocal mDNSBool PreferencesGetValueBool(CFStringRef key, mDNSBool defaultValue)
1415 {
1416 CFBooleanRef boolean;
1417 mDNSBool result = defaultValue;
1418
1419 boolean = CFPreferencesCopyAppValue(key, kProgramArguments);
1420 if (boolean != NULL)
1421 {
1422 if (CFGetTypeID(boolean) == CFBooleanGetTypeID())
1423 result = CFBooleanGetValue(boolean) ? mDNStrue : mDNSfalse;
1424 CFRelease(boolean);
1425 }
1426
1427 return result;
1428 }
1429
1430 mDNSlocal int PreferencesGetValueInt(CFStringRef key, int defaultValue)
1431 {
1432 CFNumberRef number;
1433 int numberValue;
1434 int result = defaultValue;
1435
1436 number = CFPreferencesCopyAppValue(key, kProgramArguments);
1437 if (number != NULL)
1438 {
1439 if ((CFGetTypeID(number) == CFNumberGetTypeID()) && CFNumberGetValue(number, kCFNumberIntType, &numberValue))
1440 result = numberValue;
1441 CFRelease(number);
1442 }
1443
1444 return result;
1445 }
1446 #endif
1447
1448 mDNSlocal void SandboxProcess(void)
1449 {
1450 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
1451 #if MDNS_NO_SANDBOX
1452 LogMsg("Note: Compiled without Apple Sandbox support");
1453 #else // MDNS_NO_SANDBOX
1454 if (!sandbox_init)
1455 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
1456 else
1457 {
1458 char *sandbox_msg;
1459 uint64_t sandbox_flags = SANDBOX_NAMED;
1460
1461 (void)confstr(_CS_DARWIN_USER_CACHE_DIR, NULL, 0);
1462
1463 int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg);
1464 if (sandbox_err)
1465 {
1466 LogMsg("WARNING: sandbox_init error %s", sandbox_msg);
1467 // If we have errors in the sandbox during development, to prevent
1468 // exiting, uncomment the following line.
1469 //sandbox_free_error(sandbox_msg);
1470
1471 errx(EX_OSERR, "sandbox_init() failed: %s", sandbox_msg);
1472 }
1473 else LogInfo("Now running under Apple Sandbox restrictions");
1474 }
1475 #endif // MDNS_NO_SANDBOX
1476 }
1477
1478 #if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
1479 #define MDNS_OS_LOG_CATEGORY_INIT(NAME) \
1480 do\
1481 { \
1482 mDNSLogCategory_ ## NAME = os_log_create("com.apple.mDNSResponder", # NAME ); \
1483 if (!mDNSLogCategory_ ## NAME ) \
1484 { \
1485 os_log_error(OS_LOG_DEFAULT, "Could NOT create the " # NAME " log handle in mDNSResponder"); \
1486 mDNSLogCategory_ ## NAME = OS_LOG_DEFAULT; \
1487 } \
1488 } \
1489 while (0)
1490
1491 os_log_t mDNSLogCategory_Default = NULL;
1492 os_log_t mDNSLogCategory_mDNS = NULL;
1493 os_log_t mDNSLogCategory_uDNS = NULL;
1494 os_log_t mDNSLogCategory_SPS = NULL;
1495 os_log_t mDNSLogCategory_XPC = NULL;
1496 os_log_t mDNSLogCategory_Analytics = NULL;
1497 os_log_t mDNSLogCategory_DNSSEC = NULL;
1498
1499 mDNSlocal void init_logging(void)
1500 {
1501 MDNS_OS_LOG_CATEGORY_INIT(Default);
1502 MDNS_OS_LOG_CATEGORY_INIT(mDNS);
1503 MDNS_OS_LOG_CATEGORY_INIT(uDNS);
1504 MDNS_OS_LOG_CATEGORY_INIT(SPS);
1505 MDNS_OS_LOG_CATEGORY_INIT(XPC);
1506 MDNS_OS_LOG_CATEGORY_INIT(Analytics);
1507 MDNS_OS_LOG_CATEGORY_INIT(DNSSEC);
1508 }
1509 #endif
1510
1511 mDNSexport int main(int argc, char **argv)
1512 {
1513 int i;
1514 kern_return_t status;
1515
1516 #if DEBUG
1517 bool useDebugSocket = mDNSfalse;
1518 bool useSandbox = mDNStrue;
1519 #endif
1520
1521 #if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
1522 init_logging();
1523 #endif
1524
1525 mDNSMacOSXSystemBuildNumber(NULL);
1526 LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
1527
1528 #if 0
1529 LogMsg("CacheRecord %5d", sizeof(CacheRecord));
1530 LogMsg("CacheGroup %5d", sizeof(CacheGroup));
1531 LogMsg("ResourceRecord %5d", sizeof(ResourceRecord));
1532 LogMsg("RData_small %5d", sizeof(RData_small));
1533
1534 LogMsg("sizeof(CacheEntity) %5d", sizeof(CacheEntity));
1535 LogMsg("RR_CACHE_SIZE %5d", RR_CACHE_SIZE);
1536 LogMsg("block bytes used %5d", sizeof(CacheEntity) * RR_CACHE_SIZE);
1537 LogMsg("block bytes wasted %5d", 32*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE);
1538 #endif
1539
1540 #if !DEBUG
1541 if (0 == geteuid())
1542 {
1543 LogMsg("mDNSResponder cannot be run as root !! Exiting..");
1544 return -1;
1545 }
1546 #endif // !DEBUG
1547
1548 for (i=1; i<argc; i++)
1549 {
1550 if (!strcasecmp(argv[i], "-d" )) mDNS_DebugMode = mDNStrue;
1551 if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) NoMulticastAdvertisements = mDNStrue;
1552 if (!strcasecmp(argv[i], "-DisableSleepProxyClient" )) DisableSleepProxyClient = mDNStrue;
1553 if (!strcasecmp(argv[i], "-DebugLogging" )) mDNS_LoggingEnabled = mDNStrue;
1554 if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue;
1555 if (!strcasecmp(argv[i], "-OfferSleepProxyService" ))
1556 OfferSleepProxyService = (i+1 < argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 100;
1557 if (!strcasecmp(argv[i], "-UseInternalSleepProxy" ))
1558 UseInternalSleepProxy = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && argv[i+1][1]==0) ? atoi(argv[++i]) : 1;
1559 if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue;
1560 if (!strcasecmp(argv[i], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains = mDNStrue;
1561 if (!strcasecmp(argv[i], "-DisableAllowExpired" )) EnableAllowExpired = mDNSfalse;
1562 #if DEBUG
1563 if (!strcasecmp(argv[i], "-UseDebugSocket")) useDebugSocket = mDNStrue;
1564 if (!strcasecmp(argv[i], "-NoSandbox")) useSandbox = mDNSfalse;
1565 #endif
1566 }
1567
1568
1569 #if APPLE_OSX_mDNSResponder
1570 /* Reads the external user's program arguments for mDNSResponder starting 10.11.x(El Capitan) on OSX. The options for external user are:
1571 DebugLogging, UnicastPacketLogging, NoMulticastAdvertisements, StrictUnicastOrdering and AlwaysAppendSearchDomains
1572
1573 To turn ON the particular option, here is what the user should do (as an example of setting two options)
1574 1] sudo defaults write /Library/Preferences/com.apple.mDNSResponder.plist AlwaysAppendSearchDomains -bool YES
1575 2] sudo defaults write /Library/Preferences/com.apple.mDNSResponder.plist NoMulticastAdvertisements -bool YES
1576 3] sudo reboot
1577
1578 To turn OFF all options, here is what the user should do
1579 1] sudo defaults delete /Library/Preferences/com.apple.mDNSResponder.plist
1580 2] sudo reboot
1581
1582 To view the current options set, here is what the user should do
1583 1] plutil -p /Library/Preferences/com.apple.mDNSResponder.plist
1584 OR
1585 1] sudo defaults read /Library/Preferences/com.apple.mDNSResponder.plist
1586
1587 */
1588
1589 // Currently on Fuji/Whitetail releases we are keeping the logging always enabled.
1590 // Hence mDNS_LoggingEnabled and mDNS_PacketLoggingEnabled is set to true below by default.
1591 #if 0
1592 mDNS_LoggingEnabled = PreferencesGetValueBool(kPreferencesKey_DebugLogging, mDNS_LoggingEnabled);
1593 mDNS_PacketLoggingEnabled = PreferencesGetValueBool(kPreferencesKey_UnicastPacketLogging, mDNS_PacketLoggingEnabled);
1594 #endif
1595
1596 mDNS_LoggingEnabled = mDNStrue;
1597 mDNS_PacketLoggingEnabled = mDNStrue;
1598
1599 NoMulticastAdvertisements = PreferencesGetValueBool(kPreferencesKey_NoMulticastAdvertisements, NoMulticastAdvertisements);
1600 StrictUnicastOrdering = PreferencesGetValueBool(kPreferencesKey_StrictUnicastOrdering, StrictUnicastOrdering);
1601 AlwaysAppendSearchDomains = PreferencesGetValueBool(kPreferencesKey_AlwaysAppendSearchDomains, AlwaysAppendSearchDomains);
1602 EnableAllowExpired = PreferencesGetValueBool(kPreferencesKey_EnableAllowExpired, EnableAllowExpired);
1603 OfferSleepProxyService = PreferencesGetValueInt(kPreferencesKey_OfferSleepProxyService, OfferSleepProxyService);
1604 UseInternalSleepProxy = PreferencesGetValueInt(kPreferencesKey_UseInternalSleepProxy, UseInternalSleepProxy);
1605
1606 #if ENABLE_BLE_TRIGGERED_BONJOUR
1607 EnableBLEBasedDiscovery = PreferencesGetValueBool(kPreferencesKey_EnableBLEBasedDiscovery, EnableBLEBasedDiscovery);
1608 DefaultToBLETriggered = PreferencesGetValueBool(kPreferencesKey_DefaultToBLETriggered, DefaultToBLETriggered);
1609 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
1610 #endif
1611
1612 #if MDNSRESPONDER_SUPPORTS(APPLE, PREALLOCATED_CACHE)
1613 PreallocateCacheMemory = PreferencesGetValueBool(kPreferencesKey_PreallocateCacheMemory, PreallocateCacheMemory);
1614 #endif
1615 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1616 PQWorkaroundThreshold = PreferencesGetValueInt(kPreferencesKey_PQWorkaroundThreshold, PQWorkaroundThreshold);
1617 CFDictionaryRef managedDefaults = mdns_managed_defaults_create("com.apple.mDNSResponder", NULL);
1618 if (managedDefaults)
1619 {
1620 PQWorkaroundThreshold = mdns_managed_defaults_get_int_clamped(managedDefaults,
1621 kPreferencesKey_PQWorkaroundThreshold, PQWorkaroundThreshold, NULL);
1622 CFRelease(managedDefaults);
1623 managedDefaults = NULL;
1624 }
1625 #endif
1626
1627 // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
1628 if (NoMulticastAdvertisements)
1629 LogMsg("-NoMulticastAdvertisements is set: Administratively prohibiting multicast advertisements");
1630 if (AlwaysAppendSearchDomains)
1631 LogMsg("-AlwaysAppendSearchDomains is set");
1632 if (StrictUnicastOrdering)
1633 LogMsg("-StrictUnicastOrdering is set");
1634
1635 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1636
1637 signal(SIGHUP, HandleSIG); // (Debugging) Purge the cache to check for cache handling bugs
1638 signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
1639 signal(SIGPIPE, SIG_IGN); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
1640 signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
1641 signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog
1642 signal(SIGUSR1, HandleSIG); // (Debugging) Enable Logging
1643 signal(SIGUSR2, HandleSIG); // (Debugging) Enable Packet Logging
1644 signal(SIGPROF, HandleSIG); // (Debugging) Toggle Multicast Logging
1645 signal(SIGTSTP, HandleSIG); // (Debugging) Disable all Debug Logging (USR1/USR2/PROF)
1646
1647 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1648
1649 mDNSStorage.p = &PlatformStorage; // Make sure mDNSStorage.p is set up, because validatelists uses it
1650
1651 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1652
1653 // Create the kqueue, mutex and thread to support KQSockets
1654 KQueueFD = kqueue();
1655 if (KQueueFD == -1)
1656 {
1657 const int kqueue_errno = errno;
1658 LogMsg("kqueue() failed errno %d (%s)", kqueue_errno, strerror(kqueue_errno));
1659 status = kqueue_errno;
1660 goto exit;
1661 }
1662
1663 i = pthread_mutex_init(&PlatformStorage.BigMutex, NULL);
1664 if (i != 0) { LogMsg("pthread_mutex_init() failed error %d (%s)", i, strerror(i)); status = i; goto exit; }
1665
1666 int fdpair[2] = {0, 0};
1667 i = socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair);
1668 if (i == -1)
1669 {
1670 const int socketpair_errno = errno;
1671 LogMsg("socketpair() failed errno %d (%s)", socketpair_errno, strerror(socketpair_errno));
1672 status = socketpair_errno;
1673 goto exit;
1674 }
1675
1676 // Socket pair returned us two identical sockets connected to each other
1677 // We will use the first socket to send the second socket. The second socket
1678 // will be added to the kqueue so it will wake when data is sent.
1679 static const KQueueEntry wakeKQEntry = { KQWokenFlushBytes, NULL, "kqueue wakeup after CFRunLoop event" };
1680
1681 PlatformStorage.WakeKQueueLoopFD = fdpair[0];
1682 KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
1683
1684 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1685
1686 #if DEBUG
1687 if (useSandbox)
1688 #endif
1689 SandboxProcess();
1690
1691 #if MDNSRESPONDER_SUPPORTS(APPLE, METRICS)
1692 status = MetricsInit();
1693 if (status) { LogMsg("Daemon start: MetricsInit failed (%d)", status); }
1694 #endif
1695
1696 status = mDNSDaemonInitialize();
1697 if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }
1698
1699 // Need to Start XPC Server Before LaunchdCheckin() (Reason: radar:11023750)
1700 xpc_server_init();
1701 #if DEBUG
1702 if (!useDebugSocket) {
1703 if (LaunchdCheckin() == 0)
1704 useDebugSocket = mDNStrue;
1705 }
1706 if (useDebugSocket)
1707 SetDebugBoundPath();
1708 #else
1709 LaunchdCheckin();
1710 #endif
1711
1712 status = udsserver_init(launchd_fds, (mDNSu32)launchd_fds_count);
1713 if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
1714
1715 mDNSMacOSXNetworkChanged();
1716 UpdateDebugState();
1717
1718 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1719 LogInfo("Daemon Start: Using LibDispatch");
1720 // CFRunLoopRun runs both CFRunLoop sources and dispatch sources
1721 CFRunLoopRun();
1722 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1723 // Start the kqueue thread
1724 pthread_t KQueueThread;
1725 i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
1726 if (i != 0) { LogMsg("pthread_create() failed error %d (%s)", i, strerror(i)); status = i; goto exit; }
1727 if (status == 0)
1728 {
1729 CFRunLoopRun();
1730 LogMsg("ERROR: CFRunLoopRun Exiting.");
1731 mDNS_Close(&mDNSStorage);
1732 }
1733 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1734
1735 LogMsg("%s exiting", mDNSResponderVersionString);
1736
1737 exit:
1738 return(status);
1739 }
1740
1741 // uds_daemon.c support routines /////////////////////////////////////////////
1742
1743 mDNSlocal void kqUDSEventCallback(int fd, short filter, void *context, mDNSBool encounteredEOF)
1744 {
1745 const KQSocketEventSource *const source = context;
1746 (void)filter; // unused
1747 (void)encounteredEOF; // unused
1748
1749 source->callback(fd, source->context);
1750 }
1751
1752 // Arrange things so that when data appears on fd, callback is called with context
1753 mDNSexport mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data)
1754 {
1755 KQSocketEventSource **p = &gEventSources;
1756 (void) platform_data;
1757 while (*p && (*p)->fd != fd) p = &(*p)->next;
1758 if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; }
1759
1760 KQSocketEventSource *newSource = (KQSocketEventSource*) callocL("KQSocketEventSource", sizeof(*newSource));
1761 if (!newSource) return mStatus_NoMemoryErr;
1762
1763 newSource->next = mDNSNULL;
1764 newSource->fd = fd;
1765 newSource->callback = callback;
1766 newSource->context = context;
1767 newSource->kqs.KQcallback = kqUDSEventCallback;
1768 newSource->kqs.KQcontext = newSource;
1769 newSource->kqs.KQtask = "UDS client";
1770 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1771 newSource->kqs.readSource = mDNSNULL;
1772 newSource->kqs.writeSource = mDNSNULL;
1773 newSource->kqs.fdClosed = mDNSfalse;
1774 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1775
1776 if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
1777 {
1778 *p = newSource;
1779 return mStatus_NoError;
1780 }
1781
1782 LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno));
1783 freeL("KQSocketEventSource", newSource);
1784 return mStatus_BadParamErr;
1785 }
1786
1787 int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data)
1788 {
1789 (void) platform_data;
1790 return (int)recv(fd, buf, len, flags);
1791 }
1792
1793 mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor
1794 {
1795 KQSocketEventSource **p = &gEventSources;
1796 (void) platform_data;
1797 while (*p && (*p)->fd != fd) p = &(*p)->next;
1798 if (*p)
1799 {
1800 KQSocketEventSource *s = *p;
1801 *p = (*p)->next;
1802 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
1803 // causes the kernel to automatically remove any associated kevents
1804 mDNSPlatformCloseFD(&s->kqs, s->fd);
1805 freeL("KQSocketEventSource", s);
1806 return mStatus_NoError;
1807 }
1808 LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd);
1809 return mStatus_NoSuchNameErr;
1810 }
1811
1812 #ifdef UNIT_TEST
1813 #include "../unittests/daemon_ut.c"
1814 #endif // UNIT_TEST
1815
1816 #if _BUILDING_XCODE_PROJECT_
1817 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
1818 const char *__crashreporter_info__ = mDNSResponderVersionString;
1819 asm (".desc ___crashreporter_info__, 0x10");
1820 #endif
1821
1822 // For convenience when using the "strings" command, this is the last thing in the file
1823 // The "@(#) " pattern is a special prefix the "what" command looks for
1824 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";