1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.)
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
34 #include <mach/mach.h>
35 #include <mach/mach_error.h>
36 #include <servers/bootstrap.h>
37 #include <sys/types.h>
45 #include <sys/event.h>
48 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
49 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
51 #if TARGET_OS_EMBEDDED
52 #include <bootstrap_priv.h>
54 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
57 #include "DNSServiceDiscoveryRequestServer.h"
58 #include "DNSServiceDiscoveryReply.h"
61 #include "DNSCommon.h"
62 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
64 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
66 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
68 #include "safe_vproc.h"
70 //*************************************************************************************************************
71 #if COMPILER_LIKES_PRAGMA_MARK
72 #pragma mark - Globals
75 static mDNS_PlatformSupport PlatformStorage
;
77 // Start off with a default cache of 16K (99 records)
78 // Each time we grow the cache we add another 99 records
79 // 99 * 164 = 16236 bytes.
80 // This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead
81 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
82 static CacheEntity rrcachestorage
[RR_CACHE_SIZE
];
84 static const char kmDNSBootstrapName
[] = "com.apple.mDNSResponderRestart";
85 static mach_port_t m_port
= MACH_PORT_NULL
;
87 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
88 mDNSlocal
void PrepareForIdle(void *m_param
);
89 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
90 static mach_port_t client_death_port
= MACH_PORT_NULL
;
91 static mach_port_t signal_port
= MACH_PORT_NULL
;
92 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
94 static mach_port_t server_priv_port
= MACH_PORT_NULL
;
96 static dnssd_sock_t
*launchd_fds
= mDNSNULL
;
97 static mDNSu32 launchd_fds_count
= 0;
99 // mDNS Mach Message Timeout, in milliseconds.
100 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
101 // fails to service its mach message queue, but long enough to give a well-written
102 // client a chance to service its mach message queue without getting cut off.
103 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
104 // even extra-slow clients a fair chance before we cut them off.
105 #define MDNS_MM_TIMEOUT 250
107 static int restarting_via_mach_init
= 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism
108 static int started_via_launchdaemon
= 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd
109 static mDNSBool advertise
= mDNS_Init_AdvertiseLocalAddresses
; // By default, advertise addresses (& other records) via multicast
111 extern mDNSBool StrictUnicastOrdering
;
112 extern mDNSBool AlwaysAppendSearchDomains
;
114 //*************************************************************************************************************
115 #if COMPILER_LIKES_PRAGMA_MARK
117 #pragma mark - Active client list structures
120 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
121 struct DNSServiceDomainEnumeration_struct
123 DNSServiceDomainEnumeration
*next
;
124 mach_port_t ClientMachPort
;
125 DNSQuestion dom
; // Question asking for domains
126 DNSQuestion def
; // Question asking for default domain
129 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult
;
130 struct DNSServiceBrowserResult_struct
132 DNSServiceBrowserResult
*next
;
137 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
139 typedef struct DNSServiceBrowserQuestion
141 struct DNSServiceBrowserQuestion
*next
;
144 } DNSServiceBrowserQuestion
;
146 struct DNSServiceBrowser_struct
148 DNSServiceBrowser
*next
;
149 mach_port_t ClientMachPort
;
150 DNSServiceBrowserQuestion
*qlist
;
151 DNSServiceBrowserResult
*results
;
153 mDNSBool DefaultDomain
; // was the browse started on an explicit domain?
154 domainname type
; // registration type
157 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
158 struct DNSServiceResolver_struct
160 DNSServiceResolver
*next
;
161 mach_port_t ClientMachPort
;
167 // A single registered service: ServiceRecordSet + bookkeeping
168 // Note that we duplicate some fields from parent DNSServiceRegistration object
169 // to facilitate cleanup, when instances and parent may be deallocated at different times.
170 typedef struct ServiceInstance
172 struct ServiceInstance
*next
;
173 mach_port_t ClientMachPort
;
174 mDNSBool autoname
; // Set if this name is tied to the Computer Name
175 mDNSBool renameonmemfree
; // Set if we just got a name conflict and now need to automatically pick a new name
178 ServiceRecordSet srs
;
179 // Don't add any fields after ServiceRecordSet.
180 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
183 // A client-created service. May reference several ServiceInstance objects if default
184 // settings cause registration in multiple domains.
185 typedef struct DNSServiceRegistration
187 struct DNSServiceRegistration
*next
;
188 mach_port_t ClientMachPort
;
189 mDNSBool DefaultDomain
;
193 char regtype
[MAX_ESCAPED_DOMAIN_NAME
]; // for use in AllocateSubtypes
194 domainlabel name
; // used only if autoname is false
197 unsigned char txtinfo
[1024];
200 ServiceInstance
*regs
;
201 } DNSServiceRegistration
;
203 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
204 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
205 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
206 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
208 // We keep a list of client-supplied event sources in KQSocketEventSource records
209 typedef struct KQSocketEventSource
211 struct KQSocketEventSource
*next
;
214 } KQSocketEventSource
;
216 static KQSocketEventSource
*gEventSources
;
218 //*************************************************************************************************************
219 #if COMPILER_LIKES_PRAGMA_MARK
221 #pragma mark - General Utility Functions
224 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
226 char _malloc_options
[] = "AXZ";
228 mDNSexport
void LogMemCorruption(const char *format
, ...)
232 va_start(ptr
,format
);
233 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
235 LogMsg("!!!! %s !!!!", buffer
);
236 NotifyOfElusiveBug("Memory Corruption", buffer
);
238 *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
242 mDNSlocal
void validatelists(mDNS
*const m
)
245 KQSocketEventSource
*k
;
246 for (k
= gEventSources
; k
; k
=k
->next
)
247 if (k
->next
== (KQSocketEventSource
*)~0 || k
->fd
< 0)
248 LogMemCorruption("gEventSources: %p is garbage (%d)", k
, k
->fd
);
250 // Check Mach client lists
251 DNSServiceDomainEnumeration
*e
;
252 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
253 if (e
->next
== (DNSServiceDomainEnumeration
*)~0 || e
->ClientMachPort
== 0 || e
->ClientMachPort
== (mach_port_t
)~0)
254 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e
, e
->ClientMachPort
);
256 DNSServiceBrowser
*b
;
257 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
258 if (b
->next
== (DNSServiceBrowser
*)~0 || b
->ClientMachPort
== 0 || b
->ClientMachPort
== (mach_port_t
)~0)
259 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b
, b
->ClientMachPort
);
261 DNSServiceResolver
*l
;
262 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
263 if (l
->next
== (DNSServiceResolver
*)~0 || l
->ClientMachPort
== 0 || l
->ClientMachPort
== (mach_port_t
)~0)
264 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l
, l
->ClientMachPort
);
266 DNSServiceRegistration
*r
;
267 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
268 if (r
->next
== (DNSServiceRegistration
*)~0 || r
->ClientMachPort
== 0 || r
->ClientMachPort
== (mach_port_t
)~0)
269 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r
, r
->ClientMachPort
);
271 // Check Unix Domain Socket client lists (uds_daemon.c)
274 // Check core mDNS lists
276 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
278 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
279 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
280 if (rr
->resrec
.name
!= &rr
->namestorage
)
281 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
282 rr
, rr
->resrec
.name
->c
, rr
->namestorage
.c
, rr
->namestorage
.c
);
285 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
)
286 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
287 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
289 rr
= m
->NewLocalRecords
;
291 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
292 LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
294 rr
= m
->CurrentRecord
;
296 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
297 LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
300 for (q
= m
->Questions
; q
; q
=q
->next
)
301 if (q
->next
== (DNSQuestion
*)~0 || q
->ThisQInterval
== (mDNSs32
)~0)
302 LogMemCorruption("Questions list: %p is garbage (%lX %p)", q
, q
->ThisQInterval
, q
->next
);
307 FORALL_CACHERECORDS(slot
, cg
, cr
)
309 if (cr
->resrec
.RecordType
== 0 || cr
->resrec
.RecordType
== 0xFF)
310 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot
, cr
, cr
->resrec
.RecordType
);
311 if (cr
->CRActiveQuestion
)
313 for (q
= m
->Questions
; q
; q
=q
->next
) if (q
== cr
->CRActiveQuestion
) break;
314 if (!q
) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot
, cr
->CRActiveQuestion
, CRDisplayString(m
, cr
));
318 // Check core uDNS lists
319 udns_validatelists(m
);
321 // Check platform-layer lists
322 NetworkInterfaceInfoOSX
*i
;
323 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
324 if (i
->next
== (NetworkInterfaceInfoOSX
*)~0 || !i
->m
|| i
->m
== (mDNS
*)~0)
325 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i
, i
->ifinfo
.ifname
);
328 for (t
= m
->TunnelClients
; t
; t
=t
->next
)
329 if (t
->next
== (ClientTunnel
*)~0 || t
->dstname
.c
[0] > 63)
330 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t
, t
->dstname
.c
[0]);
333 mDNSexport
void *mallocL(char *msg
, unsigned int size
)
335 // Allocate space for two words of sanity checking data before the requested block
336 mDNSu32
*mem
= malloc(sizeof(mDNSu32
) * 2 + size
);
338 { LogMsg("malloc( %s : %d ) failed", msg
, size
); return(NULL
); }
341 if (size
> 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg
, size
, &mem
[2]);
342 else if (MACOSX_MDNS_MALLOC_DEBUGGING
>= 2) LogMsg("malloc( %s : %lu ) = %p", msg
, size
, &mem
[2]);
345 //mDNSPlatformMemZero(&mem[2], size);
346 memset(&mem
[2], 0xFF, size
);
347 validatelists(&mDNSStorage
);
352 mDNSexport
void freeL(char *msg
, void *x
)
355 LogMsg("free( %s @ NULL )!", msg
);
358 mDNSu32
*mem
= ((mDNSu32
*)x
) - 2;
359 if (mem
[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
360 if (mem
[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg
, mem
[1], &mem
[2]);
361 else if (MACOSX_MDNS_MALLOC_DEBUGGING
>= 2) LogMsg("free( %s : %ld @ %p)", msg
, mem
[1], &mem
[2]);
362 //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]);
363 memset(mem
, 0xFF, sizeof(mDNSu32
) * 2 + mem
[1]);
364 validatelists(&mDNSStorage
);
371 //*************************************************************************************************************
372 #if COMPILER_LIKES_PRAGMA_MARK
374 #pragma mark - Mach client request handlers
377 //*************************************************************************************************************
378 // Client Death Detection
380 // This gets called after ALL constituent records of the Service Record Set have been deregistered
381 mDNSlocal
void FreeServiceInstance(ServiceInstance
*x
)
383 ServiceRecordSet
*s
= &x
->srs
;
384 ExtraResourceRecord
*e
= x
->srs
.Extras
, *tmp
;
388 e
->r
.RecordContext
= e
;
391 FreeExtraRR(&mDNSStorage
, &tmp
->r
, mStatus_MemFree
);
394 if (s
->RR_TXT
.resrec
.rdata
!= &s
->RR_TXT
.rdatastorage
)
395 freeL("TXT RData", s
->RR_TXT
.resrec
.rdata
);
397 if (s
->SubTypes
) freeL("ServiceSubTypes", s
->SubTypes
);
398 freeL("ServiceInstance", x
);
401 // AbortClient finds whatever client is identified by the given Mach port,
402 // stops whatever operation that client was doing, and frees its memory.
403 // In the case of a service registration, the actual freeing may be deferred
404 // until we get the mStatus_MemFree message, if necessary
405 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
, void *m
)
407 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
408 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
409 DNSServiceResolver
**l
= &DNSServiceResolverList
;
410 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
412 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
415 DNSServiceDomainEnumeration
*x
= *e
;
418 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->dom
.qname
.c
, m
, x
);
419 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort
, x
->dom
.qname
.c
);
420 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
421 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
422 freeL("DNSServiceDomainEnumeration", x
);
426 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
429 DNSServiceBrowser
*x
= *b
;
430 DNSServiceBrowserQuestion
*freePtr
, *qptr
= x
->qlist
;
435 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, qptr
->q
.qname
.c
, m
, x
);
436 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort
, qptr
->q
.qname
.c
);
437 mDNS_StopBrowse(&mDNSStorage
, &qptr
->q
);
440 freeL("DNSServiceBrowserQuestion", freePtr
);
444 DNSServiceBrowserResult
*t
= x
->results
;
445 x
->results
= x
->results
->next
;
446 freeL("DNSServiceBrowserResult", t
);
448 freeL("DNSServiceBrowser", x
);
452 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
455 DNSServiceResolver
*x
= *l
;
458 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->i
.name
.c
, m
, x
);
459 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort
, x
->i
.name
.c
);
460 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
461 freeL("DNSServiceResolver", x
);
465 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
468 ServiceInstance
*si
= NULL
;
469 DNSServiceRegistration
*x
= *r
;
475 ServiceInstance
*instance
= si
;
477 instance
->renameonmemfree
= mDNSfalse
;
478 if (m
&& m
!= x
) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort
, instance
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&instance
->srs
), m
, x
);
479 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort
, instance
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&instance
->srs
));
481 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
482 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
483 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
484 // the list, so we should go ahead and free the memory right now
485 if (mDNS_DeregisterService(&mDNSStorage
, &instance
->srs
)) FreeServiceInstance(instance
); // FreeServiceInstance invalidates pointer
488 freeL("DNSServiceRegistration", x
);
492 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort
);
495 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
497 mDNSlocal
void AbortClientWithLogMessage(mach_port_t c
, char *reason
, char *msg
, void *m
)
499 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
500 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
501 DNSServiceResolver
*l
= DNSServiceResolverList
;
502 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
503 DNSServiceBrowserQuestion
*qptr
;
505 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
506 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
507 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
508 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
510 if (e
) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c
, e
->dom
.qname
.c
, reason
, msg
);
513 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
514 LogMsg("%5d: Browser(%##s) %s%s", c
, qptr
->q
.qname
.c
, reason
, msg
);
516 else if (l
) LogMsg("%5d: Resolver(%##s) %s%s", c
, l
->i
.name
.c
, reason
, msg
);
520 for (si
= r
->regs
; si
; si
= si
->next
)
521 LogMsg("%5d: Registration(%##s) %s%s", c
, si
->srs
.RR_SRV
.resrec
.name
->c
, reason
, msg
);
523 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c
, reason
, msg
);
528 mDNSlocal mDNSBool
CheckForExistingClient(mach_port_t c
)
530 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
531 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
532 DNSServiceResolver
*l
= DNSServiceResolverList
;
533 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
534 DNSServiceBrowserQuestion
*qptr
;
536 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
537 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
538 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
539 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
540 if (e
) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c
, e
->dom
.qname
.c
);
543 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
544 LogMsg("%5d: Browser(%##s) already exists!", c
, qptr
->q
.qname
.c
);
546 if (l
) LogMsg("%5d: Resolver(%##s) already exists!", c
, l
->i
.name
.c
);
547 if (r
) LogMsg("%5d: Registration(%##s) already exists!", c
, r
->regs
? r
->regs
->srs
.RR_SRV
.resrec
.name
->c
: NULL
);
548 return(e
|| b
|| l
|| r
);
551 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
553 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
555 KQueueLock(&mDNSStorage
);
556 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
557 (void)unusedport
; // Unused
558 (void)size
; // Unused
559 (void)info
; // Unused
560 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
562 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
563 AbortClient(deathMessage
->not_port
, NULL
);
565 /* Deallocate the send right that came in the dead name notification */
566 mach_port_destroy(mach_task_self(), deathMessage
->not_port
);
568 KQueueUnlock(&mDNSStorage
, "Mach AbortClient");
571 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
573 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
, void *m
)
575 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
576 dispatch_source_t mach_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND
, ClientMachPort
, 0, dispatch_get_main_queue());
577 if (mach_source
== mDNSNULL
)
579 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
582 dispatch_source_set_event_handler(mach_source
, ^{
583 mach_port_destroy(mach_task_self(), ClientMachPort
);
585 dispatch_resume(mach_source
);
586 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
588 kern_return_t r
= mach_port_request_notification(mach_task_self(), ClientMachPort
, MACH_NOTIFY_DEAD_NAME
, 0,
589 client_death_port
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &prev
);
590 // If the port already died while we were thinking about it, then abort the operation right away
591 if (r
!= KERN_SUCCESS
)
592 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
593 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
596 //*************************************************************************************************************
597 // Domain Enumeration
599 mDNSlocal
void DomainEnumFound(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
601 kern_return_t status
;
602 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
603 DNSServiceDomainEnumerationReplyResultType rt
;
604 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->QuestionContext
;
607 debugf("DomainEnumFound: %##s PTR %##s", answer
->name
->c
, answer
->rdata
->u
.name
.c
);
608 if (answer
->rrtype
!= kDNSType_PTR
) return;
609 if (!x
) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
613 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
614 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
618 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
622 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
623 x
->ClientMachPort
, x
->dom
.qname
.c
, answer
->rdata
->u
.name
.c
,
624 !AddRecord
? "RemoveDomain" :
625 question
== &x
->dom
? "AddDomain" : "AddDomainDefault");
627 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, buffer
);
628 status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, buffer
, 0, MDNS_MM_TIMEOUT
);
629 if (status
== MACH_SEND_TIMED_OUT
)
630 AbortBlockedClient(x
->ClientMachPort
, "enumeration", x
);
633 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
636 // Check client parameter
637 (void)unusedserver
; // Unused
638 mStatus err
= mStatus_NoError
;
639 const char *errormsg
= "Unknown";
640 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
641 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
643 mDNS_DomainType dt1
= regDom
? mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
644 mDNS_DomainType dt2
= regDom
? mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
646 // Allocate memory, and handle failure
647 DNSServiceDomainEnumeration
*x
= mallocL("DNSServiceDomainEnumeration", sizeof(*x
));
648 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
650 // Set up object, and link into list
651 x
->ClientMachPort
= client
;
652 x
->next
= DNSServiceDomainEnumerationList
;
653 DNSServiceDomainEnumerationList
= x
;
655 verbosedebugf("%5d: Enumerate %s Domains", client
, regDom
? "Registration" : "Browsing");
658 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, NULL
, mDNSInterface_LocalOnly
, DomainEnumFound
, x
);
659 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, NULL
, mDNSInterface_LocalOnly
, DomainEnumFound
, x
);
660 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_GetDomains"; goto fail
; }
662 // Succeeded: Wrap up and return
663 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client
, x
->dom
.qname
.c
);
664 EnableDeathNotificationForClient(client
, x
);
665 return(mStatus_NoError
);
668 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client
, regDom
, errormsg
, err
);
672 //*************************************************************************************************************
673 // Browse for services
675 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
679 if (answer
->rrtype
!= kDNSType_PTR
)
680 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
); return; }
683 domainname type
, domain
;
684 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
686 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
687 answer
->name
->c
, answer
->rdata
->u
.name
.c
);
691 DNSServiceBrowserResult
*x
= mallocL("DNSServiceBrowserResult", sizeof(*x
));
692 if (!x
) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer
->rdata
->u
.name
.c
); return; }
694 verbosedebugf("FoundInstance: %s %##s", AddRecord
? "Add" : "Rmv", answer
->rdata
->u
.name
.c
);
695 AssignDomainName(&x
->result
, &answer
->rdata
->u
.name
);
697 x
->resultType
= DNSServiceBrowserReplyAddInstance
;
698 else x
->resultType
= DNSServiceBrowserReplyRemoveInstance
;
701 DNSServiceBrowser
*browser
= (DNSServiceBrowser
*)question
->QuestionContext
;
702 DNSServiceBrowserResult
**p
= &browser
->results
;
703 while (*p
) p
= &(*p
)->next
;
706 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
707 browser
->ClientMachPort
, question
->qname
.c
, DNSTypeName(question
->qtype
), AddRecord
? "Add" : "Rmv", RRDisplayString(m
, answer
));
710 mDNSlocal mStatus
AddDomainToBrowser(DNSServiceBrowser
*browser
, const domainname
*d
)
712 mStatus err
= mStatus_NoError
;
713 DNSServiceBrowserQuestion
*ptr
, *question
= NULL
;
715 for (ptr
= browser
->qlist
; ptr
; ptr
= ptr
->next
)
717 if (SameDomainName(&ptr
->q
.qname
, d
))
718 { debugf("Domain %##s already contained in browser", d
->c
); return mStatus_AlreadyRegistered
; }
721 question
= mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion
));
722 if (!question
) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr
; }
723 AssignDomainName(&question
->domain
, d
);
724 question
->next
= browser
->qlist
;
725 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser
->ClientMachPort
, browser
->type
.c
, d
->c
);
726 err
= mDNS_StartBrowse(&mDNSStorage
, &question
->q
, &browser
->type
, d
, mDNSInterface_Any
, mDNSfalse
, FoundInstance
, browser
);
728 browser
->qlist
= question
;
731 LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err
);
732 freeL("DNSServiceBrowserQuestion", question
);
737 mDNSexport
void machserver_automatic_browse_domain_changed(const domainname
*d
, mDNSBool add
)
739 DNSServiceBrowser
*ptr
;
740 for (ptr
= DNSServiceBrowserList
; ptr
; ptr
= ptr
->next
)
742 if (ptr
->DefaultDomain
)
746 mStatus err
= AddDomainToBrowser(ptr
, d
);
747 if (err
&& err
!= mStatus_AlreadyRegistered
) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d
, ptr
->ClientMachPort
);
751 DNSServiceBrowserQuestion
**q
= &ptr
->qlist
;
754 if (SameDomainName(&(*q
)->domain
, d
))
756 DNSServiceBrowserQuestion
*rem
= *q
;
758 mDNS_StopQueryWithRemoves(&mDNSStorage
, &rem
->q
);
759 freeL("DNSServiceBrowserQuestion", rem
);
764 LogMsg("Requested removal of default domain %##s not in client %5d's list", d
->c
, ptr
->ClientMachPort
);
770 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
771 DNSCString regtype
, DNSCString domain
)
773 // Check client parameter
774 (void)unusedserver
; // Unused
775 mStatus err
= mStatus_NoError
;
776 const char *errormsg
= "Unknown";
778 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
779 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
781 // Check other parameters
784 mDNSs32 NumSubTypes
= ChopSubTypes(regtype
); // Note: Modifies regtype string to remove trailing subtypes
785 if (NumSubTypes
< 0 || NumSubTypes
> 1) { errormsg
= "Bad Service SubType"; goto badparam
; }
786 if (NumSubTypes
== 1 && !AppendDNSNameString(&t
, regtype
+ strlen(regtype
) + 1))
787 { errormsg
= "Bad Service SubType"; goto badparam
; }
788 if (!regtype
[0] || !AppendDNSNameString(&t
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
790 if (!MakeDomainNameFromDNSNameString(&temp
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
791 if (temp
.c
[0] > 15 && (!domain
|| domain
[0] == 0)) domain
= "local."; // For over-long service types, we only allow domain "local"
793 // Allocate memory, and handle failure
794 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
795 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
797 // Set up object, and link into list
798 AssignDomainName(&x
->type
, &t
);
799 x
->ClientMachPort
= client
;
803 x
->next
= DNSServiceBrowserList
;
804 DNSServiceBrowserList
= x
;
808 // Start browser for an explicit domain
809 x
->DefaultDomain
= mDNSfalse
;
810 if (!MakeDomainNameFromDNSNameString(&d
, domain
)) { errormsg
= "Illegal domain"; goto badparam
; }
811 err
= AddDomainToBrowser(x
, &d
);
812 if (err
) { AbortClient(client
, x
); errormsg
= "AddDomainToBrowser"; goto fail
; }
816 DNameListElem
*sdPtr
;
817 // Start browser on all domains
818 x
->DefaultDomain
= mDNStrue
;
819 if (!AutoBrowseDomains
) { AbortClient(client
, x
); errormsg
= "GetSearchDomainList"; goto fail
; }
820 for (sdPtr
= AutoBrowseDomains
; sdPtr
; sdPtr
= sdPtr
->next
)
822 err
= AddDomainToBrowser(x
, &sdPtr
->name
);
825 // only terminally bail if .local fails
826 if (!SameDomainName(&localdomain
, &sdPtr
->name
))
827 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr
->name
.c
);
828 else { AbortClient(client
, x
); errormsg
= "AddDomainToBrowser"; goto fail
; }
833 // Succeeded: Wrap up and return
834 EnableDeathNotificationForClient(client
, x
);
835 return(mStatus_NoError
);
838 err
= mStatus_BadParamErr
;
840 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client
, regtype
, domain
, errormsg
, err
);
844 //*************************************************************************************************************
845 // Resolve Service Info
847 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
849 kern_return_t status
;
850 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->ServiceInfoQueryContext
;
851 NetworkInterfaceInfoOSX
*ifx
= IfindexToInterfaceInfoOSX(m
, query
->info
->InterfaceID
);
852 if (query
->info
->InterfaceID
== mDNSInterface_LocalOnly
|| query
->info
->InterfaceID
== mDNSInterface_P2P
) ifx
= mDNSNULL
;
853 struct sockaddr_storage interface
;
854 struct sockaddr_storage address
;
856 int i
, pstrlen
= query
->info
->TXTinfo
[0];
859 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
861 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
863 mDNSPlatformMemZero(&interface
, sizeof(interface
));
864 mDNSPlatformMemZero(&address
, sizeof(address
));
866 if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
)
868 struct sockaddr_in
*s
= (struct sockaddr_in
*)&interface
;
869 s
->sin_len
= sizeof(*s
);
870 s
->sin_family
= AF_INET
;
872 s
->sin_addr
.s_addr
= ifx
->ifinfo
.ip
.ip
.v4
.NotAnInteger
;
874 else if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
)
876 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&interface
;
877 sin6
->sin6_len
= sizeof(*sin6
);
878 sin6
->sin6_family
= AF_INET6
;
879 sin6
->sin6_flowinfo
= 0;
881 sin6
->sin6_addr
= *(struct in6_addr
*)&ifx
->ifinfo
.ip
.ip
.v6
;
882 sin6
->sin6_scope_id
= ifx
->scope_id
;
885 if (query
->info
->ip
.type
== mDNSAddrType_IPv4
)
887 struct sockaddr_in
*s
= (struct sockaddr_in
*)&address
;
888 s
->sin_len
= sizeof(*s
);
889 s
->sin_family
= AF_INET
;
890 s
->sin_port
= query
->info
->port
.NotAnInteger
;
891 s
->sin_addr
.s_addr
= query
->info
->ip
.ip
.v4
.NotAnInteger
;
895 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&address
;
896 sin6
->sin6_len
= sizeof(*sin6
);
897 sin6
->sin6_family
= AF_INET6
;
898 sin6
->sin6_port
= query
->info
->port
.NotAnInteger
;
899 sin6
->sin6_flowinfo
= 0;
900 sin6
->sin6_addr
= *(struct in6_addr
*)&query
->info
->ip
.ip
.v6
;
901 sin6
->sin6_scope_id
= ifx
? ifx
->scope_id
: 0;
904 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
905 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
906 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
907 // ASCII-1 characters are used in the C-string as boundary markers,
908 // to indicate the boundaries between the original constituent P-strings.
909 for (i
=1; i
<query
->info
->TXTlen
; i
++)
912 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
916 pstrlen
= query
->info
->TXTinfo
[i
];
919 cstring
[i
-1] = 0; // Put the terminating NULL on the end
921 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x
->ClientMachPort
,
922 x
->i
.name
.c
, &query
->info
->ip
, mDNSVal16(query
->info
->port
));
923 status
= DNSServiceResolverReply_rpc(x
->ClientMachPort
,
924 (char*)&interface
, (char*)&address
, cstring
, 0, MDNS_MM_TIMEOUT
);
925 if (status
== MACH_SEND_TIMED_OUT
)
926 AbortBlockedClient(x
->ClientMachPort
, "resolve", x
);
929 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
930 DNSCString name
, DNSCString regtype
, DNSCString domain
)
932 // Check client parameter
933 (void)unusedserver
; // Unused
934 mStatus err
= mStatus_NoError
;
935 const char *errormsg
= "Unknown";
936 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
937 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
939 // Check other parameters
941 domainname t
, d
, srv
;
942 if (!name
[0] || !MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
943 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
944 if (!domain
[0] || !MakeDomainNameFromDNSNameString(&d
, domain
)) { errormsg
= "Bad Domain"; goto badparam
; }
945 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
947 // Allocate memory, and handle failure
948 DNSServiceResolver
*x
= mallocL("DNSServiceResolver", sizeof(*x
));
949 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
951 // Set up object, and link into list
952 x
->ClientMachPort
= client
;
953 x
->i
.InterfaceID
= mDNSInterface_Any
;
955 x
->ReportTime
= NonZeroTime(mDNS_TimeNow(&mDNSStorage
) + 130 * mDNSPlatformOneSecond
);
956 x
->next
= DNSServiceResolverList
;
957 DNSServiceResolverList
= x
;
960 LogOperation("%5d: DNSServiceResolve(%##s) START", client
, x
->i
.name
.c
);
961 err
= mDNS_StartResolveService(&mDNSStorage
, &x
->q
, &x
->i
, FoundInstanceInfo
, x
);
962 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartResolveService"; goto fail
; }
964 // Succeeded: Wrap up and return
965 EnableDeathNotificationForClient(client
, x
);
966 return(mStatus_NoError
);
969 err
= mStatus_BadParamErr
;
971 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client
, name
, regtype
, domain
, errormsg
, err
);
975 //*************************************************************************************************************
978 mDNSexport
void RecordUpdatedNiceLabel(mDNS
*const m
, mDNSs32 delay
)
980 m
->p
->NotifyUser
= NonZeroTime(m
->timenow
+ delay
);
983 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
)
985 ServiceInstance
*si
= (ServiceInstance
*)srs
->ServiceContext
;
987 if (result
== mStatus_NoError
)
989 kern_return_t status
;
990 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
));
991 status
= DNSServiceRegistrationReply_rpc(si
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
992 if (status
== MACH_SEND_TIMED_OUT
)
993 AbortBlockedClient(si
->ClientMachPort
, "registration success", si
);
994 if (si
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
995 RecordUpdatedNiceLabel(m
, 0); // Successfully got new name, tell user immediately
998 else if (result
== mStatus_NameConflict
)
1000 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
));
1001 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1002 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1003 if (si
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
1005 // On conflict for an autoname service, rename and reregister *all* autoname services
1006 IncrementLabelSuffix(&m
->nicelabel
, mDNStrue
);
1007 mDNS_ConfigChanged(m
);
1009 else if (si
->autoname
)
1011 mDNS_RenameAndReregisterService(m
, srs
, mDNSNULL
);
1016 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1017 // of their registration in the usual way (which we will catch via client death notification).
1018 // If the Mach queue is full, we forcibly abort the client immediately.
1019 kern_return_t status
= DNSServiceRegistrationReply_rpc(si
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
1020 if (status
== MACH_SEND_TIMED_OUT
)
1021 AbortBlockedClient(si
->ClientMachPort
, "registration conflict", NULL
);
1025 else if (result
== mStatus_MemFree
)
1027 if (si
->renameonmemfree
) // We intentionally terminated registration so we could re-register with new name
1029 debugf("RegCallback renaming %#s to %#s", si
->name
.c
, m
->nicelabel
.c
);
1030 si
->renameonmemfree
= mDNSfalse
;
1031 si
->name
= m
->nicelabel
;
1032 mDNS_RenameAndReregisterService(m
, srs
, &si
->name
);
1036 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1037 DNSServiceRegistration
*r
;
1038 for (r
= DNSServiceRegistrationList
; r
; r
= r
->next
)
1040 ServiceInstance
**sp
= &r
->regs
;
1043 if (*sp
== si
) { LogMsg("RegCallback: %##s Still in list; removing", srs
->RR_SRV
.resrec
.name
->c
); *sp
= (*sp
)->next
; break; }
1048 FreeServiceInstance(si
);
1052 else if (result
!= mStatus_NATTraversal
)
1053 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
), result
);
1056 mDNSlocal mStatus
AddServiceInstance(DNSServiceRegistration
*x
, const domainname
*domain
)
1059 ServiceInstance
*si
= NULL
;
1060 AuthRecord
*SubTypes
= NULL
;
1062 for (si
= x
->regs
; si
; si
= si
->next
)
1064 if (SameDomainName(&si
->domain
, domain
))
1065 { LogMsg("Requested addition of domain %##s already in list", domain
->c
); return mStatus_AlreadyRegistered
; }
1068 SubTypes
= AllocateSubTypes(x
->NumSubTypes
, x
->regtype
);
1069 if (x
->NumSubTypes
&& !SubTypes
) return mStatus_NoMemoryErr
;
1071 si
= mallocL("ServiceInstance", sizeof(*si
) - sizeof(RDataBody
) + x
->rdsize
);
1072 if (!si
) return mStatus_NoMemoryErr
;
1074 si
->ClientMachPort
= x
->ClientMachPort
;
1075 si
->renameonmemfree
= mDNSfalse
;
1076 si
->autoname
= x
->autoname
;
1077 si
->name
= x
->autoname
? mDNSStorage
.nicelabel
: x
->name
;
1078 si
->domain
= *domain
;
1080 err
= mDNS_RegisterService(&mDNSStorage
, &si
->srs
, &si
->name
, &x
->type
, domain
, NULL
,
1081 x
->port
, x
->txtinfo
, x
->txt_len
, SubTypes
, x
->NumSubTypes
, mDNSInterface_Any
, RegCallback
, si
, 0);
1089 LogMsg("Error %d for registration of service in domain %##s", err
, domain
->c
);
1090 freeL("ServiceInstance", si
);
1095 mDNSexport
void machserver_automatic_registration_domain_changed(const domainname
*d
, mDNSBool add
)
1097 DNSServiceRegistration
*reg
;
1099 for (reg
= DNSServiceRegistrationList
; reg
; reg
= reg
->next
)
1101 if (reg
->DefaultDomain
)
1104 AddServiceInstance(reg
, d
);
1107 ServiceInstance
**si
= ®
->regs
;
1110 if (SameDomainName(&(*si
)->domain
, d
))
1112 ServiceInstance
*s
= *si
;
1114 if (mDNS_DeregisterService(&mDNSStorage
, &s
->srs
)) FreeServiceInstance(s
); // only free memory synchronously on error
1119 if (!si
) debugf("Requested removal of default domain %##s not in client %5d's list", d
, reg
->ClientMachPort
); // normal if registration failed
1125 mDNSexport kern_return_t
provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
1126 DNSCString name
, DNSCString regtype
, DNSCString domain
, IPPort IpPort
, DNSCString txtRecord
)
1128 (void)unusedserver
; // Unused
1129 mStatus err
= mStatus_NoError
;
1130 const char *errormsg
= "Unknown";
1132 // older versions of this code passed the port via mach IPC as an int.
1133 // we continue to pass it as 4 bytes to maintain binary compatibility,
1134 // but now ensure that the network byte order is preserved by using a struct
1136 port
.b
[0] = IpPort
.bytes
[2];
1137 port
.b
[1] = IpPort
.bytes
[3];
1139 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1140 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
1142 // Check for sub-types after the service type
1143 size_t reglen
= strlen(regtype
) + 1;
1144 if (reglen
> MAX_ESCAPED_DOMAIN_NAME
) { errormsg
= "reglen too long"; goto badparam
; }
1145 mDNSs32 NumSubTypes
= ChopSubTypes(regtype
); // Note: Modifies regtype string to remove trailing subtypes
1146 if (NumSubTypes
< 0) { errormsg
= "Bad Service SubType"; goto badparam
; }
1148 // Check other parameters
1152 if (!name
[0]) n
= mDNSStorage
.nicelabel
;
1153 else if (!MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
1154 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
1155 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
1156 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
1158 unsigned char txtinfo
[1024] = "";
1159 unsigned int data_len
= 0;
1160 unsigned int size
= sizeof(RDataBody
);
1161 unsigned char *pstring
= &txtinfo
[data_len
];
1162 char *ptr
= txtRecord
;
1164 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1165 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1166 // Hence we have to convert the C-string to a P-string.
1167 // ASCII-1 characters are allowed in the C-string as boundary markers,
1168 // so that a single C-string can be used to represent one or more P-strings.
1171 if (++data_len
>= sizeof(txtinfo
)) { errormsg
= "TXT record too long"; goto badtxt
; }
1172 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
1174 pstring
= &txtinfo
[data_len
];
1180 if (pstring
[0] == 255) { errormsg
= "TXT record invalid (component longer than 255)"; goto badtxt
; }
1181 pstring
[++pstring
[0]] = *ptr
++;
1186 if (size
< data_len
)
1189 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1190 // a port number of zero. When two instances of the protected client are allowed to run on one
1191 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1192 if (!mDNSIPPortIsZero(port
))
1194 int count
= CountExistingRegistrations(&srv
, port
);
1196 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1197 client
, count
+1, srv
.c
, mDNSVal16(port
));
1200 // Allocate memory, and handle failure
1201 DNSServiceRegistration
*x
= mallocL("DNSServiceRegistration", sizeof(*x
));
1202 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1203 mDNSPlatformMemZero(x
, sizeof(*x
));
1205 // Set up object, and link into list
1206 x
->ClientMachPort
= client
;
1207 x
->DefaultDomain
= !domain
[0];
1208 x
->autoname
= (!name
[0]);
1210 x
->NumSubTypes
= NumSubTypes
;
1211 memcpy(x
->regtype
, regtype
, reglen
);
1215 memcpy(x
->txtinfo
, txtinfo
, 1024);
1216 x
->txt_len
= data_len
;
1220 x
->next
= DNSServiceRegistrationList
;
1221 DNSServiceRegistrationList
= x
;
1223 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1224 x
->ClientMachPort
, name
, regtype
, domain
, mDNSVal16(port
));
1226 err
= AddServiceInstance(x
, &d
);
1227 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_RegisterService"; goto fail
; } // bail if .local (or explicit domain) fails
1229 if (x
->DefaultDomain
)
1232 for (p
= AutoRegistrationDomains
; p
; p
= p
->next
)
1233 AddServiceInstance(x
, &p
->name
);
1236 // Succeeded: Wrap up and return
1237 EnableDeathNotificationForClient(client
, x
);
1238 return(mStatus_NoError
);
1241 LogMsg("%5d: TXT record: %.100s...", client
, txtRecord
);
1243 err
= mStatus_BadParamErr
;
1245 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)",
1246 client
, name
, regtype
, domain
, mDNSVal16(port
), errormsg
, err
);
1250 mDNSlocal
void mDNSPreferencesSetNames(mDNS
*const m
, int key
, domainlabel
*old
, domainlabel
*new)
1252 domainlabel
*prevold
, *prevnew
;
1255 case kmDNSComputerName
:
1256 case kmDNSLocalHostName
:
1257 if (key
== kmDNSComputerName
)
1259 prevold
= &m
->p
->prevoldnicelabel
;
1260 prevnew
= &m
->p
->prevnewnicelabel
;
1264 prevold
= &m
->p
->prevoldhostlabel
;
1265 prevnew
= &m
->p
->prevnewhostlabel
;
1267 // There are a few cases where we need to invoke the helper.
1269 // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need
1270 // to invoke the helper so that it pops up a dialogue to inform the user about the
1273 // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label
1274 // through the preferences pane. We may have to inform the helper as it may have popped up
1275 // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking
1276 // the helper in this case if the previous values (old and new) that we told helper last time
1277 // are same. If the previous old and new values are same, helper does not care.
1279 // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old"
1280 // is not called with NULL today, but this makes it future proof.
1281 if (!old
|| !new || !SameDomainLabelCS(old
->c
, new->c
) ||
1282 !SameDomainLabelCS(old
->c
, prevold
->c
) ||
1283 !SameDomainLabelCS(new->c
, prevnew
->c
))
1293 mDNSPreferencesSetName(key
, old
, new);
1297 LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s",
1298 (key
== kmDNSComputerName
? "prevoldnicelabel" : "prevoldhostlabel"), prevold
->c
,
1299 (key
== kmDNSComputerName
? "prevnewnicelabel" : "prevnewhostlabel"), prevnew
->c
,
1304 LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key
);
1309 mDNSlocal
void mDNS_StatusCallback(mDNS
*const m
, mStatus result
)
1312 if (result
== mStatus_NoError
)
1314 if (!SameDomainLabelCS(m
->p
->userhostlabel
.c
, m
->hostlabel
.c
))
1315 LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
1316 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1317 RecordUpdatedNiceLabel(m
, mDNSPlatformOneSecond
);
1319 else if (result
== mStatus_NameConflict
)
1321 LogInfo("Local Hostname conflict for \"%#s.local\"", m
->hostlabel
.c
);
1322 if (!m
->p
->HostNameConflict
) m
->p
->HostNameConflict
= NonZeroTime(m
->timenow
);
1323 else if (m
->timenow
- m
->p
->HostNameConflict
> 60 * mDNSPlatformOneSecond
)
1325 // Tell the helper we've given up
1326 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, NULL
);
1329 else if (result
== mStatus_GrowCache
)
1331 // Allocate another chunk of cache storage
1332 CacheEntity
*storage
= mallocL("mStatus_GrowCache", sizeof(CacheEntity
) * RR_CACHE_SIZE
);
1333 //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
1334 if (storage
) mDNS_GrowCache(m
, storage
, RR_CACHE_SIZE
);
1336 else if (result
== mStatus_ConfigChanged
)
1338 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
1339 mDNSPreferencesSetNames(m
, kmDNSComputerName
, &m
->p
->usernicelabel
, &m
->nicelabel
);
1340 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, &m
->hostlabel
);
1342 // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
1343 DNSServiceRegistration
*r
;
1344 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1347 ServiceInstance
*si
;
1348 for (si
= r
->regs
; si
; si
= si
->next
)
1350 if (!SameDomainLabelCS(si
->name
.c
, m
->nicelabel
.c
))
1352 debugf("NetworkChanged renaming %##s to %#s", si
->srs
.RR_SRV
.resrec
.name
->c
, m
->nicelabel
.c
);
1353 si
->renameonmemfree
= mDNStrue
;
1354 if (mDNS_DeregisterService_drt(m
, &si
->srs
, mDNS_Dereg_rapid
))
1355 RegCallback(m
, &si
->srs
, mStatus_MemFree
); // If service deregistered already, we can re-register immediately
1360 // Then we call into the UDS daemon code, to let it do the same
1361 udsserver_handle_configchange(m
);
1365 //*************************************************************************************************************
1366 // Add / Update / Remove records from existing Registration
1368 mDNSexport kern_return_t
provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1369 int type
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
, natural_t
*reference
)
1371 // Check client parameter
1373 mStatus err
= mStatus_NoError
;
1374 const char *errormsg
= "Unknown";
1375 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1376 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1377 ServiceInstance
*si
;
1379 (void)unusedserver
; // Unused
1380 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1381 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1383 // Check other parameters
1384 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1385 if (data_len
> sizeof(RDataBody
)) size
= data_len
;
1386 else size
= sizeof(RDataBody
);
1389 *reference
= (natural_t
)id
;
1390 for (si
= x
->regs
; si
; si
= si
->next
)
1392 // Allocate memory, and handle failure
1393 ExtraResourceRecord
*extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
1394 if (!extra
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1396 // Fill in type, length, and data of new record
1397 extra
->r
.resrec
.rrtype
= type
;
1398 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1399 extra
->r
.resrec
.rdlength
= data_len
;
1400 memcpy(&extra
->r
.rdatastorage
.u
.data
, data
, data_len
);
1403 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1404 client
, si
->srs
.RR_SRV
.resrec
.name
->c
, type
, data_len
, extra
);
1405 err
= mDNS_AddRecordToService(&mDNSStorage
, &si
->srs
, extra
, &extra
->r
.rdatastorage
, ttl
, 0);
1409 freeL("Extra Resource Record", extra
);
1410 errormsg
= "mDNS_AddRecordToService";
1414 extra
->ClientID
= id
;
1417 return mStatus_NoError
;
1420 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client
, x
? x
->name
.c
: (mDNSu8
*)"\x8""«NULL»", type
, data_len
, errormsg
, err
);
1421 return mStatus_UnknownErr
;
1424 mDNSlocal
void UpdateCallback(mDNS
*const m
, AuthRecord
*const rr
, RData
*OldRData
, mDNSu16 OldRDLen
)
1427 (void)OldRDLen
; // Unused
1428 if (OldRData
!= &rr
->rdatastorage
)
1429 freeL("Old RData", OldRData
);
1432 mDNSlocal mStatus
UpdateRecord(ServiceRecordSet
*srs
, mach_port_t client
, AuthRecord
*rr
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1434 // Check client parameter
1435 mStatus err
= mStatus_NoError
;
1436 const char *errormsg
= "Unknown";
1437 const domainname
*name
= (const domainname
*)"";
1439 name
= srs
->RR_SRV
.resrec
.name
;
1441 unsigned int size
= sizeof(RDataBody
);
1442 if (size
< data_len
)
1445 // Allocate memory, and handle failure
1446 RData
*newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
1447 if (!newrdata
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1449 // Fill in new length, and data
1450 newrdata
->MaxRDLength
= size
;
1451 memcpy(&newrdata
->u
, data
, data_len
);
1453 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1454 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1455 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1456 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& data_len
== 0) { data_len
= 1; newrdata
->u
.txt
.c
[0] = 0; }
1459 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1460 client
, srs
->RR_SRV
.resrec
.name
->c
, data_len
);
1462 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, data_len
, newrdata
, UpdateCallback
);
1465 errormsg
= "mDNS_Update";
1466 freeL("RData", newrdata
);
1469 return(mStatus_NoError
);
1472 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client
, name
->c
, data_len
, errormsg
, err
);
1476 mDNSexport kern_return_t
provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1477 natural_t reference
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1479 // Check client parameter
1480 mStatus err
= mStatus_NoError
;
1481 const char *errormsg
= "Unknown";
1482 const domainname
*name
= (const domainname
*)"";
1483 ServiceInstance
*si
;
1485 (void)unusedserver
; // unused
1486 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1487 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1488 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1489 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1491 // Check other parameters
1492 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1494 for (si
= x
->regs
; si
; si
= si
->next
)
1496 AuthRecord
*r
= NULL
;
1498 // Find the record we're updating. NULL reference means update the primary TXT record
1499 if (!reference
) r
= &si
->srs
.RR_TXT
;
1502 ExtraResourceRecord
*ptr
;
1503 for (ptr
= si
->srs
.Extras
; ptr
; ptr
= ptr
->next
)
1505 if ((natural_t
)ptr
->ClientID
== reference
)
1506 { r
= &ptr
->r
; break; }
1508 if (!r
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such record"; goto fail
; }
1510 err
= UpdateRecord(&si
->srs
, client
, r
, data
, data_len
, ttl
);
1511 if (err
) goto fail
; //!!!KRS this will cause failures for non-local defaults!
1514 return mStatus_NoError
;
1517 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client
, name
->c
, reference
, data_len
, errormsg
, err
);
1521 mDNSlocal mStatus
RemoveRecord(ServiceRecordSet
*srs
, ExtraResourceRecord
*extra
, mach_port_t client
)
1523 const domainname
*const name
= srs
->RR_SRV
.resrec
.name
;
1524 mStatus err
= mStatus_NoError
;
1527 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client
, srs
->RR_SRV
.resrec
.name
->c
);
1529 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, srs
, extra
, FreeExtraRR
, extra
);
1530 if (err
) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client
, name
->c
, err
);
1535 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1536 natural_t reference
)
1538 // Check client parameter
1539 (void)unusedserver
; // Unused
1540 mStatus err
= mStatus_NoError
;
1541 const char *errormsg
= "Unknown";
1542 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1543 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1544 ServiceInstance
*si
;
1546 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1547 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1549 for (si
= x
->regs
; si
; si
= si
->next
)
1551 ExtraResourceRecord
*e
;
1552 for (e
= si
->srs
.Extras
; e
; e
= e
->next
)
1554 if ((natural_t
)e
->ClientID
== reference
)
1556 err
= RemoveRecord(&si
->srs
, e
, client
);
1560 if (!e
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such reference"; goto fail
; }
1563 return mStatus_NoError
;
1566 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client
, reference
, errormsg
, err
);
1570 //*************************************************************************************************************
1571 #if COMPILER_LIKES_PRAGMA_MARK
1573 #pragma mark - Startup, shutdown, and supporting code
1576 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1578 mig_reply_error_t
*request
= msg
;
1579 mig_reply_error_t
*reply
;
1580 mach_msg_return_t mr
;
1582 (void)port
; // Unused
1583 (void)size
; // Unused
1584 (void)info
; // Unused
1586 KQueueLock(&mDNSStorage
);
1588 /* allocate a reply buffer */
1589 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
1591 /* call the MiG server routine */
1592 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
1594 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
1596 if (reply
->RetCode
== MIG_NO_REPLY
)
1599 * This return code is a little tricky -- it appears that the
1600 * demux routine found an error of some sort, but since that
1601 * error would not normally get returned either to the local
1602 * user or the remote one, we pretend it's ok.
1604 CFAllocatorDeallocate(NULL
, reply
);
1609 * destroy any out-of-line data in the request buffer but don't destroy
1610 * the reply port right (since we need that to send an error message).
1612 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1613 mach_msg_destroy(&request
->Head
);
1616 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
1618 /* no reply port, so destroy the reply */
1619 if (reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
1620 mach_msg_destroy(&reply
->Head
);
1621 CFAllocatorDeallocate(NULL
, reply
);
1628 * We don't want to block indefinitely because the client
1629 * isn't receiving messages from the reply port.
1630 * If we have a send-once right for the reply port, then
1631 * this isn't a concern because the send won't block.
1632 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1633 * To avoid falling off the kernel's fast RPC path unnecessarily,
1634 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1637 options
= MACH_SEND_MSG
;
1638 if (MACH_MSGH_BITS_REMOTE(reply
->Head
.msgh_bits
) == MACH_MSG_TYPE_MOVE_SEND_ONCE
)
1639 options
|= MACH_SEND_TIMEOUT
;
1641 mr
= mach_msg(&reply
->Head
, /* msg */
1642 options
, /* option */
1643 reply
->Head
.msgh_size
, /* send_size */
1645 MACH_PORT_NULL
, /* rcv_name */
1646 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1647 MACH_PORT_NULL
); /* notify */
1649 /* Has a message error occurred? */
1652 case MACH_SEND_INVALID_DEST
:
1653 case MACH_SEND_TIMED_OUT
:
1654 /* the reply can't be delivered, so destroy it */
1655 mach_msg_destroy(&reply
->Head
);
1659 /* Includes success case. */
1663 CFAllocatorDeallocate(NULL
, reply
);
1666 KQueueUnlock(&mDNSStorage
, "Mach client event");
1669 mDNSlocal kern_return_t
registerBootstrapService()
1671 kern_return_t status
;
1672 mach_port_t service_send_port
, service_rcv_port
;
1674 debugf("Registering Bootstrap Service");
1677 * See if our service name is already registered and if we have privilege to check in.
1679 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1680 if (status
== KERN_SUCCESS
)
1683 * If so, we must be a followup instance of an already defined server. In that case,
1684 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1685 * that in case we have to unregister later (which requires the privilege port).
1687 server_priv_port
= bootstrap_port
;
1688 restarting_via_mach_init
= TRUE
;
1690 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
1692 status
= bootstrap_create_server(bootstrap_port
, "/usr/sbin/mDNSResponder", getuid(),
1693 FALSE
/* relaunch immediately, not on demand */, &server_priv_port
);
1694 if (status
!= KERN_SUCCESS
) return status
;
1696 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
1697 if (status
!= KERN_SUCCESS
)
1699 mach_port_deallocate(mach_task_self(), server_priv_port
);
1703 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1704 if (status
!= KERN_SUCCESS
)
1706 mach_port_deallocate(mach_task_self(), server_priv_port
);
1707 mach_port_deallocate(mach_task_self(), service_send_port
);
1710 assert(service_send_port
== service_rcv_port
);
1714 * We have no intention of responding to requests on the service port. We are not otherwise
1715 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1716 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1717 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1719 mach_port_destroy(mach_task_self(), service_rcv_port
);
1723 mDNSlocal kern_return_t
destroyBootstrapService()
1725 debugf("Destroying Bootstrap Service");
1726 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
1729 mDNSlocal
void ExitCallback(int sig
)
1731 (void)sig
; // Unused
1732 LogMsg("%s stopping", mDNSResponderVersionString
);
1734 debugf("ExitCallback");
1735 if (!mDNS_DebugMode
&& !started_via_launchdaemon
)
1736 destroyBootstrapService();
1738 debugf("ExitCallback: Aborting MIG clients");
1739 while (DNSServiceDomainEnumerationList
)
1740 AbortClient(DNSServiceDomainEnumerationList
->ClientMachPort
, DNSServiceDomainEnumerationList
);
1741 while (DNSServiceBrowserList
)
1742 AbortClient(DNSServiceBrowserList
->ClientMachPort
, DNSServiceBrowserList
);
1743 while (DNSServiceResolverList
)
1744 AbortClient(DNSServiceResolverList
->ClientMachPort
, DNSServiceResolverList
);
1745 while (DNSServiceRegistrationList
)
1746 AbortClient(DNSServiceRegistrationList
->ClientMachPort
, DNSServiceRegistrationList
);
1748 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1750 debugf("ExitCallback: mDNS_StartExit");
1751 mDNS_StartExit(&mDNSStorage
);
1754 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1756 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1757 mDNSlocal
void HandleSIG(int sig
)
1759 kern_return_t status
;
1760 mach_msg_header_t header
;
1762 // WARNING: can't call syslog or fprintf from signal handler
1763 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1764 header
.msgh_remote_port
= signal_port
;
1765 header
.msgh_local_port
= MACH_PORT_NULL
;
1766 header
.msgh_size
= sizeof(header
);
1767 header
.msgh_id
= sig
;
1769 status
= mach_msg(&header
, MACH_SEND_MSG
| MACH_SEND_TIMEOUT
, header
.msgh_size
,
1770 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1772 if (status
!= MACH_MSG_SUCCESS
)
1774 if (status
== MACH_SEND_TIMED_OUT
) mach_msg_destroy(&header
);
1775 if (sig
== SIGTERM
|| sig
== SIGINT
) exit(-1);
1779 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1781 mDNSlocal
void INFOCallback(void)
1783 mDNSs32 utc
= mDNSPlatformUTC();
1784 DNSServiceDomainEnumeration
*e
;
1785 DNSServiceBrowser
*b
;
1786 DNSServiceResolver
*l
;
1787 DNSServiceRegistration
*r
;
1788 NetworkInterfaceInfoOSX
*i
;
1792 LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString
, OSXVers
? "OSXVers" : "iOSVers", OSXVers
? OSXVers
: iOSVers
);
1794 udsserver_info(&mDNSStorage
);
1796 LogMsgNoIdent("--------- Mach Clients ---------");
1797 if (!DNSServiceDomainEnumerationList
&& !DNSServiceBrowserList
&& !DNSServiceResolverList
&& !DNSServiceRegistrationList
)
1798 LogMsgNoIdent("<None>");
1801 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
1802 LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e
->ClientMachPort
, e
->dom
.qname
.c
);
1804 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
1806 DNSServiceBrowserQuestion
*qptr
;
1807 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
1808 LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b
->ClientMachPort
, qptr
->q
.qname
.c
);
1811 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1812 LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l
->ClientMachPort
, l
->i
.name
.c
);
1814 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1816 ServiceInstance
*si
;
1817 for (si
= r
->regs
; si
; si
= si
->next
)
1818 LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si
->ClientMachPort
, si
->srs
.RR_SRV
.resrec
.name
->c
, mDNSVal16(si
->srs
.RR_SRV
.resrec
.rdata
->u
.srv
.port
));
1822 LogMsgNoIdent("----- KQSocketEventSources -----");
1823 if (!gEventSources
) LogMsgNoIdent("<None>");
1826 KQSocketEventSource
*k
;
1827 for (k
= gEventSources
; k
; k
=k
->next
)
1829 LogMsgNoIdent("%3d %s", k
->fd
, k
->kqs
.KQtask
);
1830 usleep((mDNSStorage
.KnownBugs
& mDNS_KnownBug_LossySyslog
) ? 3333 : 1000);
1834 LogMsgNoIdent("------ Network Interfaces ------");
1835 if (!mDNSStorage
.p
->InterfaceList
) LogMsgNoIdent("<None>");
1838 for (i
= mDNSStorage
.p
->InterfaceList
; i
; i
= i
->next
)
1840 // Allow six characters for interface name, for names like "vmnet8"
1842 LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
1843 i
, i
->ifinfo
.InterfaceID
, i
->Registered
,
1844 i
->sa_family
== AF_INET
? "v4" : i
->sa_family
== AF_INET6
? "v6" : "??", i
->ifinfo
.ifname
, i
->scope_id
, &i
->ifinfo
.MAC
, &i
->BSSID
,
1845 &i
->ifinfo
.ip
, utc
- i
->LastSeen
);
1848 const CacheRecord
*sps
[3];
1849 FindSPSInCache(&mDNSStorage
, &i
->ifinfo
.NetWakeBrowse
, sps
);
1850 LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
1851 i
, i
->ifinfo
.InterfaceID
, i
->Registered
,
1852 i
->sa_family
== AF_INET
? "v4" : i
->sa_family
== AF_INET6
? "v6" : "??", i
->ifinfo
.ifname
, i
->scope_id
, &i
->ifinfo
.MAC
, &i
->BSSID
,
1853 i
->ifinfo
.InterfaceActive
? "Active" : " ",
1854 i
->ifinfo
.IPv4Available
? "v4" : " ",
1855 i
->ifinfo
.IPv4Available
? (mDNSv4Addr
*)&i
->ifa_v4addr
: &zerov4Addr
,
1856 i
->ifinfo
.IPv6Available
? "v6" : " ",
1857 i
->ifinfo
.Advertise
? "⊙" : " ",
1858 i
->ifinfo
.McastTxRx
? "⇆" : " ",
1859 !(i
->ifinfo
.InterfaceActive
&& i
->ifinfo
.NetWake
) ? " " : !sps
[0] ? "☼" : "☀",
1862 if (sps
[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[0]->resrec
.rdata
->u
.name
.c
), sps
[0]->resrec
.rdata
->u
.name
.c
);
1863 if (sps
[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[1]->resrec
.rdata
->u
.name
.c
), sps
[1]->resrec
.rdata
->u
.name
.c
);
1864 if (sps
[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[2]->resrec
.rdata
->u
.name
.c
), sps
[2]->resrec
.rdata
->u
.name
.c
);
1869 LogMsgNoIdent("--------- DNS Servers ----------");
1870 if (!mDNSStorage
.DNSServers
) LogMsgNoIdent("<None>");
1873 for (s
= mDNSStorage
.DNSServers
; s
; s
= s
->next
)
1875 NetworkInterfaceInfoOSX
*ifx
= IfindexToInterfaceInfoOSX(&mDNSStorage
, s
->interface
);
1876 LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %s",
1877 s
->domain
.c
, ifx
? ifx
->ifinfo
.ifname
: "", ifx
? " " : "", &s
->addr
, mDNSVal16(s
->port
),
1878 s
->penaltyTime
? s
->penaltyTime
- mDNS_TimeNow(&mDNSStorage
) : 0, s
->scoped
? "Scoped" : "",
1880 s
->teststate
== DNSServer_Untested
? "(Untested)" :
1881 s
->teststate
== DNSServer_Passed
? "" :
1882 s
->teststate
== DNSServer_Failed
? "(Failed)" :
1883 s
->teststate
== DNSServer_Disabled
? "(Disabled)" : "(Unknown state)");
1887 LogMsgNoIdent("--------- Mcast Resolvers ----------");
1888 if (!mDNSStorage
.McastResolvers
) LogMsgNoIdent("<None>");
1891 for (mr
= mDNSStorage
.McastResolvers
; mr
; mr
= mr
->next
)
1892 LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr
->domain
.c
, mr
->timeout
);
1895 mDNSs32 now
= mDNS_TimeNow(&mDNSStorage
);
1896 LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32
)now
, now
);
1898 LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString
, OSXVers
? "OSXVers" : "iOSVers", OSXVers
? OSXVers
: iOSVers
);
1901 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1903 mDNSlocal
void SignalCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1905 (void)port
; // Unused
1906 (void)size
; // Unused
1907 (void)info
; // Unused
1908 mach_msg_header_t
*msg_header
= (mach_msg_header_t
*)msg
;
1909 mDNS
*const m
= &mDNSStorage
;
1911 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
1913 switch(msg_header
->msgh_id
)
1919 LogMsg("SIGHUP: Purge cache");
1921 FORALL_CACHERECORDS(slot
, cg
, rr
) mDNS_PurgeCacheResourceRecord(m
, rr
);
1922 // Restart unicast and multicast queries
1923 mDNSCoreRestartQueries(m
);
1927 case SIGTERM
: ExitCallback(msg_header
->msgh_id
); break;
1928 case SIGINFO
: INFOCallback(); break;
1929 case SIGUSR1
: mDNS_LoggingEnabled
= mDNS_LoggingEnabled
? 0 : 1;
1930 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled
? "Enabled" : "Disabled");
1931 WatchDogReportingThreshold
= mDNS_LoggingEnabled
? 50 : 250;
1933 case SIGUSR2
: mDNS_PacketLoggingEnabled
= mDNS_PacketLoggingEnabled
? 0 : 1;
1934 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled
? "Enabled" : "Disabled");
1936 default: LogMsg("SignalCallback: Unknown signal %d", msg_header
->msgh_id
); break;
1938 KQueueUnlock(m
, "Unix Signal");
1941 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
1942 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
1943 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
1946 CFMachPortRef s_port
;
1947 CFRunLoopSourceRef s_rls
;
1948 CFRunLoopSourceRef d_rls
;
1950 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
1951 if (m_port
!= MACH_PORT_NULL
)
1952 s_port
= CFMachPortCreateWithPort(NULL
, m_port
, DNSserverCallback
, NULL
, NULL
);
1955 s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
1956 m_port
= CFMachPortGetPort(s_port
);
1957 kern_return_t status
= bootstrap_register(bootstrap_port
, "com.apple.mDNSResponder", m_port
);
1962 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
1964 LogMsg("bootstrap_register() failed: %d %X %s", status
, status
, mach_error_string(status
));
1969 CFMachPortRef d_port
= CFMachPortCreate(NULL
, ClientDeathCallback
, NULL
, NULL
);
1971 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
1972 rrcachestorage
, RR_CACHE_SIZE
,
1974 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
1976 if (err
) { LogMsg("Daemon start: mDNS_Init failed %d", err
); return(err
); }
1978 client_death_port
= CFMachPortGetPort(d_port
);
1980 s_rls
= CFMachPortCreateRunLoopSource(NULL
, s_port
, 0);
1981 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, s_rls
, kCFRunLoopDefaultMode
);
1984 d_rls
= CFMachPortCreateRunLoopSource(NULL
, d_port
, 0);
1985 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, d_rls
, kCFRunLoopDefaultMode
);
1988 CFMachPortRef i_port
= CFMachPortCreate(NULL
, SignalCallback
, NULL
, NULL
);
1989 CFRunLoopSourceRef i_rls
= CFMachPortCreateRunLoopSource(NULL
, i_port
, 0);
1990 signal_port
= CFMachPortGetPort(i_port
);
1991 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, i_rls
, kCFRunLoopDefaultMode
);
1994 if (mDNS_DebugMode
) printf("Service registered with Mach Port %d\n", m_port
);
1998 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2000 // SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
2001 // The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
2002 // The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
2003 // of code from above. Alternatively we could remove the duplicated source code by having
2004 // single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM"
2006 mDNSlocal
void SignalDispatch(dispatch_source_t source
)
2008 int sig
= (int)dispatch_source_get_handle(source
);
2009 mDNS
*const m
= &mDNSStorage
;
2017 LogMsg("SIGHUP: Purge cache");
2019 FORALL_CACHERECORDS(slot
, cg
, rr
) mDNS_PurgeCacheResourceRecord(m
, rr
);
2020 // Restart unicast and multicast queries
2021 mDNSCoreRestartQueries(m
);
2025 case SIGTERM
: ExitCallback(sig
); break;
2026 case SIGINFO
: INFOCallback(); break;
2027 case SIGUSR1
: mDNS_LoggingEnabled
= mDNS_LoggingEnabled
? 0 : 1;
2028 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled
? "Enabled" : "Disabled");
2029 WatchDogReportingThreshold
= mDNS_LoggingEnabled
? 50 : 250;
2031 case SIGUSR2
: mDNS_PacketLoggingEnabled
= mDNS_PacketLoggingEnabled
? 0 : 1;
2032 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled
? "Enabled" : "Disabled");
2034 default: LogMsg("SignalCallback: Unknown signal %d", sig
); break;
2036 KQueueUnlock(m
, "Unix Signal");
2039 mDNSlocal
void mDNSSetupSignal(dispatch_queue_t queue
, int sig
)
2041 signal(sig
, SIG_IGN
);
2042 dispatch_source_t source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, sig
, 0, queue
);
2046 dispatch_source_set_event_handler(source
, ^{SignalDispatch(source
);});
2047 // Start processing signals
2048 dispatch_resume(source
);
2052 LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig
);
2056 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2057 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2058 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
2061 CFMachPortRef s_port
;
2062 dispatch_source_t mach_source
;
2063 dispatch_queue_t queue
= dispatch_get_main_queue();
2065 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
2066 if (m_port
!= MACH_PORT_NULL
)
2067 s_port
= CFMachPortCreateWithPort(NULL
, m_port
, DNSserverCallback
, NULL
, NULL
);
2070 s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
2071 m_port
= CFMachPortGetPort(s_port
);
2072 kern_return_t status
= bootstrap_register(bootstrap_port
, "com.apple.mDNSResponder", m_port
);
2077 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
2079 LogMsg("bootstrap_register() failed: %d %X %s", status
, status
, mach_error_string(status
));
2084 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
2085 rrcachestorage
, RR_CACHE_SIZE
,
2087 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
2089 if (err
) { LogMsg("Daemon start: mDNS_Init failed %d", err
); return(err
); }
2091 mach_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, m_port
, 0, queue
);
2092 if (mach_source
== mDNSNULL
){LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;}
2093 dispatch_source_set_event_handler(mach_source
, ^{
2094 dispatch_mig_server(mach_source
, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem
),
2095 DNSServiceDiscoveryRequest_server
);
2097 dispatch_resume(mach_source
);
2099 mDNSSetupSignal(queue
, SIGHUP
);
2100 mDNSSetupSignal(queue
, SIGINT
);
2101 mDNSSetupSignal(queue
, SIGTERM
);
2102 mDNSSetupSignal(queue
, SIGINFO
);
2103 mDNSSetupSignal(queue
, SIGUSR1
);
2104 mDNSSetupSignal(queue
, SIGUSR2
);
2105 mDNSSetupSignal(queue
, SIGHUP
);
2107 // Create a custom handler for doing the housekeeping work. This is either triggered
2108 // by the timer or an event source
2109 PlatformStorage
.custom
= dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD
, 0, 0, queue
);
2110 if (PlatformStorage
.custom
== mDNSNULL
){LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;}
2111 dispatch_source_set_event_handler(PlatformStorage
.custom
, ^{PrepareForIdle(&mDNSStorage
);});
2112 dispatch_resume(PlatformStorage
.custom
);
2114 // Create a timer source to trigger housekeeping work. The houskeeping work itself
2115 // is done in the custom handler that we set below.
2117 PlatformStorage
.timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, queue
);
2118 if (PlatformStorage
.timer
== mDNSNULL
){LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;}
2120 // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we
2121 // always reset the time to the new time computed. In effect, we ignore the interval
2122 dispatch_source_set_timer(PlatformStorage
.timer
, DISPATCH_TIME_NOW
, 1000ull * 1000000000, 0);
2123 dispatch_source_set_event_handler(PlatformStorage
.timer
, ^{
2124 dispatch_source_merge_data(PlatformStorage
.custom
, 1);
2126 dispatch_resume(PlatformStorage
.timer
);
2128 LogMsg("DaemonIntialize done successfully");
2130 if (mDNS_DebugMode
) printf("Service registered with Mach Port %d\n", m_port
);
2134 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2136 mDNSlocal mDNSs32
mDNSDaemonIdle(mDNS
*const m
)
2138 mDNSs32 now
= mDNS_TimeNow(m
);
2140 // 1. If we need to set domain secrets, do so before handling the network change
2142 // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list,
2143 // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens.
2144 if (m
->p
->KeyChainTimer
&& now
- m
->p
->KeyChainTimer
>= 0)
2146 m
->p
->KeyChainTimer
= 0;
2148 SetDomainSecrets(m
);
2152 // 2. If we have network change events to handle, do them before calling mDNS_Execute()
2154 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2155 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2156 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2157 // we then systematically lose our own looped-back packets.
2158 if (m
->p
->NetworkChanged
&& now
- m
->p
->NetworkChanged
>= 0) mDNSMacOSXNetworkChanged(m
);
2160 if (m
->p
->RequestReSleep
&& now
- m
->p
->RequestReSleep
>= 0) { m
->p
->RequestReSleep
= 0; mDNSPowerRequest(0, 0); }
2162 // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do
2163 mDNSs32 nextevent
= mDNS_Execute(m
);
2165 if (m
->p
->NetworkChanged
)
2166 if (nextevent
- m
->p
->NetworkChanged
> 0)
2167 nextevent
= m
->p
->NetworkChanged
;
2169 if (m
->p
->KeyChainTimer
)
2170 if (nextevent
- m
->p
->KeyChainTimer
> 0)
2171 nextevent
= m
->p
->KeyChainTimer
;
2173 if (m
->p
->RequestReSleep
)
2174 if (nextevent
- m
->p
->RequestReSleep
> 0)
2175 nextevent
= m
->p
->RequestReSleep
;
2177 // 4. Deliver any waiting browse messages to clients
2178 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
2182 // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2183 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2184 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2185 DNSServiceBrowser
*x
= b
;
2187 if (x
->results
) // Try to deliver the list of results
2191 DNSServiceBrowserResult
*const r
= x
->results
;
2193 domainname type
, domain
;
2194 DeconstructServiceName(&r
->result
, &name
, &type
, &domain
); // Don't need to check result; already validated in FoundInstance()
2195 char cname
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2196 char ctype
[MAX_ESCAPED_DOMAIN_NAME
];
2197 char cdom
[MAX_ESCAPED_DOMAIN_NAME
];
2198 ConvertDomainLabelToCString_unescaped(&name
, cname
);
2199 ConvertDomainNameToCString(&type
, ctype
);
2200 ConvertDomainNameToCString(&domain
, cdom
);
2201 DNSServiceDiscoveryReplyFlags flags
= (r
->next
) ? DNSServiceDiscoverReplyFlagsMoreComing
: 0;
2202 kern_return_t status
= DNSServiceBrowserReply_rpc(x
->ClientMachPort
, r
->resultType
, cname
, ctype
, cdom
, flags
, 1);
2203 // If we failed to send the mach message, try again in one second
2204 if (status
== MACH_SEND_TIMED_OUT
)
2206 if (nextevent
- now
> mDNSPlatformOneSecond
)
2207 nextevent
= now
+ mDNSPlatformOneSecond
;
2212 x
->lastsuccess
= now
;
2213 x
->results
= x
->results
->next
;
2214 freeL("DNSServiceBrowserResult", r
);
2217 // If this client hasn't read a single message in the last 60 seconds, abort it
2218 if (now
- x
->lastsuccess
>= 60 * mDNSPlatformOneSecond
)
2219 AbortBlockedClient(x
->ClientMachPort
, "browse", x
);
2223 DNSServiceResolver
*l
;
2224 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
2225 if (l
->ReportTime
&& now
- l
->ReportTime
>= 0)
2228 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2229 "This places considerable burden on the network.", l
->i
.name
.c
);
2232 if (m
->p
->NotifyUser
)
2234 if (m
->p
->NotifyUser
- now
< 0)
2236 if (!SameDomainLabelCS(m
->p
->usernicelabel
.c
, m
->nicelabel
.c
))
2238 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m
->p
->usernicelabel
.c
, m
->nicelabel
.c
);
2239 mDNSPreferencesSetNames(m
, kmDNSComputerName
, &m
->p
->usernicelabel
, &m
->nicelabel
);
2240 m
->p
->usernicelabel
= m
->nicelabel
;
2242 if (!SameDomainLabelCS(m
->p
->userhostlabel
.c
, m
->hostlabel
.c
))
2244 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
2245 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, &m
->hostlabel
);
2246 m
->p
->HostNameConflict
= 0; // Clear our indicator, now name change has been successful
2247 m
->p
->userhostlabel
= m
->hostlabel
;
2249 m
->p
->NotifyUser
= 0;
2252 if (nextevent
- m
->p
->NotifyUser
> 0)
2253 nextevent
= m
->p
->NotifyUser
;
2259 // Right now we consider *ALL* of our DHCP leases
2260 // It might make sense to be a bit more selective and only consider the leases on interfaces
2261 // (a) that are capable and enabled for wake-on-LAN, and
2262 // (b) where we have found (and successfully registered with) a Sleep Proxy
2263 // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
2264 mDNSlocal mDNSu32
DHCPWakeTime(void)
2266 mDNSu32 e
= 24 * 3600; // Maximum maintenance wake interval is 24 hours
2267 const CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
2268 if (!now
) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
2271 const SCPreferencesRef prefs
= SCPreferencesCreate(NULL
, CFSTR("mDNSResponder:DHCPWakeTime"), NULL
);
2272 if (!prefs
) LogMsg("DHCPWakeTime: SCPreferencesCreate failed");
2275 const SCNetworkSetRef currentset
= SCNetworkSetCopyCurrent(prefs
);
2276 if (!currentset
) LogMsg("DHCPWakeTime: SCNetworkSetCopyCurrent failed");
2279 const CFArrayRef services
= SCNetworkSetCopyServices(currentset
);
2280 if (!services
) LogMsg("DHCPWakeTime: SCNetworkSetCopyServices failed");
2284 for (i
= 0; i
< CFArrayGetCount(services
); i
++)
2286 const SCNetworkServiceRef service
= CFArrayGetValueAtIndex(services
, i
);
2287 if (!service
) LogMsg("DHCPWakeTime: CFArrayGetValueAtIndex %d failed", i
);
2290 const CFStringRef serviceid
= SCNetworkServiceGetServiceID(service
);
2291 if (!serviceid
) LogMsg("DHCPWakeTime: SCNetworkServiceGetServiceID %d failed", i
);
2294 // Note: It's normal for this call to return NULL, for interfaces not using DHCP
2295 const CFDictionaryRef dhcp
= SCDynamicStoreCopyDHCPInfo(NULL
, serviceid
);
2298 const CFDateRef start
= DHCPInfoGetLeaseStartTime(dhcp
);
2299 const CFDataRef lease
= DHCPInfoGetOptionData(dhcp
, 51); // Option 51 = IP Address Lease Time
2300 if (!start
|| !lease
|| CFDataGetLength(lease
) < 4)
2301 LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
2302 "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
2303 i
, start
, lease
, lease
? CFDataGetLength(lease
) : 0);
2306 const UInt8
*d
= CFDataGetBytePtr(lease
);
2307 if (!d
) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", i
);
2310 const mDNSu32 elapsed
= now
- CFDateGetAbsoluteTime(start
);
2311 const mDNSu32 lifetime
= (mDNSs32
) ((mDNSs32
)d
[0] << 24 | (mDNSs32
)d
[1] << 16 | (mDNSs32
)d
[2] << 8 | d
[3]);
2312 const mDNSu32 remaining
= lifetime
- elapsed
;
2313 const mDNSu32 wake
= remaining
> 60 ? remaining
- remaining
/10 : 54; // Wake at 90% of the lease time
2314 LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed
, lifetime
, remaining
, wake
);
2315 if (e
> wake
) e
= wake
;
2323 CFRelease(services
);
2325 CFRelease(currentset
);
2333 // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
2334 // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
2335 // If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
2336 // for a few seconds and then waking again is silly and annoying.
2337 // If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
2338 // Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
2339 // allowing us an adequate safety margin to renew our lease before we lose it.
2341 mDNSlocal mDNSBool
AllowSleepNow(mDNS
*const m
, mDNSs32 now
)
2343 mDNSBool ready
= mDNSCoreReadyForSleep(m
, now
);
2344 if (m
->SleepState
&& !ready
&& now
- m
->SleepLimit
< 0) return(mDNSfalse
);
2346 m
->p
->WakeAtUTC
= 0;
2347 int result
= kIOReturnSuccess
;
2348 CFDictionaryRef opts
= NULL
;
2350 // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
2351 // do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
2353 LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m
->SleepLimit
- now
);
2356 if (!m
->SystemWakeOnLANEnabled
|| !mDNSCoreHaveAdvertisedMulticastServices(m
))
2357 LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
2358 m
->SystemWakeOnLANEnabled
? "is" : "not",
2359 mDNSCoreHaveAdvertisedMulticastServices(m
) ? "have" : "no");
2362 mDNSs32 dhcp
= DHCPWakeTime();
2363 LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp
);
2364 mDNSs32 interval
= mDNSCoreIntervalToNextWake(m
, now
) / mDNSPlatformOneSecond
;
2365 if (interval
> dhcp
) interval
= dhcp
;
2367 // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
2368 // transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
2369 // a single SPS failure could result in a remote machine falling permanently asleep, requiring
2370 // someone to go to the machine in person to wake it up again, which would be unacceptable.
2371 if (!ready
&& interval
> 3600) interval
= 3600;
2373 //interval = 48; // For testing
2375 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2376 if (m
->p
->IOPMConnection
) // If lightweight-wake capability is available, use that
2378 const CFDateRef WakeDate
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent() + interval
);
2379 if (!WakeDate
) LogMsg("ScheduleNextWake: CFDateCreate failed");
2382 const mDNSs32 reqs
= kIOPMSystemPowerStateCapabilityNetwork
;
2383 const CFNumberRef Requirements
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &reqs
);
2384 if (!Requirements
) LogMsg("ScheduleNextWake: CFNumberCreate failed");
2387 const void *OptionKeys
[2] = { CFSTR("WakeDate"), CFSTR("Requirements") };
2388 const void *OptionVals
[2] = { WakeDate
, Requirements
};
2389 opts
= CFDictionaryCreate(NULL
, (void*)OptionKeys
, (void*)OptionVals
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2390 if (!opts
) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
2391 CFRelease(Requirements
);
2393 CFRelease(WakeDate
);
2395 LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval
);
2397 else // else schedule the wakeup using the old API instead to
2400 // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
2401 // so we should put it back to sleep. To avoid frustrating the user, we always request at least
2402 // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
2403 // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
2404 if (interval
< 60) interval
= 60;
2406 result
= mDNSPowerRequest(1, interval
);
2408 if (result
== kIOReturnNotReady
)
2411 LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval
);
2412 // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
2413 // requested wake time is "too soon", but there's no API to find out what constitutes
2414 // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
2415 // we just have to iterate with successively longer intervals until it doesn't fail.
2416 // We preserve the value of "result" because if our original power request was deemed "too soon"
2417 // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request,
2418 // since the implication is that the system won't manage to be awake again at the time we need it.
2421 interval
+= (interval
< 20) ? 1 : ((interval
+3) / 4);
2422 r
= mDNSPowerRequest(1, interval
);
2424 while (r
== kIOReturnNotReady
);
2425 if (r
) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval
, r
, r
);
2426 else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval
);
2430 if (result
) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval
, result
, result
);
2431 else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval
);
2433 m
->p
->WakeAtUTC
= mDNSPlatformUTC() + interval
;
2437 m
->SleepState
= SleepState_Sleeping
;
2438 // We used to clear our interface list to empty state here before going to sleep.
2439 // The applications that try to connect to an external server during maintenance wakes, saw
2440 // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable
2441 // flag). Thus, we don't remove our interfaces anymore on sleep.
2444 LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
2445 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2446 (m
->p
->IOPMConnection
) ? "IOPMConnectionAcknowledgeEventWithOptions" :
2448 (result
== kIOReturnSuccess
) ? "IOAllowPowerChange" : "IOCancelPowerChange",
2449 m
->p
->SleepCookie
, ready
? "ready for sleep" : "giving up", now
, m
->SleepLimit
- now
);
2451 m
->SleepLimit
= 0; // Don't clear m->SleepLimit until after we've logged it above
2453 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2454 if (m
->p
->IOPMConnection
) IOPMConnectionAcknowledgeEventWithOptions(m
->p
->IOPMConnection
, m
->p
->SleepCookie
, opts
);
2457 if (result
== kIOReturnSuccess
) IOAllowPowerChange (m
->p
->PowerConnection
, m
->p
->SleepCookie
);
2458 else IOCancelPowerChange(m
->p
->PowerConnection
, m
->p
->SleepCookie
);
2460 if (opts
) CFRelease(opts
);
2464 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2466 mDNSexport
void TriggerEventCompletion()
2468 debugf("TriggerEventCompletion: Merge data");
2469 dispatch_source_merge_data(PlatformStorage
.custom
, 1);
2472 mDNSlocal
void PrepareForIdle(void *m_param
)
2475 int64_t time_offset
;
2476 dispatch_time_t dtime
;
2478 const int multiplier
= 1000000000 / mDNSPlatformOneSecond
;
2480 // This is the main work loop:
2481 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2482 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2483 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2485 debugf("PrepareForIdle: called");
2486 // Run mDNS_Execute to find out the time we next need to wake up
2487 mDNSs32 start
= mDNSPlatformRawTime();
2488 mDNSs32 nextTimerEvent
= udsserver_idle(mDNSDaemonIdle(m
));
2489 mDNSs32 end
= mDNSPlatformRawTime();
2490 if (end
- start
>= WatchDogReportingThreshold
)
2491 LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end
- start
);
2493 mDNSs32 now
= mDNS_TimeNow(m
);
2495 if (m
->ShutdownTime
)
2497 if (mDNSStorage
.ResourceRecords
)
2499 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m
, mDNSStorage
.ResourceRecords
));
2500 if (mDNS_LoggingEnabled
) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2502 if (mDNS_ExitNow(m
, now
))
2504 if (!mDNSStorage
.ResourceRecords
)
2505 safe_vproc_transaction_end();
2506 LogInfo("IdleLoop: mDNS_FinalExit");
2507 mDNS_FinalExit(&mDNSStorage
);
2508 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2511 if (nextTimerEvent
- m
->ShutdownTime
>= 0)
2512 nextTimerEvent
= m
->ShutdownTime
;
2516 if (!AllowSleepNow(m
, now
))
2517 if (nextTimerEvent
- m
->SleepLimit
>= 0)
2518 nextTimerEvent
= m
->SleepLimit
;
2520 // Convert absolute wakeup time to a relative time from now
2521 mDNSs32 ticks
= nextTimerEvent
- now
;
2522 if (ticks
< 1) ticks
= 1;
2524 static mDNSs32 RepeatedBusy
= 0; // Debugging sanity check, to guard against CPU spins
2530 if (++RepeatedBusy
>= mDNSPlatformOneSecond
) { ShowTaskSchedulingError(&mDNSStorage
); RepeatedBusy
= 0; }
2533 time_offset
= ((mDNSu32
)ticks
/ mDNSPlatformOneSecond
) * 1000000000 + (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2534 dtime
= dispatch_time(DISPATCH_TIME_NOW
, time_offset
);
2535 dispatch_source_set_timer(PlatformStorage
.timer
, dtime
, 1000ull*1000000000, 0);
2536 debugf("PrepareForIdle: scheduling timer with ticks %d", ticks
);
2540 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2542 mDNSlocal
void KQWokenFlushBytes(int fd
, __unused
short filter
, __unused
void *context
)
2544 // Read all of the bytes so we won't wake again.
2546 while (recv(fd
, buffer
, sizeof(buffer
), MSG_DONTWAIT
) > 0) continue;
2549 mDNSlocal
void * KQueueLoop(void *m_param
)
2554 #if USE_SELECT_WITH_KQUEUEFD
2557 const int multiplier
= 1000000 / mDNSPlatformOneSecond
;
2559 const int multiplier
= 1000000000 / mDNSPlatformOneSecond
;
2562 pthread_mutex_lock(&PlatformStorage
.BigMutex
);
2563 LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32
)mDNSStorage
.timenow_last
, mDNSStorage
.timenow_last
);
2565 // This is the main work loop:
2566 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2567 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2568 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2569 // (4) On wakeup we first process *all* events
2570 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2573 #define kEventsToReadAtOnce 1
2574 struct kevent new_events
[kEventsToReadAtOnce
];
2576 // Run mDNS_Execute to find out the time we next need to wake up
2577 mDNSs32 start
= mDNSPlatformRawTime();
2578 mDNSs32 nextTimerEvent
= udsserver_idle(mDNSDaemonIdle(m
));
2579 mDNSs32 end
= mDNSPlatformRawTime();
2580 if (end
- start
>= WatchDogReportingThreshold
)
2581 LogInfo("WARNING: Idle task took %dms to complete", end
- start
);
2583 mDNSs32 now
= mDNS_TimeNow(m
);
2585 if (m
->ShutdownTime
)
2587 if (mDNSStorage
.ResourceRecords
)
2590 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
2592 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m
, rr
));
2593 if (mDNS_LoggingEnabled
) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2596 if (mDNS_ExitNow(m
, now
))
2598 if (!mDNSStorage
.ResourceRecords
)
2599 safe_vproc_transaction_end();
2600 LogInfo("mDNS_FinalExit");
2601 mDNS_FinalExit(&mDNSStorage
);
2602 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2605 if (nextTimerEvent
- m
->ShutdownTime
>= 0)
2606 nextTimerEvent
= m
->ShutdownTime
;
2610 if (!AllowSleepNow(m
, now
))
2611 if (nextTimerEvent
- m
->SleepLimit
>= 0)
2612 nextTimerEvent
= m
->SleepLimit
;
2614 // Convert absolute wakeup time to a relative time from now
2615 mDNSs32 ticks
= nextTimerEvent
- now
;
2616 if (ticks
< 1) ticks
= 1;
2618 static mDNSs32 RepeatedBusy
= 0; // Debugging sanity check, to guard against CPU spins
2624 if (++RepeatedBusy
>= mDNSPlatformOneSecond
) { ShowTaskSchedulingError(&mDNSStorage
); RepeatedBusy
= 0; }
2627 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents
, ticks
);
2630 // Release the lock, and sleep until:
2631 // 1. Something interesting happens like a packet arriving, or
2632 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
2633 // 3. The timeout expires
2634 pthread_mutex_unlock(&PlatformStorage
.BigMutex
);
2636 #if USE_SELECT_WITH_KQUEUEFD
2637 struct timeval timeout
;
2638 timeout
.tv_sec
= ticks
/ mDNSPlatformOneSecond
;
2639 timeout
.tv_usec
= (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2640 FD_SET(KQueueFD
, &readfds
);
2641 if (select(KQueueFD
+1, &readfds
, NULL
, NULL
, &timeout
) < 0)
2642 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD
, errno
, strerror(errno
)); sleep(1); }
2644 struct timespec timeout
;
2645 timeout
.tv_sec
= ticks
/ mDNSPlatformOneSecond
;
2646 timeout
.tv_nsec
= (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2647 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
2648 // and have it work similarly to the way it does with nevents non-zero --
2649 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
2650 // In fact, what happens if you do this is that it just returns immediately. So, we have
2651 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
2652 if (kevent(KQueueFD
, NULL
, 0, new_events
, 1, &timeout
) < 0)
2653 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD
, errno
, strerror(errno
)); sleep(1); }
2656 pthread_mutex_lock(&PlatformStorage
.BigMutex
);
2657 // We have to ignore the event we may have been told about above, because that
2658 // was done without holding the lock, and between the time we woke up and the
2659 // time we reclaimed the lock the other thread could have done something that
2660 // makes the event no longer valid. Now we have the lock, we call kevent again
2661 // and this time we can safely process the events it tells us about.
2663 static const struct timespec zero_timeout
= { 0, 0 };
2665 while ((events_found
= kevent(KQueueFD
, NULL
, 0, new_events
, kEventsToReadAtOnce
, &zero_timeout
)) != 0)
2667 if (events_found
> kEventsToReadAtOnce
|| (events_found
< 0 && errno
!= EINTR
))
2669 // Not sure what to do here, our kqueue has failed us - this isn't ideal
2670 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno
, strerror(errno
));
2674 numevents
+= events_found
;
2677 for (i
= 0; i
< events_found
; i
++)
2679 const KQueueEntry
*const kqentry
= new_events
[i
].udata
;
2680 mDNSs32 stime
= mDNSPlatformRawTime();
2681 const char *const KQtask
= kqentry
->KQtask
; // Grab a copy in case KQcallback deletes the task
2682 kqentry
->KQcallback(new_events
[i
].ident
, new_events
[i
].filter
, kqentry
->KQcontext
);
2683 mDNSs32 etime
= mDNSPlatformRawTime();
2684 if (etime
- stime
>= WatchDogReportingThreshold
)
2685 LogInfo("WARNING: %s took %dms to complete", KQtask
, etime
- stime
);
2693 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2695 mDNSlocal
void LaunchdCheckin(void)
2697 launch_data_t msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
2698 launch_data_t resp
= launch_msg(msg
);
2699 launch_data_free(msg
);
2700 if (!resp
) { LogMsg("launch_msg returned NULL"); return; }
2702 if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
)
2704 int err
= launch_data_get_errno(resp
);
2705 // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
2706 if (err
!= EACCES
) LogMsg("launch_msg returned %d", err
);
2707 else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err
);
2711 launch_data_t skts
= launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_SOCKETS
);
2712 if (!skts
) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
2715 launch_data_t skt
= launch_data_dict_lookup(skts
, "Listeners");
2716 if (!skt
) LogMsg("launch_data_dict_lookup Listeners returned NULL");
2719 launchd_fds_count
= launch_data_array_get_count(skt
);
2720 if (launchd_fds_count
== 0) LogMsg("launch_data_array_get_count(skt) returned 0");
2723 launchd_fds
= mallocL("LaunchdCheckin", sizeof(dnssd_sock_t
) * launchd_fds_count
);
2724 if (!launchd_fds
) LogMsg("LaunchdCheckin: malloc failed");
2728 for(i
= 0; i
< launchd_fds_count
; i
++)
2730 launch_data_t s
= launch_data_array_get_index(skt
, i
);
2733 launchd_fds
[i
] = dnssd_InvalidSocket
;
2734 LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i
);
2738 launchd_fds
[i
] = launch_data_get_fd(s
);
2739 LogInfo("Launchd Unix Domain Socket [%d]: %d", i
, launchd_fds
[i
]);
2743 // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
2744 chmod(MDNS_UDS_SERVERPATH
, S_IRUSR
|S_IWUSR
| S_IRGRP
|S_IWGRP
| S_IROTH
|S_IWOTH
);
2749 launch_data_t ports
= launch_data_dict_lookup(resp
, "MachServices");
2750 if (!ports
) LogMsg("launch_data_dict_lookup MachServices returned NULL");
2753 launch_data_t p
= launch_data_dict_lookup(ports
, "com.apple.mDNSResponder");
2754 if (!p
) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL");
2757 m_port
= launch_data_get_fd(p
);
2758 LogInfo("Launchd Mach Port: %d", m_port
);
2759 if (m_port
== ~0U) m_port
= MACH_PORT_NULL
;
2763 launch_data_free(resp
);
2766 mDNSlocal
void DropPrivileges(void)
2768 static const char login
[] = "_mdnsresponder";
2769 struct passwd
*pwd
= getpwnam(login
);
2771 LogMsg("Could not find account name \"%s\". Running as root.", login
);
2774 uid_t uid
= pwd
->pw_uid
;
2775 gid_t gid
= pwd
->pw_gid
;
2777 LogMsg("Started as root. Switching to userid \"%s\".", login
);
2779 if (unlink(MDNS_UDS_SERVERPATH
) < 0 && errno
!= ENOENT
) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH
, errno
, strerror(errno
));
2782 static char path
[] = "/var/run/mdns/mDNSResponder";
2783 char *p
= strrchr(path
, '/');
2785 if (mkdir(path
, 0755) < 0 && errno
!= EEXIST
) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path
, errno
, strerror(errno
));
2786 else if (chown(path
, uid
, gid
) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path
, errno
, strerror(errno
));
2790 if (unlink(path
) < 0 && errno
!= ENOENT
) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path
, errno
, strerror(errno
));
2791 else if (symlink(path
, MDNS_UDS_SERVERPATH
) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH
, path
, errno
, strerror(errno
));
2792 else LogInfo("DropPrivileges: Created subdirectory and symlink");
2796 if (0 != initgroups(login
, gid
)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login
, (unsigned long)gid
);
2797 if (0 != setgid(gid
)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid());
2798 if (0 != setuid(uid
)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid
);
2802 extern int sandbox_init(const char *profile
, uint64_t flags
, char **errorbuf
) __attribute__((weak_import
));
2804 mDNSexport
int main(int argc
, char **argv
)
2807 kern_return_t status
;
2809 mDNSMacOSXSystemBuildNumber(NULL
);
2810 LogMsg("%s starting %s %d", mDNSResponderVersionString
, OSXVers
? "OSXVers" : "iOSVers", OSXVers
? OSXVers
: iOSVers
);
2813 LogMsg("CacheRecord %d", sizeof(CacheRecord
));
2814 LogMsg("CacheGroup %d", sizeof(CacheGroup
));
2815 LogMsg("ResourceRecord %d", sizeof(ResourceRecord
));
2816 LogMsg("RData_small %d", sizeof(RData_small
));
2818 LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity
));
2819 LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE
);
2820 LogMsg("block usage %d", sizeof(CacheEntity
) * RR_CACHE_SIZE
);
2821 LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity
) * RR_CACHE_SIZE
);
2824 safe_vproc_transaction_begin();
2826 if (0 == geteuid()) DropPrivileges();
2828 for (i
=1; i
<argc
; i
++)
2830 if (!strcasecmp(argv
[i
], "-d" )) mDNS_DebugMode
= mDNStrue
;
2831 if (!strcasecmp(argv
[i
], "-launchd" )) started_via_launchdaemon
= mDNStrue
;
2832 if (!strcasecmp(argv
[i
], "-launchdaemon" )) started_via_launchdaemon
= mDNStrue
;
2833 if (!strcasecmp(argv
[i
], "-NoMulticastAdvertisements")) advertise
= mDNS_Init_DontAdvertiseLocalAddresses
;
2834 if (!strcasecmp(argv
[i
], "-DisableSleepProxyClient" )) DisableSleepProxyClient
= mDNStrue
;
2835 if (!strcasecmp(argv
[i
], "-DebugLogging" )) mDNS_LoggingEnabled
= mDNStrue
;
2836 if (!strcasecmp(argv
[i
], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled
= mDNStrue
;
2837 if (!strcasecmp(argv
[i
], "-OfferSleepProxyService" ))
2838 OfferSleepProxyService
= (i
+1 < argc
&& mDNSIsDigit(argv
[i
+1][0]) && mDNSIsDigit(argv
[i
+1][1]) && argv
[i
+1][2]==0) ? atoi(argv
[++i
]) : 100;
2839 if (!strcasecmp(argv
[i
], "-UseInternalSleepProxy" ))
2840 UseInternalSleepProxy
= (i
+1<argc
&& mDNSIsDigit(argv
[i
+1][0]) && argv
[i
+1][1]==0) ? atoi(argv
[++i
]) : 1;
2841 if (!strcasecmp(argv
[i
], "-StrictUnicastOrdering" )) StrictUnicastOrdering
= mDNStrue
;
2842 if (!strcasecmp(argv
[i
], "-DisableInboundRelay" )) DisableInboundRelayConnection
= mDNStrue
;
2843 if (!strcasecmp(argv
[i
], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains
= mDNStrue
;
2846 // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
2847 if (!advertise
) LogMsg("Administratively prohibiting multicast advertisements");
2849 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2851 signal(SIGHUP
, HandleSIG
); // (Debugging) Purge the cache to check for cache handling bugs
2852 signal(SIGINT
, HandleSIG
); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
2853 signal(SIGPIPE
, SIG_IGN
); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
2854 signal(SIGTERM
, HandleSIG
); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
2855 signal(SIGINFO
, HandleSIG
); // (Debugging) Write state snapshot to syslog
2856 signal(SIGUSR1
, HandleSIG
); // (Debugging) Enable Logging
2857 signal(SIGUSR2
, HandleSIG
); // (Debugging) Enable Packet Logging
2859 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2861 mDNSStorage
.p
= &PlatformStorage
; // Make sure mDNSStorage.p is set up, because validatelists uses it
2864 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
2865 if (!mDNS_DebugMode
&& !started_via_launchdaemon
)
2867 registerBootstrapService();
2868 if (!restarting_via_mach_init
) exit(0); // mach_init will restart us immediately as a daemon
2869 int fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
2870 if (fd
< 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno
, strerror(errno
));
2873 // Avoid unnecessarily duplicating a file descriptor to itself
2874 if (fd
!= STDIN_FILENO
) if (dup2(fd
, STDIN_FILENO
) < 0) LogMsg("dup2(fd, STDIN_FILENO) failed errno %d (%s)", errno
, strerror(errno
));
2875 if (fd
!= STDOUT_FILENO
) if (dup2(fd
, STDOUT_FILENO
) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno
, strerror(errno
));
2876 if (fd
!= STDERR_FILENO
) if (dup2(fd
, STDERR_FILENO
) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno
, strerror(errno
));
2877 if (fd
!= STDIN_FILENO
&& fd
!= STDOUT_FILENO
&& fd
!= STDERR_FILENO
) (void)close(fd
);
2881 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2883 // Create the kqueue, mutex and thread to support KQSockets
2884 KQueueFD
= kqueue();
2885 if (KQueueFD
== -1) { LogMsg("kqueue() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2887 i
= pthread_mutex_init(&PlatformStorage
.BigMutex
, NULL
);
2888 if (i
== -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2890 int fdpair
[2] = {0, 0};
2891 i
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, fdpair
);
2892 if (i
== -1) { LogMsg("socketpair() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2894 // Socket pair returned us two identical sockets connected to each other
2895 // We will use the first socket to send the second socket. The second socket
2896 // will be added to the kqueue so it will wake when data is sent.
2897 static const KQueueEntry wakeKQEntry
= { KQWokenFlushBytes
, NULL
, "kqueue wakeup after CFRunLoop event" };
2899 PlatformStorage
.WakeKQueueLoopFD
= fdpair
[0];
2900 KQueueSet(fdpair
[1], EV_ADD
, EVFILT_READ
, &wakeKQEntry
);
2902 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2904 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
2906 LogMsg("Note: Compiled without Apple Sandbox support");
2907 #else MDNS_NO_SANDBOX
2909 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
2913 uint64_t sandbox_flags
= SANDBOX_NAMED
;
2915 // On Desktop, the sandbox profile always exists under /usr/share/sandbox, no need to
2916 // check it. stat results in calls to opendirectoryd and to avoid deadlocks with
2917 // opendirectoryd early on, we avoid this call.
2918 #if TARGET_OS_EMBEDDED
2920 if (stat("/usr/share/sandbox/mDNSResponder.sb", &s
) == 0)
2922 sandbox_flags
= SANDBOX_NAMED_EXTERNAL
;
2923 LogInfo("Will load Sandbox profile from filesystem");
2927 int sandbox_err
= sandbox_init("mDNSResponder", sandbox_flags
, &sandbox_msg
);
2928 if (sandbox_err
) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg
); sandbox_free_error(sandbox_msg
); }
2929 else LogInfo("Now running under Apple Sandbox restrictions");
2931 #endif MDNS_NO_SANDBOX
2933 status
= mDNSDaemonInitialize();
2934 if (status
) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit
; }
2936 status
= udsserver_init(launchd_fds
, launchd_fds_count
);
2937 if (status
) { LogMsg("Daemon start: udsserver_init failed"); goto exit
; }
2939 mDNSMacOSXNetworkChanged(&mDNSStorage
);
2941 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2942 LogInfo("Daemon Start: Using LibDispatch");
2943 // CFRunLoopRun runs both CFRunLoop sources and dispatch sources
2945 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2946 // Start the kqueue thread
2947 pthread_t KQueueThread
;
2948 i
= pthread_create(&KQueueThread
, NULL
, KQueueLoop
, &mDNSStorage
);
2949 if (i
== -1) { LogMsg("pthread_create() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2953 LogMsg("ERROR: CFRunLoopRun Exiting.");
2954 mDNS_Close(&mDNSStorage
);
2956 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2958 LogMsg("%s exiting", mDNSResponderVersionString
);
2961 if (!mDNS_DebugMode
&& !started_via_launchdaemon
) destroyBootstrapService();
2965 // uds_daemon.c support routines /////////////////////////////////////////////
2967 // Arrange things so that when data appears on fd, callback is called with context
2968 mDNSexport mStatus
udsSupportAddFDToEventLoop(int fd
, udsEventCallback callback
, void *context
, void **platform_data
)
2970 KQSocketEventSource
**p
= &gEventSources
;
2971 (void) platform_data
;
2972 while (*p
&& (*p
)->fd
!= fd
) p
= &(*p
)->next
;
2973 if (*p
) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd
); return mStatus_AlreadyRegistered
; }
2975 KQSocketEventSource
*newSource
= (KQSocketEventSource
*) mallocL("KQSocketEventSource", sizeof *newSource
);
2976 if (!newSource
) return mStatus_NoMemoryErr
;
2978 newSource
->next
= mDNSNULL
;
2980 newSource
->kqs
.KQcallback
= callback
;
2981 newSource
->kqs
.KQcontext
= context
;
2982 newSource
->kqs
.KQtask
= "UDS client";
2983 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2984 newSource
->kqs
.readSource
= mDNSNULL
;
2985 newSource
->kqs
.writeSource
= mDNSNULL
;
2986 newSource
->kqs
.fdClosed
= mDNSfalse
;
2987 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2989 if (KQueueSet(fd
, EV_ADD
, EVFILT_READ
, &newSource
->kqs
) == 0)
2992 return mStatus_NoError
;
2995 LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd
, errno
, strerror(errno
));
2996 freeL("KQSocketEventSource", newSource
);
2997 return mStatus_BadParamErr
;
3000 int udsSupportReadFD(dnssd_sock_t fd
, char *buf
, int len
, int flags
, void *platform_data
)
3002 (void) platform_data
;
3003 return recv(fd
, buf
, len
, flags
);
3006 mDNSexport mStatus
udsSupportRemoveFDFromEventLoop(int fd
, void *platform_data
) // Note: This also CLOSES the file descriptor
3008 KQSocketEventSource
**p
= &gEventSources
;
3009 (void) platform_data
;
3010 while (*p
&& (*p
)->fd
!= fd
) p
= &(*p
)->next
;
3013 KQSocketEventSource
*s
= *p
;
3015 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
3016 // causes the kernel to automatically remove any associated kevents
3017 mDNSPlatformCloseFD(&s
->kqs
, s
->fd
);
3018 freeL("KQSocketEventSource", s
);
3019 return mStatus_NoError
;
3021 LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd
);
3022 return mStatus_NoSuchNameErr
;
3025 #if _BUILDING_XCODE_PROJECT_
3026 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
3027 const char *__crashreporter_info__
= mDNSResponderVersionString
;
3028 asm(".desc ___crashreporter_info__, 0x10");
3031 // For convenience when using the "strings" command, this is the last thing in the file
3032 // The "@(#) " pattern is a special prefix the "what" command looks for
3033 mDNSexport
const char mDNSResponderVersionString_SCCS
[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";