]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-320.5.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / daemon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2006 Apple Computer, 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 * Formatting notes:
18 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
19 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
20 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
21 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
22 * therefore common sense dictates that if they are part of a compound statement then they
23 * should be indented to the same level as everything else in that compound statement.
24 * Indenting curly braces at the same level as the "if" implies that curly braces are
25 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
26 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
27 * understand why variable y is not of type "char*" just proves the point that poor code
28 * layout leads people to unfortunate misunderstandings about how the C language really works.)
29 */
30
31 // We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h
32 #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4
33
34 #include <mach/mach.h>
35 #include <mach/mach_error.h>
36 #include <servers/bootstrap.h>
37 #include <sys/types.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <unistd.h>
41 #include <paths.h>
42 #include <fcntl.h>
43 #include <launch.h>
44 #include <pwd.h>
45 #include <sys/event.h>
46 #include <pthread.h>
47 #include <sandbox.h>
48 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
49 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
50
51 #if TARGET_OS_EMBEDDED
52 #include <bootstrap_priv.h>
53
54 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
55 #endif
56
57 #include "DNSServiceDiscoveryRequestServer.h"
58 #include "DNSServiceDiscoveryReply.h"
59
60 #include "uDNS.h"
61 #include "DNSCommon.h"
62 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
63
64 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
65
66 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
67 #include "helper.h"
68 #include "safe_vproc.h"
69
70 //*************************************************************************************************************
71 #if COMPILER_LIKES_PRAGMA_MARK
72 #pragma mark - Globals
73 #endif
74
75 static mDNS_PlatformSupport PlatformStorage;
76
77 // Start off with a default cache of 16K (99 records)
78 // Each time we grow the cache we add another 99 records
79 // 99 * 164 = 16236 bytes.
80 // This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead
81 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
82 static CacheEntity rrcachestorage[RR_CACHE_SIZE];
83
84 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
85 static mach_port_t m_port = MACH_PORT_NULL;
86
87 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
88 mDNSlocal void PrepareForIdle(void *m_param);
89 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
90 static mach_port_t client_death_port = MACH_PORT_NULL;
91 static mach_port_t signal_port = MACH_PORT_NULL;
92 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
93
94 static mach_port_t server_priv_port = MACH_PORT_NULL;
95
96 static dnssd_sock_t *launchd_fds = mDNSNULL;
97 static mDNSu32 launchd_fds_count = 0;
98
99 // mDNS Mach Message Timeout, in milliseconds.
100 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
101 // fails to service its mach message queue, but long enough to give a well-written
102 // client a chance to service its mach message queue without getting cut off.
103 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
104 // even extra-slow clients a fair chance before we cut them off.
105 #define MDNS_MM_TIMEOUT 250
106
107 static int restarting_via_mach_init = 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism
108 static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd
109 static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast
110
111 extern mDNSBool StrictUnicastOrdering;
112 extern mDNSBool AlwaysAppendSearchDomains;
113
114 //*************************************************************************************************************
115 #if COMPILER_LIKES_PRAGMA_MARK
116 #pragma mark -
117 #pragma mark - Active client list structures
118 #endif
119
120 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
121 struct DNSServiceDomainEnumeration_struct
122 {
123 DNSServiceDomainEnumeration *next;
124 mach_port_t ClientMachPort;
125 DNSQuestion dom; // Question asking for domains
126 DNSQuestion def; // Question asking for default domain
127 };
128
129 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
130 struct DNSServiceBrowserResult_struct
131 {
132 DNSServiceBrowserResult *next;
133 int resultType;
134 domainname result;
135 };
136
137 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
138
139 typedef struct DNSServiceBrowserQuestion
140 {
141 struct DNSServiceBrowserQuestion *next;
142 DNSQuestion q;
143 domainname domain;
144 } DNSServiceBrowserQuestion;
145
146 struct DNSServiceBrowser_struct
147 {
148 DNSServiceBrowser *next;
149 mach_port_t ClientMachPort;
150 DNSServiceBrowserQuestion *qlist;
151 DNSServiceBrowserResult *results;
152 mDNSs32 lastsuccess;
153 mDNSBool DefaultDomain; // was the browse started on an explicit domain?
154 domainname type; // registration type
155 };
156
157 typedef struct DNSServiceResolver_struct DNSServiceResolver;
158 struct DNSServiceResolver_struct
159 {
160 DNSServiceResolver *next;
161 mach_port_t ClientMachPort;
162 ServiceInfoQuery q;
163 ServiceInfo i;
164 mDNSs32 ReportTime;
165 };
166
167 // A single registered service: ServiceRecordSet + bookkeeping
168 // Note that we duplicate some fields from parent DNSServiceRegistration object
169 // to facilitate cleanup, when instances and parent may be deallocated at different times.
170 typedef struct ServiceInstance
171 {
172 struct ServiceInstance *next;
173 mach_port_t ClientMachPort;
174 mDNSBool autoname; // Set if this name is tied to the Computer Name
175 mDNSBool renameonmemfree; // Set if we just got a name conflict and now need to automatically pick a new name
176 domainlabel name;
177 domainname domain;
178 ServiceRecordSet srs;
179 // Don't add any fields after ServiceRecordSet.
180 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
181 } ServiceInstance;
182
183 // A client-created service. May reference several ServiceInstance objects if default
184 // settings cause registration in multiple domains.
185 typedef struct DNSServiceRegistration
186 {
187 struct DNSServiceRegistration *next;
188 mach_port_t ClientMachPort;
189 mDNSBool DefaultDomain;
190 mDNSBool autoname;
191 size_t rdsize;
192 int NumSubTypes;
193 char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes
194 domainlabel name; // used only if autoname is false
195 domainname type;
196 mDNSIPPort port;
197 unsigned char txtinfo[1024];
198 size_t txt_len;
199 uint32_t NextRef;
200 ServiceInstance *regs;
201 } DNSServiceRegistration;
202
203 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
204 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
205 static DNSServiceResolver *DNSServiceResolverList = NULL;
206 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
207
208 // We keep a list of client-supplied event sources in KQSocketEventSource records
209 typedef struct KQSocketEventSource
210 {
211 struct KQSocketEventSource *next;
212 int fd;
213 KQueueEntry kqs;
214 } KQSocketEventSource;
215
216 static KQSocketEventSource *gEventSources;
217
218 //*************************************************************************************************************
219 #if COMPILER_LIKES_PRAGMA_MARK
220 #pragma mark -
221 #pragma mark - General Utility Functions
222 #endif
223
224 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
225
226 char _malloc_options[] = "AXZ";
227
228 mDNSexport void LogMemCorruption(const char *format, ...)
229 {
230 char buffer[512];
231 va_list ptr;
232 va_start(ptr,format);
233 buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
234 va_end(ptr);
235 LogMsg("!!!! %s !!!!", buffer);
236 NotifyOfElusiveBug("Memory Corruption", buffer);
237 #if ForceAlerts
238 *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
239 #endif
240 }
241
242 mDNSlocal void validatelists(mDNS *const m)
243 {
244 // Check local lists
245 KQSocketEventSource *k;
246 for (k = gEventSources; k; k=k->next)
247 if (k->next == (KQSocketEventSource *)~0 || k->fd < 0)
248 LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd);
249
250 // Check Mach client lists
251 DNSServiceDomainEnumeration *e;
252 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
253 if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
254 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort);
255
256 DNSServiceBrowser *b;
257 for (b = DNSServiceBrowserList; b; b=b->next)
258 if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
259 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort);
260
261 DNSServiceResolver *l;
262 for (l = DNSServiceResolverList; l; l=l->next)
263 if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
264 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort);
265
266 DNSServiceRegistration *r;
267 for (r = DNSServiceRegistrationList; r; r=r->next)
268 if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
269 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort);
270
271 // Check Unix Domain Socket client lists (uds_daemon.c)
272 uds_validatelists();
273
274 // Check core mDNS lists
275 AuthRecord *rr;
276 for (rr = m->ResourceRecords; rr; rr=rr->next)
277 {
278 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
279 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
280 if (rr->resrec.name != &rr->namestorage)
281 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
282 rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c);
283 }
284
285 for (rr = m->DuplicateRecords; rr; rr=rr->next)
286 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
287 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
288
289 rr = m->NewLocalRecords;
290 if (rr)
291 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
292 LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType);
293
294 rr = m->CurrentRecord;
295 if (rr)
296 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
297 LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType);
298
299 DNSQuestion *q;
300 for (q = m->Questions; q; q=q->next)
301 if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32)~0)
302 LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next);
303
304 CacheGroup *cg;
305 CacheRecord *cr;
306 mDNSu32 slot;
307 FORALL_CACHERECORDS(slot, cg, cr)
308 {
309 if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
310 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType);
311 if (cr->CRActiveQuestion)
312 {
313 for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break;
314 if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr));
315 }
316 }
317
318 // Check core uDNS lists
319 udns_validatelists(m);
320
321 // Check platform-layer lists
322 NetworkInterfaceInfoOSX *i;
323 for (i = m->p->InterfaceList; i; i = i->next)
324 if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0)
325 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname);
326
327 ClientTunnel *t;
328 for (t = m->TunnelClients; t; t=t->next)
329 if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63)
330 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]);
331 }
332
333 mDNSexport void *mallocL(char *msg, unsigned int size)
334 {
335 // Allocate space for two words of sanity checking data before the requested block
336 mDNSu32 *mem = malloc(sizeof(mDNSu32) * 2 + size);
337 if (!mem)
338 { LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); }
339 else
340 {
341 if (size > 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]);
342 else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
343 mem[0] = 0xDEAD1234;
344 mem[1] = size;
345 //mDNSPlatformMemZero(&mem[2], size);
346 memset(&mem[2], 0xFF, size);
347 validatelists(&mDNSStorage);
348 return(&mem[2]);
349 }
350 }
351
352 mDNSexport void freeL(char *msg, void *x)
353 {
354 if (!x)
355 LogMsg("free( %s @ NULL )!", msg);
356 else
357 {
358 mDNSu32 *mem = ((mDNSu32 *)x) - 2;
359 if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
360 if (mem[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]);
361 else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
362 //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]);
363 memset(mem, 0xFF, sizeof(mDNSu32) * 2 + mem[1]);
364 validatelists(&mDNSStorage);
365 free(mem);
366 }
367 }
368
369 #endif
370
371 //*************************************************************************************************************
372 #if COMPILER_LIKES_PRAGMA_MARK
373 #pragma mark -
374 #pragma mark - Mach client request handlers
375 #endif
376
377 //*************************************************************************************************************
378 // Client Death Detection
379
380 // This gets called after ALL constituent records of the Service Record Set have been deregistered
381 mDNSlocal void FreeServiceInstance(ServiceInstance *x)
382 {
383 ServiceRecordSet *s = &x->srs;
384 ExtraResourceRecord *e = x->srs.Extras, *tmp;
385
386 while (e)
387 {
388 e->r.RecordContext = e;
389 tmp = e;
390 e = e->next;
391 FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
392 }
393
394 if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
395 freeL("TXT RData", s->RR_TXT.resrec.rdata);
396
397 if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
398 freeL("ServiceInstance", x);
399 }
400
401 // AbortClient finds whatever client is identified by the given Mach port,
402 // stops whatever operation that client was doing, and frees its memory.
403 // In the case of a service registration, the actual freeing may be deferred
404 // until we get the mStatus_MemFree message, if necessary
405 mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
406 {
407 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
408 DNSServiceBrowser **b = &DNSServiceBrowserList;
409 DNSServiceResolver **l = &DNSServiceResolverList;
410 DNSServiceRegistration **r = &DNSServiceRegistrationList;
411
412 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
413 if (*e)
414 {
415 DNSServiceDomainEnumeration *x = *e;
416 *e = (*e)->next;
417 if (m && m != x)
418 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
419 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
420 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
421 mDNS_StopGetDomains(&mDNSStorage, &x->def);
422 freeL("DNSServiceDomainEnumeration", x);
423 return;
424 }
425
426 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
427 if (*b)
428 {
429 DNSServiceBrowser *x = *b;
430 DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
431 *b = (*b)->next;
432 while (qptr)
433 {
434 if (m && m != x)
435 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
436 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c);
437 mDNS_StopBrowse(&mDNSStorage, &qptr->q);
438 freePtr = qptr;
439 qptr = qptr->next;
440 freeL("DNSServiceBrowserQuestion", freePtr);
441 }
442 while (x->results)
443 {
444 DNSServiceBrowserResult *t = x->results;
445 x->results = x->results->next;
446 freeL("DNSServiceBrowserResult", t);
447 }
448 freeL("DNSServiceBrowser", x);
449 return;
450 }
451
452 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
453 if (*l)
454 {
455 DNSServiceResolver *x = *l;
456 *l = (*l)->next;
457 if (m && m != x)
458 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
459 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c);
460 mDNS_StopResolveService(&mDNSStorage, &x->q);
461 freeL("DNSServiceResolver", x);
462 return;
463 }
464
465 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
466 if (*r)
467 {
468 ServiceInstance *si = NULL;
469 DNSServiceRegistration *x = *r;
470 *r = (*r)->next;
471
472 si = x->regs;
473 while (si)
474 {
475 ServiceInstance *instance = si;
476 si = si->next;
477 instance->renameonmemfree = mDNSfalse;
478 if (m && m != x) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs), m, x);
479 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs));
480
481 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
482 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
483 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
484 // the list, so we should go ahead and free the memory right now
485 if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer
486 }
487 x->regs = NULL;
488 freeL("DNSServiceRegistration", x);
489 return;
490 }
491
492 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
493 }
494
495 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
496
497 mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
498 {
499 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
500 DNSServiceBrowser *b = DNSServiceBrowserList;
501 DNSServiceResolver *l = DNSServiceResolverList;
502 DNSServiceRegistration *r = DNSServiceRegistrationList;
503 DNSServiceBrowserQuestion *qptr;
504
505 while (e && e->ClientMachPort != c) e = e->next;
506 while (b && b->ClientMachPort != c) b = b->next;
507 while (l && l->ClientMachPort != c) l = l->next;
508 while (r && r->ClientMachPort != c) r = r->next;
509
510 if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
511 else if (b)
512 {
513 for (qptr = b->qlist; qptr; qptr = qptr->next)
514 LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg);
515 }
516 else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
517 else if (r)
518 {
519 ServiceInstance *si;
520 for (si = r->regs; si; si = si->next)
521 LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name->c, reason, msg);
522 }
523 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
524
525 AbortClient(c, m);
526 }
527
528 mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
529 {
530 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
531 DNSServiceBrowser *b = DNSServiceBrowserList;
532 DNSServiceResolver *l = DNSServiceResolverList;
533 DNSServiceRegistration *r = DNSServiceRegistrationList;
534 DNSServiceBrowserQuestion *qptr;
535
536 while (e && e->ClientMachPort != c) e = e->next;
537 while (b && b->ClientMachPort != c) b = b->next;
538 while (l && l->ClientMachPort != c) l = l->next;
539 while (r && r->ClientMachPort != c) r = r->next;
540 if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
541 if (b)
542 {
543 for (qptr = b->qlist; qptr; qptr = qptr->next)
544 LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c);
545 }
546 if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
547 if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL);
548 return(e || b || l || r);
549 }
550
551 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
552
553 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
554 {
555 KQueueLock(&mDNSStorage);
556 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
557 (void)unusedport; // Unused
558 (void)size; // Unused
559 (void)info; // Unused
560 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
561 {
562 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
563 AbortClient(deathMessage->not_port, NULL);
564
565 /* Deallocate the send right that came in the dead name notification */
566 mach_port_destroy(mach_task_self(), deathMessage->not_port);
567 }
568 KQueueUnlock(&mDNSStorage, "Mach AbortClient");
569 }
570
571 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
572
573 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
574 {
575 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
576 dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue());
577 if (mach_source == mDNSNULL)
578 {
579 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
580 return;
581 }
582 dispatch_source_set_event_handler(mach_source, ^{
583 mach_port_destroy(mach_task_self(), ClientMachPort);
584 });
585 dispatch_resume(mach_source);
586 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
587 mach_port_t prev;
588 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
589 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
590 // If the port already died while we were thinking about it, then abort the operation right away
591 if (r != KERN_SUCCESS)
592 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
593 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
594 }
595
596 //*************************************************************************************************************
597 // Domain Enumeration
598
599 mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
600 {
601 kern_return_t status;
602 char buffer[MAX_ESCAPED_DOMAIN_NAME];
603 DNSServiceDomainEnumerationReplyResultType rt;
604 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
605 (void)m; // Unused
606
607 debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
608 if (answer->rrtype != kDNSType_PTR) return;
609 if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
610
611 if (AddRecord)
612 {
613 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
614 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
615 }
616 else
617 {
618 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
619 else return;
620 }
621
622 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
623 x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
624 !AddRecord ? "RemoveDomain" :
625 question == &x->dom ? "AddDomain" : "AddDomainDefault");
626
627 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
628 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
629 if (status == MACH_SEND_TIMED_OUT)
630 AbortBlockedClient(x->ClientMachPort, "enumeration", x);
631 }
632
633 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
634 int regDom)
635 {
636 // Check client parameter
637 (void)unusedserver; // Unused
638 mStatus err = mStatus_NoError;
639 const char *errormsg = "Unknown";
640 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
641 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
642
643 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
644 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
645
646 // Allocate memory, and handle failure
647 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
648 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
649
650 // Set up object, and link into list
651 x->ClientMachPort = client;
652 x->next = DNSServiceDomainEnumerationList;
653 DNSServiceDomainEnumerationList = x;
654
655 verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
656
657 // Do the operation
658 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
659 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
660 if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
661
662 // Succeeded: Wrap up and return
663 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
664 EnableDeathNotificationForClient(client, x);
665 return(mStatus_NoError);
666
667 fail:
668 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client, regDom, errormsg, err);
669 return(err);
670 }
671
672 //*************************************************************************************************************
673 // Browse for services
674
675 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
676 {
677 (void)m; // Unused
678
679 if (answer->rrtype != kDNSType_PTR)
680 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
681
682 domainlabel name;
683 domainname type, domain;
684 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
685 {
686 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
687 answer->name->c, answer->rdata->u.name.c);
688 return;
689 }
690
691 DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
692 if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
693
694 verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
695 AssignDomainName(&x->result, &answer->rdata->u.name);
696 if (AddRecord)
697 x->resultType = DNSServiceBrowserReplyAddInstance;
698 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
699 x->next = NULL;
700
701 DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
702 DNSServiceBrowserResult **p = &browser->results;
703 while (*p) p = &(*p)->next;
704 *p = x;
705
706 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
707 browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
708 }
709
710 mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d)
711 {
712 mStatus err = mStatus_NoError;
713 DNSServiceBrowserQuestion *ptr, *question = NULL;
714
715 for (ptr = browser->qlist; ptr; ptr = ptr->next)
716 {
717 if (SameDomainName(&ptr->q.qname, d))
718 { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; }
719 }
720
721 question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
722 if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; }
723 AssignDomainName(&question->domain, d);
724 question->next = browser->qlist;
725 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
726 err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser);
727 if (!err)
728 browser->qlist = question;
729 else
730 {
731 LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err);
732 freeL("DNSServiceBrowserQuestion", question);
733 }
734 return err;
735 }
736
737 mDNSexport void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add)
738 {
739 DNSServiceBrowser *ptr;
740 for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next)
741 {
742 if (ptr->DefaultDomain)
743 {
744 if (add)
745 {
746 mStatus err = AddDomainToBrowser(ptr, d);
747 if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort);
748 }
749 else
750 {
751 DNSServiceBrowserQuestion **q = &ptr->qlist;
752 while (*q)
753 {
754 if (SameDomainName(&(*q)->domain, d))
755 {
756 DNSServiceBrowserQuestion *rem = *q;
757 *q = (*q)->next;
758 mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
759 freeL("DNSServiceBrowserQuestion", rem);
760 return;
761 }
762 q = &(*q)->next;
763 }
764 LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort);
765 }
766 }
767 }
768 }
769
770 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
771 DNSCString regtype, DNSCString domain)
772 {
773 // Check client parameter
774 (void)unusedserver; // Unused
775 mStatus err = mStatus_NoError;
776 const char *errormsg = "Unknown";
777
778 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
779 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
780
781 // Check other parameters
782 domainname t, d;
783 t.c[0] = 0;
784 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
785 if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; }
786 if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
787 { errormsg = "Bad Service SubType"; goto badparam; }
788 if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
789 domainname temp;
790 if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
791 if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local"
792
793 // Allocate memory, and handle failure
794 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
795 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
796
797 // Set up object, and link into list
798 AssignDomainName(&x->type, &t);
799 x->ClientMachPort = client;
800 x->results = NULL;
801 x->lastsuccess = 0;
802 x->qlist = NULL;
803 x->next = DNSServiceBrowserList;
804 DNSServiceBrowserList = x;
805
806 if (domain[0])
807 {
808 // Start browser for an explicit domain
809 x->DefaultDomain = mDNSfalse;
810 if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; }
811 err = AddDomainToBrowser(x, &d);
812 if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
813 }
814 else
815 {
816 DNameListElem *sdPtr;
817 // Start browser on all domains
818 x->DefaultDomain = mDNStrue;
819 if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
820 for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next)
821 {
822 err = AddDomainToBrowser(x, &sdPtr->name);
823 if (err)
824 {
825 // only terminally bail if .local fails
826 if (!SameDomainName(&localdomain, &sdPtr->name))
827 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c);
828 else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
829 }
830 }
831 }
832
833 // Succeeded: Wrap up and return
834 EnableDeathNotificationForClient(client, x);
835 return(mStatus_NoError);
836
837 badparam:
838 err = mStatus_BadParamErr;
839 fail:
840 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client, regtype, domain, errormsg, err);
841 return(err);
842 }
843
844 //*************************************************************************************************************
845 // Resolve Service Info
846
847 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
848 {
849 kern_return_t status;
850 DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
851 NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID);
852 if (query->info->InterfaceID == mDNSInterface_LocalOnly || query->info->InterfaceID == mDNSInterface_P2P) ifx = mDNSNULL;
853 struct sockaddr_storage interface;
854 struct sockaddr_storage address;
855 char cstring[1024];
856 int i, pstrlen = query->info->TXTinfo[0];
857 (void)m; // Unused
858
859 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
860
861 if (query->info->TXTlen > sizeof(cstring)) return;
862
863 mDNSPlatformMemZero(&interface, sizeof(interface));
864 mDNSPlatformMemZero(&address, sizeof(address));
865
866 if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
867 {
868 struct sockaddr_in *s = (struct sockaddr_in*)&interface;
869 s->sin_len = sizeof(*s);
870 s->sin_family = AF_INET;
871 s->sin_port = 0;
872 s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
873 }
874 else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
875 {
876 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
877 sin6->sin6_len = sizeof(*sin6);
878 sin6->sin6_family = AF_INET6;
879 sin6->sin6_flowinfo = 0;
880 sin6->sin6_port = 0;
881 sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
882 sin6->sin6_scope_id = ifx->scope_id;
883 }
884
885 if (query->info->ip.type == mDNSAddrType_IPv4)
886 {
887 struct sockaddr_in *s = (struct sockaddr_in*)&address;
888 s->sin_len = sizeof(*s);
889 s->sin_family = AF_INET;
890 s->sin_port = query->info->port.NotAnInteger;
891 s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
892 }
893 else
894 {
895 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
896 sin6->sin6_len = sizeof(*sin6);
897 sin6->sin6_family = AF_INET6;
898 sin6->sin6_port = query->info->port.NotAnInteger;
899 sin6->sin6_flowinfo = 0;
900 sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
901 sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
902 }
903
904 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
905 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
906 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
907 // ASCII-1 characters are used in the C-string as boundary markers,
908 // to indicate the boundaries between the original constituent P-strings.
909 for (i=1; i<query->info->TXTlen; i++)
910 {
911 if (--pstrlen >= 0)
912 cstring[i-1] = query->info->TXTinfo[i];
913 else
914 {
915 cstring[i-1] = 1;
916 pstrlen = query->info->TXTinfo[i];
917 }
918 }
919 cstring[i-1] = 0; // Put the terminating NULL on the end
920
921 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
922 x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
923 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
924 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
925 if (status == MACH_SEND_TIMED_OUT)
926 AbortBlockedClient(x->ClientMachPort, "resolve", x);
927 }
928
929 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
930 DNSCString name, DNSCString regtype, DNSCString domain)
931 {
932 // Check client parameter
933 (void)unusedserver; // Unused
934 mStatus err = mStatus_NoError;
935 const char *errormsg = "Unknown";
936 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
937 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
938
939 // Check other parameters
940 domainlabel n;
941 domainname t, d, srv;
942 if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
943 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
944 if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
945 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
946
947 // Allocate memory, and handle failure
948 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
949 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
950
951 // Set up object, and link into list
952 x->ClientMachPort = client;
953 x->i.InterfaceID = mDNSInterface_Any;
954 x->i.name = srv;
955 x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
956 x->next = DNSServiceResolverList;
957 DNSServiceResolverList = x;
958
959 // Do the operation
960 LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c);
961 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
962 if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
963
964 // Succeeded: Wrap up and return
965 EnableDeathNotificationForClient(client, x);
966 return(mStatus_NoError);
967
968 badparam:
969 err = mStatus_BadParamErr;
970 fail:
971 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err);
972 return(err);
973 }
974
975 //*************************************************************************************************************
976 // Registration
977
978 mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
979 {
980 m->p->NotifyUser = NonZeroTime(m->timenow + delay);
981 }
982
983 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
984 {
985 ServiceInstance *si = (ServiceInstance*)srs->ServiceContext;
986
987 if (result == mStatus_NoError)
988 {
989 kern_return_t status;
990 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
991 status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
992 if (status == MACH_SEND_TIMED_OUT)
993 AbortBlockedClient(si->ClientMachPort, "registration success", si);
994 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
995 RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
996 }
997
998 else if (result == mStatus_NameConflict)
999 {
1000 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1001 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1002 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1003 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1004 {
1005 // On conflict for an autoname service, rename and reregister *all* autoname services
1006 IncrementLabelSuffix(&m->nicelabel, mDNStrue);
1007 mDNS_ConfigChanged(m);
1008 }
1009 else if (si->autoname)
1010 {
1011 mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
1012 return;
1013 }
1014 else
1015 {
1016 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1017 // of their registration in the usual way (which we will catch via client death notification).
1018 // If the Mach queue is full, we forcibly abort the client immediately.
1019 kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1020 if (status == MACH_SEND_TIMED_OUT)
1021 AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL);
1022 }
1023 }
1024
1025 else if (result == mStatus_MemFree)
1026 {
1027 if (si->renameonmemfree) // We intentionally terminated registration so we could re-register with new name
1028 {
1029 debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c);
1030 si->renameonmemfree = mDNSfalse;
1031 si->name = m->nicelabel;
1032 mDNS_RenameAndReregisterService(m, srs, &si->name);
1033 }
1034 else
1035 {
1036 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1037 DNSServiceRegistration *r;
1038 for (r = DNSServiceRegistrationList; r; r = r->next)
1039 {
1040 ServiceInstance **sp = &r->regs;
1041 while (*sp)
1042 {
1043 if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; }
1044 sp = &(*sp)->next;
1045 }
1046 }
1047 // END SANITY CHECK
1048 FreeServiceInstance(si);
1049 }
1050 }
1051
1052 else if (result != mStatus_NATTraversal)
1053 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result);
1054 }
1055
1056 mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain)
1057 {
1058 mStatus err = 0;
1059 ServiceInstance *si = NULL;
1060 AuthRecord *SubTypes = NULL;
1061
1062 for (si = x->regs; si; si = si->next)
1063 {
1064 if (SameDomainName(&si->domain, domain))
1065 { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
1066 }
1067
1068 SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype);
1069 if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
1070
1071 si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
1072 if (!si) return mStatus_NoMemoryErr;
1073
1074 si->ClientMachPort = x->ClientMachPort;
1075 si->renameonmemfree = mDNSfalse;
1076 si->autoname = x->autoname;
1077 si->name = x->autoname ? mDNSStorage.nicelabel : x->name;
1078 si->domain = *domain;
1079
1080 err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL,
1081 x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si, 0);
1082 if (!err)
1083 {
1084 si->next = x->regs;
1085 x->regs = si;
1086 }
1087 else
1088 {
1089 LogMsg("Error %d for registration of service in domain %##s", err, domain->c);
1090 freeL("ServiceInstance", si);
1091 }
1092 return err;
1093 }
1094
1095 mDNSexport void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add)
1096 {
1097 DNSServiceRegistration *reg;
1098
1099 for (reg = DNSServiceRegistrationList; reg; reg = reg->next)
1100 {
1101 if (reg->DefaultDomain)
1102 {
1103 if (add)
1104 AddServiceInstance(reg, d);
1105 else
1106 {
1107 ServiceInstance **si = &reg->regs;
1108 while (*si)
1109 {
1110 if (SameDomainName(&(*si)->domain, d))
1111 {
1112 ServiceInstance *s = *si;
1113 *si = (*si)->next;
1114 if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s); // only free memory synchronously on error
1115 break;
1116 }
1117 si = &(*si)->next;
1118 }
1119 if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed
1120 }
1121 }
1122 }
1123 }
1124
1125 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1126 DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord)
1127 {
1128 (void)unusedserver; // Unused
1129 mStatus err = mStatus_NoError;
1130 const char *errormsg = "Unknown";
1131
1132 // older versions of this code passed the port via mach IPC as an int.
1133 // we continue to pass it as 4 bytes to maintain binary compatibility,
1134 // but now ensure that the network byte order is preserved by using a struct
1135 mDNSIPPort port;
1136 port.b[0] = IpPort.bytes[2];
1137 port.b[1] = IpPort.bytes[3];
1138
1139 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1140 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1141
1142 // Check for sub-types after the service type
1143 size_t reglen = strlen(regtype) + 1;
1144 if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
1145 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1146 if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }
1147
1148 // Check other parameters
1149 domainlabel n;
1150 domainname t, d;
1151 domainname srv;
1152 if (!name[0]) n = mDNSStorage.nicelabel;
1153 else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1154 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1155 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
1156 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1157
1158 unsigned char txtinfo[1024] = "";
1159 unsigned int data_len = 0;
1160 unsigned int size = sizeof(RDataBody);
1161 unsigned char *pstring = &txtinfo[data_len];
1162 char *ptr = txtRecord;
1163
1164 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1165 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1166 // Hence we have to convert the C-string to a P-string.
1167 // ASCII-1 characters are allowed in the C-string as boundary markers,
1168 // so that a single C-string can be used to represent one or more P-strings.
1169 while (*ptr)
1170 {
1171 if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
1172 if (*ptr == 1) // If this is our boundary marker, start a new P-string
1173 {
1174 pstring = &txtinfo[data_len];
1175 pstring[0] = 0;
1176 ptr++;
1177 }
1178 else
1179 {
1180 if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
1181 pstring[++pstring[0]] = *ptr++;
1182 }
1183 }
1184
1185 data_len++;
1186 if (size < data_len)
1187 size = data_len;
1188
1189 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1190 // a port number of zero. When two instances of the protected client are allowed to run on one
1191 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1192 if (!mDNSIPPortIsZero(port))
1193 {
1194 int count = CountExistingRegistrations(&srv, port);
1195 if (count)
1196 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1197 client, count+1, srv.c, mDNSVal16(port));
1198 }
1199
1200 // Allocate memory, and handle failure
1201 DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x));
1202 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1203 mDNSPlatformMemZero(x, sizeof(*x));
1204
1205 // Set up object, and link into list
1206 x->ClientMachPort = client;
1207 x->DefaultDomain = !domain[0];
1208 x->autoname = (!name[0]);
1209 x->rdsize = size;
1210 x->NumSubTypes = NumSubTypes;
1211 memcpy(x->regtype, regtype, reglen);
1212 x->name = n;
1213 x->type = t;
1214 x->port = port;
1215 memcpy(x->txtinfo, txtinfo, 1024);
1216 x->txt_len = data_len;
1217 x->NextRef = 0;
1218 x->regs = NULL;
1219
1220 x->next = DNSServiceRegistrationList;
1221 DNSServiceRegistrationList = x;
1222
1223 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1224 x->ClientMachPort, name, regtype, domain, mDNSVal16(port));
1225
1226 err = AddServiceInstance(x, &d);
1227 if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails
1228
1229 if (x->DefaultDomain)
1230 {
1231 DNameListElem *p;
1232 for (p = AutoRegistrationDomains; p; p = p->next)
1233 AddServiceInstance(x, &p->name);
1234 }
1235
1236 // Succeeded: Wrap up and return
1237 EnableDeathNotificationForClient(client, x);
1238 return(mStatus_NoError);
1239
1240 badtxt:
1241 LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
1242 badparam:
1243 err = mStatus_BadParamErr;
1244 fail:
1245 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)",
1246 client, name, regtype, domain, mDNSVal16(port), errormsg, err);
1247 return(err);
1248 }
1249
1250 mDNSlocal void mDNSPreferencesSetNames(mDNS *const m, int key, domainlabel *old, domainlabel *new)
1251 {
1252 domainlabel *prevold, *prevnew;
1253 switch (key)
1254 {
1255 case kmDNSComputerName:
1256 case kmDNSLocalHostName:
1257 if (key == kmDNSComputerName)
1258 {
1259 prevold = &m->p->prevoldnicelabel;
1260 prevnew = &m->p->prevnewnicelabel;
1261 }
1262 else
1263 {
1264 prevold = &m->p->prevoldhostlabel;
1265 prevnew = &m->p->prevnewhostlabel;
1266 }
1267 // There are a few cases where we need to invoke the helper.
1268 //
1269 // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need
1270 // to invoke the helper so that it pops up a dialogue to inform the user about the
1271 // conflict
1272 //
1273 // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label
1274 // through the preferences pane. We may have to inform the helper as it may have popped up
1275 // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking
1276 // the helper in this case if the previous values (old and new) that we told helper last time
1277 // are same. If the previous old and new values are same, helper does not care.
1278 //
1279 // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old"
1280 // is not called with NULL today, but this makes it future proof.
1281 if (!old || !new || !SameDomainLabelCS(old->c, new->c) ||
1282 !SameDomainLabelCS(old->c, prevold->c) ||
1283 !SameDomainLabelCS(new->c, prevnew->c))
1284 {
1285 if (old)
1286 *prevold = *old;
1287 else
1288 prevold->c[0] = 0;
1289 if (new)
1290 *prevnew = *new;
1291 else
1292 prevnew->c[0] = 0;
1293 mDNSPreferencesSetName(key, old, new);
1294 }
1295 else
1296 {
1297 LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s",
1298 (key == kmDNSComputerName ? "prevoldnicelabel" : "prevoldhostlabel"), prevold->c,
1299 (key == kmDNSComputerName ? "prevnewnicelabel" : "prevnewhostlabel"), prevnew->c,
1300 old->c, new->c);
1301 }
1302 break;
1303 default:
1304 LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key);
1305 return;
1306 }
1307 }
1308
1309 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
1310 {
1311 (void)m; // Unused
1312 if (result == mStatus_NoError)
1313 {
1314 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
1315 LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
1316 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1317 RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond);
1318 }
1319 else if (result == mStatus_NameConflict)
1320 {
1321 LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c);
1322 if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow);
1323 else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond)
1324 {
1325 // Tell the helper we've given up
1326 mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, NULL);
1327 }
1328 }
1329 else if (result == mStatus_GrowCache)
1330 {
1331 // Allocate another chunk of cache storage
1332 CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE);
1333 //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
1334 if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
1335 }
1336 else if (result == mStatus_ConfigChanged)
1337 {
1338 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
1339 mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
1340 mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
1341
1342 // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
1343 DNSServiceRegistration *r;
1344 for (r = DNSServiceRegistrationList; r; r=r->next)
1345 if (r->autoname)
1346 {
1347 ServiceInstance *si;
1348 for (si = r->regs; si; si = si->next)
1349 {
1350 if (!SameDomainLabelCS(si->name.c, m->nicelabel.c))
1351 {
1352 debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c);
1353 si->renameonmemfree = mDNStrue;
1354 if (mDNS_DeregisterService_drt(m, &si->srs, mDNS_Dereg_rapid))
1355 RegCallback(m, &si->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately
1356 }
1357 }
1358 }
1359
1360 // Then we call into the UDS daemon code, to let it do the same
1361 udsserver_handle_configchange(m);
1362 }
1363 }
1364
1365 //*************************************************************************************************************
1366 // Add / Update / Remove records from existing Registration
1367
1368 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1369 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
1370 {
1371 // Check client parameter
1372 uint32_t id;
1373 mStatus err = mStatus_NoError;
1374 const char *errormsg = "Unknown";
1375 DNSServiceRegistration *x = DNSServiceRegistrationList;
1376 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1377 ServiceInstance *si;
1378 size_t size;
1379 (void)unusedserver; // Unused
1380 while (x && x->ClientMachPort != client) x = x->next;
1381 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1382
1383 // Check other parameters
1384 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1385 if (data_len > sizeof(RDataBody)) size = data_len;
1386 else size = sizeof(RDataBody);
1387
1388 id = x->NextRef++;
1389 *reference = (natural_t)id;
1390 for (si = x->regs; si; si = si->next)
1391 {
1392 // Allocate memory, and handle failure
1393 ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
1394 if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1395
1396 // Fill in type, length, and data of new record
1397 extra->r.resrec.rrtype = type;
1398 extra->r.rdatastorage.MaxRDLength = size;
1399 extra->r.resrec.rdlength = data_len;
1400 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
1401
1402 // Do the operation
1403 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1404 client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
1405 err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl, 0);
1406
1407 if (err)
1408 {
1409 freeL("Extra Resource Record", extra);
1410 errormsg = "mDNS_AddRecordToService";
1411 goto fail;
1412 }
1413
1414 extra->ClientID = id;
1415 }
1416
1417 return mStatus_NoError;
1418
1419 fail:
1420 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client, x ? x->name.c : (mDNSu8*)"\x8""«NULL»", type, data_len, errormsg, err);
1421 return mStatus_UnknownErr;
1422 }
1423
1424 mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen)
1425 {
1426 (void)m; // Unused
1427 (void)OldRDLen; // Unused
1428 if (OldRData != &rr->rdatastorage)
1429 freeL("Old RData", OldRData);
1430 }
1431
1432 mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1433 {
1434 // Check client parameter
1435 mStatus err = mStatus_NoError;
1436 const char *errormsg = "Unknown";
1437 const domainname *name = (const domainname *)"";
1438
1439 name = srs->RR_SRV.resrec.name;
1440
1441 unsigned int size = sizeof(RDataBody);
1442 if (size < data_len)
1443 size = data_len;
1444
1445 // Allocate memory, and handle failure
1446 RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
1447 if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1448
1449 // Fill in new length, and data
1450 newrdata->MaxRDLength = size;
1451 memcpy(&newrdata->u, data, data_len);
1452
1453 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1454 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1455 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1456 if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; }
1457
1458 // Do the operation
1459 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1460 client, srs->RR_SRV.resrec.name->c, data_len);
1461
1462 err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
1463 if (err)
1464 {
1465 errormsg = "mDNS_Update";
1466 freeL("RData", newrdata);
1467 return err;
1468 }
1469 return(mStatus_NoError);
1470
1471 fail:
1472 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client, name->c, data_len, errormsg, err);
1473 return(err);
1474 }
1475
1476 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1477 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1478 {
1479 // Check client parameter
1480 mStatus err = mStatus_NoError;
1481 const char *errormsg = "Unknown";
1482 const domainname *name = (const domainname *)"";
1483 ServiceInstance *si;
1484
1485 (void)unusedserver; // unused
1486 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1487 DNSServiceRegistration *x = DNSServiceRegistrationList;
1488 while (x && x->ClientMachPort != client) x = x->next;
1489 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1490
1491 // Check other parameters
1492 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1493
1494 for (si = x->regs; si; si = si->next)
1495 {
1496 AuthRecord *r = NULL;
1497
1498 // Find the record we're updating. NULL reference means update the primary TXT record
1499 if (!reference) r = &si->srs.RR_TXT;
1500 else
1501 {
1502 ExtraResourceRecord *ptr;
1503 for (ptr = si->srs.Extras; ptr; ptr = ptr->next)
1504 {
1505 if ((natural_t)ptr->ClientID == reference)
1506 { r = &ptr->r; break; }
1507 }
1508 if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
1509 }
1510 err = UpdateRecord(&si->srs, client, r, data, data_len, ttl);
1511 if (err) goto fail; //!!!KRS this will cause failures for non-local defaults!
1512 }
1513
1514 return mStatus_NoError;
1515
1516 fail:
1517 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client, name->c, reference, data_len, errormsg, err);
1518 return(err);
1519 }
1520
1521 mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
1522 {
1523 const domainname *const name = srs->RR_SRV.resrec.name;
1524 mStatus err = mStatus_NoError;
1525
1526 // Do the operation
1527 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c);
1528
1529 err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra);
1530 if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err);
1531
1532 return err;
1533 }
1534
1535 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1536 natural_t reference)
1537 {
1538 // Check client parameter
1539 (void)unusedserver; // Unused
1540 mStatus err = mStatus_NoError;
1541 const char *errormsg = "Unknown";
1542 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1543 DNSServiceRegistration *x = DNSServiceRegistrationList;
1544 ServiceInstance *si;
1545
1546 while (x && x->ClientMachPort != client) x = x->next;
1547 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1548
1549 for (si = x->regs; si; si = si->next)
1550 {
1551 ExtraResourceRecord *e;
1552 for (e = si->srs.Extras; e; e = e->next)
1553 {
1554 if ((natural_t)e->ClientID == reference)
1555 {
1556 err = RemoveRecord(&si->srs, e, client);
1557 break;
1558 }
1559 }
1560 if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
1561 }
1562
1563 return mStatus_NoError;
1564
1565 fail:
1566 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err);
1567 return(err);
1568 }
1569
1570 //*************************************************************************************************************
1571 #if COMPILER_LIKES_PRAGMA_MARK
1572 #pragma mark -
1573 #pragma mark - Startup, shutdown, and supporting code
1574 #endif
1575
1576 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1577 {
1578 mig_reply_error_t *request = msg;
1579 mig_reply_error_t *reply;
1580 mach_msg_return_t mr;
1581 int options;
1582 (void)port; // Unused
1583 (void)size; // Unused
1584 (void)info; // Unused
1585
1586 KQueueLock(&mDNSStorage);
1587
1588 /* allocate a reply buffer */
1589 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
1590
1591 /* call the MiG server routine */
1592 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
1593
1594 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
1595 {
1596 if (reply->RetCode == MIG_NO_REPLY)
1597 {
1598 /*
1599 * This return code is a little tricky -- it appears that the
1600 * demux routine found an error of some sort, but since that
1601 * error would not normally get returned either to the local
1602 * user or the remote one, we pretend it's ok.
1603 */
1604 CFAllocatorDeallocate(NULL, reply);
1605 goto done;
1606 }
1607
1608 /*
1609 * destroy any out-of-line data in the request buffer but don't destroy
1610 * the reply port right (since we need that to send an error message).
1611 */
1612 request->Head.msgh_remote_port = MACH_PORT_NULL;
1613 mach_msg_destroy(&request->Head);
1614 }
1615
1616 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
1617 {
1618 /* no reply port, so destroy the reply */
1619 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
1620 mach_msg_destroy(&reply->Head);
1621 CFAllocatorDeallocate(NULL, reply);
1622 goto done;
1623 }
1624
1625 /*
1626 * send reply.
1627 *
1628 * We don't want to block indefinitely because the client
1629 * isn't receiving messages from the reply port.
1630 * If we have a send-once right for the reply port, then
1631 * this isn't a concern because the send won't block.
1632 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1633 * To avoid falling off the kernel's fast RPC path unnecessarily,
1634 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1635 */
1636
1637 options = MACH_SEND_MSG;
1638 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
1639 options |= MACH_SEND_TIMEOUT;
1640
1641 mr = mach_msg(&reply->Head, /* msg */
1642 options, /* option */
1643 reply->Head.msgh_size, /* send_size */
1644 0, /* rcv_size */
1645 MACH_PORT_NULL, /* rcv_name */
1646 MACH_MSG_TIMEOUT_NONE, /* timeout */
1647 MACH_PORT_NULL); /* notify */
1648
1649 /* Has a message error occurred? */
1650 switch (mr)
1651 {
1652 case MACH_SEND_INVALID_DEST:
1653 case MACH_SEND_TIMED_OUT:
1654 /* the reply can't be delivered, so destroy it */
1655 mach_msg_destroy(&reply->Head);
1656 break;
1657
1658 default :
1659 /* Includes success case. */
1660 break;
1661 }
1662
1663 CFAllocatorDeallocate(NULL, reply);
1664
1665 done:
1666 KQueueUnlock(&mDNSStorage, "Mach client event");
1667 }
1668
1669 mDNSlocal kern_return_t registerBootstrapService()
1670 {
1671 kern_return_t status;
1672 mach_port_t service_send_port, service_rcv_port;
1673
1674 debugf("Registering Bootstrap Service");
1675
1676 /*
1677 * See if our service name is already registered and if we have privilege to check in.
1678 */
1679 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1680 if (status == KERN_SUCCESS)
1681 {
1682 /*
1683 * If so, we must be a followup instance of an already defined server. In that case,
1684 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1685 * that in case we have to unregister later (which requires the privilege port).
1686 */
1687 server_priv_port = bootstrap_port;
1688 restarting_via_mach_init = TRUE;
1689 }
1690 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
1691 {
1692 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
1693 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
1694 if (status != KERN_SUCCESS) return status;
1695
1696 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
1697 if (status != KERN_SUCCESS)
1698 {
1699 mach_port_deallocate(mach_task_self(), server_priv_port);
1700 return status;
1701 }
1702
1703 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1704 if (status != KERN_SUCCESS)
1705 {
1706 mach_port_deallocate(mach_task_self(), server_priv_port);
1707 mach_port_deallocate(mach_task_self(), service_send_port);
1708 return status;
1709 }
1710 assert(service_send_port == service_rcv_port);
1711 }
1712
1713 /*
1714 * We have no intention of responding to requests on the service port. We are not otherwise
1715 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1716 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1717 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1718 */
1719 mach_port_destroy(mach_task_self(), service_rcv_port);
1720 return status;
1721 }
1722
1723 mDNSlocal kern_return_t destroyBootstrapService()
1724 {
1725 debugf("Destroying Bootstrap Service");
1726 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
1727 }
1728
1729 mDNSlocal void ExitCallback(int sig)
1730 {
1731 (void)sig; // Unused
1732 LogMsg("%s stopping", mDNSResponderVersionString);
1733
1734 debugf("ExitCallback");
1735 if (!mDNS_DebugMode && !started_via_launchdaemon)
1736 destroyBootstrapService();
1737
1738 debugf("ExitCallback: Aborting MIG clients");
1739 while (DNSServiceDomainEnumerationList)
1740 AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
1741 while (DNSServiceBrowserList)
1742 AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
1743 while (DNSServiceResolverList)
1744 AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
1745 while (DNSServiceRegistrationList)
1746 AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
1747
1748 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1749
1750 debugf("ExitCallback: mDNS_StartExit");
1751 mDNS_StartExit(&mDNSStorage);
1752 }
1753
1754 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1755
1756 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1757 mDNSlocal void HandleSIG(int sig)
1758 {
1759 kern_return_t status;
1760 mach_msg_header_t header;
1761
1762 // WARNING: can't call syslog or fprintf from signal handler
1763 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1764 header.msgh_remote_port = signal_port;
1765 header.msgh_local_port = MACH_PORT_NULL;
1766 header.msgh_size = sizeof(header);
1767 header.msgh_id = sig;
1768
1769 status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size,
1770 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
1771
1772 if (status != MACH_MSG_SUCCESS)
1773 {
1774 if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
1775 if (sig == SIGTERM || sig == SIGINT) exit(-1);
1776 }
1777 }
1778
1779 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1780
1781 mDNSlocal void INFOCallback(void)
1782 {
1783 mDNSs32 utc = mDNSPlatformUTC();
1784 DNSServiceDomainEnumeration *e;
1785 DNSServiceBrowser *b;
1786 DNSServiceResolver *l;
1787 DNSServiceRegistration *r;
1788 NetworkInterfaceInfoOSX *i;
1789 DNSServer *s;
1790 McastResolver *mr;
1791
1792 LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
1793
1794 udsserver_info(&mDNSStorage);
1795
1796 LogMsgNoIdent("--------- Mach Clients ---------");
1797 if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList)
1798 LogMsgNoIdent("<None>");
1799 else
1800 {
1801 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
1802 LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
1803
1804 for (b = DNSServiceBrowserList; b; b=b->next)
1805 {
1806 DNSServiceBrowserQuestion *qptr;
1807 for (qptr = b->qlist; qptr; qptr = qptr->next)
1808 LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c);
1809 }
1810
1811 for (l = DNSServiceResolverList; l; l=l->next)
1812 LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
1813
1814 for (r = DNSServiceRegistrationList; r; r=r->next)
1815 {
1816 ServiceInstance *si;
1817 for (si = r->regs; si; si = si->next)
1818 LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name->c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port));
1819 }
1820 }
1821
1822 LogMsgNoIdent("----- KQSocketEventSources -----");
1823 if (!gEventSources) LogMsgNoIdent("<None>");
1824 else
1825 {
1826 KQSocketEventSource *k;
1827 for (k = gEventSources; k; k=k->next)
1828 {
1829 LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask);
1830 usleep((mDNSStorage.KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
1831 }
1832 }
1833
1834 LogMsgNoIdent("------ Network Interfaces ------");
1835 if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent("<None>");
1836 else
1837 {
1838 for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
1839 {
1840 // Allow six characters for interface name, for names like "vmnet8"
1841 if (!i->Exists)
1842 LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
1843 i, i->ifinfo.InterfaceID, i->Registered,
1844 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
1845 &i->ifinfo.ip, utc - i->LastSeen);
1846 else
1847 {
1848 const CacheRecord *sps[3];
1849 FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps);
1850 LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
1851 i, i->ifinfo.InterfaceID, i->Registered,
1852 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
1853 i->ifinfo.InterfaceActive ? "Active" : " ",
1854 i->ifinfo.IPv4Available ? "v4" : " ",
1855 i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr,
1856 i->ifinfo.IPv6Available ? "v6" : " ",
1857 i->ifinfo.Advertise ? "⊙" : " ",
1858 i->ifinfo.McastTxRx ? "⇆" : " ",
1859 !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀",
1860 &i->ifinfo.ip);
1861
1862 if (sps[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c);
1863 if (sps[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c);
1864 if (sps[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c);
1865 }
1866 }
1867 }
1868
1869 LogMsgNoIdent("--------- DNS Servers ----------");
1870 if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
1871 else
1872 {
1873 for (s = mDNSStorage.DNSServers; s; s = s->next)
1874 {
1875 NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface);
1876 LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %s",
1877 s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
1878 s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->scoped ? "Scoped" : "",
1879 s->timeout,
1880 s->teststate == DNSServer_Untested ? "(Untested)" :
1881 s->teststate == DNSServer_Passed ? "" :
1882 s->teststate == DNSServer_Failed ? "(Failed)" :
1883 s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)");
1884 }
1885 }
1886
1887 LogMsgNoIdent("--------- Mcast Resolvers ----------");
1888 if (!mDNSStorage.McastResolvers) LogMsgNoIdent("<None>");
1889 else
1890 {
1891 for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next)
1892 LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout);
1893 }
1894
1895 mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
1896 LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
1897
1898 LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
1899 }
1900
1901 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1902
1903 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1904 {
1905 (void)port; // Unused
1906 (void)size; // Unused
1907 (void)info; // Unused
1908 mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
1909 mDNS *const m = &mDNSStorage;
1910
1911 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
1912 KQueueLock(m);
1913 switch(msg_header->msgh_id)
1914 {
1915 case SIGHUP: {
1916 mDNSu32 slot;
1917 CacheGroup *cg;
1918 CacheRecord *rr;
1919 LogMsg("SIGHUP: Purge cache");
1920 mDNS_Lock(m);
1921 FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
1922 // Restart unicast and multicast queries
1923 mDNSCoreRestartQueries(m);
1924 mDNS_Unlock(m);
1925 } break;
1926 case SIGINT:
1927 case SIGTERM: ExitCallback(msg_header->msgh_id); break;
1928 case SIGINFO: INFOCallback(); break;
1929 case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
1930 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
1931 WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
1932 break;
1933 case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
1934 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
1935 break;
1936 default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
1937 }
1938 KQueueUnlock(m, "Unix Signal");
1939 }
1940
1941 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
1942 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
1943 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
1944 {
1945 mStatus err;
1946 CFMachPortRef s_port;
1947 CFRunLoopSourceRef s_rls;
1948 CFRunLoopSourceRef d_rls;
1949
1950 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
1951 if (m_port != MACH_PORT_NULL)
1952 s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
1953 else
1954 {
1955 s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
1956 m_port = CFMachPortGetPort(s_port);
1957 kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
1958
1959 if (status)
1960 {
1961 if (status == 1103)
1962 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
1963 else
1964 LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status));
1965 return(status);
1966 }
1967 }
1968
1969 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
1970
1971 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
1972 rrcachestorage, RR_CACHE_SIZE,
1973 advertise,
1974 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
1975
1976 if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); }
1977
1978 client_death_port = CFMachPortGetPort(d_port);
1979
1980 s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
1981 CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode);
1982 CFRelease(s_rls);
1983
1984 d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
1985 CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode);
1986 CFRelease(d_rls);
1987
1988 CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL);
1989 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
1990 signal_port = CFMachPortGetPort(i_port);
1991 CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode);
1992 CFRelease(i_rls);
1993
1994 if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
1995 return(err);
1996 }
1997
1998 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1999
2000 // SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
2001 // The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
2002 // The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
2003 // of code from above. Alternatively we could remove the duplicated source code by having
2004 // single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM"
2005
2006 mDNSlocal void SignalDispatch(dispatch_source_t source)
2007 {
2008 int sig = (int)dispatch_source_get_handle(source);
2009 mDNS *const m = &mDNSStorage;
2010 KQueueLock(m);
2011 switch(sig)
2012 {
2013 case SIGHUP: {
2014 mDNSu32 slot;
2015 CacheGroup *cg;
2016 CacheRecord *rr;
2017 LogMsg("SIGHUP: Purge cache");
2018 mDNS_Lock(m);
2019 FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
2020 // Restart unicast and multicast queries
2021 mDNSCoreRestartQueries(m);
2022 mDNS_Unlock(m);
2023 } break;
2024 case SIGINT:
2025 case SIGTERM: ExitCallback(sig); break;
2026 case SIGINFO: INFOCallback(); break;
2027 case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
2028 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
2029 WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
2030 break;
2031 case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
2032 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
2033 break;
2034 default: LogMsg("SignalCallback: Unknown signal %d", sig); break;
2035 }
2036 KQueueUnlock(m, "Unix Signal");
2037 }
2038
2039 mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig)
2040 {
2041 signal(sig, SIG_IGN);
2042 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, queue);
2043
2044 if (source)
2045 {
2046 dispatch_source_set_event_handler(source, ^{SignalDispatch(source);});
2047 // Start processing signals
2048 dispatch_resume(source);
2049 }
2050 else
2051 {
2052 LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig);
2053 }
2054 }
2055
2056 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2057 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2058 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
2059 {
2060 mStatus err;
2061 CFMachPortRef s_port;
2062 dispatch_source_t mach_source;
2063 dispatch_queue_t queue = dispatch_get_main_queue();
2064
2065 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
2066 if (m_port != MACH_PORT_NULL)
2067 s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
2068 else
2069 {
2070 s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
2071 m_port = CFMachPortGetPort(s_port);
2072 kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
2073
2074 if (status)
2075 {
2076 if (status == 1103)
2077 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
2078 else
2079 LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status));
2080 return(status);
2081 }
2082 }
2083
2084 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
2085 rrcachestorage, RR_CACHE_SIZE,
2086 advertise,
2087 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
2088
2089 if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); }
2090
2091 mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, m_port, 0, queue);
2092 if (mach_source == mDNSNULL){LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;}
2093 dispatch_source_set_event_handler(mach_source, ^{
2094 dispatch_mig_server(mach_source, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem),
2095 DNSServiceDiscoveryRequest_server);
2096 });
2097 dispatch_resume(mach_source);
2098
2099 mDNSSetupSignal(queue, SIGHUP);
2100 mDNSSetupSignal(queue, SIGINT);
2101 mDNSSetupSignal(queue, SIGTERM);
2102 mDNSSetupSignal(queue, SIGINFO);
2103 mDNSSetupSignal(queue, SIGUSR1);
2104 mDNSSetupSignal(queue, SIGUSR2);
2105 mDNSSetupSignal(queue, SIGHUP);
2106
2107 // Create a custom handler for doing the housekeeping work. This is either triggered
2108 // by the timer or an event source
2109 PlatformStorage.custom = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
2110 if (PlatformStorage.custom == mDNSNULL){LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;}
2111 dispatch_source_set_event_handler(PlatformStorage.custom, ^{PrepareForIdle(&mDNSStorage);});
2112 dispatch_resume(PlatformStorage.custom);
2113
2114 // Create a timer source to trigger housekeeping work. The houskeeping work itself
2115 // is done in the custom handler that we set below.
2116
2117 PlatformStorage.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
2118 if (PlatformStorage.timer == mDNSNULL){LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;}
2119
2120 // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we
2121 // always reset the time to the new time computed. In effect, we ignore the interval
2122 dispatch_source_set_timer(PlatformStorage.timer, DISPATCH_TIME_NOW, 1000ull * 1000000000, 0);
2123 dispatch_source_set_event_handler(PlatformStorage.timer, ^{
2124 dispatch_source_merge_data(PlatformStorage.custom, 1);
2125 });
2126 dispatch_resume(PlatformStorage.timer);
2127
2128 LogMsg("DaemonIntialize done successfully");
2129
2130 if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
2131 return(err);
2132 }
2133
2134 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2135
2136 mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
2137 {
2138 mDNSs32 now = mDNS_TimeNow(m);
2139
2140 // 1. If we need to set domain secrets, do so before handling the network change
2141 // Detailed reason:
2142 // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list,
2143 // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens.
2144 if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0)
2145 {
2146 m->p->KeyChainTimer = 0;
2147 mDNS_Lock(m);
2148 SetDomainSecrets(m);
2149 mDNS_Unlock(m);
2150 }
2151
2152 // 2. If we have network change events to handle, do them before calling mDNS_Execute()
2153 // Detailed reason:
2154 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2155 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2156 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2157 // we then systematically lose our own looped-back packets.
2158 if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);
2159
2160 if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); }
2161
2162 // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do
2163 mDNSs32 nextevent = mDNS_Execute(m);
2164
2165 if (m->p->NetworkChanged)
2166 if (nextevent - m->p->NetworkChanged > 0)
2167 nextevent = m->p->NetworkChanged;
2168
2169 if (m->p->KeyChainTimer)
2170 if (nextevent - m->p->KeyChainTimer > 0)
2171 nextevent = m->p->KeyChainTimer;
2172
2173 if (m->p->RequestReSleep)
2174 if (nextevent - m->p->RequestReSleep > 0)
2175 nextevent = m->p->RequestReSleep;
2176
2177 // 4. Deliver any waiting browse messages to clients
2178 DNSServiceBrowser *b = DNSServiceBrowserList;
2179
2180 while (b)
2181 {
2182 // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2183 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2184 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2185 DNSServiceBrowser *x = b;
2186 b = b->next;
2187 if (x->results) // Try to deliver the list of results
2188 {
2189 while (x->results)
2190 {
2191 DNSServiceBrowserResult *const r = x->results;
2192 domainlabel name;
2193 domainname type, domain;
2194 DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance()
2195 char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2196 char ctype[MAX_ESCAPED_DOMAIN_NAME];
2197 char cdom [MAX_ESCAPED_DOMAIN_NAME];
2198 ConvertDomainLabelToCString_unescaped(&name, cname);
2199 ConvertDomainNameToCString(&type, ctype);
2200 ConvertDomainNameToCString(&domain, cdom);
2201 DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
2202 kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
2203 // If we failed to send the mach message, try again in one second
2204 if (status == MACH_SEND_TIMED_OUT)
2205 {
2206 if (nextevent - now > mDNSPlatformOneSecond)
2207 nextevent = now + mDNSPlatformOneSecond;
2208 break;
2209 }
2210 else
2211 {
2212 x->lastsuccess = now;
2213 x->results = x->results->next;
2214 freeL("DNSServiceBrowserResult", r);
2215 }
2216 }
2217 // If this client hasn't read a single message in the last 60 seconds, abort it
2218 if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
2219 AbortBlockedClient(x->ClientMachPort, "browse", x);
2220 }
2221 }
2222
2223 DNSServiceResolver *l;
2224 for (l = DNSServiceResolverList; l; l=l->next)
2225 if (l->ReportTime && now - l->ReportTime >= 0)
2226 {
2227 l->ReportTime = 0;
2228 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2229 "This places considerable burden on the network.", l->i.name.c);
2230 }
2231
2232 if (m->p->NotifyUser)
2233 {
2234 if (m->p->NotifyUser - now < 0)
2235 {
2236 if (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c))
2237 {
2238 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c);
2239 mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
2240 m->p->usernicelabel = m->nicelabel;
2241 }
2242 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
2243 {
2244 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
2245 mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
2246 m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful
2247 m->p->userhostlabel = m->hostlabel;
2248 }
2249 m->p->NotifyUser = 0;
2250 }
2251 else
2252 if (nextevent - m->p->NotifyUser > 0)
2253 nextevent = m->p->NotifyUser;
2254 }
2255
2256 return(nextevent);
2257 }
2258
2259 // Right now we consider *ALL* of our DHCP leases
2260 // It might make sense to be a bit more selective and only consider the leases on interfaces
2261 // (a) that are capable and enabled for wake-on-LAN, and
2262 // (b) where we have found (and successfully registered with) a Sleep Proxy
2263 // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
2264 mDNSlocal mDNSu32 DHCPWakeTime(void)
2265 {
2266 mDNSu32 e = 24 * 3600; // Maximum maintenance wake interval is 24 hours
2267 const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
2268 if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
2269 else
2270 {
2271 const SCPreferencesRef prefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:DHCPWakeTime"), NULL);
2272 if (!prefs) LogMsg("DHCPWakeTime: SCPreferencesCreate failed");
2273 else
2274 {
2275 const SCNetworkSetRef currentset = SCNetworkSetCopyCurrent(prefs);
2276 if (!currentset) LogMsg("DHCPWakeTime: SCNetworkSetCopyCurrent failed");
2277 else
2278 {
2279 const CFArrayRef services = SCNetworkSetCopyServices(currentset);
2280 if (!services) LogMsg("DHCPWakeTime: SCNetworkSetCopyServices failed");
2281 else
2282 {
2283 int i;
2284 for (i = 0; i < CFArrayGetCount(services); i++)
2285 {
2286 const SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, i);
2287 if (!service) LogMsg("DHCPWakeTime: CFArrayGetValueAtIndex %d failed", i);
2288 else
2289 {
2290 const CFStringRef serviceid = SCNetworkServiceGetServiceID(service);
2291 if (!serviceid) LogMsg("DHCPWakeTime: SCNetworkServiceGetServiceID %d failed", i);
2292 else
2293 {
2294 // Note: It's normal for this call to return NULL, for interfaces not using DHCP
2295 const CFDictionaryRef dhcp = SCDynamicStoreCopyDHCPInfo(NULL, serviceid);
2296 if (dhcp)
2297 {
2298 const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp);
2299 const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51); // Option 51 = IP Address Lease Time
2300 if (!start || !lease || CFDataGetLength(lease) < 4)
2301 LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
2302 "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
2303 i, start, lease, lease ? CFDataGetLength(lease) : 0);
2304 else
2305 {
2306 const UInt8 *d = CFDataGetBytePtr(lease);
2307 if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", i);
2308 else
2309 {
2310 const mDNSu32 elapsed = now - CFDateGetAbsoluteTime(start);
2311 const mDNSu32 lifetime = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]);
2312 const mDNSu32 remaining = lifetime - elapsed;
2313 const mDNSu32 wake = remaining > 60 ? remaining - remaining/10 : 54; // Wake at 90% of the lease time
2314 LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake);
2315 if (e > wake) e = wake;
2316 }
2317 }
2318 CFRelease(dhcp);
2319 }
2320 }
2321 }
2322 }
2323 CFRelease(services);
2324 }
2325 CFRelease(currentset);
2326 }
2327 CFRelease(prefs);
2328 }
2329 }
2330 return(e);
2331 }
2332
2333 // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
2334 // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
2335 // If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
2336 // for a few seconds and then waking again is silly and annoying.
2337 // If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
2338 // Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
2339 // allowing us an adequate safety margin to renew our lease before we lose it.
2340
2341 mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now)
2342 {
2343 mDNSBool ready = mDNSCoreReadyForSleep(m, now);
2344 if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse);
2345
2346 m->p->WakeAtUTC = 0;
2347 int result = kIOReturnSuccess;
2348 CFDictionaryRef opts = NULL;
2349
2350 // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
2351 // do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
2352 if (!m->SleepState)
2353 LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now);
2354 else
2355 {
2356 if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m))
2357 LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
2358 m->SystemWakeOnLANEnabled ? "is" : "not",
2359 mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no");
2360 else
2361 {
2362 mDNSs32 dhcp = DHCPWakeTime();
2363 LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp);
2364 mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now) / mDNSPlatformOneSecond;
2365 if (interval > dhcp) interval = dhcp;
2366
2367 // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
2368 // transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
2369 // a single SPS failure could result in a remote machine falling permanently asleep, requiring
2370 // someone to go to the machine in person to wake it up again, which would be unacceptable.
2371 if (!ready && interval > 3600) interval = 3600;
2372
2373 //interval = 48; // For testing
2374
2375 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2376 if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that
2377 {
2378 const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval);
2379 if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed");
2380 else
2381 {
2382 const mDNSs32 reqs = kIOPMSystemPowerStateCapabilityNetwork;
2383 const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs);
2384 if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed");
2385 else
2386 {
2387 const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") };
2388 const void *OptionVals[2] = { WakeDate, Requirements };
2389 opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2390 if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
2391 CFRelease(Requirements);
2392 }
2393 CFRelease(WakeDate);
2394 }
2395 LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval);
2396 }
2397 else // else schedule the wakeup using the old API instead to
2398 #endif
2399 {
2400 // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
2401 // so we should put it back to sleep. To avoid frustrating the user, we always request at least
2402 // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
2403 // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
2404 if (interval < 60) interval = 60;
2405
2406 result = mDNSPowerRequest(1, interval);
2407
2408 if (result == kIOReturnNotReady)
2409 {
2410 int r;
2411 LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
2412 // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
2413 // requested wake time is "too soon", but there's no API to find out what constitutes
2414 // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
2415 // we just have to iterate with successively longer intervals until it doesn't fail.
2416 // We preserve the value of "result" because if our original power request was deemed "too soon"
2417 // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request,
2418 // since the implication is that the system won't manage to be awake again at the time we need it.
2419 do
2420 {
2421 interval += (interval < 20) ? 1 : ((interval+3) / 4);
2422 r = mDNSPowerRequest(1, interval);
2423 }
2424 while (r == kIOReturnNotReady);
2425 if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r);
2426 else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval);
2427 }
2428 else
2429 {
2430 if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
2431 else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
2432 }
2433 m->p->WakeAtUTC = mDNSPlatformUTC() + interval;
2434 }
2435 }
2436
2437 m->SleepState = SleepState_Sleeping;
2438 // We used to clear our interface list to empty state here before going to sleep.
2439 // The applications that try to connect to an external server during maintenance wakes, saw
2440 // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable
2441 // flag). Thus, we don't remove our interfaces anymore on sleep.
2442 }
2443
2444 LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
2445 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2446 (m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" :
2447 #endif
2448 (result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange",
2449 m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now);
2450
2451 m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above
2452
2453 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2454 if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts);
2455 else
2456 #endif
2457 if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie);
2458 else IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie);
2459
2460 if (opts) CFRelease(opts);
2461 return(mDNStrue);
2462 }
2463
2464 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2465
2466 mDNSexport void TriggerEventCompletion()
2467 {
2468 debugf("TriggerEventCompletion: Merge data");
2469 dispatch_source_merge_data(PlatformStorage.custom, 1);
2470 }
2471
2472 mDNSlocal void PrepareForIdle(void *m_param)
2473 {
2474 mDNS *m = m_param;
2475 int64_t time_offset;
2476 dispatch_time_t dtime;
2477
2478 const int multiplier = 1000000000 / mDNSPlatformOneSecond;
2479
2480 // This is the main work loop:
2481 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2482 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2483 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2484
2485 debugf("PrepareForIdle: called");
2486 // Run mDNS_Execute to find out the time we next need to wake up
2487 mDNSs32 start = mDNSPlatformRawTime();
2488 mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
2489 mDNSs32 end = mDNSPlatformRawTime();
2490 if (end - start >= WatchDogReportingThreshold)
2491 LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end - start);
2492
2493 mDNSs32 now = mDNS_TimeNow(m);
2494
2495 if (m->ShutdownTime)
2496 {
2497 if (mDNSStorage.ResourceRecords)
2498 {
2499 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords));
2500 if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2501 }
2502 if (mDNS_ExitNow(m, now))
2503 {
2504 if (!mDNSStorage.ResourceRecords)
2505 safe_vproc_transaction_end();
2506 LogInfo("IdleLoop: mDNS_FinalExit");
2507 mDNS_FinalExit(&mDNSStorage);
2508 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2509 exit(0);
2510 }
2511 if (nextTimerEvent - m->ShutdownTime >= 0)
2512 nextTimerEvent = m->ShutdownTime;
2513 }
2514
2515 if (m->SleepLimit)
2516 if (!AllowSleepNow(m, now))
2517 if (nextTimerEvent - m->SleepLimit >= 0)
2518 nextTimerEvent = m->SleepLimit;
2519
2520 // Convert absolute wakeup time to a relative time from now
2521 mDNSs32 ticks = nextTimerEvent - now;
2522 if (ticks < 1) ticks = 1;
2523
2524 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
2525 if (ticks > 1)
2526 RepeatedBusy = 0;
2527 else
2528 {
2529 ticks = 1;
2530 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
2531 }
2532
2533 time_offset = ((mDNSu32)ticks / mDNSPlatformOneSecond) * 1000000000 + (ticks % mDNSPlatformOneSecond) * multiplier;
2534 dtime = dispatch_time(DISPATCH_TIME_NOW, time_offset);
2535 dispatch_source_set_timer(PlatformStorage.timer, dtime, 1000ull*1000000000, 0);
2536 debugf("PrepareForIdle: scheduling timer with ticks %d", ticks);
2537 return;
2538 }
2539
2540 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2541
2542 mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context)
2543 {
2544 // Read all of the bytes so we won't wake again.
2545 char buffer[100];
2546 while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue;
2547 }
2548
2549 mDNSlocal void * KQueueLoop(void *m_param)
2550 {
2551 mDNS *m = m_param;
2552 int numevents = 0;
2553
2554 #if USE_SELECT_WITH_KQUEUEFD
2555 fd_set readfds;
2556 FD_ZERO(&readfds);
2557 const int multiplier = 1000000 / mDNSPlatformOneSecond;
2558 #else
2559 const int multiplier = 1000000000 / mDNSPlatformOneSecond;
2560 #endif
2561
2562 pthread_mutex_lock(&PlatformStorage.BigMutex);
2563 LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last);
2564
2565 // This is the main work loop:
2566 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2567 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2568 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2569 // (4) On wakeup we first process *all* events
2570 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2571 for ( ; ; )
2572 {
2573 #define kEventsToReadAtOnce 1
2574 struct kevent new_events[kEventsToReadAtOnce];
2575
2576 // Run mDNS_Execute to find out the time we next need to wake up
2577 mDNSs32 start = mDNSPlatformRawTime();
2578 mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
2579 mDNSs32 end = mDNSPlatformRawTime();
2580 if (end - start >= WatchDogReportingThreshold)
2581 LogInfo("WARNING: Idle task took %dms to complete", end - start);
2582
2583 mDNSs32 now = mDNS_TimeNow(m);
2584
2585 if (m->ShutdownTime)
2586 {
2587 if (mDNSStorage.ResourceRecords)
2588 {
2589 AuthRecord *rr;
2590 for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
2591 {
2592 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, rr));
2593 if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2594 }
2595 }
2596 if (mDNS_ExitNow(m, now))
2597 {
2598 if (!mDNSStorage.ResourceRecords)
2599 safe_vproc_transaction_end();
2600 LogInfo("mDNS_FinalExit");
2601 mDNS_FinalExit(&mDNSStorage);
2602 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2603 exit(0);
2604 }
2605 if (nextTimerEvent - m->ShutdownTime >= 0)
2606 nextTimerEvent = m->ShutdownTime;
2607 }
2608
2609 if (m->SleepLimit)
2610 if (!AllowSleepNow(m, now))
2611 if (nextTimerEvent - m->SleepLimit >= 0)
2612 nextTimerEvent = m->SleepLimit;
2613
2614 // Convert absolute wakeup time to a relative time from now
2615 mDNSs32 ticks = nextTimerEvent - now;
2616 if (ticks < 1) ticks = 1;
2617
2618 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
2619 if (ticks > 1)
2620 RepeatedBusy = 0;
2621 else
2622 {
2623 ticks = 1;
2624 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
2625 }
2626
2627 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks);
2628 numevents = 0;
2629
2630 // Release the lock, and sleep until:
2631 // 1. Something interesting happens like a packet arriving, or
2632 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
2633 // 3. The timeout expires
2634 pthread_mutex_unlock(&PlatformStorage.BigMutex);
2635
2636 #if USE_SELECT_WITH_KQUEUEFD
2637 struct timeval timeout;
2638 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
2639 timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier;
2640 FD_SET(KQueueFD, &readfds);
2641 if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0)
2642 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
2643 #else
2644 struct timespec timeout;
2645 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
2646 timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier;
2647 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
2648 // and have it work similarly to the way it does with nevents non-zero --
2649 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
2650 // In fact, what happens if you do this is that it just returns immediately. So, we have
2651 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
2652 if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0)
2653 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
2654 #endif
2655
2656 pthread_mutex_lock(&PlatformStorage.BigMutex);
2657 // We have to ignore the event we may have been told about above, because that
2658 // was done without holding the lock, and between the time we woke up and the
2659 // time we reclaimed the lock the other thread could have done something that
2660 // makes the event no longer valid. Now we have the lock, we call kevent again
2661 // and this time we can safely process the events it tells us about.
2662
2663 static const struct timespec zero_timeout = { 0, 0 };
2664 int events_found;
2665 while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0)
2666 {
2667 if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR))
2668 {
2669 // Not sure what to do here, our kqueue has failed us - this isn't ideal
2670 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno));
2671 exit(errno);
2672 }
2673
2674 numevents += events_found;
2675
2676 int i;
2677 for (i = 0; i < events_found; i++)
2678 {
2679 const KQueueEntry *const kqentry = new_events[i].udata;
2680 mDNSs32 stime = mDNSPlatformRawTime();
2681 const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task
2682 kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext);
2683 mDNSs32 etime = mDNSPlatformRawTime();
2684 if (etime - stime >= WatchDogReportingThreshold)
2685 LogInfo("WARNING: %s took %dms to complete", KQtask, etime - stime);
2686 }
2687 }
2688 }
2689
2690 return NULL;
2691 }
2692
2693 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2694
2695 mDNSlocal void LaunchdCheckin(void)
2696 {
2697 launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
2698 launch_data_t resp = launch_msg(msg);
2699 launch_data_free(msg);
2700 if (!resp) { LogMsg("launch_msg returned NULL"); return; }
2701
2702 if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
2703 {
2704 int err = launch_data_get_errno(resp);
2705 // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
2706 if (err != EACCES) LogMsg("launch_msg returned %d", err);
2707 else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err);
2708 }
2709 else
2710 {
2711 launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
2712 if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
2713 else
2714 {
2715 launch_data_t skt = launch_data_dict_lookup(skts, "Listeners");
2716 if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL");
2717 else
2718 {
2719 launchd_fds_count = launch_data_array_get_count(skt);
2720 if (launchd_fds_count == 0) LogMsg("launch_data_array_get_count(skt) returned 0");
2721 else
2722 {
2723 launchd_fds = mallocL("LaunchdCheckin", sizeof(dnssd_sock_t) * launchd_fds_count);
2724 if (!launchd_fds) LogMsg("LaunchdCheckin: malloc failed");
2725 else
2726 {
2727 size_t i;
2728 for(i = 0; i < launchd_fds_count; i++)
2729 {
2730 launch_data_t s = launch_data_array_get_index(skt, i);
2731 if (!s)
2732 {
2733 launchd_fds[i] = dnssd_InvalidSocket;
2734 LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i);
2735 }
2736 else
2737 {
2738 launchd_fds[i] = launch_data_get_fd(s);
2739 LogInfo("Launchd Unix Domain Socket [%d]: %d", i, launchd_fds[i]);
2740 }
2741 }
2742 }
2743 // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
2744 chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH);
2745 }
2746 }
2747 }
2748
2749 launch_data_t ports = launch_data_dict_lookup(resp, "MachServices");
2750 if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL");
2751 else
2752 {
2753 launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder");
2754 if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL");
2755 else
2756 {
2757 m_port = launch_data_get_fd(p);
2758 LogInfo("Launchd Mach Port: %d", m_port);
2759 if (m_port == ~0U) m_port = MACH_PORT_NULL;
2760 }
2761 }
2762 }
2763 launch_data_free(resp);
2764 }
2765
2766 mDNSlocal void DropPrivileges(void)
2767 {
2768 static const char login[] = "_mdnsresponder";
2769 struct passwd *pwd = getpwnam(login);
2770 if (NULL == pwd)
2771 LogMsg("Could not find account name \"%s\". Running as root.", login);
2772 else
2773 {
2774 uid_t uid = pwd->pw_uid;
2775 gid_t gid = pwd->pw_gid;
2776
2777 LogMsg("Started as root. Switching to userid \"%s\".", login);
2778
2779 if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno));
2780 else
2781 {
2782 static char path[] = "/var/run/mdns/mDNSResponder";
2783 char *p = strrchr(path, '/');
2784 *p = '\0';
2785 if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno));
2786 else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno));
2787 else
2788 {
2789 *p = '/';
2790 if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno));
2791 else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno));
2792 else LogInfo("DropPrivileges: Created subdirectory and symlink");
2793 }
2794 }
2795
2796 if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login, (unsigned long)gid);
2797 if (0 != setgid(gid)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid());
2798 if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid);
2799 }
2800 }
2801
2802 extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));
2803
2804 mDNSexport int main(int argc, char **argv)
2805 {
2806 int i;
2807 kern_return_t status;
2808
2809 mDNSMacOSXSystemBuildNumber(NULL);
2810 LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
2811
2812 #if 0
2813 LogMsg("CacheRecord %d", sizeof(CacheRecord));
2814 LogMsg("CacheGroup %d", sizeof(CacheGroup));
2815 LogMsg("ResourceRecord %d", sizeof(ResourceRecord));
2816 LogMsg("RData_small %d", sizeof(RData_small));
2817
2818 LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity));
2819 LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE);
2820 LogMsg("block usage %d", sizeof(CacheEntity) * RR_CACHE_SIZE);
2821 LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE);
2822 #endif
2823
2824 safe_vproc_transaction_begin();
2825
2826 if (0 == geteuid()) DropPrivileges();
2827
2828 for (i=1; i<argc; i++)
2829 {
2830 if (!strcasecmp(argv[i], "-d" )) mDNS_DebugMode = mDNStrue;
2831 if (!strcasecmp(argv[i], "-launchd" )) started_via_launchdaemon = mDNStrue;
2832 if (!strcasecmp(argv[i], "-launchdaemon" )) started_via_launchdaemon = mDNStrue;
2833 if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) advertise = mDNS_Init_DontAdvertiseLocalAddresses;
2834 if (!strcasecmp(argv[i], "-DisableSleepProxyClient" )) DisableSleepProxyClient = mDNStrue;
2835 if (!strcasecmp(argv[i], "-DebugLogging" )) mDNS_LoggingEnabled = mDNStrue;
2836 if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue;
2837 if (!strcasecmp(argv[i], "-OfferSleepProxyService" ))
2838 OfferSleepProxyService = (i+1 < argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 100;
2839 if (!strcasecmp(argv[i], "-UseInternalSleepProxy" ))
2840 UseInternalSleepProxy = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && argv[i+1][1]==0) ? atoi(argv[++i]) : 1;
2841 if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue;
2842 if (!strcasecmp(argv[i], "-DisableInboundRelay" )) DisableInboundRelayConnection = mDNStrue;
2843 if (!strcasecmp(argv[i], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains = mDNStrue;
2844 }
2845
2846 // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
2847 if (!advertise) LogMsg("Administratively prohibiting multicast advertisements");
2848
2849 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2850
2851 signal(SIGHUP, HandleSIG); // (Debugging) Purge the cache to check for cache handling bugs
2852 signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
2853 signal(SIGPIPE, SIG_IGN ); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
2854 signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
2855 signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog
2856 signal(SIGUSR1, HandleSIG); // (Debugging) Enable Logging
2857 signal(SIGUSR2, HandleSIG); // (Debugging) Enable Packet Logging
2858
2859 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2860
2861 mDNSStorage.p = &PlatformStorage; // Make sure mDNSStorage.p is set up, because validatelists uses it
2862 LaunchdCheckin();
2863
2864 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
2865 if (!mDNS_DebugMode && !started_via_launchdaemon)
2866 {
2867 registerBootstrapService();
2868 if (!restarting_via_mach_init) exit(0); // mach_init will restart us immediately as a daemon
2869 int fd = open(_PATH_DEVNULL, O_RDWR, 0);
2870 if (fd < 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno, strerror(errno));
2871 else
2872 {
2873 // Avoid unnecessarily duplicating a file descriptor to itself
2874 if (fd != STDIN_FILENO) if (dup2(fd, STDIN_FILENO) < 0) LogMsg("dup2(fd, STDIN_FILENO) failed errno %d (%s)", errno, strerror(errno));
2875 if (fd != STDOUT_FILENO) if (dup2(fd, STDOUT_FILENO) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno, strerror(errno));
2876 if (fd != STDERR_FILENO) if (dup2(fd, STDERR_FILENO) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno, strerror(errno));
2877 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) (void)close(fd);
2878 }
2879 }
2880
2881 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2882
2883 // Create the kqueue, mutex and thread to support KQSockets
2884 KQueueFD = kqueue();
2885 if (KQueueFD == -1) { LogMsg("kqueue() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2886
2887 i = pthread_mutex_init(&PlatformStorage.BigMutex, NULL);
2888 if (i == -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2889
2890 int fdpair[2] = {0, 0};
2891 i = socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair);
2892 if (i == -1) { LogMsg("socketpair() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2893
2894 // Socket pair returned us two identical sockets connected to each other
2895 // We will use the first socket to send the second socket. The second socket
2896 // will be added to the kqueue so it will wake when data is sent.
2897 static const KQueueEntry wakeKQEntry = { KQWokenFlushBytes, NULL, "kqueue wakeup after CFRunLoop event" };
2898
2899 PlatformStorage.WakeKQueueLoopFD = fdpair[0];
2900 KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
2901
2902 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2903
2904 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
2905 #if MDNS_NO_SANDBOX
2906 LogMsg("Note: Compiled without Apple Sandbox support");
2907 #else MDNS_NO_SANDBOX
2908 if (!sandbox_init)
2909 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
2910 else
2911 {
2912 char *sandbox_msg;
2913 uint64_t sandbox_flags = SANDBOX_NAMED;
2914
2915 // On Desktop, the sandbox profile always exists under /usr/share/sandbox, no need to
2916 // check it. stat results in calls to opendirectoryd and to avoid deadlocks with
2917 // opendirectoryd early on, we avoid this call.
2918 #if TARGET_OS_EMBEDDED
2919 struct stat s;
2920 if (stat("/usr/share/sandbox/mDNSResponder.sb", &s) == 0)
2921 {
2922 sandbox_flags = SANDBOX_NAMED_EXTERNAL;
2923 LogInfo("Will load Sandbox profile from filesystem");
2924 }
2925 #endif
2926
2927 int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg);
2928 if (sandbox_err) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg); sandbox_free_error(sandbox_msg); }
2929 else LogInfo("Now running under Apple Sandbox restrictions");
2930 }
2931 #endif MDNS_NO_SANDBOX
2932
2933 status = mDNSDaemonInitialize();
2934 if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }
2935
2936 status = udsserver_init(launchd_fds, launchd_fds_count);
2937 if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
2938
2939 mDNSMacOSXNetworkChanged(&mDNSStorage);
2940
2941 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2942 LogInfo("Daemon Start: Using LibDispatch");
2943 // CFRunLoopRun runs both CFRunLoop sources and dispatch sources
2944 CFRunLoopRun();
2945 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2946 // Start the kqueue thread
2947 pthread_t KQueueThread;
2948 i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
2949 if (i == -1) { LogMsg("pthread_create() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2950 if (status == 0)
2951 {
2952 CFRunLoopRun();
2953 LogMsg("ERROR: CFRunLoopRun Exiting.");
2954 mDNS_Close(&mDNSStorage);
2955 }
2956 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2957
2958 LogMsg("%s exiting", mDNSResponderVersionString);
2959
2960 exit:
2961 if (!mDNS_DebugMode && !started_via_launchdaemon) destroyBootstrapService();
2962 return(status);
2963 }
2964
2965 // uds_daemon.c support routines /////////////////////////////////////////////
2966
2967 // Arrange things so that when data appears on fd, callback is called with context
2968 mDNSexport mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data)
2969 {
2970 KQSocketEventSource **p = &gEventSources;
2971 (void) platform_data;
2972 while (*p && (*p)->fd != fd) p = &(*p)->next;
2973 if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; }
2974
2975 KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource);
2976 if (!newSource) return mStatus_NoMemoryErr;
2977
2978 newSource->next = mDNSNULL;
2979 newSource->fd = fd;
2980 newSource->kqs.KQcallback = callback;
2981 newSource->kqs.KQcontext = context;
2982 newSource->kqs.KQtask = "UDS client";
2983 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2984 newSource->kqs.readSource = mDNSNULL;
2985 newSource->kqs.writeSource = mDNSNULL;
2986 newSource->kqs.fdClosed = mDNSfalse;
2987 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2988
2989 if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
2990 {
2991 *p = newSource;
2992 return mStatus_NoError;
2993 }
2994
2995 LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno));
2996 freeL("KQSocketEventSource", newSource);
2997 return mStatus_BadParamErr;
2998 }
2999
3000 int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data)
3001 {
3002 (void) platform_data;
3003 return recv(fd, buf, len, flags);
3004 }
3005
3006 mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor
3007 {
3008 KQSocketEventSource **p = &gEventSources;
3009 (void) platform_data;
3010 while (*p && (*p)->fd != fd) p = &(*p)->next;
3011 if (*p)
3012 {
3013 KQSocketEventSource *s = *p;
3014 *p = (*p)->next;
3015 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
3016 // causes the kernel to automatically remove any associated kevents
3017 mDNSPlatformCloseFD(&s->kqs, s->fd);
3018 freeL("KQSocketEventSource", s);
3019 return mStatus_NoError;
3020 }
3021 LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd);
3022 return mStatus_NoSuchNameErr;
3023 }
3024
3025 #if _BUILDING_XCODE_PROJECT_
3026 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
3027 const char *__crashreporter_info__ = mDNSResponderVersionString;
3028 asm(".desc ___crashreporter_info__, 0x10");
3029 #endif
3030
3031 // For convenience when using the "strings" command, this is the last thing in the file
3032 // The "@(#) " pattern is a special prefix the "what" command looks for
3033 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";