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