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