1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2011 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.
19 #include <mach/mach.h>
20 #include <mach/mach_error.h>
21 #include <sys/types.h>
28 #include <launch_priv.h> // for launch_socket_service_check_in()
31 #include <sys/event.h>
34 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
39 #include <bootstrap_priv.h> // for bootstrap_check_in()
41 #include "DNSServiceDiscoveryRequestServer.h"
42 #include "DNSServiceDiscoveryReply.h"
45 #include "DNSCommon.h"
46 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
48 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
49 #include "xpc_services.h" // Interface to XPC services
51 #include "../mDNSMacOSX/DNSServiceDiscovery.h"
54 static aslclient log_client
= NULL
;
55 static aslmsg log_msg
= NULL
;
57 // Used on Embedded Side for Reading mDNSResponder Managed Preferences Profile
58 #if TARGET_OS_EMBEDDED
59 #define kmDNSEnableLoggingStr CFSTR("EnableLogging")
60 #define kmDNSResponderPrefIDStr "com.apple.mDNSResponder.plist"
61 #define kmDNSResponderPrefID CFSTR(kmDNSResponderPrefIDStr)
64 //*************************************************************************************************************
65 #if COMPILER_LIKES_PRAGMA_MARK
66 #pragma mark - Globals
69 static mDNS_PlatformSupport PlatformStorage
;
71 // Start off with a default cache of 16K (99 records)
72 // Each time we grow the cache we add another 99 records
73 // 99 * 164 = 16236 bytes.
74 // This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead
75 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
76 static CacheEntity rrcachestorage
[RR_CACHE_SIZE
];
78 static mach_port_t m_port
= MACH_PORT_NULL
;
80 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
81 mDNSlocal
void PrepareForIdle(void *m_param
);
82 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
83 static mach_port_t client_death_port
= MACH_PORT_NULL
;
84 static mach_port_t signal_port
= MACH_PORT_NULL
;
85 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
87 static dnssd_sock_t
*launchd_fds
= mDNSNULL
;
88 static mDNSu32 launchd_fds_count
= 0;
90 // mDNS Mach Message Timeout, in milliseconds.
91 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
92 // fails to service its mach message queue, but long enough to give a well-written
93 // client a chance to service its mach message queue without getting cut off.
94 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
95 // even extra-slow clients a fair chance before we cut them off.
96 #define MDNS_MM_TIMEOUT 250
98 static mDNSBool advertise
= mDNS_Init_AdvertiseLocalAddresses
; // By default, advertise addresses (& other records) via multicast
100 extern mDNSBool StrictUnicastOrdering
;
101 extern mDNSBool AlwaysAppendSearchDomains
;
103 //*************************************************************************************************************
104 #if COMPILER_LIKES_PRAGMA_MARK
106 #pragma mark - Active client list structures
109 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
110 struct DNSServiceDomainEnumeration_struct
112 DNSServiceDomainEnumeration
*next
;
113 mach_port_t ClientMachPort
;
114 DNSQuestion dom
; // Question asking for domains
115 DNSQuestion def
; // Question asking for default domain
118 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult
;
119 struct DNSServiceBrowserResult_struct
121 DNSServiceBrowserResult
*next
;
126 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
128 typedef struct DNSServiceBrowserQuestion
130 struct DNSServiceBrowserQuestion
*next
;
133 } DNSServiceBrowserQuestion
;
135 struct DNSServiceBrowser_struct
137 DNSServiceBrowser
*next
;
138 mach_port_t ClientMachPort
;
139 DNSServiceBrowserQuestion
*qlist
;
140 DNSServiceBrowserResult
*results
;
142 mDNSBool DefaultDomain
; // was the browse started on an explicit domain?
143 domainname type
; // registration type
146 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
147 struct DNSServiceResolver_struct
149 DNSServiceResolver
*next
;
150 mach_port_t ClientMachPort
;
156 // A single registered service: ServiceRecordSet + bookkeeping
157 // Note that we duplicate some fields from parent DNSServiceRegistration object
158 // to facilitate cleanup, when instances and parent may be deallocated at different times.
159 typedef struct ServiceInstance
161 struct ServiceInstance
*next
;
162 mach_port_t ClientMachPort
;
163 mDNSBool autoname
; // Set if this name is tied to the Computer Name
164 mDNSBool renameonmemfree
; // Set if we just got a name conflict and now need to automatically pick a new name
167 ServiceRecordSet srs
;
168 // Don't add any fields after ServiceRecordSet.
169 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
172 // A client-created service. May reference several ServiceInstance objects if default
173 // settings cause registration in multiple domains.
174 typedef struct DNSServiceRegistration
176 struct DNSServiceRegistration
*next
;
177 mach_port_t ClientMachPort
;
178 mDNSBool DefaultDomain
;
182 char regtype
[MAX_ESCAPED_DOMAIN_NAME
]; // for use in AllocateSubtypes
183 domainlabel name
; // used only if autoname is false
186 unsigned char txtinfo
[1024];
189 ServiceInstance
*regs
;
190 } DNSServiceRegistration
;
192 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
193 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
194 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
195 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
197 // We keep a list of client-supplied event sources in KQSocketEventSource records
198 typedef struct KQSocketEventSource
200 struct KQSocketEventSource
*next
;
203 } KQSocketEventSource
;
205 static KQSocketEventSource
*gEventSources
;
207 //*************************************************************************************************************
208 #if COMPILER_LIKES_PRAGMA_MARK
210 #pragma mark - General Utility Functions
213 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
215 char _malloc_options
[] = "AXZ";
217 mDNSexport
void LogMemCorruption(const char *format
, ...)
221 va_start(ptr
,format
);
222 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
224 LogMsg("!!!! %s !!!!", buffer
);
225 NotifyOfElusiveBug("Memory Corruption", buffer
);
227 *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
231 mDNSlocal
void validatelists(mDNS
*const m
)
234 KQSocketEventSource
*k
;
235 for (k
= gEventSources
; k
; k
=k
->next
)
236 if (k
->next
== (KQSocketEventSource
*)~0 || k
->fd
< 0)
237 LogMemCorruption("gEventSources: %p is garbage (%d)", k
, k
->fd
);
239 // Check Mach client lists
240 DNSServiceDomainEnumeration
*e
;
241 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
242 if (e
->next
== (DNSServiceDomainEnumeration
*)~0 || e
->ClientMachPort
== 0 || e
->ClientMachPort
== (mach_port_t
) ~0)
243 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e
, e
->ClientMachPort
);
245 DNSServiceBrowser
*b
;
246 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
247 if (b
->next
== (DNSServiceBrowser
*)~0 || b
->ClientMachPort
== 0 || b
->ClientMachPort
== (mach_port_t
) ~0)
248 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b
, b
->ClientMachPort
);
250 DNSServiceResolver
*l
;
251 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
252 if (l
->next
== (DNSServiceResolver
*)~0 || l
->ClientMachPort
== 0 || l
->ClientMachPort
== (mach_port_t
) ~0)
253 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l
, l
->ClientMachPort
);
255 DNSServiceRegistration
*r
;
256 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
257 if (r
->next
== (DNSServiceRegistration
*)~0 || r
->ClientMachPort
== 0 || r
->ClientMachPort
== (mach_port_t
) ~0)
258 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r
, r
->ClientMachPort
);
260 // Check Unix Domain Socket client lists (uds_daemon.c)
263 // Check core mDNS lists
265 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
267 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
268 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
269 if (rr
->resrec
.name
!= &rr
->namestorage
)
270 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
271 rr
, rr
->resrec
.name
->c
, rr
->namestorage
.c
, rr
->namestorage
.c
);
274 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
)
275 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
276 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
278 rr
= m
->NewLocalRecords
;
280 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
281 LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
283 rr
= m
->CurrentRecord
;
285 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
286 LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
289 for (q
= m
->Questions
; q
; q
=q
->next
)
290 if (q
->next
== (DNSQuestion
*)~0 || q
->ThisQInterval
== (mDNSs32
) ~0)
291 LogMemCorruption("Questions list: %p is garbage (%lX %p)", q
, q
->ThisQInterval
, q
->next
);
296 FORALL_CACHERECORDS(slot
, cg
, cr
)
298 if (cr
->resrec
.RecordType
== 0 || cr
->resrec
.RecordType
== 0xFF)
299 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot
, cr
, cr
->resrec
.RecordType
);
300 if (cr
->CRActiveQuestion
)
302 for (q
= m
->Questions
; q
; q
=q
->next
) if (q
== cr
->CRActiveQuestion
) break;
303 if (!q
) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot
, cr
->CRActiveQuestion
, CRDisplayString(m
, cr
));
307 // Check core uDNS lists
308 udns_validatelists(m
);
310 // Check platform-layer lists
311 NetworkInterfaceInfoOSX
*i
;
312 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
313 if (i
->next
== (NetworkInterfaceInfoOSX
*)~0 || !i
->m
|| i
->m
== (mDNS
*)~0)
314 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i
, i
->ifinfo
.ifname
);
317 for (t
= m
->TunnelClients
; t
; t
=t
->next
)
318 if (t
->next
== (ClientTunnel
*)~0 || t
->dstname
.c
[0] > 63)
319 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t
, t
->dstname
.c
[0]);
322 mDNSexport
void *mallocL(char *msg
, unsigned int size
)
324 // Allocate space for two words of sanity checking data before the requested block
325 mDNSu32
*mem
= malloc(sizeof(mDNSu32
) * 2 + size
);
327 { LogMsg("malloc( %s : %d ) failed", msg
, size
); return(NULL
); }
330 if (size
> 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg
, size
, &mem
[2]);
331 else if (MACOSX_MDNS_MALLOC_DEBUGGING
>= 2) LogMsg("malloc( %s : %lu ) = %p", msg
, size
, &mem
[2]);
334 //mDNSPlatformMemZero(&mem[2], size);
335 memset(&mem
[2], 0xFF, size
);
336 validatelists(&mDNSStorage
);
341 mDNSexport
void freeL(char *msg
, void *x
)
344 LogMsg("free( %s @ NULL )!", msg
);
347 mDNSu32
*mem
= ((mDNSu32
*)x
) - 2;
348 if (mem
[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
349 if (mem
[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg
, mem
[1], &mem
[2]);
350 else if (MACOSX_MDNS_MALLOC_DEBUGGING
>= 2) LogMsg("free( %s : %ld @ %p)", msg
, mem
[1], &mem
[2]);
351 //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]);
352 memset(mem
, 0xFF, sizeof(mDNSu32
) * 2 + mem
[1]);
353 validatelists(&mDNSStorage
);
360 //*************************************************************************************************************
361 #if COMPILER_LIKES_PRAGMA_MARK
363 #pragma mark - Mach client request handlers
366 //*************************************************************************************************************
367 // Client Death Detection
369 // This gets called after ALL constituent records of the Service Record Set have been deregistered
370 mDNSlocal
void FreeServiceInstance(ServiceInstance
*x
)
372 ServiceRecordSet
*s
= &x
->srs
;
373 ExtraResourceRecord
*e
= x
->srs
.Extras
, *tmp
;
377 e
->r
.RecordContext
= e
;
380 FreeExtraRR(&mDNSStorage
, &tmp
->r
, mStatus_MemFree
);
383 if (s
->RR_TXT
.resrec
.rdata
!= &s
->RR_TXT
.rdatastorage
)
384 freeL("TXT RData", s
->RR_TXT
.resrec
.rdata
);
386 if (s
->SubTypes
) freeL("ServiceSubTypes", s
->SubTypes
);
387 freeL("ServiceInstance", x
);
390 // AbortClient finds whatever client is identified by the given Mach port,
391 // stops whatever operation that client was doing, and frees its memory.
392 // In the case of a service registration, the actual freeing may be deferred
393 // until we get the mStatus_MemFree message, if necessary
394 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
, void *m
)
396 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
397 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
398 DNSServiceResolver
**l
= &DNSServiceResolverList
;
399 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
401 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
404 DNSServiceDomainEnumeration
*x
= *e
;
407 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->dom
.qname
.c
, m
, x
);
408 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort
, x
->dom
.qname
.c
);
409 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
410 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
411 freeL("DNSServiceDomainEnumeration", x
);
415 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
418 DNSServiceBrowser
*x
= *b
;
419 DNSServiceBrowserQuestion
*freePtr
, *qptr
= x
->qlist
;
424 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, qptr
->q
.qname
.c
, m
, x
);
425 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort
, qptr
->q
.qname
.c
);
426 mDNS_StopBrowse(&mDNSStorage
, &qptr
->q
);
429 freeL("DNSServiceBrowserQuestion", freePtr
);
433 DNSServiceBrowserResult
*t
= x
->results
;
434 x
->results
= x
->results
->next
;
435 freeL("DNSServiceBrowserResult", t
);
437 freeL("DNSServiceBrowser", x
);
441 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
444 DNSServiceResolver
*x
= *l
;
447 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->i
.name
.c
, m
, x
);
448 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort
, x
->i
.name
.c
);
449 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
450 freeL("DNSServiceResolver", x
);
454 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
457 ServiceInstance
*si
= NULL
;
458 DNSServiceRegistration
*x
= *r
;
464 ServiceInstance
*instance
= si
;
466 instance
->renameonmemfree
= mDNSfalse
;
467 if (m
&& m
!= x
) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort
, instance
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&instance
->srs
), m
, x
);
468 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort
, instance
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&instance
->srs
));
470 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
471 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
472 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
473 // the list, so we should go ahead and free the memory right now
474 if (mDNS_DeregisterService(&mDNSStorage
, &instance
->srs
)) FreeServiceInstance(instance
); // FreeServiceInstance invalidates pointer
477 freeL("DNSServiceRegistration", x
);
481 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort
);
484 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
486 mDNSlocal
void AbortClientWithLogMessage(mach_port_t c
, char *reason
, char *msg
, void *m
)
488 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
489 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
490 DNSServiceResolver
*l
= DNSServiceResolverList
;
491 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
492 DNSServiceBrowserQuestion
*qptr
;
494 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
495 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
496 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
497 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
499 if (e
) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c
, e
->dom
.qname
.c
, reason
, msg
);
502 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
503 LogMsg("%5d: Browser(%##s) %s%s", c
, qptr
->q
.qname
.c
, reason
, msg
);
505 else if (l
) LogMsg("%5d: Resolver(%##s) %s%s", c
, l
->i
.name
.c
, reason
, msg
);
509 for (si
= r
->regs
; si
; si
= si
->next
)
510 LogMsg("%5d: Registration(%##s) %s%s", c
, si
->srs
.RR_SRV
.resrec
.name
->c
, reason
, msg
);
512 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c
, reason
, msg
);
517 mDNSlocal mDNSBool
CheckForExistingClient(mach_port_t c
)
519 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
520 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
521 DNSServiceResolver
*l
= DNSServiceResolverList
;
522 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
523 DNSServiceBrowserQuestion
*qptr
;
525 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
526 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
527 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
528 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
529 if (e
) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c
, e
->dom
.qname
.c
);
532 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
533 LogMsg("%5d: Browser(%##s) already exists!", c
, qptr
->q
.qname
.c
);
535 if (l
) LogMsg("%5d: Resolver(%##s) already exists!", c
, l
->i
.name
.c
);
536 if (r
) LogMsg("%5d: Registration(%##s) already exists!", c
, r
->regs
? r
->regs
->srs
.RR_SRV
.resrec
.name
->c
: NULL
);
537 return(e
|| b
|| l
|| r
);
540 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
542 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
544 KQueueLock(&mDNSStorage
);
545 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
546 (void)unusedport
; // Unused
547 (void)size
; // Unused
548 (void)info
; // Unused
549 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
551 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
552 AbortClient(deathMessage
->not_port
, NULL
);
554 /* Deallocate the send right that came in the dead name notification */
555 mach_port_destroy(mach_task_self(), deathMessage
->not_port
);
557 KQueueUnlock(&mDNSStorage
, "Mach AbortClient");
560 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
562 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
, void *m
)
564 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
565 dispatch_source_t mach_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND
, ClientMachPort
, 0, dispatch_get_main_queue());
566 if (mach_source
== mDNSNULL
)
568 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
571 dispatch_source_set_event_handler(mach_source
, ^{
572 mach_port_destroy(mach_task_self(), ClientMachPort
);
574 dispatch_resume(mach_source
);
575 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
577 kern_return_t r
= mach_port_request_notification(mach_task_self(), ClientMachPort
, MACH_NOTIFY_DEAD_NAME
, 0,
578 client_death_port
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &prev
);
579 // If the port already died while we were thinking about it, then abort the operation right away
580 if (r
!= KERN_SUCCESS
)
581 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
582 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
585 //*************************************************************************************************************
586 // Domain Enumeration
588 mDNSlocal
void DomainEnumFound(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
590 kern_return_t status
;
591 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
592 DNSServiceDomainEnumerationReplyResultType rt
;
593 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->QuestionContext
;
596 debugf("DomainEnumFound: %##s PTR %##s", answer
->name
->c
, answer
->rdata
->u
.name
.c
);
597 if (answer
->rrtype
!= kDNSType_PTR
) return;
598 if (!x
) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
602 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
603 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
607 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
611 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
612 x
->ClientMachPort
, x
->dom
.qname
.c
, answer
->rdata
->u
.name
.c
,
613 !AddRecord
? "RemoveDomain" :
614 question
== &x
->dom
? "AddDomain" : "AddDomainDefault");
616 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, buffer
);
617 status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, buffer
, 0, MDNS_MM_TIMEOUT
);
618 if (status
== MACH_SEND_TIMED_OUT
)
619 AbortBlockedClient(x
->ClientMachPort
, "enumeration", x
);
622 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
625 // Check client parameter
626 (void)unusedserver
; // Unused
627 mStatus err
= mStatus_NoError
;
628 const char *errormsg
= "Unknown";
629 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
630 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
632 mDNS_DomainType dt1
= regDom
? mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
633 mDNS_DomainType dt2
= regDom
? mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
635 // Allocate memory, and handle failure
636 DNSServiceDomainEnumeration
*x
= mallocL("DNSServiceDomainEnumeration", sizeof(*x
));
637 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
639 // Set up object, and link into list
640 x
->ClientMachPort
= client
;
641 x
->next
= DNSServiceDomainEnumerationList
;
642 DNSServiceDomainEnumerationList
= x
;
644 verbosedebugf("%5d: Enumerate %s Domains", client
, regDom
? "Registration" : "Browsing");
647 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, NULL
, mDNSInterface_LocalOnly
, DomainEnumFound
, x
);
648 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, NULL
, mDNSInterface_LocalOnly
, DomainEnumFound
, x
);
649 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_GetDomains"; goto fail
; }
651 // Succeeded: Wrap up and return
652 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client
, x
->dom
.qname
.c
);
653 EnableDeathNotificationForClient(client
, x
);
654 return(mStatus_NoError
);
657 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client
, regDom
, errormsg
, err
);
661 //*************************************************************************************************************
662 // Browse for services
664 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
668 if (answer
->rrtype
!= kDNSType_PTR
)
669 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
); return; }
672 domainname type
, domain
;
673 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
675 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
676 answer
->name
->c
, answer
->rdata
->u
.name
.c
);
680 DNSServiceBrowserResult
*x
= mallocL("DNSServiceBrowserResult", sizeof(*x
));
681 if (!x
) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer
->rdata
->u
.name
.c
); return; }
683 verbosedebugf("FoundInstance: %s %##s", AddRecord
? "Add" : "Rmv", answer
->rdata
->u
.name
.c
);
684 AssignDomainName(&x
->result
, &answer
->rdata
->u
.name
);
686 x
->resultType
= DNSServiceBrowserReplyAddInstance
;
687 else x
->resultType
= DNSServiceBrowserReplyRemoveInstance
;
690 DNSServiceBrowser
*browser
= (DNSServiceBrowser
*)question
->QuestionContext
;
691 DNSServiceBrowserResult
**p
= &browser
->results
;
692 while (*p
) p
= &(*p
)->next
;
695 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
696 browser
->ClientMachPort
, question
->qname
.c
, DNSTypeName(question
->qtype
), AddRecord
? "Add" : "Rmv", RRDisplayString(m
, answer
));
699 mDNSlocal mStatus
AddDomainToBrowser(DNSServiceBrowser
*browser
, const domainname
*d
)
701 mStatus err
= mStatus_NoError
;
702 DNSServiceBrowserQuestion
*ptr
, *question
= NULL
;
704 for (ptr
= browser
->qlist
; ptr
; ptr
= ptr
->next
)
706 if (SameDomainName(&ptr
->q
.qname
, d
))
707 { debugf("Domain %##s already contained in browser", d
->c
); return mStatus_AlreadyRegistered
; }
710 question
= mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion
));
711 if (!question
) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr
; }
712 AssignDomainName(&question
->domain
, d
);
713 question
->next
= browser
->qlist
;
714 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser
->ClientMachPort
, browser
->type
.c
, d
->c
);
715 err
= mDNS_StartBrowse(&mDNSStorage
, &question
->q
, &browser
->type
, d
, mDNSNULL
, mDNSInterface_Any
, 0, mDNSfalse
, mDNSfalse
, FoundInstance
, browser
);
717 browser
->qlist
= question
;
720 LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err
);
721 freeL("DNSServiceBrowserQuestion", question
);
726 mDNSexport
void machserver_automatic_browse_domain_changed(const domainname
*d
, mDNSBool add
)
728 DNSServiceBrowser
*ptr
;
729 for (ptr
= DNSServiceBrowserList
; ptr
; ptr
= ptr
->next
)
731 if (ptr
->DefaultDomain
)
735 mStatus err
= AddDomainToBrowser(ptr
, d
);
736 if (err
&& err
!= mStatus_AlreadyRegistered
) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d
, ptr
->ClientMachPort
);
740 DNSServiceBrowserQuestion
**q
= &ptr
->qlist
;
743 if (SameDomainName(&(*q
)->domain
, d
))
745 DNSServiceBrowserQuestion
*rem
= *q
;
747 mDNS_StopQueryWithRemoves(&mDNSStorage
, &rem
->q
);
748 freeL("DNSServiceBrowserQuestion", rem
);
753 LogMsg("Requested removal of default domain %##s not in client %5d's list", d
->c
, ptr
->ClientMachPort
);
759 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
760 DNSCString regtype
, DNSCString domain
)
762 // Check client parameter
763 (void)unusedserver
; // Unused
764 mStatus err
= mStatus_NoError
;
765 const char *errormsg
= "Unknown";
767 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
768 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
770 // Check other parameters
773 mDNSs32 NumSubTypes
= ChopSubTypes(regtype
, mDNSNULL
); // Note: Modifies regtype string to remove trailing subtypes
774 if (NumSubTypes
< 0 || NumSubTypes
> 1) { errormsg
= "Bad Service SubType"; goto badparam
; }
775 if (NumSubTypes
== 1 && !AppendDNSNameString(&t
, regtype
+ strlen(regtype
) + 1))
776 { errormsg
= "Bad Service SubType"; goto badparam
; }
777 if (!regtype
[0] || !AppendDNSNameString(&t
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
779 if (!MakeDomainNameFromDNSNameString(&temp
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
780 if (temp
.c
[0] > 15 && (!domain
|| domain
[0] == 0)) domain
= "local."; // For over-long service types, we only allow domain "local"
782 // Allocate memory, and handle failure
783 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
784 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
786 // Set up object, and link into list
787 AssignDomainName(&x
->type
, &t
);
788 x
->ClientMachPort
= client
;
792 x
->next
= DNSServiceBrowserList
;
793 DNSServiceBrowserList
= x
;
797 // Start browser for an explicit domain
798 x
->DefaultDomain
= mDNSfalse
;
799 if (!MakeDomainNameFromDNSNameString(&d
, domain
)) { errormsg
= "Illegal domain"; goto badparam
; }
800 err
= AddDomainToBrowser(x
, &d
);
801 if (err
) { AbortClient(client
, x
); errormsg
= "AddDomainToBrowser"; goto fail
; }
805 DNameListElem
*sdPtr
;
806 // Start browser on all domains
807 x
->DefaultDomain
= mDNStrue
;
808 if (!AutoBrowseDomains
) { AbortClient(client
, x
); errormsg
= "GetSearchDomainList"; goto fail
; }
809 for (sdPtr
= AutoBrowseDomains
; sdPtr
; sdPtr
= sdPtr
->next
)
811 err
= AddDomainToBrowser(x
, &sdPtr
->name
);
814 // only terminally bail if .local fails
815 if (!SameDomainName(&localdomain
, &sdPtr
->name
))
816 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr
->name
.c
);
817 else { AbortClient(client
, x
); errormsg
= "AddDomainToBrowser"; goto fail
; }
822 // Succeeded: Wrap up and return
823 EnableDeathNotificationForClient(client
, x
);
824 return(mStatus_NoError
);
827 err
= mStatus_BadParamErr
;
829 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client
, regtype
, domain
, errormsg
, err
);
833 //*************************************************************************************************************
834 // Resolve Service Info
836 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
838 kern_return_t status
;
839 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->ServiceInfoQueryContext
;
840 NetworkInterfaceInfoOSX
*ifx
= IfindexToInterfaceInfoOSX(m
, query
->info
->InterfaceID
);
841 if (query
->info
->InterfaceID
== mDNSInterface_LocalOnly
|| query
->info
->InterfaceID
== mDNSInterface_P2P
) ifx
= mDNSNULL
;
842 struct sockaddr_storage interface
;
843 struct sockaddr_storage address
;
845 int i
, pstrlen
= query
->info
->TXTinfo
[0];
848 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
850 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
852 mDNSPlatformMemZero(&interface
, sizeof(interface
));
853 mDNSPlatformMemZero(&address
, sizeof(address
));
855 if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
)
857 struct sockaddr_in
*s
= (struct sockaddr_in
*)&interface
;
858 s
->sin_len
= sizeof(*s
);
859 s
->sin_family
= AF_INET
;
861 s
->sin_addr
.s_addr
= ifx
->ifinfo
.ip
.ip
.v4
.NotAnInteger
;
863 else if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
)
865 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&interface
;
866 sin6
->sin6_len
= sizeof(*sin6
);
867 sin6
->sin6_family
= AF_INET6
;
868 sin6
->sin6_flowinfo
= 0;
870 sin6
->sin6_addr
= *(struct in6_addr
*)&ifx
->ifinfo
.ip
.ip
.v6
;
871 sin6
->sin6_scope_id
= ifx
->scope_id
;
874 if (query
->info
->ip
.type
== mDNSAddrType_IPv4
)
876 struct sockaddr_in
*s
= (struct sockaddr_in
*)&address
;
877 s
->sin_len
= sizeof(*s
);
878 s
->sin_family
= AF_INET
;
879 s
->sin_port
= query
->info
->port
.NotAnInteger
;
880 s
->sin_addr
.s_addr
= query
->info
->ip
.ip
.v4
.NotAnInteger
;
884 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&address
;
885 sin6
->sin6_len
= sizeof(*sin6
);
886 sin6
->sin6_family
= AF_INET6
;
887 sin6
->sin6_port
= query
->info
->port
.NotAnInteger
;
888 sin6
->sin6_flowinfo
= 0;
889 sin6
->sin6_addr
= *(struct in6_addr
*)&query
->info
->ip
.ip
.v6
;
890 sin6
->sin6_scope_id
= ifx
? ifx
->scope_id
: 0;
893 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
894 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
895 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
896 // ASCII-1 characters are used in the C-string as boundary markers,
897 // to indicate the boundaries between the original constituent P-strings.
898 for (i
=1; i
<query
->info
->TXTlen
; i
++)
901 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
905 pstrlen
= query
->info
->TXTinfo
[i
];
908 cstring
[i
-1] = 0; // Put the terminating NULL on the end
910 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x
->ClientMachPort
,
911 x
->i
.name
.c
, &query
->info
->ip
, mDNSVal16(query
->info
->port
));
912 status
= DNSServiceResolverReply_rpc(x
->ClientMachPort
,
913 (char*)&interface
, (char*)&address
, cstring
, 0, MDNS_MM_TIMEOUT
);
914 if (status
== MACH_SEND_TIMED_OUT
)
915 AbortBlockedClient(x
->ClientMachPort
, "resolve", x
);
918 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
919 DNSCString name
, DNSCString regtype
, DNSCString domain
)
921 // Check client parameter
922 (void)unusedserver
; // Unused
923 mStatus err
= mStatus_NoError
;
924 const char *errormsg
= "Unknown";
925 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
926 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
928 // Check other parameters
930 domainname t
, d
, srv
;
931 if (!name
[0] || !MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
932 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
933 if (!domain
[0] || !MakeDomainNameFromDNSNameString(&d
, domain
)) { errormsg
= "Bad Domain"; goto badparam
; }
934 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
936 // Allocate memory, and handle failure
937 DNSServiceResolver
*x
= mallocL("DNSServiceResolver", sizeof(*x
));
938 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
940 // Set up object, and link into list
941 x
->ClientMachPort
= client
;
942 x
->i
.InterfaceID
= mDNSInterface_Any
;
944 x
->ReportTime
= NonZeroTime(mDNS_TimeNow(&mDNSStorage
) + 130 * mDNSPlatformOneSecond
);
945 x
->next
= DNSServiceResolverList
;
946 DNSServiceResolverList
= x
;
949 LogOperation("%5d: DNSServiceResolve(%##s) START", client
, x
->i
.name
.c
);
950 err
= mDNS_StartResolveService(&mDNSStorage
, &x
->q
, &x
->i
, FoundInstanceInfo
, x
);
951 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartResolveService"; goto fail
; }
953 // Succeeded: Wrap up and return
954 EnableDeathNotificationForClient(client
, x
);
955 return(mStatus_NoError
);
958 err
= mStatus_BadParamErr
;
960 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client
, name
, regtype
, domain
, errormsg
, err
);
964 //*************************************************************************************************************
967 mDNSexport
void RecordUpdatedNiceLabel(mDNS
*const m
, mDNSs32 delay
)
969 m
->p
->NotifyUser
= NonZeroTime(m
->timenow
+ delay
);
972 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
)
974 ServiceInstance
*si
= (ServiceInstance
*)srs
->ServiceContext
;
976 if (result
== mStatus_NoError
)
978 kern_return_t status
;
979 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
));
980 status
= DNSServiceRegistrationReply_rpc(si
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
981 if (status
== MACH_SEND_TIMED_OUT
)
982 AbortBlockedClient(si
->ClientMachPort
, "registration success", si
);
983 if (si
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
984 RecordUpdatedNiceLabel(m
, 0); // Successfully got new name, tell user immediately
987 else if (result
== mStatus_NameConflict
)
989 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
));
990 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
991 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
992 if (si
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
994 // On conflict for an autoname service, rename and reregister *all* autoname services
995 IncrementLabelSuffix(&m
->nicelabel
, mDNStrue
);
996 mDNS_ConfigChanged(m
);
998 else if (si
->autoname
)
1000 mDNS_RenameAndReregisterService(m
, srs
, mDNSNULL
);
1005 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1006 // of their registration in the usual way (which we will catch via client death notification).
1007 // If the Mach queue is full, we forcibly abort the client immediately.
1008 kern_return_t status
= DNSServiceRegistrationReply_rpc(si
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
1009 if (status
== MACH_SEND_TIMED_OUT
)
1010 AbortBlockedClient(si
->ClientMachPort
, "registration conflict", NULL
);
1014 else if (result
== mStatus_MemFree
)
1016 if (si
->renameonmemfree
) // We intentionally terminated registration so we could re-register with new name
1018 debugf("RegCallback renaming %#s to %#s", si
->name
.c
, m
->nicelabel
.c
);
1019 si
->renameonmemfree
= mDNSfalse
;
1020 si
->name
= m
->nicelabel
;
1021 mDNS_RenameAndReregisterService(m
, srs
, &si
->name
);
1025 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1026 DNSServiceRegistration
*r
;
1027 for (r
= DNSServiceRegistrationList
; r
; r
= r
->next
)
1029 ServiceInstance
**sp
= &r
->regs
;
1032 if (*sp
== si
) { LogMsg("RegCallback: %##s Still in list; removing", srs
->RR_SRV
.resrec
.name
->c
); *sp
= (*sp
)->next
; break; }
1037 FreeServiceInstance(si
);
1041 else if (result
!= mStatus_NATTraversal
)
1042 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
), result
);
1045 mDNSlocal mStatus
AddServiceInstance(DNSServiceRegistration
*x
, const domainname
*domain
)
1048 ServiceInstance
*si
= NULL
;
1049 AuthRecord
*SubTypes
= NULL
;
1051 for (si
= x
->regs
; si
; si
= si
->next
)
1053 if (SameDomainName(&si
->domain
, domain
))
1054 { LogMsg("Requested addition of domain %##s already in list", domain
->c
); return mStatus_AlreadyRegistered
; }
1057 SubTypes
= AllocateSubTypes(x
->NumSubTypes
, x
->regtype
, mDNSNULL
);
1058 if (x
->NumSubTypes
&& !SubTypes
) return mStatus_NoMemoryErr
;
1060 si
= mallocL("ServiceInstance", sizeof(*si
) - sizeof(RDataBody
) + x
->rdsize
);
1061 if (!si
) return mStatus_NoMemoryErr
;
1063 si
->ClientMachPort
= x
->ClientMachPort
;
1064 si
->renameonmemfree
= mDNSfalse
;
1065 si
->autoname
= x
->autoname
;
1066 si
->name
= x
->autoname
? mDNSStorage
.nicelabel
: x
->name
;
1067 si
->domain
= *domain
;
1068 si
->srs
.AnonData
= mDNSNULL
;
1070 err
= mDNS_RegisterService(&mDNSStorage
, &si
->srs
, &si
->name
, &x
->type
, domain
, NULL
,
1071 x
->port
, x
->txtinfo
, x
->txt_len
, SubTypes
, x
->NumSubTypes
, mDNSInterface_Any
, RegCallback
, si
, 0);
1079 LogMsg("Error %d for registration of service in domain %##s", err
, domain
->c
);
1080 freeL("ServiceInstance", si
);
1085 mDNSexport
void machserver_automatic_registration_domain_changed(const domainname
*d
, mDNSBool add
)
1087 DNSServiceRegistration
*reg
;
1089 for (reg
= DNSServiceRegistrationList
; reg
; reg
= reg
->next
)
1091 if (reg
->DefaultDomain
)
1094 AddServiceInstance(reg
, d
);
1097 ServiceInstance
**si
= ®
->regs
;
1100 if (SameDomainName(&(*si
)->domain
, d
))
1102 ServiceInstance
*s
= *si
;
1104 if (mDNS_DeregisterService(&mDNSStorage
, &s
->srs
)) FreeServiceInstance(s
); // only free memory synchronously on error
1109 if (!si
) debugf("Requested removal of default domain %##s not in client %5d's list", d
, reg
->ClientMachPort
); // normal if registration failed
1115 mDNSexport kern_return_t
provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
1116 DNSCString name
, DNSCString regtype
, DNSCString domain
, IPPort IpPort
, DNSCString txtRecord
)
1118 (void)unusedserver
; // Unused
1119 mStatus err
= mStatus_NoError
;
1120 const char *errormsg
= "Unknown";
1122 // older versions of this code passed the port via mach IPC as an int.
1123 // we continue to pass it as 4 bytes to maintain binary compatibility,
1124 // but now ensure that the network byte order is preserved by using a struct
1126 port
.b
[0] = IpPort
.bytes
[2];
1127 port
.b
[1] = IpPort
.bytes
[3];
1129 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1130 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
1132 // Check for sub-types after the service type
1133 size_t reglen
= strlen(regtype
) + 1;
1134 if (reglen
> MAX_ESCAPED_DOMAIN_NAME
) { errormsg
= "reglen too long"; goto badparam
; }
1135 mDNSs32 NumSubTypes
= ChopSubTypes(regtype
, mDNSNULL
); // Note: Modifies regtype string to remove trailing subtypes
1136 if (NumSubTypes
< 0) { errormsg
= "Bad Service SubType"; goto badparam
; }
1138 // Check other parameters
1142 if (!name
[0]) n
= mDNSStorage
.nicelabel
;
1143 else if (!MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
1144 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
1145 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
1146 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
1148 unsigned char txtinfo
[1024] = "";
1149 unsigned int data_len
= 0;
1150 unsigned int size
= sizeof(RDataBody
);
1151 unsigned char *pstring
= &txtinfo
[data_len
];
1152 char *ptr
= txtRecord
;
1154 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1155 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1156 // Hence we have to convert the C-string to a P-string.
1157 // ASCII-1 characters are allowed in the C-string as boundary markers,
1158 // so that a single C-string can be used to represent one or more P-strings.
1161 if (++data_len
>= sizeof(txtinfo
)) { errormsg
= "TXT record too long"; goto badtxt
; }
1162 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
1164 pstring
= &txtinfo
[data_len
];
1170 if (pstring
[0] == 255) { errormsg
= "TXT record invalid (component longer than 255)"; goto badtxt
; }
1171 pstring
[++pstring
[0]] = *ptr
++;
1176 if (size
< data_len
)
1179 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1180 // a port number of zero. When two instances of the protected client are allowed to run on one
1181 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1182 if (!mDNSIPPortIsZero(port
))
1184 int count
= CountExistingRegistrations(&srv
, port
);
1186 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1187 client
, count
+1, srv
.c
, mDNSVal16(port
));
1190 // Allocate memory, and handle failure
1191 DNSServiceRegistration
*x
= mallocL("DNSServiceRegistration", sizeof(*x
));
1192 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1193 mDNSPlatformMemZero(x
, sizeof(*x
));
1195 // Set up object, and link into list
1196 x
->ClientMachPort
= client
;
1197 x
->DefaultDomain
= !domain
[0];
1198 x
->autoname
= (!name
[0]);
1200 x
->NumSubTypes
= NumSubTypes
;
1201 memcpy(x
->regtype
, regtype
, reglen
);
1205 memcpy(x
->txtinfo
, txtinfo
, 1024);
1206 x
->txt_len
= data_len
;
1210 x
->next
= DNSServiceRegistrationList
;
1211 DNSServiceRegistrationList
= x
;
1213 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1214 x
->ClientMachPort
, name
, regtype
, domain
, mDNSVal16(port
));
1216 err
= AddServiceInstance(x
, &d
);
1217 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_RegisterService"; goto fail
; } // bail if .local (or explicit domain) fails
1219 if (x
->DefaultDomain
)
1222 for (p
= AutoRegistrationDomains
; p
; p
= p
->next
)
1223 AddServiceInstance(x
, &p
->name
);
1226 // Succeeded: Wrap up and return
1227 EnableDeathNotificationForClient(client
, x
);
1228 return(mStatus_NoError
);
1231 LogMsg("%5d: TXT record: %.100s...", client
, txtRecord
);
1233 err
= mStatus_BadParamErr
;
1235 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)",
1236 client
, name
, regtype
, domain
, mDNSVal16(port
), errormsg
, err
);
1240 mDNSlocal
void mDNSPreferencesSetNames(mDNS
*const m
, int key
, domainlabel
*old
, domainlabel
*new)
1242 domainlabel
*prevold
, *prevnew
;
1245 case kmDNSComputerName
:
1246 case kmDNSLocalHostName
:
1247 if (key
== kmDNSComputerName
)
1249 prevold
= &m
->p
->prevoldnicelabel
;
1250 prevnew
= &m
->p
->prevnewnicelabel
;
1254 prevold
= &m
->p
->prevoldhostlabel
;
1255 prevnew
= &m
->p
->prevnewhostlabel
;
1257 // There are a few cases where we need to invoke the helper.
1259 // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need
1260 // to invoke the helper so that it pops up a dialogue to inform the user about the
1263 // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label
1264 // through the preferences pane. We may have to inform the helper as it may have popped up
1265 // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking
1266 // the helper in this case if the previous values (old and new) that we told helper last time
1267 // are same. If the previous old and new values are same, helper does not care.
1269 // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old"
1270 // is not called with NULL today, but this makes it future proof.
1271 if (!old
|| !new || !SameDomainLabelCS(old
->c
, new->c
) ||
1272 !SameDomainLabelCS(old
->c
, prevold
->c
) ||
1273 !SameDomainLabelCS(new->c
, prevnew
->c
))
1283 mDNSPreferencesSetName(key
, old
, new);
1287 LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s",
1288 (key
== kmDNSComputerName
? "prevoldnicelabel" : "prevoldhostlabel"), prevold
->c
,
1289 (key
== kmDNSComputerName
? "prevnewnicelabel" : "prevnewhostlabel"), prevnew
->c
,
1294 LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key
);
1299 mDNSlocal
void mDNS_StatusCallback(mDNS
*const m
, mStatus result
)
1302 if (result
== mStatus_NoError
)
1304 if (!SameDomainLabelCS(m
->p
->userhostlabel
.c
, m
->hostlabel
.c
))
1305 LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
1306 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1307 RecordUpdatedNiceLabel(m
, mDNSPlatformOneSecond
);
1309 else if (result
== mStatus_NameConflict
)
1311 LogInfo("Local Hostname conflict for \"%#s.local\"", m
->hostlabel
.c
);
1312 if (!m
->p
->HostNameConflict
) m
->p
->HostNameConflict
= NonZeroTime(m
->timenow
);
1313 else if (m
->timenow
- m
->p
->HostNameConflict
> 60 * mDNSPlatformOneSecond
)
1315 // Tell the helper we've given up
1316 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, NULL
);
1319 else if (result
== mStatus_GrowCache
)
1321 // Allocate another chunk of cache storage
1322 CacheEntity
*storage
= mallocL("mStatus_GrowCache", sizeof(CacheEntity
) * RR_CACHE_SIZE
);
1323 //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
1324 if (storage
) mDNS_GrowCache(m
, storage
, RR_CACHE_SIZE
);
1326 else if (result
== mStatus_ConfigChanged
)
1328 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
1329 mDNSPreferencesSetNames(m
, kmDNSComputerName
, &m
->p
->usernicelabel
, &m
->nicelabel
);
1330 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, &m
->hostlabel
);
1332 // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
1333 DNSServiceRegistration
*r
;
1334 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1337 ServiceInstance
*si
;
1338 for (si
= r
->regs
; si
; si
= si
->next
)
1340 if (!SameDomainLabelCS(si
->name
.c
, m
->nicelabel
.c
))
1342 debugf("NetworkChanged renaming %##s to %#s", si
->srs
.RR_SRV
.resrec
.name
->c
, m
->nicelabel
.c
);
1343 si
->renameonmemfree
= mDNStrue
;
1344 if (mDNS_DeregisterService_drt(m
, &si
->srs
, mDNS_Dereg_rapid
))
1345 RegCallback(m
, &si
->srs
, mStatus_MemFree
); // If service deregistered already, we can re-register immediately
1350 // Then we call into the UDS daemon code, to let it do the same
1351 udsserver_handle_configchange(m
);
1355 //*************************************************************************************************************
1356 // Add / Update / Remove records from existing Registration
1358 mDNSexport kern_return_t
provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1359 int type
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
, natural_t
*reference
)
1361 // Check client parameter
1363 mStatus err
= mStatus_NoError
;
1364 const char *errormsg
= "Unknown";
1365 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1366 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1367 ServiceInstance
*si
;
1369 (void)unusedserver
; // Unused
1370 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1371 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1373 // Check other parameters
1374 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1375 if (data_len
> sizeof(RDataBody
)) size
= data_len
;
1376 else size
= sizeof(RDataBody
);
1379 *reference
= (natural_t
)id
;
1380 for (si
= x
->regs
; si
; si
= si
->next
)
1382 // Allocate memory, and handle failure
1383 ExtraResourceRecord
*extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
1384 if (!extra
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1386 // Fill in type, length, and data of new record
1387 extra
->r
.resrec
.rrtype
= type
;
1388 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1389 extra
->r
.resrec
.rdlength
= data_len
;
1390 memcpy(&extra
->r
.rdatastorage
.u
.data
, data
, data_len
);
1393 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1394 client
, si
->srs
.RR_SRV
.resrec
.name
->c
, type
, data_len
, extra
);
1395 err
= mDNS_AddRecordToService(&mDNSStorage
, &si
->srs
, extra
, &extra
->r
.rdatastorage
, ttl
, 0);
1399 freeL("Extra Resource Record", extra
);
1400 errormsg
= "mDNS_AddRecordToService";
1404 extra
->ClientID
= id
;
1407 return mStatus_NoError
;
1410 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client
, x
? x
->name
.c
: (mDNSu8
*)"\x8" "«NULL»", type
, data_len
, errormsg
, err
);
1411 return mStatus_UnknownErr
;
1414 mDNSlocal
void UpdateCallback(mDNS
*const m
, AuthRecord
*const rr
, RData
*OldRData
, mDNSu16 OldRDLen
)
1417 (void)OldRDLen
; // Unused
1418 if (OldRData
!= &rr
->rdatastorage
)
1419 freeL("Old RData", OldRData
);
1422 mDNSlocal mStatus
UpdateRecord(ServiceRecordSet
*srs
, mach_port_t client
, AuthRecord
*rr
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1424 // Check client parameter
1425 mStatus err
= mStatus_NoError
;
1426 const char *errormsg
= "Unknown";
1427 const domainname
*name
= (const domainname
*)"";
1429 name
= srs
->RR_SRV
.resrec
.name
;
1431 unsigned int size
= sizeof(RDataBody
);
1432 if (size
< data_len
)
1435 // Allocate memory, and handle failure
1436 RData
*newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
1437 if (!newrdata
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1439 // Fill in new length, and data
1440 newrdata
->MaxRDLength
= size
;
1441 memcpy(&newrdata
->u
, data
, data_len
);
1443 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1444 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1445 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1446 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& data_len
== 0) { data_len
= 1; newrdata
->u
.txt
.c
[0] = 0; }
1449 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1450 client
, srs
->RR_SRV
.resrec
.name
->c
, data_len
);
1452 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, data_len
, newrdata
, UpdateCallback
);
1455 errormsg
= "mDNS_Update";
1456 freeL("RData", newrdata
);
1459 return(mStatus_NoError
);
1462 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client
, name
->c
, data_len
, errormsg
, err
);
1466 mDNSexport kern_return_t
provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1467 natural_t reference
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1469 // Check client parameter
1470 mStatus err
= mStatus_NoError
;
1471 const char *errormsg
= "Unknown";
1472 const domainname
*name
= (const domainname
*)"";
1473 ServiceInstance
*si
;
1475 (void)unusedserver
; // unused
1476 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1477 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1478 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1479 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1481 // Check other parameters
1482 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1484 for (si
= x
->regs
; si
; si
= si
->next
)
1486 AuthRecord
*r
= NULL
;
1488 // Find the record we're updating. NULL reference means update the primary TXT record
1489 if (!reference
) r
= &si
->srs
.RR_TXT
;
1492 ExtraResourceRecord
*ptr
;
1493 for (ptr
= si
->srs
.Extras
; ptr
; ptr
= ptr
->next
)
1495 if ((natural_t
)ptr
->ClientID
== reference
)
1496 { r
= &ptr
->r
; break; }
1498 if (!r
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such record"; goto fail
; }
1500 err
= UpdateRecord(&si
->srs
, client
, r
, data
, data_len
, ttl
);
1501 if (err
) goto fail
; //!!!KRS this will cause failures for non-local defaults!
1504 return mStatus_NoError
;
1507 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client
, name
->c
, reference
, data_len
, errormsg
, err
);
1511 mDNSlocal mStatus
RemoveRecord(ServiceRecordSet
*srs
, ExtraResourceRecord
*extra
, mach_port_t client
)
1513 const domainname
*const name
= srs
->RR_SRV
.resrec
.name
;
1514 mStatus err
= mStatus_NoError
;
1517 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client
, srs
->RR_SRV
.resrec
.name
->c
);
1519 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, srs
, extra
, FreeExtraRR
, extra
);
1520 if (err
) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client
, name
->c
, err
);
1525 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1526 natural_t reference
)
1528 // Check client parameter
1529 (void)unusedserver
; // Unused
1530 mStatus err
= mStatus_NoError
;
1531 const char *errormsg
= "Unknown";
1532 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1533 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1534 ServiceInstance
*si
;
1536 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1537 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1539 for (si
= x
->regs
; si
; si
= si
->next
)
1541 ExtraResourceRecord
*e
;
1542 for (e
= si
->srs
.Extras
; e
; e
= e
->next
)
1544 if ((natural_t
)e
->ClientID
== reference
)
1546 err
= RemoveRecord(&si
->srs
, e
, client
);
1550 if (!e
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such reference"; goto fail
; }
1553 return mStatus_NoError
;
1556 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client
, reference
, errormsg
, err
);
1560 //*************************************************************************************************************
1561 #if COMPILER_LIKES_PRAGMA_MARK
1563 #pragma mark - Startup, shutdown, and supporting code
1566 mDNSlocal
void ExitCallback(int sig
)
1568 (void)sig
; // Unused
1569 LogMsg("%s stopping", mDNSResponderVersionString
);
1571 debugf("ExitCallback: Aborting MIG clients");
1572 while (DNSServiceDomainEnumerationList
)
1573 AbortClient(DNSServiceDomainEnumerationList
->ClientMachPort
, DNSServiceDomainEnumerationList
);
1574 while (DNSServiceBrowserList
)
1575 AbortClient(DNSServiceBrowserList
->ClientMachPort
, DNSServiceBrowserList
);
1576 while (DNSServiceResolverList
)
1577 AbortClient(DNSServiceResolverList
->ClientMachPort
, DNSServiceResolverList
);
1578 while (DNSServiceRegistrationList
)
1579 AbortClient(DNSServiceRegistrationList
->ClientMachPort
, DNSServiceRegistrationList
);
1581 if (udsserver_exit() < 0)
1582 LogMsg("ExitCallback: udsserver_exit failed");
1584 debugf("ExitCallback: mDNS_StartExit");
1585 mDNS_StartExit(&mDNSStorage
);
1588 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1590 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1592 mig_reply_error_t
*request
= msg
;
1593 mig_reply_error_t
*reply
;
1594 mach_msg_return_t mr
;
1596 (void)port
; // Unused
1597 (void)size
; // Unused
1598 (void)info
; // Unused
1600 KQueueLock(&mDNSStorage
);
1602 /* allocate a reply buffer */
1603 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
1605 /* call the MiG server routine */
1606 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
1608 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
1610 if (reply
->RetCode
== MIG_NO_REPLY
)
1613 * This return code is a little tricky -- it appears that the
1614 * demux routine found an error of some sort, but since that
1615 * error would not normally get returned either to the local
1616 * user or the remote one, we pretend it's ok.
1618 CFAllocatorDeallocate(NULL
, reply
);
1623 * destroy any out-of-line data in the request buffer but don't destroy
1624 * the reply port right (since we need that to send an error message).
1626 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1627 mach_msg_destroy(&request
->Head
);
1630 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
1632 /* no reply port, so destroy the reply */
1633 if (reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
1634 mach_msg_destroy(&reply
->Head
);
1635 CFAllocatorDeallocate(NULL
, reply
);
1642 * We don't want to block indefinitely because the client
1643 * isn't receiving messages from the reply port.
1644 * If we have a send-once right for the reply port, then
1645 * this isn't a concern because the send won't block.
1646 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1647 * To avoid falling off the kernel's fast RPC path unnecessarily,
1648 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1651 options
= MACH_SEND_MSG
;
1652 if (MACH_MSGH_BITS_REMOTE(reply
->Head
.msgh_bits
) == MACH_MSG_TYPE_MOVE_SEND_ONCE
)
1653 options
|= MACH_SEND_TIMEOUT
;
1655 mr
= mach_msg(&reply
->Head
, /* msg */
1656 options
, /* option */
1657 reply
->Head
.msgh_size
, /* send_size */
1659 MACH_PORT_NULL
, /* rcv_name */
1660 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1661 MACH_PORT_NULL
); /* notify */
1663 /* Has a message error occurred? */
1666 case MACH_SEND_INVALID_DEST
:
1667 case MACH_SEND_TIMED_OUT
:
1668 /* the reply can't be delivered, so destroy it */
1669 mach_msg_destroy(&reply
->Head
);
1673 /* Includes success case. */
1677 CFAllocatorDeallocate(NULL
, reply
);
1680 KQueueUnlock(&mDNSStorage
, "Mach client event");
1683 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1684 mDNSlocal
void HandleSIG(int sig
)
1686 kern_return_t status
;
1687 mach_msg_header_t header
;
1689 // WARNING: can't call syslog or fprintf from signal handler
1690 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1691 header
.msgh_remote_port
= signal_port
;
1692 header
.msgh_local_port
= MACH_PORT_NULL
;
1693 header
.msgh_size
= sizeof(header
);
1694 header
.msgh_id
= sig
;
1696 status
= mach_msg(&header
, MACH_SEND_MSG
| MACH_SEND_TIMEOUT
, header
.msgh_size
,
1697 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1699 if (status
!= MACH_MSG_SUCCESS
)
1701 if (status
== MACH_SEND_TIMED_OUT
) mach_msg_destroy(&header
);
1702 if (sig
== SIGTERM
|| sig
== SIGINT
) exit(-1);
1706 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1708 mDNSlocal
void INFOCallback(void)
1710 mDNSs32 utc
= mDNSPlatformUTC();
1711 NetworkInterfaceInfoOSX
*i
;
1715 // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when SIGINFO is received.
1716 // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file
1717 // present in /etc/asl/com.apple.networking.mDNSResponder.
1718 asl_set(log_msg
, "LoggerID", "com.apple.networking.mDNSResponder");
1720 LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString
, OSXVers
? "OSXVers" : "iOSVers", OSXVers
? OSXVers
: iOSVers
);
1722 udsserver_info(&mDNSStorage
);
1723 xpcserver_info(&mDNSStorage
);
1725 LogMsgNoIdent("----- KQSocketEventSources -----");
1726 if (!gEventSources
) LogMsgNoIdent("<None>");
1729 KQSocketEventSource
*k
;
1730 for (k
= gEventSources
; k
; k
=k
->next
)
1731 LogMsgNoIdent("%3d %s %s", k
->fd
, k
->kqs
.KQtask
, k
->fd
== mDNSStorage
.uds_listener_skt
? "Listener for incoming UDS clients" : " ");
1734 LogMsgNoIdent("------ Network Interfaces ------");
1735 if (!mDNSStorage
.p
->InterfaceList
) LogMsgNoIdent("<None>");
1738 for (i
= mDNSStorage
.p
->InterfaceList
; i
; i
= i
->next
)
1740 // Allow six characters for interface name, for names like "vmnet8"
1742 LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
1743 i
, i
->ifinfo
.InterfaceID
, i
->Registered
,
1744 i
->sa_family
== AF_INET
? "v4" : i
->sa_family
== AF_INET6
? "v6" : "??", i
->ifinfo
.ifname
, i
->scope_id
, &i
->ifinfo
.MAC
, &i
->BSSID
,
1745 &i
->ifinfo
.ip
, utc
- i
->LastSeen
);
1748 const CacheRecord
*sps
[3];
1749 FindSPSInCache(&mDNSStorage
, &i
->ifinfo
.NetWakeBrowse
, sps
);
1750 LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
1751 i
, i
->ifinfo
.InterfaceID
, i
->Registered
,
1752 i
->sa_family
== AF_INET
? "v4" : i
->sa_family
== AF_INET6
? "v6" : "??", i
->ifinfo
.ifname
, i
->scope_id
, &i
->ifinfo
.MAC
, &i
->BSSID
,
1753 i
->ifinfo
.InterfaceActive
? "Active" : " ",
1754 i
->ifinfo
.IPv4Available
? "v4" : " ",
1755 i
->ifinfo
.IPv4Available
? (mDNSv4Addr
*)&i
->ifa_v4addr
: &zerov4Addr
,
1756 i
->ifinfo
.IPv6Available
? "v6" : " ",
1757 i
->ifinfo
.Advertise
? "A" : " ",
1758 i
->ifinfo
.McastTxRx
? "M" : " ",
1759 !(i
->ifinfo
.InterfaceActive
&& i
->ifinfo
.NetWake
) ? " " : !sps
[0] ? "p" : "P",
1762 if (sps
[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[0]->resrec
.rdata
->u
.name
.c
), sps
[0]->resrec
.rdata
->u
.name
.c
);
1763 if (sps
[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[1]->resrec
.rdata
->u
.name
.c
), sps
[1]->resrec
.rdata
->u
.name
.c
);
1764 if (sps
[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[2]->resrec
.rdata
->u
.name
.c
), sps
[2]->resrec
.rdata
->u
.name
.c
);
1769 LogMsgNoIdent("--------- DNS Servers(%d) ----------", NumUnicastDNSServers
);
1770 if (!mDNSStorage
.DNSServers
) LogMsgNoIdent("<None>");
1773 for (s
= mDNSStorage
.DNSServers
; s
; s
= s
->next
)
1775 NetworkInterfaceInfoOSX
*ifx
= IfindexToInterfaceInfoOSX(&mDNSStorage
, s
->interface
);
1776 LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s %s %s %s %s",
1777 s
->domain
.c
, ifx
? ifx
->ifinfo
.ifname
: "", ifx
? " " : "", &s
->addr
, mDNSVal16(s
->port
),
1778 s
->penaltyTime
? s
->penaltyTime
- mDNS_TimeNow(&mDNSStorage
) : 0, DNSScopeToString(s
->scoped
),
1779 s
->timeout
, s
->resGroupID
,
1780 s
->teststate
== DNSServer_Untested
? "(Untested)" :
1781 s
->teststate
== DNSServer_Passed
? "" :
1782 s
->teststate
== DNSServer_Failed
? "(Failed)" :
1783 s
->teststate
== DNSServer_Disabled
? "(Disabled)" : "(Unknown state)",
1784 s
->req_A
? "v4" : "!v4",
1785 s
->req_AAAA
? "v6" : "!v6",
1786 s
->cellIntf
? "cell" : "!cell",
1787 s
->DNSSECAware
? "DNSSECAware" : "!DNSSECAware");
1790 mDNSs32 now
= mDNS_TimeNow(&mDNSStorage
);
1791 LogMsgNoIdent("v4answers %d", mDNSStorage
.p
->v4answers
);
1792 LogMsgNoIdent("v6answers %d", mDNSStorage
.p
->v6answers
);
1793 LogMsgNoIdent("Last DNS Trigger: %d ms ago", (now
- mDNSStorage
.p
->DNSTrigger
));
1795 LogMsgNoIdent("--------- Mcast Resolvers ----------");
1796 if (!mDNSStorage
.McastResolvers
) LogMsgNoIdent("<None>");
1799 for (mr
= mDNSStorage
.McastResolvers
; mr
; mr
= mr
->next
)
1800 LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr
->domain
.c
, mr
->timeout
);
1803 LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32
)now
, now
);
1804 LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString
, OSXVers
? "OSXVers" : "iOSVers", OSXVers
? OSXVers
: iOSVers
);
1806 // If logging is disabled, only then clear the key we set at the top of this func
1807 if (!mDNS_LoggingEnabled
)
1808 asl_unset(log_msg
, "LoggerID");
1811 mDNSlocal
void DebugSetFilter()
1816 // When USR1 is turned on, we log only the LOG_WARNING and LOG_NOTICE messages by default.
1817 // The user has to manually do "syslog -c mDNSResponder -i" to get the LOG_INFO messages
1818 // also to be logged. Most of the times, we need the INFO level messages for debugging.
1819 // Hence, we set the filter to INFO level when USR1 logging is turned on to avoid
1820 // having the user to do this extra step manually.
1822 if (mDNS_LoggingEnabled
)
1824 asl_set_filter(log_client
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO
));
1825 asl_set(log_msg
, "LoggerID", "com.apple.networking.mDNSResponder");
1826 // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when USR1 Logging is Enabled.
1827 // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file
1828 // present in /etc/asl/com.apple.networking.mDNSResponder.
1832 asl_set_filter(log_client
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_ERR
));
1833 asl_unset(log_msg
, "LoggerID");
1834 // Clear the key-value pair when USR1 Logging is Disabled, as we do not want to log to
1835 // com.apple.networking.mDNSResponder.log file in this case.
1839 mDNSexport
void mDNSPlatformLogToFile(int log_level
, const char *buffer
)
1841 int asl_level
= ASL_LEVEL_ERR
;
1845 syslog(log_level
, "%s", buffer
);
1851 asl_level
= ASL_LEVEL_ERR
;
1854 asl_level
= ASL_LEVEL_WARNING
;
1857 asl_level
= ASL_LEVEL_NOTICE
;
1860 asl_level
= ASL_LEVEL_INFO
;
1863 asl_level
= ASL_LEVEL_DEBUG
;
1868 asl_log(log_client
, log_msg
, asl_level
, "%s", buffer
);
1871 // Writes the state out to the dynamic store and also affects the ASL filter level
1872 mDNSexport
void UpdateDebugState()
1877 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1880 LogMsg("UpdateDebugState: Could not create dict");
1884 CFNumberRef numOne
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &one
);
1887 LogMsg("UpdateDebugState: Could not create CFNumber one");
1890 CFNumberRef numZero
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &zero
);
1893 LogMsg("UpdateDebugState: Could not create CFNumber zero");
1898 if (mDNS_LoggingEnabled
)
1899 CFDictionarySetValue(dict
, CFSTR("VerboseLogging"), numOne
);
1901 CFDictionarySetValue(dict
, CFSTR("VerboseLogging"), numZero
);
1903 if (mDNS_PacketLoggingEnabled
)
1904 CFDictionarySetValue(dict
, CFSTR("PacketLogging"), numOne
);
1906 CFDictionarySetValue(dict
, CFSTR("PacketLogging"), numZero
);
1908 if (mDNS_McastLoggingEnabled
)
1909 CFDictionarySetValue(dict
, CFSTR("McastLogging"), numOne
);
1911 CFDictionarySetValue(dict
, CFSTR("McastLogging"), numZero
);
1913 if (mDNS_McastTracingEnabled
)
1914 CFDictionarySetValue(dict
, CFSTR("McastTracing"), numOne
);
1916 CFDictionarySetValue(dict
, CFSTR("McastTracing"), numZero
);
1920 mDNSDynamicStoreSetConfig(kmDNSDebugState
, mDNSNULL
, dict
);
1922 // If we turned off USR1 logging, we need to reset the filter
1926 #if TARGET_OS_EMBEDDED
1927 mDNSlocal
void Prefschanged()
1929 mDNSBool mDNSProf_installed
;
1930 LogMsg("Prefschanged: mDNSResponder Managed Preferences have changed");
1931 mDNSProf_installed
= GetmDNSManagedPref(kmDNSEnableLoggingStr
);
1932 dispatch_async(dispatch_get_main_queue(),
1934 if (mDNSProf_installed
)
1936 mDNS_LoggingEnabled
= mDNS_PacketLoggingEnabled
= 1;
1940 LogMsg("Prefschanged: mDNSDebugProfile is uninstalled -> Turning OFF USR1/USR2 Logging with SIGINFO o/p");
1942 mDNS_LoggingEnabled
= mDNS_PacketLoggingEnabled
= 0;
1945 // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log (has to be LogInfo)
1946 LogInfo("Prefschanged: mDNSDebugProfile is installed -> Turned ON USR1/USR2 Logging");
1950 #endif //TARGET_OS_EMBEDDED
1952 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
1954 mDNSlocal
void SignalCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1956 (void)port
; // Unused
1957 (void)size
; // Unused
1958 (void)info
; // Unused
1959 mach_msg_header_t
*msg_header
= (mach_msg_header_t
*)msg
;
1960 mDNS
*const m
= &mDNSStorage
;
1962 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
1964 switch(msg_header
->msgh_id
)
1967 m
->mDNSOppCaching
= m
->mDNSOppCaching
? mDNSfalse
: mDNStrue
;
1968 LogMsg("SIGURG: Opportunistic Caching %s", m
->mDNSOppCaching
? "Enabled" : "Disabled");
1969 // FALL THROUGH to purge the cache so that we re-do the caching based on the new setting
1974 LogMsg("SIGHUP: Purge cache");
1976 FORALL_CACHERECORDS(slot
, cg
, rr
)
1978 mDNS_PurgeCacheResourceRecord(m
, rr
);
1980 // Restart unicast and multicast queries
1981 mDNSCoreRestartQueries(m
);
1985 case SIGTERM
: ExitCallback(msg_header
->msgh_id
); break;
1986 case SIGINFO
: INFOCallback(); break;
1987 case SIGUSR1
: mDNS_LoggingEnabled
= mDNS_LoggingEnabled
? 0 : 1;
1988 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled
? "Enabled" : "Disabled");
1989 WatchDogReportingThreshold
= mDNS_LoggingEnabled
? 50 : 250;
1991 // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log
1992 LogInfo("USR1 Logging Enabled: Start Logging to mDNSResponder Log file");
1994 case SIGUSR2
: mDNS_PacketLoggingEnabled
= mDNS_PacketLoggingEnabled
? 0 : 1;
1995 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled
? "Enabled" : "Disabled");
1996 mDNS_McastTracingEnabled
= (mDNS_PacketLoggingEnabled
&& mDNS_McastLoggingEnabled
) ? mDNStrue
: mDNSfalse
;
1997 LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled
? "Enabled" : "Disabled");
2000 case SIGPROF
: mDNS_McastLoggingEnabled
= mDNS_McastLoggingEnabled
? mDNSfalse
: mDNStrue
;
2001 LogMsg("SIGPROF: Multicast Logging %s", mDNS_McastLoggingEnabled
? "Enabled" : "Disabled");
2002 LogMcastStateInfo(m
, mDNSfalse
, mDNStrue
, mDNStrue
);
2003 mDNS_McastTracingEnabled
= (mDNS_PacketLoggingEnabled
&& mDNS_McastLoggingEnabled
) ? mDNStrue
: mDNSfalse
;
2004 LogMsg("SIGPROF: Multicast Tracing is %s", mDNS_McastTracingEnabled
? "Enabled" : "Disabled");
2007 case SIGTSTP
: mDNS_LoggingEnabled
= mDNS_PacketLoggingEnabled
= mDNS_McastLoggingEnabled
= mDNS_McastTracingEnabled
= mDNSfalse
;
2008 LogMsg("All mDNSResponder Debug Logging/Tracing Disabled (USR1/USR2/PROF)");
2012 default: LogMsg("SignalCallback: Unknown signal %d", msg_header
->msgh_id
); break;
2014 KQueueUnlock(m
, "Unix Signal");
2017 // MachServerName is com.apple.mDNSResponder (Supported only till 10.9.x)
2018 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
2021 CFMachPortRef s_port
;
2022 CFRunLoopSourceRef s_rls
;
2023 CFRunLoopSourceRef d_rls
;
2025 s_port
= CFMachPortCreateWithPort(NULL
, m_port
, DNSserverCallback
, NULL
, NULL
);
2026 CFMachPortRef d_port
= CFMachPortCreate(NULL
, ClientDeathCallback
, NULL
, NULL
);
2028 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
2029 rrcachestorage
, RR_CACHE_SIZE
,
2031 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
2033 if (err
) { LogMsg("Daemon start: mDNS_Init failed %d", err
); return(err
); }
2035 client_death_port
= CFMachPortGetPort(d_port
);
2037 s_rls
= CFMachPortCreateRunLoopSource(NULL
, s_port
, 0);
2038 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, s_rls
, kCFRunLoopDefaultMode
);
2041 d_rls
= CFMachPortCreateRunLoopSource(NULL
, d_port
, 0);
2042 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, d_rls
, kCFRunLoopDefaultMode
);
2045 CFMachPortRef i_port
= CFMachPortCreate(NULL
, SignalCallback
, NULL
, NULL
);
2046 CFRunLoopSourceRef i_rls
= CFMachPortCreateRunLoopSource(NULL
, i_port
, 0);
2047 signal_port
= CFMachPortGetPort(i_port
);
2048 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, i_rls
, kCFRunLoopDefaultMode
);
2051 if (mDNS_DebugMode
) printf("Service registered with Mach Port %d\n", m_port
);
2055 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2057 // SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
2058 // The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
2059 // The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
2060 // of code from above. Alternatively we could remove the duplicated source code by having
2061 // single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM"
2063 mDNSlocal
void SignalDispatch(dispatch_source_t source
)
2065 int sig
= (int)dispatch_source_get_handle(source
);
2066 mDNS
*const m
= &mDNSStorage
;
2074 LogMsg("SIGHUP: Purge cache");
2076 FORALL_CACHERECORDS(slot
, cg
, rr
)
2078 mDNS_PurgeCacheResourceRecord(m
, rr
);
2080 // Restart unicast and multicast queries
2081 mDNSCoreRestartQueries(m
);
2085 case SIGTERM
: ExitCallback(sig
); break;
2086 case SIGINFO
: INFOCallback(); break;
2087 case SIGUSR1
: mDNS_LoggingEnabled
= mDNS_LoggingEnabled
? 0 : 1;
2088 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled
? "Enabled" : "Disabled");
2089 WatchDogReportingThreshold
= mDNS_LoggingEnabled
? 50 : 250;
2092 case SIGUSR2
: mDNS_PacketLoggingEnabled
= mDNS_PacketLoggingEnabled
? 0 : 1;
2093 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled
? "Enabled" : "Disabled");
2096 default: LogMsg("SignalCallback: Unknown signal %d", sig
); break;
2098 KQueueUnlock(m
, "Unix Signal");
2101 mDNSlocal
void mDNSSetupSignal(dispatch_queue_t queue
, int sig
)
2103 signal(sig
, SIG_IGN
);
2104 dispatch_source_t source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, sig
, 0, queue
);
2108 dispatch_source_set_event_handler(source
, ^{SignalDispatch(source
);});
2109 // Start processing signals
2110 dispatch_resume(source
);
2114 LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig
);
2118 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2119 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2120 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
2123 dispatch_source_t mach_source
;
2124 dispatch_queue_t queue
= dispatch_get_main_queue();
2126 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
2127 rrcachestorage
, RR_CACHE_SIZE
,
2129 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
2131 if (err
) { LogMsg("Daemon start: mDNS_Init failed %d", err
); return(err
); }
2133 mach_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, m_port
, 0, queue
);
2134 if (mach_source
== mDNSNULL
) {LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;}
2135 dispatch_source_set_event_handler(mach_source
, ^{
2136 dispatch_mig_server(mach_source
, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem
),
2137 DNSServiceDiscoveryRequest_server
);
2139 dispatch_resume(mach_source
);
2141 mDNSSetupSignal(queue
, SIGHUP
);
2142 mDNSSetupSignal(queue
, SIGINT
);
2143 mDNSSetupSignal(queue
, SIGTERM
);
2144 mDNSSetupSignal(queue
, SIGINFO
);
2145 mDNSSetupSignal(queue
, SIGUSR1
);
2146 mDNSSetupSignal(queue
, SIGUSR2
);
2147 mDNSSetupSignal(queue
, SIGURG
);
2149 // Create a custom handler for doing the housekeeping work. This is either triggered
2150 // by the timer or an event source
2151 PlatformStorage
.custom
= dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD
, 0, 0, queue
);
2152 if (PlatformStorage
.custom
== mDNSNULL
) {LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;}
2153 dispatch_source_set_event_handler(PlatformStorage
.custom
, ^{PrepareForIdle(&mDNSStorage
);});
2154 dispatch_resume(PlatformStorage
.custom
);
2156 // Create a timer source to trigger housekeeping work. The houskeeping work itself
2157 // is done in the custom handler that we set below.
2159 PlatformStorage
.timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, queue
);
2160 if (PlatformStorage
.timer
== mDNSNULL
) {LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;}
2162 // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we
2163 // always reset the time to the new time computed. In effect, we ignore the interval
2164 dispatch_source_set_timer(PlatformStorage
.timer
, DISPATCH_TIME_NOW
, 1000ull * 1000000000, 0);
2165 dispatch_source_set_event_handler(PlatformStorage
.timer
, ^{
2166 dispatch_source_merge_data(PlatformStorage
.custom
, 1);
2168 dispatch_resume(PlatformStorage
.timer
);
2170 LogMsg("DaemonIntialize done successfully");
2172 if (mDNS_DebugMode
) printf("Service registered with Mach Port %d\n", m_port
);
2176 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2178 mDNSlocal mDNSs32
mDNSDaemonIdle(mDNS
*const m
)
2180 mDNSs32 now
= mDNS_TimeNow(m
);
2182 // 1. If we need to set domain secrets, do so before handling the network change
2184 // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list,
2185 // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens.
2186 if (m
->p
->KeyChainTimer
&& now
- m
->p
->KeyChainTimer
>= 0)
2188 m
->p
->KeyChainTimer
= 0;
2190 SetDomainSecrets(m
);
2194 // 2. If we have network change events to handle, do them before calling mDNS_Execute()
2196 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2197 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2198 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2199 // we then systematically lose our own looped-back packets.
2200 if (m
->p
->NetworkChanged
&& now
- m
->p
->NetworkChanged
>= 0) mDNSMacOSXNetworkChanged(m
);
2202 if (m
->p
->RequestReSleep
&& now
- m
->p
->RequestReSleep
>= 0) { m
->p
->RequestReSleep
= 0; mDNSPowerRequest(0, 0); }
2204 // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do
2205 mDNSs32 nextevent
= mDNS_Execute(m
);
2207 if (m
->p
->NetworkChanged
)
2208 if (nextevent
- m
->p
->NetworkChanged
> 0)
2209 nextevent
= m
->p
->NetworkChanged
;
2211 if (m
->p
->KeyChainTimer
)
2212 if (nextevent
- m
->p
->KeyChainTimer
> 0)
2213 nextevent
= m
->p
->KeyChainTimer
;
2215 if (m
->p
->RequestReSleep
)
2216 if (nextevent
- m
->p
->RequestReSleep
> 0)
2217 nextevent
= m
->p
->RequestReSleep
;
2219 // 4. Deliver any waiting browse messages to clients
2220 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
2224 // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2225 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2226 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2227 DNSServiceBrowser
*x
= b
;
2229 if (x
->results
) // Try to deliver the list of results
2233 DNSServiceBrowserResult
*const r
= x
->results
;
2235 domainname type
, domain
;
2236 DeconstructServiceName(&r
->result
, &name
, &type
, &domain
); // Don't need to check result; already validated in FoundInstance()
2237 char cname
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2238 char ctype
[MAX_ESCAPED_DOMAIN_NAME
];
2239 char cdom
[MAX_ESCAPED_DOMAIN_NAME
];
2240 ConvertDomainLabelToCString_unescaped(&name
, cname
);
2241 ConvertDomainNameToCString(&type
, ctype
);
2242 ConvertDomainNameToCString(&domain
, cdom
);
2243 DNSServiceDiscoveryReplyFlags flags
= (r
->next
) ? DNSServiceDiscoverReplyFlagsMoreComing
: 0;
2244 kern_return_t status
= DNSServiceBrowserReply_rpc(x
->ClientMachPort
, r
->resultType
, cname
, ctype
, cdom
, flags
, 1);
2245 // If we failed to send the mach message, try again in one second
2246 if (status
== MACH_SEND_TIMED_OUT
)
2248 if (nextevent
- now
> mDNSPlatformOneSecond
)
2249 nextevent
= now
+ mDNSPlatformOneSecond
;
2254 x
->lastsuccess
= now
;
2255 x
->results
= x
->results
->next
;
2256 freeL("DNSServiceBrowserResult", r
);
2259 // If this client hasn't read a single message in the last 60 seconds, abort it
2260 if (now
- x
->lastsuccess
>= 60 * mDNSPlatformOneSecond
)
2261 AbortBlockedClient(x
->ClientMachPort
, "browse", x
);
2265 DNSServiceResolver
*l
;
2266 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
2267 if (l
->ReportTime
&& now
- l
->ReportTime
>= 0)
2270 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2271 "This places considerable burden on the network.", l
->i
.name
.c
);
2274 if (m
->p
->NotifyUser
)
2276 if (m
->p
->NotifyUser
- now
< 0)
2278 if (!SameDomainLabelCS(m
->p
->usernicelabel
.c
, m
->nicelabel
.c
))
2280 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m
->p
->usernicelabel
.c
, m
->nicelabel
.c
);
2281 mDNSPreferencesSetNames(m
, kmDNSComputerName
, &m
->p
->usernicelabel
, &m
->nicelabel
);
2282 m
->p
->usernicelabel
= m
->nicelabel
;
2284 if (!SameDomainLabelCS(m
->p
->userhostlabel
.c
, m
->hostlabel
.c
))
2286 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
2287 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, &m
->hostlabel
);
2288 m
->p
->HostNameConflict
= 0; // Clear our indicator, now name change has been successful
2289 m
->p
->userhostlabel
= m
->hostlabel
;
2291 m
->p
->NotifyUser
= 0;
2294 if (nextevent
- m
->p
->NotifyUser
> 0)
2295 nextevent
= m
->p
->NotifyUser
;
2301 // Right now we consider *ALL* of our DHCP leases
2302 // It might make sense to be a bit more selective and only consider the leases on interfaces
2303 // (a) that are capable and enabled for wake-on-LAN, and
2304 // (b) where we have found (and successfully registered with) a Sleep Proxy
2305 // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
2306 mDNSlocal mDNSu32
DHCPWakeTime(void)
2308 mDNSu32 e
= 24 * 3600; // Maximum maintenance wake interval is 24 hours
2309 const CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
2310 if (!now
) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
2315 const void *pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, kSCDynamicStoreDomainState
, kSCCompAnyRegex
, kSCEntNetDHCP
);
2318 LogMsg("DHCPWakeTime: SCDynamicStoreKeyCreateNetworkServiceEntity failed\n");
2321 CFArrayRef dhcpinfo
= CFArrayCreate(NULL
, (const void **)&pattern
, 1, &kCFTypeArrayCallBacks
);
2325 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("DHCP-LEASES"), NULL
, NULL
);
2328 CFDictionaryRef dict
= SCDynamicStoreCopyMultiple(store
, NULL
, dhcpinfo
);
2331 ic
= CFDictionaryGetCount(dict
);
2332 const void *vals
[ic
];
2333 CFDictionaryGetKeysAndValues(dict
, NULL
, vals
);
2335 for (j
= 0; j
< ic
; j
++)
2337 const CFDictionaryRef dhcp
= (CFDictionaryRef
)vals
[j
];
2340 const CFDateRef start
= DHCPInfoGetLeaseStartTime(dhcp
);
2341 const CFDataRef lease
= DHCPInfoGetOptionData(dhcp
, 51); // Option 51 = IP Address Lease Time
2342 if (!start
|| !lease
|| CFDataGetLength(lease
) < 4)
2343 LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
2344 "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
2345 j
, start
, lease
, lease
? CFDataGetLength(lease
) : 0);
2348 const UInt8
*d
= CFDataGetBytePtr(lease
);
2349 if (!d
) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", j
);
2352 const mDNSu32 elapsed
= now
- CFDateGetAbsoluteTime(start
);
2353 const mDNSu32 lifetime
= (mDNSs32
) ((mDNSs32
)d
[0] << 24 | (mDNSs32
)d
[1] << 16 | (mDNSs32
)d
[2] << 8 | d
[3]);
2354 const mDNSu32 remaining
= lifetime
- elapsed
;
2355 const mDNSu32 wake
= remaining
> 60 ? remaining
- remaining
/10 : 54; // Wake at 90% of the lease time
2356 LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed
, lifetime
, remaining
, wake
);
2357 if (e
> wake
) e
= wake
;
2366 CFRelease(dhcpinfo
);
2372 // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
2373 // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
2374 // If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
2375 // for a few seconds and then waking again is silly and annoying.
2376 // If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
2377 // Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
2378 // allowing us an adequate safety margin to renew our lease before we lose it.
2380 mDNSlocal mDNSBool
AllowSleepNow(mDNS
*const m
, mDNSs32 now
)
2382 mDNSBool ready
= mDNSCoreReadyForSleep(m
, now
);
2383 if (m
->SleepState
&& !ready
&& now
- m
->SleepLimit
< 0) return(mDNSfalse
);
2385 m
->p
->WakeAtUTC
= 0;
2386 int result
= kIOReturnSuccess
;
2387 CFDictionaryRef opts
= NULL
;
2389 // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
2390 // do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
2392 LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m
->SleepLimit
- now
);
2395 if (!m
->SystemWakeOnLANEnabled
|| !mDNSCoreHaveAdvertisedMulticastServices(m
))
2396 LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
2397 m
->SystemWakeOnLANEnabled
? "is" : "not",
2398 mDNSCoreHaveAdvertisedMulticastServices(m
) ? "have" : "no");
2401 mDNSs32 dhcp
= DHCPWakeTime();
2402 LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp
);
2403 mDNSs32 interval
= mDNSCoreIntervalToNextWake(m
, now
) / mDNSPlatformOneSecond
;
2404 if (interval
> dhcp
) interval
= dhcp
;
2406 // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
2407 // transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
2408 // a single SPS failure could result in a remote machine falling permanently asleep, requiring
2409 // someone to go to the machine in person to wake it up again, which would be unacceptable.
2410 if (!ready
&& interval
> 3600) interval
= 3600;
2412 //interval = 48; // For testing
2414 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2415 if (m
->p
->IOPMConnection
) // If lightweight-wake capability is available, use that
2417 const CFDateRef WakeDate
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent() + interval
);
2418 if (!WakeDate
) LogMsg("ScheduleNextWake: CFDateCreate failed");
2421 const mDNSs32 reqs
= kIOPMSystemPowerStateCapabilityNetwork
;
2422 const CFNumberRef Requirements
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &reqs
);
2423 if (!Requirements
) LogMsg("ScheduleNextWake: CFNumberCreate failed");
2426 const void *OptionKeys
[2] = { CFSTR("WakeDate"), CFSTR("Requirements") };
2427 const void *OptionVals
[2] = { WakeDate
, Requirements
};
2428 opts
= CFDictionaryCreate(NULL
, (void*)OptionKeys
, (void*)OptionVals
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2429 if (!opts
) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
2430 CFRelease(Requirements
);
2432 CFRelease(WakeDate
);
2434 LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval
);
2436 else // else schedule the wakeup using the old API instead to
2439 // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
2440 // so we should put it back to sleep. To avoid frustrating the user, we always request at least
2441 // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
2442 // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
2443 if (interval
< 60) interval
= 60;
2445 result
= mDNSPowerRequest(1, interval
);
2447 if (result
== kIOReturnNotReady
)
2450 LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval
);
2451 // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
2452 // requested wake time is "too soon", but there's no API to find out what constitutes
2453 // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
2454 // we just have to iterate with successively longer intervals until it doesn't fail.
2455 // We preserve the value of "result" because if our original power request was deemed "too soon"
2456 // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request,
2457 // since the implication is that the system won't manage to be awake again at the time we need it.
2460 interval
+= (interval
< 20) ? 1 : ((interval
+3) / 4);
2461 r
= mDNSPowerRequest(1, interval
);
2463 while (r
== kIOReturnNotReady
);
2464 if (r
) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval
, r
, r
);
2465 else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval
);
2469 if (result
) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval
, result
, result
);
2470 else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval
);
2472 m
->p
->WakeAtUTC
= mDNSPlatformUTC() + interval
;
2476 m
->SleepState
= SleepState_Sleeping
;
2477 // We used to clear our interface list to empty state here before going to sleep.
2478 // The applications that try to connect to an external server during maintenance wakes, saw
2479 // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable
2480 // flag). Thus, we don't remove our interfaces anymore on sleep.
2483 LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
2484 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2485 (m
->p
->IOPMConnection
) ? "IOPMConnectionAcknowledgeEventWithOptions" :
2487 (result
== kIOReturnSuccess
) ? "IOAllowPowerChange" : "IOCancelPowerChange",
2488 m
->p
->SleepCookie
, ready
? "ready for sleep" : "giving up", now
, m
->SleepLimit
- now
);
2490 m
->SleepLimit
= 0; // Don't clear m->SleepLimit until after we've logged it above
2491 m
->TimeSlept
= mDNSPlatformUTC();
2493 // accumulate total time awake for this statistics gathering interval
2494 if (m
->StatStartTime
)
2496 m
->ActiveStatTime
+= (m
->TimeSlept
- m
->StatStartTime
);
2498 // indicate this value is invalid until reinitialzed on wakeup
2499 m
->StatStartTime
= 0;
2502 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2503 if (m
->p
->IOPMConnection
) IOPMConnectionAcknowledgeEventWithOptions(m
->p
->IOPMConnection
, m
->p
->SleepCookie
, opts
);
2506 if (result
== kIOReturnSuccess
) IOAllowPowerChange (m
->p
->PowerConnection
, m
->p
->SleepCookie
);
2507 else IOCancelPowerChange(m
->p
->PowerConnection
, m
->p
->SleepCookie
);
2509 if (opts
) CFRelease(opts
);
2513 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2515 mDNSexport
void TriggerEventCompletion()
2517 debugf("TriggerEventCompletion: Merge data");
2518 dispatch_source_merge_data(PlatformStorage
.custom
, 1);
2521 mDNSlocal
void PrepareForIdle(void *m_param
)
2524 int64_t time_offset
;
2525 dispatch_time_t dtime
;
2527 const int multiplier
= 1000000000 / mDNSPlatformOneSecond
;
2529 // This is the main work loop:
2530 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2531 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2532 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2534 debugf("PrepareForIdle: called");
2535 // Run mDNS_Execute to find out the time we next need to wake up
2536 mDNSs32 start
= mDNSPlatformRawTime();
2537 mDNSs32 nextTimerEvent
= udsserver_idle(mDNSDaemonIdle(m
));
2538 mDNSs32 end
= mDNSPlatformRawTime();
2539 if (end
- start
>= WatchDogReportingThreshold
)
2540 LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end
- start
);
2542 mDNSs32 now
= mDNS_TimeNow(m
);
2544 if (m
->ShutdownTime
)
2546 if (mDNSStorage
.ResourceRecords
)
2548 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m
, mDNSStorage
.ResourceRecords
));
2549 if (mDNS_LoggingEnabled
) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2551 if (mDNS_ExitNow(m
, now
))
2553 LogInfo("IdleLoop: mDNS_FinalExit");
2554 mDNS_FinalExit(&mDNSStorage
);
2555 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2558 if (nextTimerEvent
- m
->ShutdownTime
>= 0)
2559 nextTimerEvent
= m
->ShutdownTime
;
2563 if (!AllowSleepNow(m
, now
))
2564 if (nextTimerEvent
- m
->SleepLimit
>= 0)
2565 nextTimerEvent
= m
->SleepLimit
;
2567 // Convert absolute wakeup time to a relative time from now
2568 mDNSs32 ticks
= nextTimerEvent
- now
;
2569 if (ticks
< 1) ticks
= 1;
2571 static mDNSs32 RepeatedBusy
= 0; // Debugging sanity check, to guard against CPU spins
2577 if (++RepeatedBusy
>= mDNSPlatformOneSecond
) { ShowTaskSchedulingError(&mDNSStorage
); RepeatedBusy
= 0; }
2580 time_offset
= ((mDNSu32
)ticks
/ mDNSPlatformOneSecond
) * 1000000000 + (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2581 dtime
= dispatch_time(DISPATCH_TIME_NOW
, time_offset
);
2582 dispatch_source_set_timer(PlatformStorage
.timer
, dtime
, 1000ull*1000000000, 0);
2583 debugf("PrepareForIdle: scheduling timer with ticks %d", ticks
);
2587 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2589 mDNSlocal
void KQWokenFlushBytes(int fd
, __unused
short filter
, __unused
void *context
)
2591 // Read all of the bytes so we won't wake again.
2593 while (recv(fd
, buffer
, sizeof(buffer
), MSG_DONTWAIT
) > 0) continue;
2596 mDNSlocal
void * KQueueLoop(void *m_param
)
2601 #if USE_SELECT_WITH_KQUEUEFD
2604 const int multiplier
= 1000000 / mDNSPlatformOneSecond
;
2606 const int multiplier
= 1000000000 / mDNSPlatformOneSecond
;
2609 pthread_mutex_lock(&PlatformStorage
.BigMutex
);
2610 LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32
)mDNSStorage
.timenow_last
, mDNSStorage
.timenow_last
);
2612 // This is the main work loop:
2613 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2614 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2615 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2616 // (4) On wakeup we first process *all* events
2617 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2620 #define kEventsToReadAtOnce 1
2621 struct kevent new_events
[kEventsToReadAtOnce
];
2623 // Run mDNS_Execute to find out the time we next need to wake up
2624 mDNSs32 start
= mDNSPlatformRawTime();
2625 mDNSs32 nextTimerEvent
= udsserver_idle(mDNSDaemonIdle(m
));
2626 mDNSs32 end
= mDNSPlatformRawTime();
2627 if (end
- start
>= WatchDogReportingThreshold
)
2628 LogInfo("WARNING: Idle task took %dms to complete", end
- start
);
2630 mDNSs32 now
= mDNS_TimeNow(m
);
2632 if (m
->ShutdownTime
)
2634 if (mDNSStorage
.ResourceRecords
)
2637 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
2639 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m
, rr
));
2640 if (mDNS_LoggingEnabled
) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2643 if (mDNS_ExitNow(m
, now
))
2645 LogInfo("mDNS_FinalExit");
2646 mDNS_FinalExit(&mDNSStorage
);
2647 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2650 if (nextTimerEvent
- m
->ShutdownTime
>= 0)
2651 nextTimerEvent
= m
->ShutdownTime
;
2655 if (!AllowSleepNow(m
, now
))
2656 if (nextTimerEvent
- m
->SleepLimit
>= 0)
2657 nextTimerEvent
= m
->SleepLimit
;
2659 // Convert absolute wakeup time to a relative time from now
2660 mDNSs32 ticks
= nextTimerEvent
- now
;
2661 if (ticks
< 1) ticks
= 1;
2663 static mDNSs32 RepeatedBusy
= 0; // Debugging sanity check, to guard against CPU spins
2669 if (++RepeatedBusy
>= mDNSPlatformOneSecond
) { ShowTaskSchedulingError(&mDNSStorage
); RepeatedBusy
= 0; }
2672 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents
, ticks
);
2675 // Release the lock, and sleep until:
2676 // 1. Something interesting happens like a packet arriving, or
2677 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
2678 // 3. The timeout expires
2679 pthread_mutex_unlock(&PlatformStorage
.BigMutex
);
2681 #if USE_SELECT_WITH_KQUEUEFD
2682 struct timeval timeout
;
2683 timeout
.tv_sec
= ticks
/ mDNSPlatformOneSecond
;
2684 timeout
.tv_usec
= (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2685 FD_SET(KQueueFD
, &readfds
);
2686 if (select(KQueueFD
+1, &readfds
, NULL
, NULL
, &timeout
) < 0)
2687 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD
, errno
, strerror(errno
)); sleep(1); }
2689 struct timespec timeout
;
2690 timeout
.tv_sec
= ticks
/ mDNSPlatformOneSecond
;
2691 timeout
.tv_nsec
= (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2692 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
2693 // and have it work similarly to the way it does with nevents non-zero --
2694 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
2695 // In fact, what happens if you do this is that it just returns immediately. So, we have
2696 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
2697 if (kevent(KQueueFD
, NULL
, 0, new_events
, 1, &timeout
) < 0)
2698 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD
, errno
, strerror(errno
)); sleep(1); }
2701 pthread_mutex_lock(&PlatformStorage
.BigMutex
);
2702 // We have to ignore the event we may have been told about above, because that
2703 // was done without holding the lock, and between the time we woke up and the
2704 // time we reclaimed the lock the other thread could have done something that
2705 // makes the event no longer valid. Now we have the lock, we call kevent again
2706 // and this time we can safely process the events it tells us about.
2708 static const struct timespec zero_timeout
= { 0, 0 };
2710 while ((events_found
= kevent(KQueueFD
, NULL
, 0, new_events
, kEventsToReadAtOnce
, &zero_timeout
)) != 0)
2712 if (events_found
> kEventsToReadAtOnce
|| (events_found
< 0 && errno
!= EINTR
))
2714 // Not sure what to do here, our kqueue has failed us - this isn't ideal
2715 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno
, strerror(errno
));
2719 numevents
+= events_found
;
2722 for (i
= 0; i
< events_found
; i
++)
2724 const KQueueEntry
*const kqentry
= new_events
[i
].udata
;
2725 mDNSs32 stime
= mDNSPlatformRawTime();
2726 const char *const KQtask
= kqentry
->KQtask
; // Grab a copy in case KQcallback deletes the task
2727 kqentry
->KQcallback(new_events
[i
].ident
, new_events
[i
].filter
, kqentry
->KQcontext
);
2728 mDNSs32 etime
= mDNSPlatformRawTime();
2729 if (etime
- stime
>= WatchDogReportingThreshold
)
2730 LogInfo("WARNING: %s took %dms to complete", KQtask
, etime
- stime
);
2738 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2740 mDNSlocal
void LaunchdCheckin(void)
2742 // Ask launchd for our socket
2743 launch_data_t resp_sd
= launch_socket_service_check_in();
2746 LogMsg("launch_socket_service_check_in returned NULL");
2751 launch_data_t skts
= launch_data_dict_lookup(resp_sd
, LAUNCH_JOBKEY_SOCKETS
);
2752 if (!skts
) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
2755 launch_data_t skt
= launch_data_dict_lookup(skts
, "Listeners");
2756 if (!skt
) LogMsg("launch_data_dict_lookup Listeners returned NULL");
2759 launchd_fds_count
= launch_data_array_get_count(skt
);
2760 if (launchd_fds_count
== 0) LogMsg("launch_data_array_get_count(skt) returned 0");
2763 launchd_fds
= mallocL("LaunchdCheckin", sizeof(dnssd_sock_t
) * launchd_fds_count
);
2764 if (!launchd_fds
) LogMsg("LaunchdCheckin: malloc failed");
2768 for(i
= 0; i
< launchd_fds_count
; i
++)
2770 launch_data_t s
= launch_data_array_get_index(skt
, i
);
2773 launchd_fds
[i
] = dnssd_InvalidSocket
;
2774 LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i
);
2778 launchd_fds
[i
] = launch_data_get_fd(s
);
2779 LogInfo("Launchd Unix Domain Socket [%d]: %d", i
, launchd_fds
[i
]);
2783 // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
2784 chmod(MDNS_UDS_SERVERPATH
, S_IRUSR
|S_IWUSR
| S_IRGRP
|S_IWGRP
| S_IROTH
|S_IWOTH
);
2789 launch_data_free(resp_sd
);
2792 static mach_port_t
RegisterMachService(const char *service_name
)
2794 mach_port_t port
= MACH_PORT_NULL
;
2797 if (KERN_SUCCESS
!= (kr
= bootstrap_check_in(bootstrap_port
, (char *)service_name
, &port
)))
2799 LogMsg("RegisterMachService: %d %X %s", kr
, kr
, mach_error_string(kr
));
2800 return MACH_PORT_NULL
;
2803 if (KERN_SUCCESS
!= (kr
= mach_port_insert_right(mach_task_self(), port
, port
, MACH_MSG_TYPE_MAKE_SEND
)))
2805 LogMsg("RegisterMachService: %d %X %s", kr
, kr
, mach_error_string(kr
));
2806 mach_port_deallocate(mach_task_self(), port
);
2807 return MACH_PORT_NULL
;
2813 extern int sandbox_init(const char *profile
, uint64_t flags
, char **errorbuf
) __attribute__((weak_import
));
2815 mDNSexport
int main(int argc
, char **argv
)
2818 kern_return_t status
;
2820 mDNSMacOSXSystemBuildNumber(NULL
);
2821 LogMsg("%s starting %s %d", mDNSResponderVersionString
, OSXVers
? "OSXVers" : "iOSVers", OSXVers
? OSXVers
: iOSVers
);
2824 LogMsg("CacheRecord %d", sizeof(CacheRecord
));
2825 LogMsg("CacheGroup %d", sizeof(CacheGroup
));
2826 LogMsg("ResourceRecord %d", sizeof(ResourceRecord
));
2827 LogMsg("RData_small %d", sizeof(RData_small
));
2829 LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity
));
2830 LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE
);
2831 LogMsg("block usage %d", sizeof(CacheEntity
) * RR_CACHE_SIZE
);
2832 LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity
) * RR_CACHE_SIZE
);
2837 LogMsg("mDNSResponder cannot be run as root !! Exiting..");
2841 for (i
=1; i
<argc
; i
++)
2843 if (!strcasecmp(argv
[i
], "-d" )) mDNS_DebugMode
= mDNStrue
;
2844 if (!strcasecmp(argv
[i
], "-NoMulticastAdvertisements")) advertise
= mDNS_Init_DontAdvertiseLocalAddresses
;
2845 if (!strcasecmp(argv
[i
], "-DisableSleepProxyClient" )) DisableSleepProxyClient
= mDNStrue
;
2846 if (!strcasecmp(argv
[i
], "-DebugLogging" )) mDNS_LoggingEnabled
= mDNStrue
;
2847 if (!strcasecmp(argv
[i
], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled
= mDNStrue
;
2848 if (!strcasecmp(argv
[i
], "-OfferSleepProxyService" ))
2849 OfferSleepProxyService
= (i
+1 < argc
&& mDNSIsDigit(argv
[i
+1][0]) && mDNSIsDigit(argv
[i
+1][1]) && argv
[i
+1][2]==0) ? atoi(argv
[++i
]) : 100;
2850 if (!strcasecmp(argv
[i
], "-UseInternalSleepProxy" ))
2851 UseInternalSleepProxy
= (i
+1<argc
&& mDNSIsDigit(argv
[i
+1][0]) && argv
[i
+1][1]==0) ? atoi(argv
[++i
]) : 1;
2852 if (!strcasecmp(argv
[i
], "-StrictUnicastOrdering" )) StrictUnicastOrdering
= mDNStrue
;
2853 if (!strcasecmp(argv
[i
], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains
= mDNStrue
;
2856 // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
2857 if (!advertise
) LogMsg("Administratively prohibiting multicast advertisements");
2859 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2861 signal(SIGHUP
, HandleSIG
); // (Debugging) Purge the cache to check for cache handling bugs
2862 signal(SIGINT
, HandleSIG
); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
2863 signal(SIGPIPE
, SIG_IGN
); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
2864 signal(SIGTERM
, HandleSIG
); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
2865 signal(SIGINFO
, HandleSIG
); // (Debugging) Write state snapshot to syslog
2866 signal(SIGUSR1
, HandleSIG
); // (Debugging) Enable Logging
2867 signal(SIGUSR2
, HandleSIG
); // (Debugging) Enable Packet Logging
2868 signal(SIGURG
, HandleSIG
); // (Debugging) Toggle Opportunistic Caching
2869 signal(SIGPROF
, HandleSIG
); // (Debugging) Toggle Multicast Logging
2870 signal(SIGTSTP
, HandleSIG
); // (Debugging) Disable all Debug Logging (USR1/USR2/PROF)
2872 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2874 mDNSStorage
.p
= &PlatformStorage
; // Make sure mDNSStorage.p is set up, because validatelists uses it
2875 // Need to Start XPC Server Before LaunchdCheckin() (Reason: rdar11023750)
2879 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2881 // Create the kqueue, mutex and thread to support KQSockets
2882 KQueueFD
= kqueue();
2883 if (KQueueFD
== -1) { LogMsg("kqueue() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2885 i
= pthread_mutex_init(&PlatformStorage
.BigMutex
, NULL
);
2886 if (i
== -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2888 int fdpair
[2] = {0, 0};
2889 i
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, fdpair
);
2890 if (i
== -1) { LogMsg("socketpair() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2892 // Socket pair returned us two identical sockets connected to each other
2893 // We will use the first socket to send the second socket. The second socket
2894 // will be added to the kqueue so it will wake when data is sent.
2895 static const KQueueEntry wakeKQEntry
= { KQWokenFlushBytes
, NULL
, "kqueue wakeup after CFRunLoop event" };
2897 PlatformStorage
.WakeKQueueLoopFD
= fdpair
[0];
2898 KQueueSet(fdpair
[1], EV_ADD
, EVFILT_READ
, &wakeKQEntry
);
2900 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2902 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
2904 LogMsg("Note: Compiled without Apple Sandbox support");
2905 #else // MDNS_NO_SANDBOX
2907 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
2911 uint64_t sandbox_flags
= SANDBOX_NAMED
;
2913 int sandbox_err
= sandbox_init("mDNSResponder", sandbox_flags
, &sandbox_msg
);
2916 LogMsg("WARNING: sandbox_init error %s", sandbox_msg
);
2917 // If we have errors in the sandbox during development, to prevent
2918 // exiting, uncomment the following line.
2919 //sandbox_free_error(sandbox_msg);
2921 errx(EX_OSERR
, "sandbox_init() failed: %s", sandbox_msg
);
2923 else LogInfo("Now running under Apple Sandbox restrictions");
2925 #endif // MDNS_NO_SANDBOX
2927 // We use BeginTransactionAtShutdown in the plist that ensures that we will
2928 // receive a SIGTERM during shutdown rather than a SIGKILL. But launchd (due to some
2929 // limitation) currently requires us to still start and end the transaction for
2930 // its proper initialization.
2931 vproc_transaction_t vt
= vproc_transaction_begin(NULL
);
2932 if (vt
) vproc_transaction_end(NULL
, vt
);
2934 m_port
= RegisterMachService(kmDNSResponderServName
);
2935 // We should ALWAYS receive our Mach port from RegisterMachService() but sanity check before initializing daemon
2936 if (m_port
== MACH_PORT_NULL
)
2938 LogMsg("! MACH PORT IS NULL ! bootstrap_checkin failed to give a mach port");
2942 status
= mDNSDaemonInitialize();
2943 if (status
) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit
; }
2945 status
= udsserver_init(launchd_fds
, launchd_fds_count
);
2946 if (status
) { LogMsg("Daemon start: udsserver_init failed"); goto exit
; }
2948 log_client
= asl_open(NULL
, "mDNSResponder", 0);
2949 log_msg
= asl_new(ASL_TYPE_MSG
);
2951 #if TARGET_OS_EMBEDDED
2952 _scprefs_observer_watch(scprefs_observer_type_global
, kmDNSResponderPrefIDStr
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0),
2958 mDNSMacOSXNetworkChanged(&mDNSStorage
);
2961 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2962 LogInfo("Daemon Start: Using LibDispatch");
2963 // CFRunLoopRun runs both CFRunLoop sources and dispatch sources
2965 #else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2966 // Start the kqueue thread
2967 pthread_t KQueueThread
;
2968 i
= pthread_create(&KQueueThread
, NULL
, KQueueLoop
, &mDNSStorage
);
2969 if (i
== -1) { LogMsg("pthread_create() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2973 LogMsg("ERROR: CFRunLoopRun Exiting.");
2974 mDNS_Close(&mDNSStorage
);
2976 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
2978 LogMsg("%s exiting", mDNSResponderVersionString
);
2984 // uds_daemon.c support routines /////////////////////////////////////////////
2986 // Arrange things so that when data appears on fd, callback is called with context
2987 mDNSexport mStatus
udsSupportAddFDToEventLoop(int fd
, udsEventCallback callback
, void *context
, void **platform_data
)
2989 KQSocketEventSource
**p
= &gEventSources
;
2990 (void) platform_data
;
2991 while (*p
&& (*p
)->fd
!= fd
) p
= &(*p
)->next
;
2992 if (*p
) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd
); return mStatus_AlreadyRegistered
; }
2994 KQSocketEventSource
*newSource
= (KQSocketEventSource
*) mallocL("KQSocketEventSource", sizeof *newSource
);
2995 if (!newSource
) return mStatus_NoMemoryErr
;
2997 newSource
->next
= mDNSNULL
;
2999 newSource
->kqs
.KQcallback
= callback
;
3000 newSource
->kqs
.KQcontext
= context
;
3001 newSource
->kqs
.KQtask
= "UDS client";
3002 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3003 newSource
->kqs
.readSource
= mDNSNULL
;
3004 newSource
->kqs
.writeSource
= mDNSNULL
;
3005 newSource
->kqs
.fdClosed
= mDNSfalse
;
3006 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
3008 if (KQueueSet(fd
, EV_ADD
, EVFILT_READ
, &newSource
->kqs
) == 0)
3011 return mStatus_NoError
;
3014 LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd
, errno
, strerror(errno
));
3015 freeL("KQSocketEventSource", newSource
);
3016 return mStatus_BadParamErr
;
3019 int udsSupportReadFD(dnssd_sock_t fd
, char *buf
, int len
, int flags
, void *platform_data
)
3021 (void) platform_data
;
3022 return recv(fd
, buf
, len
, flags
);
3025 mDNSexport mStatus
udsSupportRemoveFDFromEventLoop(int fd
, void *platform_data
) // Note: This also CLOSES the file descriptor
3027 KQSocketEventSource
**p
= &gEventSources
;
3028 (void) platform_data
;
3029 while (*p
&& (*p
)->fd
!= fd
) p
= &(*p
)->next
;
3032 KQSocketEventSource
*s
= *p
;
3034 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
3035 // causes the kernel to automatically remove any associated kevents
3036 mDNSPlatformCloseFD(&s
->kqs
, s
->fd
);
3037 freeL("KQSocketEventSource", s
);
3038 return mStatus_NoError
;
3040 LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd
);
3041 return mStatus_NoSuchNameErr
;
3044 #if _BUILDING_XCODE_PROJECT_
3045 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
3046 const char *__crashreporter_info__
= mDNSResponderVersionString
;
3047 asm (".desc ___crashreporter_info__, 0x10");
3050 // For convenience when using the "strings" command, this is the last thing in the file
3051 // The "@(#) " pattern is a special prefix the "what" command looks for
3052 mDNSexport
const char mDNSResponderVersionString_SCCS
[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";