1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
19 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
20 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
21 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
22 * therefore common sense dictates that if they are part of a compound statement then they
23 * should be indented to the same level as everything else in that compound statement.
24 * Indenting curly braces at the same level as the "if" implies that curly braces are
25 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
26 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
27 * understand why variable y is not of type "char*" just proves the point that poor code
28 * layout leads people to unfortunate misunderstandings about how the C language really works.)
31 // We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h
32 #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4
34 #include <mach/mach.h>
35 #include <mach/mach_error.h>
36 #include <servers/bootstrap.h>
37 #include <sys/types.h>
45 #include <sys/event.h>
48 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
49 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
51 #if TARGET_OS_EMBEDDED
52 #include <bootstrap_priv.h>
54 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
57 #include "DNSServiceDiscoveryRequestServer.h"
58 #include "DNSServiceDiscoveryReply.h"
61 #include "DNSCommon.h"
62 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
64 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
66 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
68 #include "safe_vproc.h"
70 //*************************************************************************************************************
71 #if COMPILER_LIKES_PRAGMA_MARK
72 #pragma mark - Globals
75 static mDNS_PlatformSupport PlatformStorage
;
77 // Start off with a default cache of 16K (99 records)
78 // Each time we grow the cache we add another 99 records
79 // 99 * 164 = 16236 bytes.
80 // This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead
81 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
82 static CacheEntity rrcachestorage
[RR_CACHE_SIZE
];
84 static const char kmDNSBootstrapName
[] = "com.apple.mDNSResponderRestart";
85 static mach_port_t m_port
= MACH_PORT_NULL
;
87 #ifdef __LIB_DISPATCH__
88 mDNSlocal
void PrepareForIdle(void *m_param
);
89 #else __LIB_DISPATCH__
90 static mach_port_t client_death_port
= MACH_PORT_NULL
;
91 static mach_port_t signal_port
= MACH_PORT_NULL
;
92 #endif __LIB_DISPATCH__
94 static mach_port_t server_priv_port
= MACH_PORT_NULL
;
96 static dnssd_sock_t
*launchd_fds
= mDNSNULL
;
97 static mDNSu32 launchd_fds_count
= 0;
99 // mDNS Mach Message Timeout, in milliseconds.
100 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
101 // fails to service its mach message queue, but long enough to give a well-written
102 // client a chance to service its mach message queue without getting cut off.
103 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
104 // even extra-slow clients a fair chance before we cut them off.
105 #define MDNS_MM_TIMEOUT 250
107 static int restarting_via_mach_init
= 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism
108 static int started_via_launchdaemon
= 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd
109 static mDNSBool advertise
= mDNS_Init_AdvertiseLocalAddresses
; // By default, advertise addresses (& other records) via multicast
111 extern mDNSBool StrictUnicastOrdering
;
113 //*************************************************************************************************************
114 #if COMPILER_LIKES_PRAGMA_MARK
116 #pragma mark - Active client list structures
119 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
120 struct DNSServiceDomainEnumeration_struct
122 DNSServiceDomainEnumeration
*next
;
123 mach_port_t ClientMachPort
;
124 DNSQuestion dom
; // Question asking for domains
125 DNSQuestion def
; // Question asking for default domain
128 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult
;
129 struct DNSServiceBrowserResult_struct
131 DNSServiceBrowserResult
*next
;
136 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
138 typedef struct DNSServiceBrowserQuestion
140 struct DNSServiceBrowserQuestion
*next
;
143 } DNSServiceBrowserQuestion
;
145 struct DNSServiceBrowser_struct
147 DNSServiceBrowser
*next
;
148 mach_port_t ClientMachPort
;
149 DNSServiceBrowserQuestion
*qlist
;
150 DNSServiceBrowserResult
*results
;
152 mDNSBool DefaultDomain
; // was the browse started on an explicit domain?
153 domainname type
; // registration type
156 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
157 struct DNSServiceResolver_struct
159 DNSServiceResolver
*next
;
160 mach_port_t ClientMachPort
;
166 // A single registered service: ServiceRecordSet + bookkeeping
167 // Note that we duplicate some fields from parent DNSServiceRegistration object
168 // to facilitate cleanup, when instances and parent may be deallocated at different times.
169 typedef struct ServiceInstance
171 struct ServiceInstance
*next
;
172 mach_port_t ClientMachPort
;
173 mDNSBool autoname
; // Set if this name is tied to the Computer Name
174 mDNSBool renameonmemfree
; // Set if we just got a name conflict and now need to automatically pick a new name
177 ServiceRecordSet srs
;
178 // Don't add any fields after ServiceRecordSet.
179 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
182 // A client-created service. May reference several ServiceInstance objects if default
183 // settings cause registration in multiple domains.
184 typedef struct DNSServiceRegistration
186 struct DNSServiceRegistration
*next
;
187 mach_port_t ClientMachPort
;
188 mDNSBool DefaultDomain
;
192 char regtype
[MAX_ESCAPED_DOMAIN_NAME
]; // for use in AllocateSubtypes
193 domainlabel name
; // used only if autoname is false
196 unsigned char txtinfo
[1024];
199 ServiceInstance
*regs
;
200 } DNSServiceRegistration
;
202 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
203 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
204 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
205 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
207 // We keep a list of client-supplied event sources in KQSocketEventSource records
208 typedef struct KQSocketEventSource
210 struct KQSocketEventSource
*next
;
213 } KQSocketEventSource
;
215 static KQSocketEventSource
*gEventSources
;
217 //*************************************************************************************************************
218 #if COMPILER_LIKES_PRAGMA_MARK
220 #pragma mark - General Utility Functions
223 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
225 char _malloc_options
[] = "AXZ";
227 mDNSexport
void LogMemCorruption(const char *format
, ...)
231 va_start(ptr
,format
);
232 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
234 LogMsg("!!!! %s !!!!", buffer
);
235 NotifyOfElusiveBug("Memory Corruption", buffer
);
237 *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
241 mDNSlocal
void validatelists(mDNS
*const m
)
244 KQSocketEventSource
*k
;
245 for (k
= gEventSources
; k
; k
=k
->next
)
246 if (k
->next
== (KQSocketEventSource
*)~0 || k
->fd
< 0)
247 LogMemCorruption("gEventSources: %p is garbage (%d)", k
, k
->fd
);
249 // Check Mach client lists
250 DNSServiceDomainEnumeration
*e
;
251 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
252 if (e
->next
== (DNSServiceDomainEnumeration
*)~0 || e
->ClientMachPort
== 0 || e
->ClientMachPort
== (mach_port_t
)~0)
253 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e
, e
->ClientMachPort
);
255 DNSServiceBrowser
*b
;
256 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
257 if (b
->next
== (DNSServiceBrowser
*)~0 || b
->ClientMachPort
== 0 || b
->ClientMachPort
== (mach_port_t
)~0)
258 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b
, b
->ClientMachPort
);
260 DNSServiceResolver
*l
;
261 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
262 if (l
->next
== (DNSServiceResolver
*)~0 || l
->ClientMachPort
== 0 || l
->ClientMachPort
== (mach_port_t
)~0)
263 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l
, l
->ClientMachPort
);
265 DNSServiceRegistration
*r
;
266 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
267 if (r
->next
== (DNSServiceRegistration
*)~0 || r
->ClientMachPort
== 0 || r
->ClientMachPort
== (mach_port_t
)~0)
268 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r
, r
->ClientMachPort
);
270 // Check Unix Domain Socket client lists (uds_daemon.c)
273 // Check core mDNS lists
275 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
277 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
278 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
279 if (rr
->resrec
.name
!= &rr
->namestorage
)
280 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
281 rr
, rr
->resrec
.name
->c
, rr
->namestorage
.c
, rr
->namestorage
.c
);
284 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
)
285 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
286 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
288 rr
= m
->NewLocalRecords
;
290 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
291 LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
293 rr
= m
->CurrentRecord
;
295 if (rr
->next
== (AuthRecord
*)~0 || rr
->resrec
.RecordType
== 0 || rr
->resrec
.RecordType
== 0xFF)
296 LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr
, rr
->resrec
.RecordType
);
299 for (q
= m
->Questions
; q
; q
=q
->next
)
300 if (q
->next
== (DNSQuestion
*)~0 || q
->ThisQInterval
== (mDNSs32
)~0)
301 LogMemCorruption("Questions list: %p is garbage (%lX %p)", q
, q
->ThisQInterval
, q
->next
);
306 FORALL_CACHERECORDS(slot
, cg
, cr
)
308 if (cr
->resrec
.RecordType
== 0 || cr
->resrec
.RecordType
== 0xFF)
309 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot
, cr
, cr
->resrec
.RecordType
);
310 if (cr
->CRActiveQuestion
)
312 for (q
= m
->Questions
; q
; q
=q
->next
) if (q
== cr
->CRActiveQuestion
) break;
313 if (!q
) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot
, cr
->CRActiveQuestion
, CRDisplayString(m
, cr
));
317 // Check core uDNS lists
318 udns_validatelists(m
);
320 // Check platform-layer lists
321 NetworkInterfaceInfoOSX
*i
;
322 for (i
= m
->p
->InterfaceList
; i
; i
= i
->next
)
323 if (i
->next
== (NetworkInterfaceInfoOSX
*)~0 || !i
->m
|| i
->m
== (mDNS
*)~0)
324 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i
, i
->ifinfo
.ifname
);
327 for (t
= m
->TunnelClients
; t
; t
=t
->next
)
328 if (t
->next
== (ClientTunnel
*)~0 || t
->dstname
.c
[0] > 63)
329 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t
, t
->dstname
.c
[0]);
332 mDNSexport
void *mallocL(char *msg
, unsigned int size
)
334 // Allocate space for two words of sanity checking data before the requested block
335 mDNSu32
*mem
= malloc(sizeof(mDNSu32
) * 2 + size
);
337 { LogMsg("malloc( %s : %d ) failed", msg
, size
); return(NULL
); }
340 if (size
> 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg
, size
, &mem
[2]);
341 else if (MACOSX_MDNS_MALLOC_DEBUGGING
>= 2) LogMsg("malloc( %s : %lu ) = %p", msg
, size
, &mem
[2]);
344 //mDNSPlatformMemZero(&mem[2], size);
345 memset(&mem
[2], 0xFF, size
);
346 validatelists(&mDNSStorage
);
351 mDNSexport
void freeL(char *msg
, void *x
)
354 LogMsg("free( %s @ NULL )!", msg
);
357 mDNSu32
*mem
= ((mDNSu32
*)x
) - 2;
358 if (mem
[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
359 if (mem
[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg
, mem
[1], &mem
[2]);
360 else if (MACOSX_MDNS_MALLOC_DEBUGGING
>= 2) LogMsg("free( %s : %ld @ %p)", msg
, mem
[1], &mem
[2]);
361 //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]);
362 memset(mem
, 0xFF, sizeof(mDNSu32
) * 2 + mem
[1]);
363 validatelists(&mDNSStorage
);
370 //*************************************************************************************************************
371 #if COMPILER_LIKES_PRAGMA_MARK
373 #pragma mark - Mach client request handlers
376 //*************************************************************************************************************
377 // Client Death Detection
379 // This gets called after ALL constituent records of the Service Record Set have been deregistered
380 mDNSlocal
void FreeServiceInstance(ServiceInstance
*x
)
382 ServiceRecordSet
*s
= &x
->srs
;
383 ExtraResourceRecord
*e
= x
->srs
.Extras
, *tmp
;
387 e
->r
.RecordContext
= e
;
390 FreeExtraRR(&mDNSStorage
, &tmp
->r
, mStatus_MemFree
);
393 if (s
->RR_TXT
.resrec
.rdata
!= &s
->RR_TXT
.rdatastorage
)
394 freeL("TXT RData", s
->RR_TXT
.resrec
.rdata
);
396 if (s
->SubTypes
) freeL("ServiceSubTypes", s
->SubTypes
);
397 freeL("ServiceInstance", x
);
400 // AbortClient finds whatever client is identified by the given Mach port,
401 // stops whatever operation that client was doing, and frees its memory.
402 // In the case of a service registration, the actual freeing may be deferred
403 // until we get the mStatus_MemFree message, if necessary
404 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
, void *m
)
406 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
407 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
408 DNSServiceResolver
**l
= &DNSServiceResolverList
;
409 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
411 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
414 DNSServiceDomainEnumeration
*x
= *e
;
417 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->dom
.qname
.c
, m
, x
);
418 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort
, x
->dom
.qname
.c
);
419 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
420 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
421 freeL("DNSServiceDomainEnumeration", x
);
425 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
428 DNSServiceBrowser
*x
= *b
;
429 DNSServiceBrowserQuestion
*freePtr
, *qptr
= x
->qlist
;
434 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, qptr
->q
.qname
.c
, m
, x
);
435 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort
, qptr
->q
.qname
.c
);
436 mDNS_StopBrowse(&mDNSStorage
, &qptr
->q
);
439 freeL("DNSServiceBrowserQuestion", freePtr
);
443 DNSServiceBrowserResult
*t
= x
->results
;
444 x
->results
= x
->results
->next
;
445 freeL("DNSServiceBrowserResult", t
);
447 freeL("DNSServiceBrowser", x
);
451 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
454 DNSServiceResolver
*x
= *l
;
457 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->i
.name
.c
, m
, x
);
458 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort
, x
->i
.name
.c
);
459 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
460 freeL("DNSServiceResolver", x
);
464 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
467 ServiceInstance
*si
= NULL
;
468 DNSServiceRegistration
*x
= *r
;
474 ServiceInstance
*instance
= si
;
476 instance
->renameonmemfree
= mDNSfalse
;
477 if (m
&& m
!= x
) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort
, instance
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&instance
->srs
), m
, x
);
478 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort
, instance
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&instance
->srs
));
480 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
481 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
482 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
483 // the list, so we should go ahead and free the memory right now
484 if (mDNS_DeregisterService(&mDNSStorage
, &instance
->srs
)) FreeServiceInstance(instance
); // FreeServiceInstance invalidates pointer
487 freeL("DNSServiceRegistration", x
);
491 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort
);
494 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
496 mDNSlocal
void AbortClientWithLogMessage(mach_port_t c
, char *reason
, char *msg
, void *m
)
498 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
499 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
500 DNSServiceResolver
*l
= DNSServiceResolverList
;
501 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
502 DNSServiceBrowserQuestion
*qptr
;
504 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
505 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
506 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
507 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
509 if (e
) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c
, e
->dom
.qname
.c
, reason
, msg
);
512 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
513 LogMsg("%5d: Browser(%##s) %s%s", c
, qptr
->q
.qname
.c
, reason
, msg
);
515 else if (l
) LogMsg("%5d: Resolver(%##s) %s%s", c
, l
->i
.name
.c
, reason
, msg
);
519 for (si
= r
->regs
; si
; si
= si
->next
)
520 LogMsg("%5d: Registration(%##s) %s%s", c
, si
->srs
.RR_SRV
.resrec
.name
->c
, reason
, msg
);
522 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c
, reason
, msg
);
527 mDNSlocal mDNSBool
CheckForExistingClient(mach_port_t c
)
529 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
530 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
531 DNSServiceResolver
*l
= DNSServiceResolverList
;
532 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
533 DNSServiceBrowserQuestion
*qptr
;
535 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
536 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
537 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
538 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
539 if (e
) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c
, e
->dom
.qname
.c
);
542 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
543 LogMsg("%5d: Browser(%##s) already exists!", c
, qptr
->q
.qname
.c
);
545 if (l
) LogMsg("%5d: Resolver(%##s) already exists!", c
, l
->i
.name
.c
);
546 if (r
) LogMsg("%5d: Registration(%##s) already exists!", c
, r
->regs
? r
->regs
->srs
.RR_SRV
.resrec
.name
->c
: NULL
);
547 return(e
|| b
|| l
|| r
);
550 #ifndef __LIB_DISPATCH__
552 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
554 KQueueLock(&mDNSStorage
);
555 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
556 (void)unusedport
; // Unused
557 (void)size
; // Unused
558 (void)info
; // Unused
559 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
561 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
562 AbortClient(deathMessage
->not_port
, NULL
);
564 /* Deallocate the send right that came in the dead name notification */
565 mach_port_destroy(mach_task_self(), deathMessage
->not_port
);
567 KQueueUnlock(&mDNSStorage
, "Mach AbortClient");
570 #endif __LIB_DISPATCH__
572 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
, void *m
)
574 #ifdef __LIB_DISPATCH__
575 dispatch_source_t mach_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND
, ClientMachPort
, 0, dispatch_get_main_queue());
576 if (mach_source
== mDNSNULL
)
578 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
581 dispatch_source_set_event_handler(mach_source
, ^{
582 mach_port_destroy(mach_task_self(), ClientMachPort
);
584 dispatch_resume(mach_source
);
585 #else __LIB_DISPATCH__
587 kern_return_t r
= mach_port_request_notification(mach_task_self(), ClientMachPort
, MACH_NOTIFY_DEAD_NAME
, 0,
588 client_death_port
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &prev
);
589 // If the port already died while we were thinking about it, then abort the operation right away
590 if (r
!= KERN_SUCCESS
)
591 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
592 #endif __LIB_DISPATCH__
595 //*************************************************************************************************************
596 // Domain Enumeration
598 mDNSlocal
void DomainEnumFound(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
600 kern_return_t status
;
601 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
602 DNSServiceDomainEnumerationReplyResultType rt
;
603 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->QuestionContext
;
606 debugf("DomainEnumFound: %##s PTR %##s", answer
->name
->c
, answer
->rdata
->u
.name
.c
);
607 if (answer
->rrtype
!= kDNSType_PTR
) return;
608 if (!x
) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
612 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
613 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
617 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
621 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
622 x
->ClientMachPort
, x
->dom
.qname
.c
, answer
->rdata
->u
.name
.c
,
623 !AddRecord
? "RemoveDomain" :
624 question
== &x
->dom
? "AddDomain" : "AddDomainDefault");
626 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, buffer
);
627 status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, buffer
, 0, MDNS_MM_TIMEOUT
);
628 if (status
== MACH_SEND_TIMED_OUT
)
629 AbortBlockedClient(x
->ClientMachPort
, "enumeration", x
);
632 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
635 // Check client parameter
636 (void)unusedserver
; // Unused
637 mStatus err
= mStatus_NoError
;
638 const char *errormsg
= "Unknown";
639 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
640 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
642 mDNS_DomainType dt1
= regDom
? mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
643 mDNS_DomainType dt2
= regDom
? mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
645 // Allocate memory, and handle failure
646 DNSServiceDomainEnumeration
*x
= mallocL("DNSServiceDomainEnumeration", sizeof(*x
));
647 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
649 // Set up object, and link into list
650 x
->ClientMachPort
= client
;
651 x
->next
= DNSServiceDomainEnumerationList
;
652 DNSServiceDomainEnumerationList
= x
;
654 verbosedebugf("%5d: Enumerate %s Domains", client
, regDom
? "Registration" : "Browsing");
657 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, NULL
, mDNSInterface_LocalOnly
, DomainEnumFound
, x
);
658 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, NULL
, mDNSInterface_LocalOnly
, DomainEnumFound
, x
);
659 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_GetDomains"; goto fail
; }
661 // Succeeded: Wrap up and return
662 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client
, x
->dom
.qname
.c
);
663 EnableDeathNotificationForClient(client
, x
);
664 return(mStatus_NoError
);
667 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client
, regDom
, errormsg
, err
);
671 //*************************************************************************************************************
672 // Browse for services
674 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
678 if (answer
->rrtype
!= kDNSType_PTR
)
679 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
); return; }
682 domainname type
, domain
;
683 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
685 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
686 answer
->name
->c
, answer
->rdata
->u
.name
.c
);
690 DNSServiceBrowserResult
*x
= mallocL("DNSServiceBrowserResult", sizeof(*x
));
691 if (!x
) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer
->rdata
->u
.name
.c
); return; }
693 verbosedebugf("FoundInstance: %s %##s", AddRecord
? "Add" : "Rmv", answer
->rdata
->u
.name
.c
);
694 AssignDomainName(&x
->result
, &answer
->rdata
->u
.name
);
696 x
->resultType
= DNSServiceBrowserReplyAddInstance
;
697 else x
->resultType
= DNSServiceBrowserReplyRemoveInstance
;
700 DNSServiceBrowser
*browser
= (DNSServiceBrowser
*)question
->QuestionContext
;
701 DNSServiceBrowserResult
**p
= &browser
->results
;
702 while (*p
) p
= &(*p
)->next
;
705 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
706 browser
->ClientMachPort
, question
->qname
.c
, DNSTypeName(question
->qtype
), AddRecord
? "Add" : "Rmv", RRDisplayString(m
, answer
));
709 mDNSlocal mStatus
AddDomainToBrowser(DNSServiceBrowser
*browser
, const domainname
*d
)
711 mStatus err
= mStatus_NoError
;
712 DNSServiceBrowserQuestion
*ptr
, *question
= NULL
;
714 for (ptr
= browser
->qlist
; ptr
; ptr
= ptr
->next
)
716 if (SameDomainName(&ptr
->q
.qname
, d
))
717 { debugf("Domain %##s already contained in browser", d
->c
); return mStatus_AlreadyRegistered
; }
720 question
= mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion
));
721 if (!question
) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr
; }
722 AssignDomainName(&question
->domain
, d
);
723 question
->next
= browser
->qlist
;
724 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser
->ClientMachPort
, browser
->type
.c
, d
->c
);
725 err
= mDNS_StartBrowse(&mDNSStorage
, &question
->q
, &browser
->type
, d
, mDNSInterface_Any
, mDNSfalse
, FoundInstance
, browser
);
727 browser
->qlist
= question
;
730 LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err
);
731 freeL("DNSServiceBrowserQuestion", question
);
736 mDNSexport
void machserver_automatic_browse_domain_changed(const domainname
*d
, mDNSBool add
)
738 DNSServiceBrowser
*ptr
;
739 for (ptr
= DNSServiceBrowserList
; ptr
; ptr
= ptr
->next
)
741 if (ptr
->DefaultDomain
)
745 mStatus err
= AddDomainToBrowser(ptr
, d
);
746 if (err
&& err
!= mStatus_AlreadyRegistered
) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d
, ptr
->ClientMachPort
);
750 DNSServiceBrowserQuestion
**q
= &ptr
->qlist
;
753 if (SameDomainName(&(*q
)->domain
, d
))
755 DNSServiceBrowserQuestion
*rem
= *q
;
757 mDNS_StopQueryWithRemoves(&mDNSStorage
, &rem
->q
);
758 freeL("DNSServiceBrowserQuestion", rem
);
763 LogMsg("Requested removal of default domain %##s not in client %5d's list", d
->c
, ptr
->ClientMachPort
);
769 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
770 DNSCString regtype
, DNSCString domain
)
772 // Check client parameter
773 (void)unusedserver
; // Unused
774 mStatus err
= mStatus_NoError
;
775 const char *errormsg
= "Unknown";
777 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
778 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
780 // Check other parameters
783 mDNSs32 NumSubTypes
= ChopSubTypes(regtype
); // Note: Modifies regtype string to remove trailing subtypes
784 if (NumSubTypes
< 0 || NumSubTypes
> 1) { errormsg
= "Bad Service SubType"; goto badparam
; }
785 if (NumSubTypes
== 1 && !AppendDNSNameString(&t
, regtype
+ strlen(regtype
) + 1))
786 { errormsg
= "Bad Service SubType"; goto badparam
; }
787 if (!regtype
[0] || !AppendDNSNameString(&t
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
789 if (!MakeDomainNameFromDNSNameString(&temp
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
790 if (temp
.c
[0] > 15 && (!domain
|| domain
[0] == 0)) domain
= "local."; // For over-long service types, we only allow domain "local"
792 // Allocate memory, and handle failure
793 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
794 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
796 // Set up object, and link into list
797 AssignDomainName(&x
->type
, &t
);
798 x
->ClientMachPort
= client
;
802 x
->next
= DNSServiceBrowserList
;
803 DNSServiceBrowserList
= x
;
807 // Start browser for an explicit domain
808 x
->DefaultDomain
= mDNSfalse
;
809 if (!MakeDomainNameFromDNSNameString(&d
, domain
)) { errormsg
= "Illegal domain"; goto badparam
; }
810 err
= AddDomainToBrowser(x
, &d
);
811 if (err
) { AbortClient(client
, x
); errormsg
= "AddDomainToBrowser"; goto fail
; }
815 DNameListElem
*sdPtr
;
816 // Start browser on all domains
817 x
->DefaultDomain
= mDNStrue
;
818 if (!AutoBrowseDomains
) { AbortClient(client
, x
); errormsg
= "GetSearchDomainList"; goto fail
; }
819 for (sdPtr
= AutoBrowseDomains
; sdPtr
; sdPtr
= sdPtr
->next
)
821 err
= AddDomainToBrowser(x
, &sdPtr
->name
);
824 // only terminally bail if .local fails
825 if (!SameDomainName(&localdomain
, &sdPtr
->name
))
826 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr
->name
.c
);
827 else { AbortClient(client
, x
); errormsg
= "AddDomainToBrowser"; goto fail
; }
832 // Succeeded: Wrap up and return
833 EnableDeathNotificationForClient(client
, x
);
834 return(mStatus_NoError
);
837 err
= mStatus_BadParamErr
;
839 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client
, regtype
, domain
, errormsg
, err
);
843 //*************************************************************************************************************
844 // Resolve Service Info
846 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
848 kern_return_t status
;
849 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->ServiceInfoQueryContext
;
850 NetworkInterfaceInfoOSX
*ifx
= IfindexToInterfaceInfoOSX(m
, query
->info
->InterfaceID
);
851 if (query
->info
->InterfaceID
== mDNSInterface_LocalOnly
|| query
->info
->InterfaceID
== mDNSInterface_P2P
) ifx
= mDNSNULL
;
852 struct sockaddr_storage interface
;
853 struct sockaddr_storage address
;
855 int i
, pstrlen
= query
->info
->TXTinfo
[0];
858 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
860 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
862 mDNSPlatformMemZero(&interface
, sizeof(interface
));
863 mDNSPlatformMemZero(&address
, sizeof(address
));
865 if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
)
867 struct sockaddr_in
*s
= (struct sockaddr_in
*)&interface
;
868 s
->sin_len
= sizeof(*s
);
869 s
->sin_family
= AF_INET
;
871 s
->sin_addr
.s_addr
= ifx
->ifinfo
.ip
.ip
.v4
.NotAnInteger
;
873 else if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
)
875 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&interface
;
876 sin6
->sin6_len
= sizeof(*sin6
);
877 sin6
->sin6_family
= AF_INET6
;
878 sin6
->sin6_flowinfo
= 0;
880 sin6
->sin6_addr
= *(struct in6_addr
*)&ifx
->ifinfo
.ip
.ip
.v6
;
881 sin6
->sin6_scope_id
= ifx
->scope_id
;
884 if (query
->info
->ip
.type
== mDNSAddrType_IPv4
)
886 struct sockaddr_in
*s
= (struct sockaddr_in
*)&address
;
887 s
->sin_len
= sizeof(*s
);
888 s
->sin_family
= AF_INET
;
889 s
->sin_port
= query
->info
->port
.NotAnInteger
;
890 s
->sin_addr
.s_addr
= query
->info
->ip
.ip
.v4
.NotAnInteger
;
894 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&address
;
895 sin6
->sin6_len
= sizeof(*sin6
);
896 sin6
->sin6_family
= AF_INET6
;
897 sin6
->sin6_port
= query
->info
->port
.NotAnInteger
;
898 sin6
->sin6_flowinfo
= 0;
899 sin6
->sin6_addr
= *(struct in6_addr
*)&query
->info
->ip
.ip
.v6
;
900 sin6
->sin6_scope_id
= ifx
? ifx
->scope_id
: 0;
903 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
904 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
905 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
906 // ASCII-1 characters are used in the C-string as boundary markers,
907 // to indicate the boundaries between the original constituent P-strings.
908 for (i
=1; i
<query
->info
->TXTlen
; i
++)
911 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
915 pstrlen
= query
->info
->TXTinfo
[i
];
918 cstring
[i
-1] = 0; // Put the terminating NULL on the end
920 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x
->ClientMachPort
,
921 x
->i
.name
.c
, &query
->info
->ip
, mDNSVal16(query
->info
->port
));
922 status
= DNSServiceResolverReply_rpc(x
->ClientMachPort
,
923 (char*)&interface
, (char*)&address
, cstring
, 0, MDNS_MM_TIMEOUT
);
924 if (status
== MACH_SEND_TIMED_OUT
)
925 AbortBlockedClient(x
->ClientMachPort
, "resolve", x
);
928 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
929 DNSCString name
, DNSCString regtype
, DNSCString domain
)
931 // Check client parameter
932 (void)unusedserver
; // Unused
933 mStatus err
= mStatus_NoError
;
934 const char *errormsg
= "Unknown";
935 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
936 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
938 // Check other parameters
940 domainname t
, d
, srv
;
941 if (!name
[0] || !MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
942 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
943 if (!domain
[0] || !MakeDomainNameFromDNSNameString(&d
, domain
)) { errormsg
= "Bad Domain"; goto badparam
; }
944 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
946 // Allocate memory, and handle failure
947 DNSServiceResolver
*x
= mallocL("DNSServiceResolver", sizeof(*x
));
948 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
950 // Set up object, and link into list
951 x
->ClientMachPort
= client
;
952 x
->i
.InterfaceID
= mDNSInterface_Any
;
954 x
->ReportTime
= NonZeroTime(mDNS_TimeNow(&mDNSStorage
) + 130 * mDNSPlatformOneSecond
);
955 x
->next
= DNSServiceResolverList
;
956 DNSServiceResolverList
= x
;
959 LogOperation("%5d: DNSServiceResolve(%##s) START", client
, x
->i
.name
.c
);
960 err
= mDNS_StartResolveService(&mDNSStorage
, &x
->q
, &x
->i
, FoundInstanceInfo
, x
);
961 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartResolveService"; goto fail
; }
963 // Succeeded: Wrap up and return
964 EnableDeathNotificationForClient(client
, x
);
965 return(mStatus_NoError
);
968 err
= mStatus_BadParamErr
;
970 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client
, name
, regtype
, domain
, errormsg
, err
);
974 //*************************************************************************************************************
977 mDNSexport
void RecordUpdatedNiceLabel(mDNS
*const m
, mDNSs32 delay
)
979 m
->p
->NotifyUser
= NonZeroTime(m
->timenow
+ delay
);
982 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
)
984 ServiceInstance
*si
= (ServiceInstance
*)srs
->ServiceContext
;
986 if (result
== mStatus_NoError
)
988 kern_return_t status
;
989 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
));
990 status
= DNSServiceRegistrationReply_rpc(si
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
991 if (status
== MACH_SEND_TIMED_OUT
)
992 AbortBlockedClient(si
->ClientMachPort
, "registration success", si
);
993 if (si
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
994 RecordUpdatedNiceLabel(m
, 0); // Successfully got new name, tell user immediately
997 else if (result
== mStatus_NameConflict
)
999 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
));
1000 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1001 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1002 if (si
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
1004 // On conflict for an autoname service, rename and reregister *all* autoname services
1005 IncrementLabelSuffix(&m
->nicelabel
, mDNStrue
);
1006 mDNS_ConfigChanged(m
);
1008 else if (si
->autoname
)
1010 mDNS_RenameAndReregisterService(m
, srs
, mDNSNULL
);
1015 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1016 // of their registration in the usual way (which we will catch via client death notification).
1017 // If the Mach queue is full, we forcibly abort the client immediately.
1018 kern_return_t status
= DNSServiceRegistrationReply_rpc(si
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
1019 if (status
== MACH_SEND_TIMED_OUT
)
1020 AbortBlockedClient(si
->ClientMachPort
, "registration conflict", NULL
);
1024 else if (result
== mStatus_MemFree
)
1026 if (si
->renameonmemfree
) // We intentionally terminated registration so we could re-register with new name
1028 debugf("RegCallback renaming %#s to %#s", si
->name
.c
, m
->nicelabel
.c
);
1029 si
->renameonmemfree
= mDNSfalse
;
1030 si
->name
= m
->nicelabel
;
1031 mDNS_RenameAndReregisterService(m
, srs
, &si
->name
);
1035 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1036 DNSServiceRegistration
*r
;
1037 for (r
= DNSServiceRegistrationList
; r
; r
= r
->next
)
1039 ServiceInstance
**sp
= &r
->regs
;
1042 if (*sp
== si
) { LogMsg("RegCallback: %##s Still in list; removing", srs
->RR_SRV
.resrec
.name
->c
); *sp
= (*sp
)->next
; break; }
1047 FreeServiceInstance(si
);
1051 else if (result
!= mStatus_NATTraversal
)
1052 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si
->ClientMachPort
, srs
->RR_SRV
.resrec
.name
->c
, SRS_PORT(srs
), result
);
1055 mDNSlocal mStatus
AddServiceInstance(DNSServiceRegistration
*x
, const domainname
*domain
)
1058 ServiceInstance
*si
= NULL
;
1059 AuthRecord
*SubTypes
= NULL
;
1061 for (si
= x
->regs
; si
; si
= si
->next
)
1063 if (SameDomainName(&si
->domain
, domain
))
1064 { LogMsg("Requested addition of domain %##s already in list", domain
->c
); return mStatus_AlreadyRegistered
; }
1067 SubTypes
= AllocateSubTypes(x
->NumSubTypes
, x
->regtype
);
1068 if (x
->NumSubTypes
&& !SubTypes
) return mStatus_NoMemoryErr
;
1070 si
= mallocL("ServiceInstance", sizeof(*si
) - sizeof(RDataBody
) + x
->rdsize
);
1071 if (!si
) return mStatus_NoMemoryErr
;
1073 si
->ClientMachPort
= x
->ClientMachPort
;
1074 si
->renameonmemfree
= mDNSfalse
;
1075 si
->autoname
= x
->autoname
;
1076 si
->name
= x
->autoname
? mDNSStorage
.nicelabel
: x
->name
;
1077 si
->domain
= *domain
;
1079 err
= mDNS_RegisterService(&mDNSStorage
, &si
->srs
, &si
->name
, &x
->type
, domain
, NULL
,
1080 x
->port
, x
->txtinfo
, x
->txt_len
, SubTypes
, x
->NumSubTypes
, mDNSInterface_Any
, RegCallback
, si
);
1088 LogMsg("Error %d for registration of service in domain %##s", err
, domain
->c
);
1089 freeL("ServiceInstance", si
);
1094 mDNSexport
void machserver_automatic_registration_domain_changed(const domainname
*d
, mDNSBool add
)
1096 DNSServiceRegistration
*reg
;
1098 for (reg
= DNSServiceRegistrationList
; reg
; reg
= reg
->next
)
1100 if (reg
->DefaultDomain
)
1103 AddServiceInstance(reg
, d
);
1106 ServiceInstance
**si
= ®
->regs
;
1109 if (SameDomainName(&(*si
)->domain
, d
))
1111 ServiceInstance
*s
= *si
;
1113 if (mDNS_DeregisterService(&mDNSStorage
, &s
->srs
)) FreeServiceInstance(s
); // only free memory synchronously on error
1118 if (!si
) debugf("Requested removal of default domain %##s not in client %5d's list", d
, reg
->ClientMachPort
); // normal if registration failed
1124 mDNSexport kern_return_t
provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
1125 DNSCString name
, DNSCString regtype
, DNSCString domain
, IPPort IpPort
, DNSCString txtRecord
)
1127 (void)unusedserver
; // Unused
1128 mStatus err
= mStatus_NoError
;
1129 const char *errormsg
= "Unknown";
1131 // older versions of this code passed the port via mach IPC as an int.
1132 // we continue to pass it as 4 bytes to maintain binary compatibility,
1133 // but now ensure that the network byte order is preserved by using a struct
1135 port
.b
[0] = IpPort
.bytes
[2];
1136 port
.b
[1] = IpPort
.bytes
[3];
1138 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1139 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
1141 // Check for sub-types after the service type
1142 size_t reglen
= strlen(regtype
) + 1;
1143 if (reglen
> MAX_ESCAPED_DOMAIN_NAME
) { errormsg
= "reglen too long"; goto badparam
; }
1144 mDNSs32 NumSubTypes
= ChopSubTypes(regtype
); // Note: Modifies regtype string to remove trailing subtypes
1145 if (NumSubTypes
< 0) { errormsg
= "Bad Service SubType"; goto badparam
; }
1147 // Check other parameters
1151 if (!name
[0]) n
= mDNSStorage
.nicelabel
;
1152 else if (!MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
1153 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
1154 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
1155 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
1157 unsigned char txtinfo
[1024] = "";
1158 unsigned int data_len
= 0;
1159 unsigned int size
= sizeof(RDataBody
);
1160 unsigned char *pstring
= &txtinfo
[data_len
];
1161 char *ptr
= txtRecord
;
1163 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1164 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1165 // Hence we have to convert the C-string to a P-string.
1166 // ASCII-1 characters are allowed in the C-string as boundary markers,
1167 // so that a single C-string can be used to represent one or more P-strings.
1170 if (++data_len
>= sizeof(txtinfo
)) { errormsg
= "TXT record too long"; goto badtxt
; }
1171 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
1173 pstring
= &txtinfo
[data_len
];
1179 if (pstring
[0] == 255) { errormsg
= "TXT record invalid (component longer than 255)"; goto badtxt
; }
1180 pstring
[++pstring
[0]] = *ptr
++;
1185 if (size
< data_len
)
1188 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1189 // a port number of zero. When two instances of the protected client are allowed to run on one
1190 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1191 if (!mDNSIPPortIsZero(port
))
1193 int count
= CountExistingRegistrations(&srv
, port
);
1195 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1196 client
, count
+1, srv
.c
, mDNSVal16(port
));
1199 // Allocate memory, and handle failure
1200 DNSServiceRegistration
*x
= mallocL("DNSServiceRegistration", sizeof(*x
));
1201 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1202 mDNSPlatformMemZero(x
, sizeof(*x
));
1204 // Set up object, and link into list
1205 x
->ClientMachPort
= client
;
1206 x
->DefaultDomain
= !domain
[0];
1207 x
->autoname
= (!name
[0]);
1209 x
->NumSubTypes
= NumSubTypes
;
1210 memcpy(x
->regtype
, regtype
, reglen
);
1214 memcpy(x
->txtinfo
, txtinfo
, 1024);
1215 x
->txt_len
= data_len
;
1219 x
->next
= DNSServiceRegistrationList
;
1220 DNSServiceRegistrationList
= x
;
1222 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1223 x
->ClientMachPort
, name
, regtype
, domain
, mDNSVal16(port
));
1225 err
= AddServiceInstance(x
, &d
);
1226 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_RegisterService"; goto fail
; } // bail if .local (or explicit domain) fails
1228 if (x
->DefaultDomain
)
1231 for (p
= AutoRegistrationDomains
; p
; p
= p
->next
)
1232 AddServiceInstance(x
, &p
->name
);
1235 // Succeeded: Wrap up and return
1236 EnableDeathNotificationForClient(client
, x
);
1237 return(mStatus_NoError
);
1240 LogMsg("%5d: TXT record: %.100s...", client
, txtRecord
);
1242 err
= mStatus_BadParamErr
;
1244 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)",
1245 client
, name
, regtype
, domain
, mDNSVal16(port
), errormsg
, err
);
1249 mDNSlocal
void mDNSPreferencesSetNames(mDNS
*const m
, int key
, domainlabel
*old
, domainlabel
*new)
1251 domainlabel
*prevold
, *prevnew
;
1254 case kmDNSComputerName
:
1255 case kmDNSLocalHostName
:
1256 if (key
== kmDNSComputerName
)
1258 prevold
= &m
->p
->prevoldnicelabel
;
1259 prevnew
= &m
->p
->prevnewnicelabel
;
1263 prevold
= &m
->p
->prevoldhostlabel
;
1264 prevnew
= &m
->p
->prevnewhostlabel
;
1266 // There are a few cases where we need to invoke the helper.
1268 // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need
1269 // to invoke the helper so that it pops up a dialogue to inform the user about the
1272 // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label
1273 // through the preferences pane. We may have to inform the helper as it may have popped up
1274 // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking
1275 // the helper in this case if the previous values (old and new) that we told helper last time
1276 // are same. If the previous old and new values are same, helper does not care.
1278 // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old"
1279 // is not called with NULL today, but this makes it future proof.
1280 if (!old
|| !new || !SameDomainLabelCS(old
->c
, new->c
) ||
1281 !SameDomainLabelCS(old
->c
, prevold
->c
) ||
1282 !SameDomainLabelCS(new->c
, prevnew
->c
))
1292 mDNSPreferencesSetName(key
, old
, new);
1296 LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s",
1297 (key
== kmDNSComputerName
? "prevoldnicelabel" : "prevoldhostlabel"), prevold
->c
,
1298 (key
== kmDNSComputerName
? "prevnewnicelabel" : "prevnewhostlabel"), prevnew
->c
,
1303 LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key
);
1308 mDNSlocal
void mDNS_StatusCallback(mDNS
*const m
, mStatus result
)
1311 if (result
== mStatus_NoError
)
1313 if (!SameDomainLabelCS(m
->p
->userhostlabel
.c
, m
->hostlabel
.c
))
1314 LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
1315 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1316 RecordUpdatedNiceLabel(m
, mDNSPlatformOneSecond
);
1318 else if (result
== mStatus_NameConflict
)
1320 LogInfo("Local Hostname conflict for \"%#s.local\"", m
->hostlabel
.c
);
1321 if (!m
->p
->HostNameConflict
) m
->p
->HostNameConflict
= NonZeroTime(m
->timenow
);
1322 else if (m
->timenow
- m
->p
->HostNameConflict
> 60 * mDNSPlatformOneSecond
)
1324 // Tell the helper we've given up
1325 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, NULL
);
1328 else if (result
== mStatus_GrowCache
)
1330 // Allocate another chunk of cache storage
1331 CacheEntity
*storage
= mallocL("mStatus_GrowCache", sizeof(CacheEntity
) * RR_CACHE_SIZE
);
1332 //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
1333 if (storage
) mDNS_GrowCache(m
, storage
, RR_CACHE_SIZE
);
1335 else if (result
== mStatus_ConfigChanged
)
1337 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
1338 mDNSPreferencesSetNames(m
, kmDNSComputerName
, &m
->p
->usernicelabel
, &m
->nicelabel
);
1339 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, &m
->hostlabel
);
1341 // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
1342 DNSServiceRegistration
*r
;
1343 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1346 ServiceInstance
*si
;
1347 for (si
= r
->regs
; si
; si
= si
->next
)
1349 if (!SameDomainLabelCS(si
->name
.c
, m
->nicelabel
.c
))
1351 debugf("NetworkChanged renaming %##s to %#s", si
->srs
.RR_SRV
.resrec
.name
->c
, m
->nicelabel
.c
);
1352 si
->renameonmemfree
= mDNStrue
;
1353 if (mDNS_DeregisterService_drt(m
, &si
->srs
, mDNS_Dereg_rapid
))
1354 RegCallback(m
, &si
->srs
, mStatus_MemFree
); // If service deregistered already, we can re-register immediately
1359 // Then we call into the UDS daemon code, to let it do the same
1360 udsserver_handle_configchange(m
);
1364 //*************************************************************************************************************
1365 // Add / Update / Remove records from existing Registration
1367 mDNSexport kern_return_t
provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1368 int type
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
, natural_t
*reference
)
1370 // Check client parameter
1372 mStatus err
= mStatus_NoError
;
1373 const char *errormsg
= "Unknown";
1374 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1375 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1376 ServiceInstance
*si
;
1378 (void)unusedserver
; // Unused
1379 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1380 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1382 // Check other parameters
1383 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1384 if (data_len
> sizeof(RDataBody
)) size
= data_len
;
1385 else size
= sizeof(RDataBody
);
1388 *reference
= (natural_t
)id
;
1389 for (si
= x
->regs
; si
; si
= si
->next
)
1391 // Allocate memory, and handle failure
1392 ExtraResourceRecord
*extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
1393 if (!extra
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1395 // Fill in type, length, and data of new record
1396 extra
->r
.resrec
.rrtype
= type
;
1397 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1398 extra
->r
.resrec
.rdlength
= data_len
;
1399 memcpy(&extra
->r
.rdatastorage
.u
.data
, data
, data_len
);
1402 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1403 client
, si
->srs
.RR_SRV
.resrec
.name
->c
, type
, data_len
, extra
);
1404 err
= mDNS_AddRecordToService(&mDNSStorage
, &si
->srs
, extra
, &extra
->r
.rdatastorage
, ttl
);
1408 freeL("Extra Resource Record", extra
);
1409 errormsg
= "mDNS_AddRecordToService";
1413 extra
->ClientID
= id
;
1416 return mStatus_NoError
;
1419 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client
, x
? x
->name
.c
: (mDNSu8
*)"\x8""«NULL»", type
, data_len
, errormsg
, err
);
1420 return mStatus_UnknownErr
;
1423 mDNSlocal
void UpdateCallback(mDNS
*const m
, AuthRecord
*const rr
, RData
*OldRData
, mDNSu16 OldRDLen
)
1426 (void)OldRDLen
; // Unused
1427 if (OldRData
!= &rr
->rdatastorage
)
1428 freeL("Old RData", OldRData
);
1431 mDNSlocal mStatus
UpdateRecord(ServiceRecordSet
*srs
, mach_port_t client
, AuthRecord
*rr
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1433 // Check client parameter
1434 mStatus err
= mStatus_NoError
;
1435 const char *errormsg
= "Unknown";
1436 const domainname
*name
= (const domainname
*)"";
1438 name
= srs
->RR_SRV
.resrec
.name
;
1440 unsigned int size
= sizeof(RDataBody
);
1441 if (size
< data_len
)
1444 // Allocate memory, and handle failure
1445 RData
*newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
1446 if (!newrdata
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1448 // Fill in new length, and data
1449 newrdata
->MaxRDLength
= size
;
1450 memcpy(&newrdata
->u
, data
, data_len
);
1452 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1453 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1454 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1455 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& data_len
== 0) { data_len
= 1; newrdata
->u
.txt
.c
[0] = 0; }
1458 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1459 client
, srs
->RR_SRV
.resrec
.name
->c
, data_len
);
1461 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, data_len
, newrdata
, UpdateCallback
);
1464 errormsg
= "mDNS_Update";
1465 freeL("RData", newrdata
);
1468 return(mStatus_NoError
);
1471 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client
, name
->c
, data_len
, errormsg
, err
);
1475 mDNSexport kern_return_t
provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1476 natural_t reference
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1478 // Check client parameter
1479 mStatus err
= mStatus_NoError
;
1480 const char *errormsg
= "Unknown";
1481 const domainname
*name
= (const domainname
*)"";
1482 ServiceInstance
*si
;
1484 (void)unusedserver
; // unused
1485 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1486 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1487 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1488 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1490 // Check other parameters
1491 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1493 for (si
= x
->regs
; si
; si
= si
->next
)
1495 AuthRecord
*r
= NULL
;
1497 // Find the record we're updating. NULL reference means update the primary TXT record
1498 if (!reference
) r
= &si
->srs
.RR_TXT
;
1501 ExtraResourceRecord
*ptr
;
1502 for (ptr
= si
->srs
.Extras
; ptr
; ptr
= ptr
->next
)
1504 if ((natural_t
)ptr
->ClientID
== reference
)
1505 { r
= &ptr
->r
; break; }
1507 if (!r
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such record"; goto fail
; }
1509 err
= UpdateRecord(&si
->srs
, client
, r
, data
, data_len
, ttl
);
1510 if (err
) goto fail
; //!!!KRS this will cause failures for non-local defaults!
1513 return mStatus_NoError
;
1516 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client
, name
->c
, reference
, data_len
, errormsg
, err
);
1520 mDNSlocal mStatus
RemoveRecord(ServiceRecordSet
*srs
, ExtraResourceRecord
*extra
, mach_port_t client
)
1522 const domainname
*const name
= srs
->RR_SRV
.resrec
.name
;
1523 mStatus err
= mStatus_NoError
;
1526 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client
, srs
->RR_SRV
.resrec
.name
->c
);
1528 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, srs
, extra
, FreeExtraRR
, extra
);
1529 if (err
) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client
, name
->c
, err
);
1534 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1535 natural_t reference
)
1537 // Check client parameter
1538 (void)unusedserver
; // Unused
1539 mStatus err
= mStatus_NoError
;
1540 const char *errormsg
= "Unknown";
1541 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1542 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1543 ServiceInstance
*si
;
1545 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1546 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1548 for (si
= x
->regs
; si
; si
= si
->next
)
1550 ExtraResourceRecord
*e
;
1551 for (e
= si
->srs
.Extras
; e
; e
= e
->next
)
1553 if ((natural_t
)e
->ClientID
== reference
)
1555 err
= RemoveRecord(&si
->srs
, e
, client
);
1559 if (!e
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such reference"; goto fail
; }
1562 return mStatus_NoError
;
1565 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client
, reference
, errormsg
, err
);
1569 //*************************************************************************************************************
1570 #if COMPILER_LIKES_PRAGMA_MARK
1572 #pragma mark - Startup, shutdown, and supporting code
1575 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1577 mig_reply_error_t
*request
= msg
;
1578 mig_reply_error_t
*reply
;
1579 mach_msg_return_t mr
;
1581 (void)port
; // Unused
1582 (void)size
; // Unused
1583 (void)info
; // Unused
1585 KQueueLock(&mDNSStorage
);
1587 /* allocate a reply buffer */
1588 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
1590 /* call the MiG server routine */
1591 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
1593 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
1595 if (reply
->RetCode
== MIG_NO_REPLY
)
1598 * This return code is a little tricky -- it appears that the
1599 * demux routine found an error of some sort, but since that
1600 * error would not normally get returned either to the local
1601 * user or the remote one, we pretend it's ok.
1603 CFAllocatorDeallocate(NULL
, reply
);
1608 * destroy any out-of-line data in the request buffer but don't destroy
1609 * the reply port right (since we need that to send an error message).
1611 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1612 mach_msg_destroy(&request
->Head
);
1615 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
1617 /* no reply port, so destroy the reply */
1618 if (reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
1619 mach_msg_destroy(&reply
->Head
);
1620 CFAllocatorDeallocate(NULL
, reply
);
1627 * We don't want to block indefinitely because the client
1628 * isn't receiving messages from the reply port.
1629 * If we have a send-once right for the reply port, then
1630 * this isn't a concern because the send won't block.
1631 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1632 * To avoid falling off the kernel's fast RPC path unnecessarily,
1633 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1636 options
= MACH_SEND_MSG
;
1637 if (MACH_MSGH_BITS_REMOTE(reply
->Head
.msgh_bits
) == MACH_MSG_TYPE_MOVE_SEND_ONCE
)
1638 options
|= MACH_SEND_TIMEOUT
;
1640 mr
= mach_msg(&reply
->Head
, /* msg */
1641 options
, /* option */
1642 reply
->Head
.msgh_size
, /* send_size */
1644 MACH_PORT_NULL
, /* rcv_name */
1645 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1646 MACH_PORT_NULL
); /* notify */
1648 /* Has a message error occurred? */
1651 case MACH_SEND_INVALID_DEST
:
1652 case MACH_SEND_TIMED_OUT
:
1653 /* the reply can't be delivered, so destroy it */
1654 mach_msg_destroy(&reply
->Head
);
1658 /* Includes success case. */
1662 CFAllocatorDeallocate(NULL
, reply
);
1665 KQueueUnlock(&mDNSStorage
, "Mach client event");
1668 mDNSlocal kern_return_t
registerBootstrapService()
1670 kern_return_t status
;
1671 mach_port_t service_send_port
, service_rcv_port
;
1673 debugf("Registering Bootstrap Service");
1676 * See if our service name is already registered and if we have privilege to check in.
1678 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1679 if (status
== KERN_SUCCESS
)
1682 * If so, we must be a followup instance of an already defined server. In that case,
1683 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1684 * that in case we have to unregister later (which requires the privilege port).
1686 server_priv_port
= bootstrap_port
;
1687 restarting_via_mach_init
= TRUE
;
1689 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
1691 status
= bootstrap_create_server(bootstrap_port
, "/usr/sbin/mDNSResponder", getuid(),
1692 FALSE
/* relaunch immediately, not on demand */, &server_priv_port
);
1693 if (status
!= KERN_SUCCESS
) return status
;
1695 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
1696 if (status
!= KERN_SUCCESS
)
1698 mach_port_deallocate(mach_task_self(), server_priv_port
);
1702 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1703 if (status
!= KERN_SUCCESS
)
1705 mach_port_deallocate(mach_task_self(), server_priv_port
);
1706 mach_port_deallocate(mach_task_self(), service_send_port
);
1709 assert(service_send_port
== service_rcv_port
);
1713 * We have no intention of responding to requests on the service port. We are not otherwise
1714 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1715 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1716 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1718 mach_port_destroy(mach_task_self(), service_rcv_port
);
1722 mDNSlocal kern_return_t
destroyBootstrapService()
1724 debugf("Destroying Bootstrap Service");
1725 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
1728 mDNSlocal
void ExitCallback(int sig
)
1730 (void)sig
; // Unused
1731 LogMsg("%s stopping", mDNSResponderVersionString
);
1733 debugf("ExitCallback");
1734 if (!mDNS_DebugMode
&& !started_via_launchdaemon
)
1735 destroyBootstrapService();
1737 debugf("ExitCallback: Aborting MIG clients");
1738 while (DNSServiceDomainEnumerationList
)
1739 AbortClient(DNSServiceDomainEnumerationList
->ClientMachPort
, DNSServiceDomainEnumerationList
);
1740 while (DNSServiceBrowserList
)
1741 AbortClient(DNSServiceBrowserList
->ClientMachPort
, DNSServiceBrowserList
);
1742 while (DNSServiceResolverList
)
1743 AbortClient(DNSServiceResolverList
->ClientMachPort
, DNSServiceResolverList
);
1744 while (DNSServiceRegistrationList
)
1745 AbortClient(DNSServiceRegistrationList
->ClientMachPort
, DNSServiceRegistrationList
);
1747 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1749 debugf("ExitCallback: mDNS_StartExit");
1750 mDNS_StartExit(&mDNSStorage
);
1753 #ifndef __LIB_DISPATCH__
1755 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1756 mDNSlocal
void HandleSIG(int sig
)
1758 // WARNING: can't call syslog or fprintf from signal handler
1759 mach_msg_header_t header
;
1760 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1761 header
.msgh_remote_port
= signal_port
;
1762 header
.msgh_local_port
= MACH_PORT_NULL
;
1763 header
.msgh_size
= sizeof(header
);
1764 header
.msgh_id
= sig
;
1765 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1766 if (sig
== SIGTERM
|| sig
== SIGINT
) exit(-1);
1769 mDNSlocal
void CatchABRT(int sig
)
1771 // WARNING: can't call syslog or fprintf from signal handler
1772 // We want a CrashReporter stack trace so we can find out what library called abort()
1773 // So that we will crash, unblock all signals (that abort() may have blocked)
1776 sigprocmask(SIG_UNBLOCK
, &mask
, NULL
);
1778 while(1) *(long*)0 = 0;
1781 #endif __LIB_DISPATCH__
1783 mDNSlocal
void INFOCallback(void)
1785 mDNSs32 utc
= mDNSPlatformUTC();
1786 DNSServiceDomainEnumeration
*e
;
1787 DNSServiceBrowser
*b
;
1788 DNSServiceResolver
*l
;
1789 DNSServiceRegistration
*r
;
1790 NetworkInterfaceInfoOSX
*i
;
1793 LogMsg("---- BEGIN STATE LOG ---- %s", mDNSResponderVersionString
);
1795 udsserver_info(&mDNSStorage
);
1797 LogMsgNoIdent("--------- Mach Clients ---------");
1798 if (!DNSServiceDomainEnumerationList
&& !DNSServiceBrowserList
&& !DNSServiceResolverList
&& !DNSServiceRegistrationList
)
1799 LogMsgNoIdent("<None>");
1802 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
1803 LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e
->ClientMachPort
, e
->dom
.qname
.c
);
1805 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
1807 DNSServiceBrowserQuestion
*qptr
;
1808 for (qptr
= b
->qlist
; qptr
; qptr
= qptr
->next
)
1809 LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b
->ClientMachPort
, qptr
->q
.qname
.c
);
1812 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1813 LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l
->ClientMachPort
, l
->i
.name
.c
);
1815 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1817 ServiceInstance
*si
;
1818 for (si
= r
->regs
; si
; si
= si
->next
)
1819 LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si
->ClientMachPort
, si
->srs
.RR_SRV
.resrec
.name
->c
, mDNSVal16(si
->srs
.RR_SRV
.resrec
.rdata
->u
.srv
.port
));
1823 LogMsgNoIdent("----- KQSocketEventSources -----");
1824 if (!gEventSources
) LogMsgNoIdent("<None>");
1827 KQSocketEventSource
*k
;
1828 for (k
= gEventSources
; k
; k
=k
->next
)
1830 LogMsgNoIdent("%3d %s", k
->fd
, k
->kqs
.KQtask
);
1831 usleep((mDNSStorage
.KnownBugs
& mDNS_KnownBug_LossySyslog
) ? 3333 : 1000);
1835 LogMsgNoIdent("------ Network Interfaces ------");
1836 if (!mDNSStorage
.p
->InterfaceList
) LogMsgNoIdent("<None>");
1839 for (i
= mDNSStorage
.p
->InterfaceList
; i
; i
= i
->next
)
1841 // Allow six characters for interface name, for names like "vmnet8"
1843 LogMsgNoIdent("%p (%p), Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
1844 i
->ifinfo
.InterfaceID
, i
, i
->Registered
,
1845 i
->sa_family
== AF_INET
? "v4" : i
->sa_family
== AF_INET6
? "v6" : "??", i
->ifinfo
.ifname
, i
->scope_id
, &i
->ifinfo
.MAC
, &i
->BSSID
,
1846 &i
->ifinfo
.ip
, utc
- i
->LastSeen
);
1849 const CacheRecord
*sps
[3];
1850 FindSPSInCache(&mDNSStorage
, &i
->ifinfo
.NetWakeBrowse
, sps
);
1851 LogMsgNoIdent("%p (%p), Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
1852 i
->ifinfo
.InterfaceID
, i
, i
->Registered
,
1853 i
->sa_family
== AF_INET
? "v4" : i
->sa_family
== AF_INET6
? "v6" : "??", i
->ifinfo
.ifname
, i
->scope_id
, &i
->ifinfo
.MAC
, &i
->BSSID
,
1854 i
->ifinfo
.InterfaceActive
? "Active" : " ",
1855 i
->ifinfo
.IPv4Available
? "v4" : " ",
1856 i
->ifinfo
.IPv4Available
? (mDNSv4Addr
*)&i
->ifa_v4addr
: &zerov4Addr
,
1857 i
->ifinfo
.IPv6Available
? "v6" : " ",
1858 i
->ifinfo
.Advertise
? "⊙" : " ",
1859 i
->ifinfo
.McastTxRx
? "⇆" : " ",
1860 !(i
->ifinfo
.InterfaceActive
&& i
->ifinfo
.NetWake
) ? " " : !sps
[0] ? "☼" : "☀",
1863 if (sps
[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[0]->resrec
.rdata
->u
.name
.c
), sps
[0]->resrec
.rdata
->u
.name
.c
);
1864 if (sps
[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[1]->resrec
.rdata
->u
.name
.c
), sps
[1]->resrec
.rdata
->u
.name
.c
);
1865 if (sps
[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps
[2]->resrec
.rdata
->u
.name
.c
), sps
[2]->resrec
.rdata
->u
.name
.c
);
1870 LogMsgNoIdent("--------- DNS Servers ----------");
1871 if (!mDNSStorage
.DNSServers
) LogMsgNoIdent("<None>");
1874 for (s
= mDNSStorage
.DNSServers
; s
; s
= s
->next
)
1876 NetworkInterfaceInfoOSX
*ifx
= IfindexToInterfaceInfoOSX(&mDNSStorage
, s
->interface
);
1877 LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %s",
1878 s
->domain
.c
, ifx
? ifx
->ifinfo
.ifname
: "", ifx
? " " : "", &s
->addr
, mDNSVal16(s
->port
),
1879 s
->penaltyTime
? s
->penaltyTime
- mDNS_TimeNow(&mDNSStorage
) : 0, s
->scoped
? "Scoped" : "",
1880 s
->teststate
== DNSServer_Untested
? "(Untested)" :
1881 s
->teststate
== DNSServer_Passed
? "" :
1882 s
->teststate
== DNSServer_Failed
? "(Failed)" :
1883 s
->teststate
== DNSServer_Disabled
? "(Disabled)" : "(Unknown state)");
1887 mDNSs32 now
= mDNS_TimeNow(&mDNSStorage
);
1888 LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32
)now
, now
);
1890 LogMsg("---- END STATE LOG ---- %s", mDNSResponderVersionString
);
1893 #ifndef __LIB_DISPATCH__
1895 mDNSlocal
void SignalCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1897 (void)port
; // Unused
1898 (void)size
; // Unused
1899 (void)info
; // Unused
1900 mach_msg_header_t
*msg_header
= (mach_msg_header_t
*)msg
;
1901 mDNS
*const m
= &mDNSStorage
;
1903 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
1905 switch(msg_header
->msgh_id
)
1911 LogMsg("SIGHUP: Purge cache");
1913 FORALL_CACHERECORDS(slot
, cg
, rr
) mDNS_PurgeCacheResourceRecord(m
, rr
);
1914 // Restart unicast and multicast queries
1915 mDNSCoreRestartQueries(m
);
1919 case SIGTERM
: ExitCallback(msg_header
->msgh_id
); break;
1920 case SIGINFO
: INFOCallback(); break;
1921 case SIGUSR1
: mDNS_LoggingEnabled
= mDNS_LoggingEnabled
? 0 : 1;
1922 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled
? "Enabled" : "Disabled");
1923 WatchDogReportingThreshold
= mDNS_LoggingEnabled
? 50 : 250;
1925 case SIGUSR2
: mDNS_PacketLoggingEnabled
= mDNS_PacketLoggingEnabled
? 0 : 1;
1926 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled
? "Enabled" : "Disabled");
1928 default: LogMsg("SignalCallback: Unknown signal %d", msg_header
->msgh_id
); break;
1930 KQueueUnlock(m
, "Unix Signal");
1933 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
1934 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
1935 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
1938 CFMachPortRef s_port
;
1939 CFRunLoopSourceRef s_rls
;
1940 CFRunLoopSourceRef d_rls
;
1942 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
1943 if (m_port
!= MACH_PORT_NULL
)
1944 s_port
= CFMachPortCreateWithPort(NULL
, m_port
, DNSserverCallback
, NULL
, NULL
);
1947 s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
1948 m_port
= CFMachPortGetPort(s_port
);
1949 char *MachServerName
= OSXVers
< OSXVers_10_3_Panther
? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
1950 kern_return_t status
= bootstrap_register(bootstrap_port
, MachServerName
, m_port
);
1955 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
1957 LogMsg("bootstrap_register() failed: %d %X %s", status
, status
, mach_error_string(status
));
1962 CFMachPortRef d_port
= CFMachPortCreate(NULL
, ClientDeathCallback
, NULL
, NULL
);
1964 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
1965 rrcachestorage
, RR_CACHE_SIZE
,
1967 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
1969 if (err
) { LogMsg("Daemon start: mDNS_Init failed %d", err
); return(err
); }
1971 client_death_port
= CFMachPortGetPort(d_port
);
1973 s_rls
= CFMachPortCreateRunLoopSource(NULL
, s_port
, 0);
1974 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, s_rls
, kCFRunLoopDefaultMode
);
1977 d_rls
= CFMachPortCreateRunLoopSource(NULL
, d_port
, 0);
1978 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, d_rls
, kCFRunLoopDefaultMode
);
1981 CFMachPortRef i_port
= CFMachPortCreate(NULL
, SignalCallback
, NULL
, NULL
);
1982 CFRunLoopSourceRef i_rls
= CFMachPortCreateRunLoopSource(NULL
, i_port
, 0);
1983 signal_port
= CFMachPortGetPort(i_port
);
1984 CFRunLoopAddSource(PlatformStorage
.CFRunLoop
, i_rls
, kCFRunLoopDefaultMode
);
1987 if (mDNS_DebugMode
) printf("Service registered with Mach Port %d\n", m_port
);
1991 #else __LIB_DISPATCH__
1993 // SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
1994 // The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
1995 // The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
1996 // of code from above. Alternatively we could remove the duplicated source code by having
1997 // single routines, with the few differing parts bracketed with "#ifndef __LIB_DISPATCH__"
1999 mDNSlocal
void SignalDispatch(dispatch_source_t source
)
2001 int sig
= (int)dispatch_source_get_handle(source
);
2002 mDNS
*const m
= &mDNSStorage
;
2010 LogMsg("SIGHUP: Purge cache");
2012 FORALL_CACHERECORDS(slot
, cg
, rr
) mDNS_PurgeCacheResourceRecord(m
, rr
);
2013 // Restart unicast and multicast queries
2014 mDNSCoreRestartQueries(m
);
2018 case SIGTERM
: ExitCallback(sig
); break;
2019 case SIGINFO
: INFOCallback(); break;
2020 case SIGUSR1
: mDNS_LoggingEnabled
= mDNS_LoggingEnabled
? 0 : 1;
2021 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled
? "Enabled" : "Disabled");
2022 WatchDogReportingThreshold
= mDNS_LoggingEnabled
? 50 : 250;
2024 case SIGUSR2
: mDNS_PacketLoggingEnabled
= mDNS_PacketLoggingEnabled
? 0 : 1;
2025 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled
? "Enabled" : "Disabled");
2027 default: LogMsg("SignalCallback: Unknown signal %d", sig
); break;
2029 KQueueUnlock(m
, "Unix Signal");
2032 mDNSlocal
void mDNSSetupSignal(dispatch_queue_t queue
, int sig
)
2034 signal(sig
, SIG_IGN
);
2035 dispatch_source_t source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, sig
, 0, queue
);
2039 dispatch_source_set_event_handler(source
, ^{SignalDispatch(source
);});
2040 // Start processing signals
2041 dispatch_resume(source
);
2045 LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig
);
2049 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2050 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2051 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
2054 CFMachPortRef s_port
;
2055 dispatch_source_t mach_source
;
2056 dispatch_queue_t queue
= dispatch_get_main_queue();
2058 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
2059 if (m_port
!= MACH_PORT_NULL
)
2060 s_port
= CFMachPortCreateWithPort(NULL
, m_port
, DNSserverCallback
, NULL
, NULL
);
2063 s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
2064 m_port
= CFMachPortGetPort(s_port
);
2065 char *MachServerName
= OSXVers
< OSXVers_10_3_Panther
? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
2066 kern_return_t status
= bootstrap_register(bootstrap_port
, MachServerName
, m_port
);
2071 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
2073 LogMsg("bootstrap_register() failed: %d %X %s", status
, status
, mach_error_string(status
));
2078 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
2079 rrcachestorage
, RR_CACHE_SIZE
,
2081 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
2083 if (err
) { LogMsg("Daemon start: mDNS_Init failed %d", err
); return(err
); }
2085 mach_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, m_port
, 0, queue
);
2086 if (mach_source
== mDNSNULL
){LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;}
2087 dispatch_source_set_event_handler(mach_source
, ^{
2088 dispatch_mig_server(mach_source
, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem
),
2089 DNSServiceDiscoveryRequest_server
);
2091 dispatch_resume(mach_source
);
2093 mDNSSetupSignal(queue
, SIGHUP
);
2094 mDNSSetupSignal(queue
, SIGINT
);
2095 mDNSSetupSignal(queue
, SIGTERM
);
2096 mDNSSetupSignal(queue
, SIGINFO
);
2097 mDNSSetupSignal(queue
, SIGUSR1
);
2098 mDNSSetupSignal(queue
, SIGUSR2
);
2099 mDNSSetupSignal(queue
, SIGHUP
);
2101 // Create a custom handler for doing the housekeeping work. This is either triggered
2102 // by the timer or an event source
2103 PlatformStorage
.custom
= dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD
, 0, 0, queue
);
2104 if (PlatformStorage
.custom
== mDNSNULL
){LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;}
2105 dispatch_source_set_event_handler(PlatformStorage
.custom
, ^{PrepareForIdle(&mDNSStorage
);});
2106 dispatch_resume(PlatformStorage
.custom
);
2108 // Create a timer source to trigger housekeeping work. The houskeeping work itself
2109 // is done in the custom handler that we set below.
2111 PlatformStorage
.timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, queue
);
2112 if (PlatformStorage
.timer
== mDNSNULL
){LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;}
2114 // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we
2115 // always reset the time to the new time computed. In effect, we ignore the interval
2116 dispatch_source_set_timer(PlatformStorage
.timer
, DISPATCH_TIME_NOW
, 1000ull * 1000000000, 0);
2117 dispatch_source_set_event_handler(PlatformStorage
.timer
, ^{
2118 dispatch_source_merge_data(PlatformStorage
.custom
, 1);
2120 dispatch_resume(PlatformStorage
.timer
);
2122 LogMsg("DaemonIntialize done successfully");
2124 if (mDNS_DebugMode
) printf("Service registered with Mach Port %d\n", m_port
);
2128 #endif __LIB_DISPATCH__
2130 mDNSlocal mDNSs32
mDNSDaemonIdle(mDNS
*const m
)
2132 mDNSs32 now
= mDNS_TimeNow(m
);
2134 // 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
2136 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2137 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2138 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2139 // we then systematically lose our own looped-back packets.
2140 if (m
->p
->NetworkChanged
&& now
- m
->p
->NetworkChanged
>= 0) mDNSMacOSXNetworkChanged(m
);
2142 if (m
->p
->RequestReSleep
&& now
- m
->p
->RequestReSleep
>= 0) { m
->p
->RequestReSleep
= 0; mDNSPowerRequest(0, 0); }
2144 // KeyChain frequently fails to notify clients of change events. To work around this
2145 // we set a timer and periodically poll to detect if any changes have occurred.
2146 // Without this Back To My Mac just does't work for a large number of users.
2147 // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
2148 if (m
->p
->KeyChainBugTimer
&& now
- m
->p
->KeyChainBugTimer
>= 0)
2150 m
->p
->KeyChainBugInterval
*= 2;
2151 m
->p
->KeyChainBugTimer
= NonZeroTime(now
+ m
->p
->KeyChainBugInterval
);
2152 if (m
->p
->KeyChainBugInterval
> 2 * mDNSPlatformOneSecond
) m
->p
->KeyChainBugTimer
= 0;
2154 SetDomainSecrets(m
);
2158 // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
2159 mDNSs32 nextevent
= mDNS_Execute(m
);
2161 if (m
->p
->NetworkChanged
)
2162 if (nextevent
- m
->p
->NetworkChanged
> 0)
2163 nextevent
= m
->p
->NetworkChanged
;
2165 if (m
->p
->KeyChainBugTimer
)
2166 if (nextevent
- m
->p
->KeyChainBugTimer
> 0)
2167 nextevent
= m
->p
->KeyChainBugTimer
;
2169 if (m
->p
->RequestReSleep
)
2170 if (nextevent
- m
->p
->RequestReSleep
> 0)
2171 nextevent
= m
->p
->RequestReSleep
;
2173 // 3. Deliver any waiting browse messages to clients
2174 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
2178 // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2179 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2180 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2181 DNSServiceBrowser
*x
= b
;
2183 if (x
->results
) // Try to deliver the list of results
2187 DNSServiceBrowserResult
*const r
= x
->results
;
2189 domainname type
, domain
;
2190 DeconstructServiceName(&r
->result
, &name
, &type
, &domain
); // Don't need to check result; already validated in FoundInstance()
2191 char cname
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2192 char ctype
[MAX_ESCAPED_DOMAIN_NAME
];
2193 char cdom
[MAX_ESCAPED_DOMAIN_NAME
];
2194 ConvertDomainLabelToCString_unescaped(&name
, cname
);
2195 ConvertDomainNameToCString(&type
, ctype
);
2196 ConvertDomainNameToCString(&domain
, cdom
);
2197 DNSServiceDiscoveryReplyFlags flags
= (r
->next
) ? DNSServiceDiscoverReplyFlagsMoreComing
: 0;
2198 kern_return_t status
= DNSServiceBrowserReply_rpc(x
->ClientMachPort
, r
->resultType
, cname
, ctype
, cdom
, flags
, 1);
2199 // If we failed to send the mach message, try again in one second
2200 if (status
== MACH_SEND_TIMED_OUT
)
2202 if (nextevent
- now
> mDNSPlatformOneSecond
)
2203 nextevent
= now
+ mDNSPlatformOneSecond
;
2208 x
->lastsuccess
= now
;
2209 x
->results
= x
->results
->next
;
2210 freeL("DNSServiceBrowserResult", r
);
2213 // If this client hasn't read a single message in the last 60 seconds, abort it
2214 if (now
- x
->lastsuccess
>= 60 * mDNSPlatformOneSecond
)
2215 AbortBlockedClient(x
->ClientMachPort
, "browse", x
);
2219 DNSServiceResolver
*l
;
2220 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
2221 if (l
->ReportTime
&& now
- l
->ReportTime
>= 0)
2224 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2225 "This places considerable burden on the network.", l
->i
.name
.c
);
2228 if (m
->p
->NotifyUser
)
2230 if (m
->p
->NotifyUser
- now
< 0)
2232 if (!SameDomainLabelCS(m
->p
->usernicelabel
.c
, m
->nicelabel
.c
))
2234 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m
->p
->usernicelabel
.c
, m
->nicelabel
.c
);
2235 mDNSPreferencesSetNames(m
, kmDNSComputerName
, &m
->p
->usernicelabel
, &m
->nicelabel
);
2236 m
->p
->usernicelabel
= m
->nicelabel
;
2238 if (!SameDomainLabelCS(m
->p
->userhostlabel
.c
, m
->hostlabel
.c
))
2240 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m
->p
->userhostlabel
.c
, m
->hostlabel
.c
);
2241 mDNSPreferencesSetNames(m
, kmDNSLocalHostName
, &m
->p
->userhostlabel
, &m
->hostlabel
);
2242 m
->p
->HostNameConflict
= 0; // Clear our indicator, now name change has been successful
2243 m
->p
->userhostlabel
= m
->hostlabel
;
2245 m
->p
->NotifyUser
= 0;
2248 if (nextevent
- m
->p
->NotifyUser
> 0)
2249 nextevent
= m
->p
->NotifyUser
;
2255 // Right now we consider *ALL* of our DHCP leases
2256 // It might make sense to be a bit more selective and only consider the leases on interfaces
2257 // (a) that are capable and enabled for wake-on-LAN, and
2258 // (b) where we have found (and successfully registered with) a Sleep Proxy
2259 // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
2260 mDNSlocal mDNSu32
DHCPWakeTime(void)
2262 mDNSu32 e
= 24 * 3600; // Maximum maintenance wake interval is 24 hours
2263 const CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
2264 if (!now
) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
2267 const SCPreferencesRef prefs
= SCPreferencesCreate(NULL
, CFSTR("mDNSResponder:DHCPWakeTime"), NULL
);
2268 if (!prefs
) LogMsg("DHCPWakeTime: SCPreferencesCreate failed");
2271 const SCNetworkSetRef currentset
= SCNetworkSetCopyCurrent(prefs
);
2272 if (!currentset
) LogMsg("DHCPWakeTime: SCNetworkSetCopyCurrent failed");
2275 const CFArrayRef services
= SCNetworkSetCopyServices(currentset
);
2276 if (!services
) LogMsg("DHCPWakeTime: SCNetworkSetCopyServices failed");
2280 for (i
= 0; i
< CFArrayGetCount(services
); i
++)
2282 const SCNetworkServiceRef service
= CFArrayGetValueAtIndex(services
, i
);
2283 if (!service
) LogMsg("DHCPWakeTime: CFArrayGetValueAtIndex %d failed", i
);
2286 const CFStringRef serviceid
= SCNetworkServiceGetServiceID(service
);
2287 if (!serviceid
) LogMsg("DHCPWakeTime: SCNetworkServiceGetServiceID %d failed", i
);
2290 // Note: It's normal for this call to return NULL, for interfaces not using DHCP
2291 const CFDictionaryRef dhcp
= SCDynamicStoreCopyDHCPInfo(NULL
, serviceid
);
2294 const CFDateRef start
= DHCPInfoGetLeaseStartTime(dhcp
);
2295 const CFDataRef lease
= DHCPInfoGetOptionData(dhcp
, 51); // Option 51 = IP Address Lease Time
2296 if (!start
|| !lease
|| CFDataGetLength(lease
) < 4)
2297 LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
2298 "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
2299 i
, start
, lease
, lease
? CFDataGetLength(lease
) : 0);
2302 const UInt8
*d
= CFDataGetBytePtr(lease
);
2303 if (!d
) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", i
);
2306 const mDNSu32 elapsed
= now
- CFDateGetAbsoluteTime(start
);
2307 const mDNSu32 lifetime
= (mDNSs32
) ((mDNSs32
)d
[0] << 24 | (mDNSs32
)d
[1] << 16 | (mDNSs32
)d
[2] << 8 | d
[3]);
2308 const mDNSu32 remaining
= lifetime
- elapsed
;
2309 const mDNSu32 wake
= remaining
> 60 ? remaining
- remaining
/10 : 54; // Wake at 90% of the lease time
2310 LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed
, lifetime
, remaining
, wake
);
2311 if (e
> wake
) e
= wake
;
2319 CFRelease(services
);
2321 CFRelease(currentset
);
2329 // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
2330 // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
2331 // If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
2332 // for a few seconds and then waking again is silly and annoying.
2333 // If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
2334 // Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
2335 // allowing us an adequate safety margin to renew our lease before we lose it.
2337 mDNSlocal mDNSBool
AllowSleepNow(mDNS
*const m
, mDNSs32 now
)
2339 mDNSBool ready
= mDNSCoreReadyForSleep(m
, now
);
2340 if (m
->SleepState
&& !ready
&& now
- m
->SleepLimit
< 0) return(mDNSfalse
);
2342 m
->p
->WakeAtUTC
= 0;
2343 int result
= kIOReturnSuccess
;
2344 CFDictionaryRef opts
= NULL
;
2346 // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
2347 // do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
2349 LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m
->SleepLimit
- now
);
2352 if (!m
->SystemWakeOnLANEnabled
|| !mDNSCoreHaveAdvertisedMulticastServices(m
))
2353 LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
2354 m
->SystemWakeOnLANEnabled
? "is" : "not",
2355 mDNSCoreHaveAdvertisedMulticastServices(m
) ? "have" : "no");
2358 mDNSs32 dhcp
= DHCPWakeTime();
2359 LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp
);
2360 mDNSs32 interval
= mDNSCoreIntervalToNextWake(m
, now
) / mDNSPlatformOneSecond
;
2361 if (interval
> dhcp
) interval
= dhcp
;
2363 // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
2364 // transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
2365 // a single SPS failure could result in a remote machine falling permanently asleep, requiring
2366 // someone to go to the machine in person to wake it up again, which would be unacceptable.
2367 if (!ready
&& interval
> 3600) interval
= 3600;
2369 //interval = 48; // For testing
2371 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2372 if (m
->p
->IOPMConnection
) // If lightweight-wake capability is available, use that
2374 const CFDateRef WakeDate
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent() + interval
);
2375 if (!WakeDate
) LogMsg("ScheduleNextWake: CFDateCreate failed");
2378 const mDNSs32 reqs
= kIOPMSystemPowerStateCapabilityNetwork
;
2379 const CFNumberRef Requirements
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &reqs
);
2380 if (!Requirements
) LogMsg("ScheduleNextWake: CFNumberCreate failed");
2383 const void *OptionKeys
[2] = { CFSTR("WakeDate"), CFSTR("Requirements") };
2384 const void *OptionVals
[2] = { WakeDate
, Requirements
};
2385 opts
= CFDictionaryCreate(NULL
, (void*)OptionKeys
, (void*)OptionVals
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2386 if (!opts
) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
2387 CFRelease(Requirements
);
2389 CFRelease(WakeDate
);
2391 LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval
);
2393 else // else schedule the wakeup using the old API instead to
2396 // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
2397 // so we should put it back to sleep. To avoid frustrating the user, we always request at least
2398 // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
2399 // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
2400 if (interval
< 60) interval
= 60;
2402 result
= mDNSPowerRequest(1, interval
);
2404 if (result
== kIOReturnNotReady
)
2406 LogMsg("Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval
);
2407 // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
2408 // requested wake time is "too soon", but there's no API to find out what constitutes
2409 // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
2410 // we just have to iterate with successively longer intervals until it doesn't fail.
2411 // Additionally, if our power request is deemed "too soon" for the machine to get to
2412 // sleep and wake back up again, we attempt to cancel the sleep request, since the
2413 // implication is that the system won't manage to be awake again at the time we need it.
2416 interval
+= (interval
< 20) ? 1 : ((interval
+3) / 4);
2417 result
= mDNSPowerRequest(1, interval
);
2419 while (result
== kIOReturnNotReady
);
2422 if (result
) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval
, result
, result
);
2423 else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval
);
2424 m
->p
->WakeAtUTC
= mDNSPlatformUTC() + interval
;
2428 // Clear our interface list to empty state, ready to go to sleep
2429 // As a side effect of doing this, we'll also cancel any outstanding SPS Resolve calls that didn't complete
2430 m
->SleepState
= SleepState_Sleeping
;
2431 mDNSMacOSXNetworkChanged(m
);
2434 LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
2435 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2436 (m
->p
->IOPMConnection
) ? "IOPMConnectionAcknowledgeEventWithOptions" :
2438 (result
== kIOReturnSuccess
) ? "IOAllowPowerChange" : "IOCancelPowerChange",
2439 m
->p
->SleepCookie
, ready
? "ready for sleep" : "giving up", now
, m
->SleepLimit
- now
);
2441 m
->SleepLimit
= 0; // Don't clear m->SleepLimit until after we've logged it above
2443 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
2444 if (m
->p
->IOPMConnection
) IOPMConnectionAcknowledgeEventWithOptions(m
->p
->IOPMConnection
, m
->p
->SleepCookie
, opts
);
2447 if (result
== kIOReturnSuccess
) IOAllowPowerChange (m
->p
->PowerConnection
, m
->p
->SleepCookie
);
2448 else IOCancelPowerChange(m
->p
->PowerConnection
, m
->p
->SleepCookie
);
2450 if (opts
) CFRelease(opts
);
2454 #ifdef __LIB_DISPATCH__
2456 mDNSexport
void TriggerEventCompletion()
2458 debugf("TriggerEventCompletion: Merge data");
2459 dispatch_source_merge_data(PlatformStorage
.custom
, 1);
2462 mDNSlocal
void PrepareForIdle(void *m_param
)
2465 int64_t time_offset
;
2466 dispatch_time_t dtime
;
2468 const int multiplier
= 1000000000 / mDNSPlatformOneSecond
;
2470 // This is the main work loop:
2471 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2472 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2473 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2475 debugf("PrepareForIdle: called");
2476 // Run mDNS_Execute to find out the time we next need to wake up
2477 mDNSs32 start
= mDNSPlatformRawTime();
2478 mDNSs32 nextTimerEvent
= udsserver_idle(mDNSDaemonIdle(m
));
2479 mDNSs32 end
= mDNSPlatformRawTime();
2480 if (end
- start
>= WatchDogReportingThreshold
)
2481 LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end
- start
);
2483 mDNSs32 now
= mDNS_TimeNow(m
);
2485 if (m
->ShutdownTime
)
2487 if (mDNSStorage
.ResourceRecords
)
2489 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m
, mDNSStorage
.ResourceRecords
));
2490 if (mDNS_LoggingEnabled
) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2492 if (mDNS_ExitNow(m
, now
))
2494 if (!mDNSStorage
.ResourceRecords
)
2495 safe_vproc_transaction_end();
2496 LogInfo("IdleLoop: mDNS_FinalExit");
2497 mDNS_FinalExit(&mDNSStorage
);
2498 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2501 if (nextTimerEvent
- m
->ShutdownTime
>= 0)
2502 nextTimerEvent
= m
->ShutdownTime
;
2506 if (!AllowSleepNow(m
, now
))
2507 if (nextTimerEvent
- m
->SleepLimit
>= 0)
2508 nextTimerEvent
= m
->SleepLimit
;
2510 // Convert absolute wakeup time to a relative time from now
2511 mDNSs32 ticks
= nextTimerEvent
- now
;
2512 if (ticks
< 1) ticks
= 1;
2514 static mDNSs32 RepeatedBusy
= 0; // Debugging sanity check, to guard against CPU spins
2520 if (++RepeatedBusy
>= mDNSPlatformOneSecond
) { ShowTaskSchedulingError(&mDNSStorage
); RepeatedBusy
= 0; }
2523 time_offset
= ((mDNSu32
)ticks
/ mDNSPlatformOneSecond
) * 1000000000 + (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2524 dtime
= dispatch_time(DISPATCH_TIME_NOW
, time_offset
);
2525 dispatch_source_set_timer(PlatformStorage
.timer
, dtime
, 1000ull*1000000000, 0);
2526 debugf("PrepareForIdle: scheduling timer with ticks %d", ticks
);
2530 #else __LIB_DISPATCH__
2532 mDNSlocal
void KQWokenFlushBytes(int fd
, __unused
short filter
, __unused
void *context
)
2534 // Read all of the bytes so we won't wake again.
2536 while (recv(fd
, buffer
, sizeof(buffer
), MSG_DONTWAIT
) > 0) continue;
2539 mDNSlocal
void * KQueueLoop(void *m_param
)
2544 #if USE_SELECT_WITH_KQUEUEFD
2547 const int multiplier
= 1000000 / mDNSPlatformOneSecond
;
2549 const int multiplier
= 1000000000 / mDNSPlatformOneSecond
;
2552 pthread_mutex_lock(&PlatformStorage
.BigMutex
);
2553 LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32
)mDNSStorage
.timenow_last
, mDNSStorage
.timenow_last
);
2555 // This is the main work loop:
2556 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2557 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2558 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2559 // (4) On wakeup we first process *all* events
2560 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2563 #define kEventsToReadAtOnce 1
2564 struct kevent new_events
[kEventsToReadAtOnce
];
2566 // Run mDNS_Execute to find out the time we next need to wake up
2567 mDNSs32 start
= mDNSPlatformRawTime();
2568 mDNSs32 nextTimerEvent
= udsserver_idle(mDNSDaemonIdle(m
));
2569 mDNSs32 end
= mDNSPlatformRawTime();
2570 if (end
- start
>= WatchDogReportingThreshold
)
2571 LogInfo("WARNING: Idle task took %dms to complete", end
- start
);
2573 mDNSs32 now
= mDNS_TimeNow(m
);
2575 if (m
->ShutdownTime
)
2577 if (mDNSStorage
.ResourceRecords
)
2580 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
2582 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m
, rr
));
2583 if (mDNS_LoggingEnabled
) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2586 if (mDNS_ExitNow(m
, now
))
2588 if (!mDNSStorage
.ResourceRecords
)
2589 safe_vproc_transaction_end();
2590 LogInfo("mDNS_FinalExit");
2591 mDNS_FinalExit(&mDNSStorage
);
2592 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2595 if (nextTimerEvent
- m
->ShutdownTime
>= 0)
2596 nextTimerEvent
= m
->ShutdownTime
;
2600 if (!AllowSleepNow(m
, now
))
2601 if (nextTimerEvent
- m
->SleepLimit
>= 0)
2602 nextTimerEvent
= m
->SleepLimit
;
2604 // Convert absolute wakeup time to a relative time from now
2605 mDNSs32 ticks
= nextTimerEvent
- now
;
2606 if (ticks
< 1) ticks
= 1;
2608 static mDNSs32 RepeatedBusy
= 0; // Debugging sanity check, to guard against CPU spins
2614 if (++RepeatedBusy
>= mDNSPlatformOneSecond
) { ShowTaskSchedulingError(&mDNSStorage
); RepeatedBusy
= 0; }
2617 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents
, ticks
);
2620 // Release the lock, and sleep until:
2621 // 1. Something interesting happens like a packet arriving, or
2622 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
2623 // 3. The timeout expires
2624 pthread_mutex_unlock(&PlatformStorage
.BigMutex
);
2626 #if USE_SELECT_WITH_KQUEUEFD
2627 struct timeval timeout
;
2628 timeout
.tv_sec
= ticks
/ mDNSPlatformOneSecond
;
2629 timeout
.tv_usec
= (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2630 FD_SET(KQueueFD
, &readfds
);
2631 if (select(KQueueFD
+1, &readfds
, NULL
, NULL
, &timeout
) < 0)
2632 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD
, errno
, strerror(errno
)); sleep(1); }
2634 struct timespec timeout
;
2635 timeout
.tv_sec
= ticks
/ mDNSPlatformOneSecond
;
2636 timeout
.tv_nsec
= (ticks
% mDNSPlatformOneSecond
) * multiplier
;
2637 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
2638 // and have it work similarly to the way it does with nevents non-zero --
2639 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
2640 // In fact, what happens if you do this is that it just returns immediately. So, we have
2641 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
2642 if (kevent(KQueueFD
, NULL
, 0, new_events
, 1, &timeout
) < 0)
2643 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD
, errno
, strerror(errno
)); sleep(1); }
2646 pthread_mutex_lock(&PlatformStorage
.BigMutex
);
2647 // We have to ignore the event we may have been told about above, because that
2648 // was done without holding the lock, and between the time we woke up and the
2649 // time we reclaimed the lock the other thread could have done something that
2650 // makes the event no longer valid. Now we have the lock, we call kevent again
2651 // and this time we can safely process the events it tells us about.
2653 static const struct timespec zero_timeout
= { 0, 0 };
2655 while ((events_found
= kevent(KQueueFD
, NULL
, 0, new_events
, kEventsToReadAtOnce
, &zero_timeout
)) != 0)
2657 if (events_found
> kEventsToReadAtOnce
|| (events_found
< 0 && errno
!= EINTR
))
2659 // Not sure what to do here, our kqueue has failed us - this isn't ideal
2660 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno
, strerror(errno
));
2664 numevents
+= events_found
;
2667 for (i
= 0; i
< events_found
; i
++)
2669 const KQueueEntry
*const kqentry
= new_events
[i
].udata
;
2670 mDNSs32 stime
= mDNSPlatformRawTime();
2671 const char *const KQtask
= kqentry
->KQtask
; // Grab a copy in case KQcallback deletes the task
2672 kqentry
->KQcallback(new_events
[i
].ident
, new_events
[i
].filter
, kqentry
->KQcontext
);
2673 mDNSs32 etime
= mDNSPlatformRawTime();
2674 if (etime
- stime
>= WatchDogReportingThreshold
)
2675 LogInfo("WARNING: %s took %dms to complete", KQtask
, etime
- stime
);
2683 #endif __LIB_DISPATCH__
2685 mDNSlocal
void LaunchdCheckin(void)
2687 launch_data_t msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
2688 launch_data_t resp
= launch_msg(msg
);
2689 launch_data_free(msg
);
2690 if (!resp
) { LogMsg("launch_msg returned NULL"); return; }
2692 if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
)
2694 int err
= launch_data_get_errno(resp
);
2695 // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
2696 if (err
!= EACCES
) LogMsg("launch_msg returned %d", err
);
2697 else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err
);
2701 launch_data_t skts
= launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_SOCKETS
);
2702 if (!skts
) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
2705 launch_data_t skt
= launch_data_dict_lookup(skts
, "Listeners");
2706 if (!skt
) LogMsg("launch_data_dict_lookup Listeners returned NULL");
2709 launchd_fds_count
= launch_data_array_get_count(skt
);
2710 if (launchd_fds_count
== 0) LogMsg("launch_data_array_get_count(skt) returned 0");
2713 launchd_fds
= mallocL("LaunchdCheckin", sizeof(dnssd_sock_t
) * launchd_fds_count
);
2714 if (!launchd_fds
) LogMsg("LaunchdCheckin: malloc failed");
2718 for(i
= 0; i
< launchd_fds_count
; i
++)
2720 launch_data_t s
= launch_data_array_get_index(skt
, i
);
2723 launchd_fds
[i
] = dnssd_InvalidSocket
;
2724 LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i
);
2728 launchd_fds
[i
] = launch_data_get_fd(s
);
2729 LogInfo("Launchd Unix Domain Socket [%d]: %d", i
, launchd_fds
[i
]);
2733 // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
2734 chmod(MDNS_UDS_SERVERPATH
, S_IRUSR
|S_IWUSR
| S_IRGRP
|S_IWGRP
| S_IROTH
|S_IWOTH
);
2739 launch_data_t ports
= launch_data_dict_lookup(resp
, "MachServices");
2740 if (!ports
) LogMsg("launch_data_dict_lookup MachServices returned NULL");
2743 launch_data_t p
= launch_data_dict_lookup(ports
, "com.apple.mDNSResponder");
2744 if (!p
) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL");
2747 m_port
= launch_data_get_fd(p
);
2748 LogInfo("Launchd Mach Port: %d", m_port
);
2749 if (m_port
== ~0U) m_port
= MACH_PORT_NULL
;
2753 launch_data_free(resp
);
2756 mDNSlocal
void DropPrivileges(void)
2758 static const char login
[] = "_mdnsresponder";
2759 struct passwd
*pwd
= getpwnam(login
);
2761 LogMsg("Could not find account name \"%s\". Running as root.", login
);
2764 uid_t uid
= pwd
->pw_uid
;
2765 gid_t gid
= pwd
->pw_gid
;
2767 LogMsg("Started as root. Switching to userid \"%s\".", login
);
2769 if (unlink(MDNS_UDS_SERVERPATH
) < 0 && errno
!= ENOENT
) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH
, errno
, strerror(errno
));
2772 static char path
[] = "/var/run/mdns/mDNSResponder";
2773 char *p
= strrchr(path
, '/');
2775 if (mkdir(path
, 0755) < 0 && errno
!= EEXIST
) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path
, errno
, strerror(errno
));
2776 else if (chown(path
, uid
, gid
) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path
, errno
, strerror(errno
));
2780 if (unlink(path
) < 0 && errno
!= ENOENT
) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path
, errno
, strerror(errno
));
2781 else if (symlink(path
, MDNS_UDS_SERVERPATH
) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH
, path
, errno
, strerror(errno
));
2782 else LogInfo("DropPrivileges: Created subdirectory and symlink");
2786 if (0 != initgroups(login
, gid
)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login
, (unsigned long)gid
);
2787 if (0 != setgid(gid
)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid());
2788 if (0 != setuid(uid
)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid
);
2792 extern int sandbox_init(const char *profile
, uint64_t flags
, char **errorbuf
) __attribute__((weak_import
));
2794 mDNSexport
int main(int argc
, char **argv
)
2797 kern_return_t status
;
2799 LogMsg("%s starting", mDNSResponderVersionString
);
2802 LogMsg("CacheRecord %d", sizeof(CacheRecord
));
2803 LogMsg("CacheGroup %d", sizeof(CacheGroup
));
2804 LogMsg("ResourceRecord %d", sizeof(ResourceRecord
));
2805 LogMsg("RData_small %d", sizeof(RData_small
));
2807 LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity
));
2808 LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE
);
2809 LogMsg("block usage %d", sizeof(CacheEntity
) * RR_CACHE_SIZE
);
2810 LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity
) * RR_CACHE_SIZE
);
2813 safe_vproc_transaction_begin();
2815 if (0 == geteuid()) DropPrivileges();
2817 for (i
=1; i
<argc
; i
++)
2819 if (!strcasecmp(argv
[i
], "-d" )) mDNS_DebugMode
= mDNStrue
;
2820 if (!strcasecmp(argv
[i
], "-launchd" )) started_via_launchdaemon
= mDNStrue
;
2821 if (!strcasecmp(argv
[i
], "-launchdaemon" )) started_via_launchdaemon
= mDNStrue
;
2822 if (!strcasecmp(argv
[i
], "-NoMulticastAdvertisements")) advertise
= mDNS_Init_DontAdvertiseLocalAddresses
;
2823 if (!strcasecmp(argv
[i
], "-DisableSleepProxyClient" )) DisableSleepProxyClient
= mDNStrue
;
2824 if (!strcasecmp(argv
[i
], "-DebugLogging" )) mDNS_LoggingEnabled
= mDNStrue
;
2825 if (!strcasecmp(argv
[i
], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled
= mDNStrue
;
2826 if (!strcasecmp(argv
[i
], "-OfferSleepProxyService" ))
2827 OfferSleepProxyService
= (i
+1<argc
&& mDNSIsDigit(argv
[i
+1][0]) && mDNSIsDigit(argv
[i
+1][1]) && argv
[i
+1][2]==0) ? atoi(argv
[++i
]) : 80;
2828 if (!strcasecmp(argv
[i
], "-StrictUnicastOrdering" )) StrictUnicastOrdering
= mDNStrue
;
2829 if (!strcasecmp(argv
[i
], "-DisableInboundRelay" )) DisableInboundRelayConnection
= mDNStrue
;
2832 // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
2833 if (!advertise
) LogMsg("Administratively prohibiting multicast advertisements");
2835 OSXVers
= mDNSMacOSXSystemBuildNumber(NULL
);
2837 #ifndef __LIB_DISPATCH__
2839 signal(SIGHUP
, HandleSIG
); // (Debugging) Purge the cache to check for cache handling bugs
2840 signal(SIGINT
, HandleSIG
); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
2841 // On 10.5 and later, the default action for SIGABRT is to generate a crash report, so we only need our CatchABRT handler on 10.4
2842 if (OSXVers
<= OSXVers_10_4_Tiger
)
2844 LogInfo("Adding SIGABRT handler");
2845 signal(SIGABRT
, CatchABRT
); // For debugging -- SIGABRT should never happen
2847 signal(SIGPIPE
, SIG_IGN
); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
2848 signal(SIGTERM
, HandleSIG
); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
2849 signal(SIGINFO
, HandleSIG
); // (Debugging) Write state snapshot to syslog
2850 signal(SIGUSR1
, HandleSIG
); // (Debugging) Enable Logging
2851 signal(SIGUSR2
, HandleSIG
); // (Debugging) Enable Packet Logging
2853 #endif __LIB_DISPATCH__
2855 mDNSStorage
.p
= &PlatformStorage
; // Make sure mDNSStorage.p is set up, because validatelists uses it
2858 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
2859 if (!mDNS_DebugMode
&& !started_via_launchdaemon
)
2861 registerBootstrapService();
2862 if (!restarting_via_mach_init
) exit(0); // mach_init will restart us immediately as a daemon
2863 int fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
2864 if (fd
< 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno
, strerror(errno
));
2867 // Avoid unnecessarily duplicating a file descriptor to itself
2868 if (fd
!= STDIN_FILENO
) if (dup2(fd
, STDIN_FILENO
) < 0) LogMsg("dup2(fd, STDIN_FILENO) failed errno %d (%s)", errno
, strerror(errno
));
2869 if (fd
!= STDOUT_FILENO
) if (dup2(fd
, STDOUT_FILENO
) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno
, strerror(errno
));
2870 if (fd
!= STDERR_FILENO
) if (dup2(fd
, STDERR_FILENO
) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno
, strerror(errno
));
2871 if (fd
!= STDIN_FILENO
&& fd
!= STDOUT_FILENO
&& fd
!= STDERR_FILENO
) (void)close(fd
);
2875 #ifndef __LIB_DISPATCH__
2877 // Create the kqueue, mutex and thread to support KQSockets
2878 KQueueFD
= kqueue();
2879 if (KQueueFD
== -1) { LogMsg("kqueue() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2881 i
= pthread_mutex_init(&PlatformStorage
.BigMutex
, NULL
);
2882 if (i
== -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2884 int fdpair
[2] = {0, 0};
2885 i
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, fdpair
);
2886 if (i
== -1) { LogMsg("socketpair() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2888 // Socket pair returned us two identical sockets connected to each other
2889 // We will use the first socket to send the second socket. The second socket
2890 // will be added to the kqueue so it will wake when data is sent.
2891 static const KQueueEntry wakeKQEntry
= { KQWokenFlushBytes
, NULL
, "kqueue wakeup after CFRunLoop event" };
2893 PlatformStorage
.WakeKQueueLoopFD
= fdpair
[0];
2894 KQueueSet(fdpair
[1], EV_ADD
, EVFILT_READ
, &wakeKQEntry
);
2896 #endif __LIB_DISPATCH__
2898 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
2900 LogMsg("Note: Compiled without Apple Sandbox support");
2901 #else MDNS_NO_SANDBOX
2903 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
2908 uint64_t sandbox_flags
= SANDBOX_NAMED
;
2910 if (stat("/usr/share/sandbox/mDNSResponder.sb", &s
) == 0)
2912 sandbox_flags
= SANDBOX_NAMED_EXTERNAL
;
2913 LogInfo("Will load Sandbox profile from filesystem");
2916 int sandbox_err
= sandbox_init("mDNSResponder", sandbox_flags
, &sandbox_msg
);
2917 if (sandbox_err
) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg
); sandbox_free_error(sandbox_msg
); }
2918 else LogInfo("Now running under Apple Sandbox restrictions");
2920 #endif MDNS_NO_SANDBOX
2922 status
= mDNSDaemonInitialize();
2923 if (status
) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit
; }
2925 status
= udsserver_init(launchd_fds
, launchd_fds_count
);
2926 if (status
) { LogMsg("Daemon start: udsserver_init failed"); goto exit
; }
2928 mDNSMacOSXNetworkChanged(&mDNSStorage
);
2930 #ifdef __LIB_DISPATCH__
2931 LogInfo("Daemon Start: Using LibDispatch");
2932 // CFRunLoopRun runs both CFRunLoop sources and dispatch sources
2934 #else __LIB_DISPATCH__
2935 // Start the kqueue thread
2936 pthread_t KQueueThread
;
2937 i
= pthread_create(&KQueueThread
, NULL
, KQueueLoop
, &mDNSStorage
);
2938 if (i
== -1) { LogMsg("pthread_create() failed errno %d (%s)", errno
, strerror(errno
)); status
= errno
; goto exit
; }
2942 LogMsg("ERROR: CFRunLoopRun Exiting.");
2943 mDNS_Close(&mDNSStorage
);
2945 #endif __LIB_DISPATCH__
2947 LogMsg("%s exiting", mDNSResponderVersionString
);
2950 if (!mDNS_DebugMode
&& !started_via_launchdaemon
) destroyBootstrapService();
2954 // uds_daemon.c support routines /////////////////////////////////////////////
2956 // Arrange things so that when data appears on fd, callback is called with context
2957 mDNSexport mStatus
udsSupportAddFDToEventLoop(int fd
, udsEventCallback callback
, void *context
, void **platform_data
)
2959 KQSocketEventSource
**p
= &gEventSources
;
2960 (void) platform_data
;
2961 while (*p
&& (*p
)->fd
!= fd
) p
= &(*p
)->next
;
2962 if (*p
) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd
); return mStatus_AlreadyRegistered
; }
2964 KQSocketEventSource
*newSource
= (KQSocketEventSource
*) mallocL("KQSocketEventSource", sizeof *newSource
);
2965 if (!newSource
) return mStatus_NoMemoryErr
;
2967 newSource
->next
= mDNSNULL
;
2969 newSource
->kqs
.KQcallback
= callback
;
2970 newSource
->kqs
.KQcontext
= context
;
2971 newSource
->kqs
.KQtask
= "UDS client";
2972 #ifdef __LIB_DISPATCH__
2973 newSource
->kqs
.readSource
= mDNSNULL
;
2974 newSource
->kqs
.writeSource
= mDNSNULL
;
2975 newSource
->kqs
.fdClosed
= mDNSfalse
;
2976 #endif __LIB_DISPATCH__
2978 if (KQueueSet(fd
, EV_ADD
, EVFILT_READ
, &newSource
->kqs
) == 0)
2981 return mStatus_NoError
;
2984 LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd
, errno
, strerror(errno
));
2985 freeL("KQSocketEventSource", newSource
);
2986 return mStatus_BadParamErr
;
2989 int udsSupportReadFD(dnssd_sock_t fd
, char *buf
, int len
, int flags
, void *platform_data
)
2991 (void) platform_data
;
2992 return recv(fd
, buf
, len
, flags
);
2995 mDNSexport mStatus
udsSupportRemoveFDFromEventLoop(int fd
, void *platform_data
) // Note: This also CLOSES the file descriptor
2997 KQSocketEventSource
**p
= &gEventSources
;
2998 (void) platform_data
;
2999 while (*p
&& (*p
)->fd
!= fd
) p
= &(*p
)->next
;
3002 KQSocketEventSource
*s
= *p
;
3004 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
3005 // causes the kernel to automatically remove any associated kevents
3006 mDNSPlatformCloseFD(&s
->kqs
, s
->fd
);
3007 freeL("KQSocketEventSource", s
);
3008 return mStatus_NoError
;
3010 LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd
);
3011 return mStatus_NoSuchNameErr
;
3014 #if _BUILDING_XCODE_PROJECT_
3015 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
3016 const char *__crashreporter_info__
= mDNSResponderVersionString
;
3017 asm(".desc ___crashreporter_info__, 0x10");
3020 // For convenience when using the "strings" command, this is the last thing in the file
3021 // The "@(#) " pattern is a special prefix the "what" command looks for
3022 mDNSexport
const char mDNSResponderVersionString_SCCS
[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";