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