2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
38 Change History (most recent first):
41 Revision 1.134.2.2 2003/12/05 00:03:35 cheshire
42 <rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
44 Revision 1.134.2.1 2003/12/03 11:00:09 cheshire
45 Update "mDNSResponderVersion" mechanism to allow dots so we can do mDNSResponder-58.1 for SUPan
47 Revision 1.134 2003/08/21 20:01:37 cheshire
48 <rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
50 Revision 1.133 2003/08/20 23:39:31 cheshire
51 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
53 Revision 1.132 2003/08/20 01:44:56 cheshire
54 Fix errors in LogOperation() calls (only used for debugging)
56 Revision 1.131 2003/08/19 05:39:43 cheshire
57 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
59 Revision 1.130 2003/08/16 03:39:01 cheshire
60 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
62 Revision 1.129 2003/08/15 20:16:03 cheshire
63 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
64 We want to avoid touching the rdata pages, so we don't page them in.
65 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
66 Moved this from the RData to the ResourceRecord object.
67 2. To avoid unnecessarily touching the rdata just to compare it,
68 compute a hash of the rdata and store the hash in the ResourceRecord object.
70 Revision 1.128 2003/08/14 19:30:36 cheshire
71 <rdar://problem/3378473> Include list of cache records in SIGINFO output
73 Revision 1.127 2003/08/14 02:18:21 cheshire
74 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
76 Revision 1.126 2003/08/12 19:56:25 cheshire
79 Revision 1.125 2003/08/08 18:36:04 cheshire
80 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
82 Revision 1.124 2003/07/25 18:28:23 cheshire
83 Minor fix to error messages in syslog: Display string parameters with quotes
85 Revision 1.123 2003/07/23 17:45:28 cheshire
86 <rdar://problem/3339388> mDNSResponder leaks a bit
87 Don't allocate memory for the reply until after we've verified that the reply is valid
89 Revision 1.122 2003/07/23 00:00:04 cheshire
92 Revision 1.121 2003/07/20 03:38:51 ksekar
94 Completed support for Unix-domain socket based API.
96 Revision 1.120 2003/07/18 00:30:00 cheshire
97 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
99 Revision 1.119 2003/07/17 19:08:58 cheshire
100 <rdar://problem/3332153> Remove calls to enable obsolete UDS code
102 Revision 1.118 2003/07/15 21:12:28 cheshire
103 Added extra debugging checks in validatelists() (not used in final shipping version)
105 Revision 1.117 2003/07/15 01:55:15 cheshire
106 <rdar://problem/3315777> Need to implement service registration with subtypes
108 Revision 1.116 2003/07/02 21:19:51 cheshire
109 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
111 Revision 1.115 2003/07/02 02:41:24 cheshire
112 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
114 Revision 1.114 2003/07/01 21:10:20 cheshire
115 Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
117 Revision 1.113 2003/06/28 17:27:43 vlubet
118 <rdar://problem/3221246> Redirect standard input, standard output, and
119 standard error file descriptors to /dev/null just like any other
122 Revision 1.112 2003/06/25 23:42:19 ksekar
123 Bug #: <rdar://problem/3249292>: Feature: New Rendezvous APIs (#7875)
124 Reviewed by: Stuart Cheshire
125 Added files necessary to implement Unix domain sockets based enhanced
126 Rendezvous APIs, and integrated with existing Mach-port based daemon.
128 Revision 1.111 2003/06/11 01:02:43 cheshire
129 <rdar://problem/3287858> mDNSResponder binary compatibility
130 Make single binary that can run on both Jaguar and Panther.
132 Revision 1.110 2003/06/10 01:14:11 cheshire
133 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
135 Revision 1.109 2003/06/06 19:53:43 cheshire
136 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
137 (Global search-and-replace; no functional change to code execution.)
139 Revision 1.108 2003/06/06 14:08:06 cheshire
140 For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
142 Revision 1.107 2003/05/29 05:44:55 cheshire
143 Minor fixes to log messages
145 Revision 1.106 2003/05/27 18:30:55 cheshire
146 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
147 Dean Reece suggested SIGINFO is more appropriate than SIGHUP
149 Revision 1.105 2003/05/26 03:21:29 cheshire
150 Tidy up address structure naming:
151 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
152 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
153 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
155 Revision 1.104 2003/05/26 00:42:06 cheshire
156 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
158 Revision 1.103 2003/05/23 23:07:44 cheshire
159 <rdar://problem/3268199> Must not write to stderr when running as daemon
161 Revision 1.102 2003/05/22 01:32:31 cheshire
162 Fix typo in Log message format string
164 Revision 1.101 2003/05/22 00:26:55 cheshire
165 <rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
166 Modify error message to explain that this is technically legal, but may indicate a bug.
168 Revision 1.100 2003/05/21 21:02:24 ksekar
169 Bug #: <rdar://problem/3247035>: Service should be prefixed
170 Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
171 Mach message port to "com.apple.mDNSResponder.
173 Revision 1.99 2003/05/21 17:33:49 cheshire
174 Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
176 Revision 1.98 2003/05/20 00:33:07 cheshire
177 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
178 SIGHUP now writes state summary to syslog
180 Revision 1.97 2003/05/08 00:19:08 cheshire
181 <rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
183 Revision 1.96 2003/05/07 22:10:46 cheshire
184 <rdar://problem/3250330> Add a few more error logging messages
186 Revision 1.95 2003/05/07 19:20:17 cheshire
187 <rdar://problem/3251391> Add version number to mDNSResponder builds
189 Revision 1.94 2003/05/07 00:28:18 cheshire
190 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
192 Revision 1.93 2003/05/06 00:00:49 cheshire
193 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
195 Revision 1.92 2003/04/04 20:38:57 cheshire
200 #include <mach/mach.h>
201 #include <mach/mach_error.h>
202 #include <servers/bootstrap.h>
203 #include <sys/types.h>
208 #include "DNSServiceDiscoveryRequestServer.h"
209 #include "DNSServiceDiscoveryReply.h"
211 #include "mDNSClientAPI.h" // Defines the interface to the client layer above
212 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
214 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
218 //*************************************************************************************************************
221 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
222 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
223 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
224 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
225 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
227 //*************************************************************************************************************
230 mDNSexport mDNS mDNSStorage
;
231 static mDNS_PlatformSupport PlatformStorage
;
232 #define RR_CACHE_SIZE 64
233 static CacheRecord rrcachestorage
[RR_CACHE_SIZE
];
234 static const char PID_FILE
[] = "/var/run/mDNSResponder.pid";
236 static const char kmDNSBootstrapName
[] = "com.apple.mDNSResponderRestart";
237 static mach_port_t client_death_port
= MACH_PORT_NULL
;
238 static mach_port_t exit_m_port
= MACH_PORT_NULL
;
239 static mach_port_t info_m_port
= MACH_PORT_NULL
;
240 static mach_port_t server_priv_port
= MACH_PORT_NULL
;
242 // mDNS Mach Message Timeout, in milliseconds.
243 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
244 // fails to service its mach message queue, but long enough to give a well-written
245 // client a chance to service its mach message queue without getting cut off.
246 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
247 // even extra-slow clients a fair chance before we cut them off.
248 #define MDNS_MM_TIMEOUT 250
250 static int restarting_via_mach_init
= 0;
258 //*************************************************************************************************************
259 // Active client list structures
261 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
262 struct DNSServiceDomainEnumeration_struct
264 DNSServiceDomainEnumeration
*next
;
265 mach_port_t ClientMachPort
;
266 DNSQuestion dom
; // Question asking for domains
267 DNSQuestion def
; // Question asking for default domain
270 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult
;
271 struct DNSServiceBrowserResult_struct
273 DNSServiceBrowserResult
*next
;
278 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
279 struct DNSServiceBrowser_struct
281 DNSServiceBrowser
*next
;
282 mach_port_t ClientMachPort
;
284 DNSServiceBrowserResult
*results
;
288 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
289 struct DNSServiceResolver_struct
291 DNSServiceResolver
*next
;
292 mach_port_t ClientMachPort
;
298 typedef struct DNSServiceRegistration_struct DNSServiceRegistration
;
299 struct DNSServiceRegistration_struct
301 DNSServiceRegistration
*next
;
302 mach_port_t ClientMachPort
;
307 // Don't add any fields after ServiceRecordSet.
308 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
311 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
312 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
313 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
314 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
316 //*************************************************************************************************************
317 // General Utility Functions
319 #if MACOSX_MDNS_MALLOC_DEBUGGING
321 char _malloc_options
[] = "AXZ";
323 static void validatelists(mDNS
*const m
)
325 DNSServiceDomainEnumeration
*e
;
326 DNSServiceBrowser
*b
;
327 DNSServiceResolver
*l
;
328 DNSServiceRegistration
*r
;
334 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
335 if (e
->ClientMachPort
== 0 || e
->ClientMachPort
== (mach_port_t
)~0)
336 LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e
, e
->ClientMachPort
);
338 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
339 if (b
->ClientMachPort
== 0 || b
->ClientMachPort
== (mach_port_t
)~0)
340 LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b
, b
->ClientMachPort
);
342 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
343 if (l
->ClientMachPort
== 0 || l
->ClientMachPort
== (mach_port_t
)~0)
344 LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l
, l
->ClientMachPort
);
346 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
347 if (r
->ClientMachPort
== 0 || r
->ClientMachPort
== (mach_port_t
)~0)
348 LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r
, r
->ClientMachPort
);
350 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
351 if (rr
->RecordType
== 0 || rr
->RecordType
== 0xFF)
352 LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr
, rr
->RecordType
);
354 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
)
355 if (rr
->RecordType
== 0 || rr
->RecordType
== 0xFF)
356 LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr
, rr
->RecordType
);
358 for (q
= m
->Questions
; q
; q
=q
->next
)
359 if (q
->ThisQInterval
== (mDNSs32
)~0)
360 LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q
, q
->ThisQInterval
);
362 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
363 for (cr
= mDNSStorage
.rrcache_hash
[slot
]; cr
; cr
=cr
->next
)
364 if (cr
->RecordType
== 0 || cr
->RecordType
== 0xFF)
365 LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot
, rr
, rr
->RecordType
);
368 void *mallocL(char *msg
, unsigned int size
)
370 unsigned long *mem
= malloc(size
+8);
373 LogMsg("malloc( %s : %d ) failed", msg
, size
);
378 LogMalloc("malloc( %s : %lu ) = %p", msg
, size
, &mem
[2]);
381 //bzero(&mem[2], size);
382 memset(&mem
[2], 0xFF, size
);
383 validatelists(&mDNSStorage
);
388 void freeL(char *msg
, void *x
)
391 LogMsg("free( %s @ NULL )!", msg
);
394 unsigned long *mem
= ((unsigned long *)x
) - 2;
395 if (mem
[0] != 0xDEAD1234)
396 { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
398 { LogMsg("free( %s : %ld @ %p) too big!", msg
, mem
[1], &mem
[2]); return; }
399 LogMalloc("free( %s : %ld @ %p)", msg
, mem
[1], &mem
[2]);
400 //bzero(mem, mem[1]+8);
401 memset(mem
, 0xFF, mem
[1]+8);
402 validatelists(&mDNSStorage
);
409 //*************************************************************************************************************
410 // Client Death Detection
412 mDNSlocal
void FreeDNSServiceRegistration(DNSServiceRegistration
*x
)
416 ExtraResourceRecord
*extras
= x
->s
.Extras
;
417 x
->s
.Extras
= x
->s
.Extras
->next
;
418 if (extras
->r
.resrec
.rdata
!= &extras
->r
.rdatastorage
)
419 freeL("Extra RData", extras
->r
.resrec
.rdata
);
420 freeL("ExtraResourceRecord", extras
);
423 if (x
->s
.RR_TXT
.resrec
.rdata
!= &x
->s
.RR_TXT
.rdatastorage
)
424 freeL("TXT RData", x
->s
.RR_TXT
.resrec
.rdata
);
426 if (x
->s
.SubTypes
) freeL("ServiceSubTypes", x
->s
.SubTypes
);
428 freeL("DNSServiceRegistration", x
);
431 // AbortClient finds whatever client is identified by the given Mach port,
432 // stops whatever operation that client was doing, and frees its memory.
433 // In the case of a service registration, the actual freeing may be deferred
434 // until we get the mStatus_MemFree message, if necessary
435 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
, void *m
)
437 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
438 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
439 DNSServiceResolver
**l
= &DNSServiceResolverList
;
440 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
442 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
445 DNSServiceDomainEnumeration
*x
= *e
;
448 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->dom
.qname
.c
, m
, x
);
449 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort
, x
->dom
.qname
.c
);
450 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
451 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
452 freeL("DNSServiceDomainEnumeration", x
);
456 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
459 DNSServiceBrowser
*x
= *b
;
462 LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->q
.qname
.c
, m
, x
);
463 else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort
, x
->q
.qname
.c
);
464 mDNS_StopBrowse(&mDNSStorage
, &x
->q
);
467 DNSServiceBrowserResult
*r
= x
->results
;
468 x
->results
= x
->results
->next
;
469 freeL("DNSServiceBrowserResult", r
);
471 freeL("DNSServiceBrowser", x
);
475 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
478 DNSServiceResolver
*x
= *l
;
481 LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->i
.name
.c
, m
, x
);
482 else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort
, x
->i
.name
.c
);
483 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
484 freeL("DNSServiceResolver", x
);
488 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
491 DNSServiceRegistration
*x
= *r
;
493 x
->autorename
= mDNSfalse
;
495 LogMsg("%5d: DNSServiceRegistration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->s
.RR_SRV
.resrec
.name
.c
, m
, x
);
496 else LogOperation("%5d: DNSServiceRegistration(%##s) STOP", ClientMachPort
, x
->s
.RR_SRV
.resrec
.name
.c
);
497 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
498 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
499 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
500 // the list, so we should go ahead and free the memory right now
501 if (mDNS_DeregisterService(&mDNSStorage
, &x
->s
) != mStatus_NoError
)
502 FreeDNSServiceRegistration(x
);
506 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort
);
509 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
511 mDNSlocal
void AbortClientWithLogMessage(mach_port_t c
, char *reason
, char *msg
, void *m
)
513 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
514 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
515 DNSServiceResolver
*l
= DNSServiceResolverList
;
516 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
517 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
518 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
519 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
520 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
521 if (e
) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c
, e
->dom
.qname
.c
, reason
, msg
);
522 else if (b
) LogMsg("%5d: Browser(%##s) %s%s", c
, b
->q
.qname
.c
, reason
, msg
);
523 else if (l
) LogMsg("%5d: Resolver(%##s) %s%s", c
, l
->i
.name
.c
, reason
, msg
);
524 else if (r
) LogMsg("%5d: Registration(%##s) %s%s", c
, r
->s
.RR_SRV
.resrec
.name
.c
, reason
, msg
);
525 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c
, reason
, msg
);
530 mDNSlocal mDNSBool
CheckForExistingClient(mach_port_t c
)
532 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
533 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
534 DNSServiceResolver
*l
= DNSServiceResolverList
;
535 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
536 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
537 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
538 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
539 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
540 if (e
) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c
, e
->dom
.qname
.c
);
541 if (b
) LogMsg("%5d: Browser(%##s) already exists!", c
, b
->q
.qname
.c
);
542 if (l
) LogMsg("%5d: Resolver(%##s) already exists!", c
, l
->i
.name
.c
);
543 if (r
) LogMsg("%5d: Registration(%##s) already exists!", c
, r
->s
.RR_SRV
.resrec
.name
.c
);
544 return(e
|| b
|| l
|| r
);
547 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
549 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
550 (void)unusedport
; // Unused
551 (void)size
; // Unused
552 (void)info
; // Unused
553 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
555 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
556 AbortClient(deathMessage
->not_port
, NULL
);
558 /* Deallocate the send right that came in the dead name notification */
559 mach_port_destroy( mach_task_self(), deathMessage
->not_port
);
563 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
, void *m
)
566 kern_return_t r
= mach_port_request_notification(mach_task_self(), ClientMachPort
, MACH_NOTIFY_DEAD_NAME
, 0,
567 client_death_port
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &prev
);
568 // If the port already died while we were thinking about it, then abort the operation right away
569 if (r
!= KERN_SUCCESS
)
570 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
573 //*************************************************************************************************************
574 // Domain Enumeration
576 mDNSlocal
void FoundDomain(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
578 kern_return_t status
;
580 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
581 DNSServiceDomainEnumerationReplyResultType rt
;
582 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->QuestionContext
;
584 debugf("FoundDomain: %##s PTR %##s", answer
->name
.c
, answer
->rdata
->u
.name
.c
);
585 if (answer
->rrtype
!= kDNSType_PTR
) return;
586 if (!x
) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
590 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
591 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
595 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
599 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
600 x
->ClientMachPort
, x
->dom
.qname
.c
, answer
->rdata
->u
.name
.c
,
601 !AddRecord
? "RemoveDomain" :
602 question
== &x
->dom
? "AddDomain" : "AddDomainDefault");
604 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, buffer
);
605 status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, buffer
, 0, MDNS_MM_TIMEOUT
);
606 if (status
== MACH_SEND_TIMED_OUT
)
607 AbortBlockedClient(x
->ClientMachPort
, "enumeration", x
);
610 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
613 // Check client parameter
614 (void)unusedserver
; // Unused
615 mStatus err
= mStatus_NoError
;
616 const char *errormsg
= "Unknown";
617 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
618 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
620 mDNS_DomainType dt1
= regDom
? mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
621 mDNS_DomainType dt2
= regDom
? mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
622 const DNSServiceDomainEnumerationReplyResultType rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
624 // Allocate memory, and handle failure
625 DNSServiceDomainEnumeration
*x
= mallocL("DNSServiceDomainEnumeration", sizeof(*x
));
626 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
628 // Set up object, and link into list
629 x
->ClientMachPort
= client
;
630 x
->next
= DNSServiceDomainEnumerationList
;
631 DNSServiceDomainEnumerationList
= x
;
633 // Generate initial response
634 verbosedebugf("%5d: Enumerate %s Domains", client
, regDom
? "Registration" : "Browsing");
635 // We always give local. as the initial default browse domain, and then look for more
636 kern_return_t status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, "local.", 0, MDNS_MM_TIMEOUT
);
637 if (status
== MACH_SEND_TIMED_OUT
)
638 { AbortBlockedClient(x
->ClientMachPort
, "local enumeration", x
); return(mStatus_UnknownErr
); }
641 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, mDNSInterface_Any
, FoundDomain
, x
);
642 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, mDNSInterface_Any
, FoundDomain
, x
);
643 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_GetDomains"; goto fail
; }
645 // Succeeded: Wrap up and return
646 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client
, x
->dom
.qname
.c
);
647 EnableDeathNotificationForClient(client
, x
);
648 return(mStatus_NoError
);
651 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client
, regDom
, errormsg
, err
);
655 //*************************************************************************************************************
656 // Browse for services
658 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
662 if (answer
->rrtype
!= kDNSType_PTR
)
663 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
); return; }
666 domainname type
, domain
;
667 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
669 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
670 answer
->name
.c
, answer
->rdata
->u
.name
.c
);
674 DNSServiceBrowserResult
*x
= mallocL("DNSServiceBrowserResult", sizeof(*x
));
675 if (!x
) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer
->rdata
->u
.name
.c
); return; }
677 verbosedebugf("FoundInstance: %s %##s", AddRecord
? "Add" : "Rmv", answer
->rdata
->u
.name
.c
);
678 AssignDomainName(x
->result
, answer
->rdata
->u
.name
);
680 x
->resultType
= DNSServiceBrowserReplyAddInstance
;
681 else x
->resultType
= DNSServiceBrowserReplyRemoveInstance
;
684 DNSServiceBrowser
*browser
= (DNSServiceBrowser
*)question
->QuestionContext
;
685 DNSServiceBrowserResult
**p
= &browser
->results
;
686 while (*p
) p
= &(*p
)->next
;
690 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
691 DNSCString regtype
, DNSCString domain
)
693 // Check client parameter
694 (void)unusedserver
; // Unused
695 mStatus err
= mStatus_NoError
;
696 const char *errormsg
= "Unknown";
697 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
698 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
700 // Check other parameters
702 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
703 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Illegal domain"; goto badparam
; }
705 // Allocate memory, and handle failure
706 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
707 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
709 // Set up object, and link into list
710 x
->ClientMachPort
= client
;
713 x
->next
= DNSServiceBrowserList
;
714 DNSServiceBrowserList
= x
;
717 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client
, t
.c
, d
.c
);
718 err
= mDNS_StartBrowse(&mDNSStorage
, &x
->q
, &t
, &d
, mDNSInterface_Any
, FoundInstance
, x
);
719 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartBrowse"; goto fail
; }
721 // Succeeded: Wrap up and return
722 EnableDeathNotificationForClient(client
, x
);
723 return(mStatus_NoError
);
726 err
= mStatus_BadParamErr
;
728 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client
, regtype
, domain
, errormsg
, err
);
732 //*************************************************************************************************************
733 // Resolve Service Info
735 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
737 kern_return_t status
;
738 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->ServiceInfoQueryContext
;
739 NetworkInterfaceInfoOSX
*ifx
= (NetworkInterfaceInfoOSX
*)query
->info
->InterfaceID
;
740 if (query
->info
->InterfaceID
== (mDNSInterfaceID
)~0) ifx
= mDNSNULL
;
741 struct sockaddr_storage interface
;
742 struct sockaddr_storage address
;
744 int i
, pstrlen
= query
->info
->TXTinfo
[0];
747 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
749 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
751 bzero(&interface
, sizeof(interface
));
752 bzero(&address
, sizeof(address
));
754 if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
)
756 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&interface
;
757 sin
->sin_len
= sizeof(*sin
);
758 sin
->sin_family
= AF_INET
;
760 sin
->sin_addr
.s_addr
= ifx
->ifinfo
.ip
.ip
.v4
.NotAnInteger
;
762 else if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
)
764 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&interface
;
765 sin6
->sin6_len
= sizeof(*sin6
);
766 sin6
->sin6_family
= AF_INET6
;
767 sin6
->sin6_flowinfo
= 0;
769 sin6
->sin6_addr
= *(struct in6_addr
*)&ifx
->ifinfo
.ip
.ip
.v6
;
770 sin6
->sin6_scope_id
= ifx
->scope_id
;
773 if (query
->info
->ip
.type
== mDNSAddrType_IPv4
)
775 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&address
;
776 sin
->sin_len
= sizeof(*sin
);
777 sin
->sin_family
= AF_INET
;
778 sin
->sin_port
= query
->info
->port
.NotAnInteger
;
779 sin
->sin_addr
.s_addr
= query
->info
->ip
.ip
.v4
.NotAnInteger
;
783 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&address
;
784 sin6
->sin6_len
= sizeof(*sin6
);
785 sin6
->sin6_family
= AF_INET6
;
786 sin6
->sin6_port
= query
->info
->port
.NotAnInteger
;
787 sin6
->sin6_flowinfo
= 0;
788 sin6
->sin6_addr
= *(struct in6_addr
*)&query
->info
->ip
.ip
.v6
;
789 sin6
->sin6_scope_id
= ifx
? ifx
->scope_id
: 0;
792 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
793 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
794 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
795 // ASCII-1 characters are used in the C-string as boundary markers,
796 // to indicate the boundaries between the original constituent P-strings.
797 for (i
=1; i
<query
->info
->TXTlen
; i
++)
800 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
804 pstrlen
= query
->info
->TXTinfo
[i
];
807 cstring
[i
-1] = 0; // Put the terminating NULL on the end
809 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%d", x
->ClientMachPort
,
810 x
->i
.name
.c
, &query
->info
->ip
, (int)query
->info
->port
.b
[0] << 8 | query
->info
->port
.b
[1]);
811 status
= DNSServiceResolverReply_rpc(x
->ClientMachPort
,
812 (char*)&interface
, (char*)&address
, cstring
, 0, MDNS_MM_TIMEOUT
);
813 if (status
== MACH_SEND_TIMED_OUT
)
814 AbortBlockedClient(x
->ClientMachPort
, "resolve", x
);
817 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
818 DNSCString name
, DNSCString regtype
, DNSCString domain
)
820 // Check client parameter
821 (void)unusedserver
; // Unused
822 mStatus err
= mStatus_NoError
;
823 const char *errormsg
= "Unknown";
824 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
825 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
827 // Check other parameters
829 domainname t
, d
, srv
;
830 if (!name
[0] || !MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
831 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
832 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
833 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
835 // Allocate memory, and handle failure
836 DNSServiceResolver
*x
= mallocL("DNSServiceResolver", sizeof(*x
));
837 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
839 // Set up object, and link into list
840 x
->ClientMachPort
= client
;
841 x
->i
.InterfaceID
= mDNSInterface_Any
;
843 x
->ReportTime
= (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond
) | 1;
844 // Don't report errors for old iChat ("_ichat._tcp") service.
845 // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead,
846 // and so should other applications that have valid reasons to be doing ongoing record monitoring.
847 if (SameDomainLabel(t
.c
, (mDNSu8
*)"\x6_ichat")) x
->ReportTime
= 0;
848 x
->next
= DNSServiceResolverList
;
849 DNSServiceResolverList
= x
;
852 LogOperation("%5d: DNSServiceResolver(%##s) START", client
, x
->i
.name
.c
);
853 err
= mDNS_StartResolveService(&mDNSStorage
, &x
->q
, &x
->i
, FoundInstanceInfo
, x
);
854 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartResolveService"; goto fail
; }
856 // Succeeded: Wrap up and return
857 EnableDeathNotificationForClient(client
, x
);
858 return(mStatus_NoError
);
861 err
= mStatus_BadParamErr
;
863 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client
, name
, regtype
, domain
, errormsg
, err
);
867 //*************************************************************************************************************
870 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
872 DNSServiceRegistration
*x
= (DNSServiceRegistration
*)sr
->ServiceContext
;
874 if (result
== mStatus_NoError
)
876 kern_return_t status
;
877 LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
878 status
= DNSServiceRegistrationReply_rpc(x
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
879 if (status
== MACH_SEND_TIMED_OUT
)
880 AbortBlockedClient(x
->ClientMachPort
, "registration success", x
);
883 else if (result
== mStatus_NameConflict
)
885 LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
886 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
887 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
889 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
892 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
893 // of their registration in the usual way (which we will catch via client death notification).
894 // If the Mach queue is full, we forcibly abort the client immediately.
895 kern_return_t status
= DNSServiceRegistrationReply_rpc(x
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
896 if (status
== MACH_SEND_TIMED_OUT
)
897 AbortBlockedClient(x
->ClientMachPort
, "registration conflict", x
);
901 else if (result
== mStatus_MemFree
)
905 debugf("RegCallback renaming %#s to %#s", x
->name
.c
, mDNSStorage
.nicelabel
.c
);
906 x
->autorename
= mDNSfalse
;
907 x
->name
= mDNSStorage
.nicelabel
;
908 mDNS_RenameAndReregisterService(m
, &x
->s
, &x
->name
);
912 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
913 while (*r
&& *r
!= x
) r
= &(*r
)->next
;
916 LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr
->RR_SRV
.resrec
.name
.c
);
919 LogOperation("%5d: DNSServiceRegistration(%##s) Memory Free", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
920 FreeDNSServiceRegistration(x
);
925 LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld",
926 x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
, result
);
929 mDNSlocal
void CheckForDuplicateRegistrations(DNSServiceRegistration
*x
, domainname
*srv
, mDNSIPPort port
)
931 int count
= 1; // Start with the one we're planning to register, then see if there are any more
933 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
934 if (rr
->resrec
.rrtype
== kDNSType_SRV
&&
935 rr
->resrec
.rdata
->u
.srv
.port
.NotAnInteger
== port
.NotAnInteger
&&
936 SameDomainName(&rr
->resrec
.name
, srv
))
940 LogMsg("%5d: Client application registered %d identical instances of service %##s port %d.",
941 x
->ClientMachPort
, count
, srv
->c
, (int)port
.b
[0] << 8 | port
.b
[1]);
944 mDNSexport kern_return_t
provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
945 DNSCString name
, DNSCString regtype
, DNSCString domain
, int notAnIntPort
, DNSCString txtRecord
)
947 // Check client parameter
948 (void)unusedserver
; // Unused
949 mStatus err
= mStatus_NoError
;
950 const char *errormsg
= "Unknown";
951 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
952 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
954 // Check for sub-types after the service type
955 AuthRecord
*SubTypes
= mDNSNULL
;
956 mDNSu32 i
, NumSubTypes
= 0;
957 char *comma
= regtype
;
958 while (*comma
&& *comma
!= ',') comma
++;
959 if (*comma
) // If we found a comma...
961 *comma
= 0; // Overwrite the first comma with a nul
962 char *p
= comma
+ 1; // Start scanning from the next character
965 if ( !(*p
&& *p
!= ',')) { errormsg
= "Bad Service SubType"; goto badparam
; }
966 while (*p
&& *p
!= ',') p
++;
972 // Check other parameters
976 if (!name
[0]) n
= mDNSStorage
.nicelabel
;
977 else if (!MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
978 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
979 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
980 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
983 port
.NotAnInteger
= notAnIntPort
;
985 unsigned char txtinfo
[1024] = "";
986 unsigned int data_len
= 0;
987 unsigned int size
= sizeof(RDataBody
);
988 unsigned char *pstring
= &txtinfo
[data_len
];
989 char *ptr
= txtRecord
;
991 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
992 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
993 // Hence we have to convert the C-string to a P-string.
994 // ASCII-1 characters are allowed in the C-string as boundary markers,
995 // so that a single C-string can be used to represent one or more P-strings.
998 if (++data_len
>= sizeof(txtinfo
)) { errormsg
= "TXT record too long"; goto badtxt
; }
999 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
1001 pstring
= &txtinfo
[data_len
];
1007 if (pstring
[0] == 255) { errormsg
= "TXT record invalid (component longer than 255)"; goto badtxt
; }
1008 pstring
[++pstring
[0]] = *ptr
++;
1013 if (size
< data_len
)
1016 // Allocate memory, and handle failure
1017 DNSServiceRegistration
*x
= mallocL("DNSServiceRegistration", sizeof(*x
) - sizeof(RDataBody
) + size
);
1018 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1022 SubTypes
= mallocL("ServiceSubTypes", NumSubTypes
* sizeof(AuthRecord
));
1023 if (!SubTypes
) { freeL("DNSServiceRegistration", x
); err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1024 for (i
= 0; i
< NumSubTypes
; i
++)
1026 comma
++; // Advance over the nul character
1027 MakeDomainNameFromDNSNameString(&SubTypes
[i
].resrec
.name
, comma
);
1028 while (*comma
) comma
++; // Advance comma to point to the next terminating nul
1032 // Set up object, and link into list
1033 x
->ClientMachPort
= client
;
1034 x
->autoname
= (!name
[0]);
1035 x
->autorename
= mDNSfalse
;
1037 x
->next
= DNSServiceRegistrationList
;
1038 DNSServiceRegistrationList
= x
;
1041 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x
->ClientMachPort
, name
, regtype
, domain
);
1042 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1043 // a port number of zero. When two instances of the protected client are allowed to run on one
1044 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1045 if (port
.NotAnInteger
) CheckForDuplicateRegistrations(x
, &srv
, port
);
1047 err
= mDNS_RegisterService(&mDNSStorage
, &x
->s
,
1048 &x
->name
, &t
, &d
, // Name, type, domain
1049 mDNSNULL
, port
, // Host and port
1050 txtinfo
, data_len
, // TXT data, length
1051 SubTypes
, NumSubTypes
, // Subtypes
1052 mDNSInterface_Any
, // Interace ID
1053 RegCallback
, x
); // Callback and context
1055 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_RegisterService"; goto fail
; }
1057 // Succeeded: Wrap up and return
1058 EnableDeathNotificationForClient(client
, x
);
1059 return(mStatus_NoError
);
1062 LogMsg("%5d: TXT record: %.100s...", client
, txtRecord
);
1064 err
= mStatus_BadParamErr
;
1066 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1067 client
, name
, regtype
, domain
, notAnIntPort
, errormsg
, err
);
1071 mDNSlocal
void mDNS_StatusCallback(mDNS
*const m
, mStatus result
)
1074 if (result
== mStatus_ConfigChanged
)
1076 DNSServiceRegistration
*r
;
1077 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1078 if (r
->autoname
&& !SameDomainLabel(r
->name
.c
, mDNSStorage
.nicelabel
.c
))
1080 debugf("NetworkChanged renaming %#s to %#s", r
->name
.c
, mDNSStorage
.nicelabel
.c
);
1081 r
->autorename
= mDNStrue
;
1082 mDNS_DeregisterService(&mDNSStorage
, &r
->s
);
1085 else if (result
== mStatus_GrowCache
)
1087 // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore
1088 mDNSu32 numrecords
= m
->rrcache_size
;
1089 CacheRecord
*storage
= mallocL("mStatus_GrowCache", sizeof(CacheRecord
) * numrecords
);
1090 if (storage
) mDNS_GrowCache(&mDNSStorage
, storage
, numrecords
);
1094 //*************************************************************************************************************
1095 // Add / Update / Remove records from existing Registration
1097 mDNSexport kern_return_t
provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1098 int type
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
, natural_t
*reference
)
1100 // Check client parameter
1101 (void)unusedserver
; // Unused
1102 mStatus err
= mStatus_NoError
;
1103 const char *errormsg
= "Unknown";
1104 domainname
*name
= (domainname
*)"";
1105 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1106 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1107 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1108 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1109 name
= &x
->s
.RR_SRV
.resrec
.name
;
1111 // Check other parameters
1112 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1113 unsigned int size
= sizeof(RDataBody
);
1114 if (size
< data_len
)
1117 // Allocate memory, and handle failure
1118 ExtraResourceRecord
*extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
1119 if (!extra
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1121 // Fill in type, length, and data of new record
1122 extra
->r
.resrec
.rrtype
= type
;
1123 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1124 extra
->r
.resrec
.rdlength
= data_len
;
1125 memcpy(&extra
->r
.rdatastorage
.u
.data
, data
, data_len
);
1128 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1129 client
, x
->s
.RR_SRV
.resrec
.name
.c
, type
, data_len
, extra
);
1130 err
= mDNS_AddRecordToService(&mDNSStorage
, &x
->s
, extra
, &extra
->r
.rdatastorage
, ttl
);
1131 *reference
= (natural_t
)extra
;
1132 if (err
) { errormsg
= "mDNS_AddRecordToService"; goto fail
; }
1134 // Succeeded: Wrap up and return
1135 return(mStatus_NoError
);
1138 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client
, name
->c
, type
, data_len
, errormsg
, err
);
1142 mDNSlocal
void UpdateCallback(mDNS
*const m
, AuthRecord
*const rr
, RData
*OldRData
)
1145 if (OldRData
!= &rr
->rdatastorage
)
1146 freeL("Old RData", OldRData
);
1149 mDNSexport kern_return_t
provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1150 natural_t reference
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1152 // Check client parameter
1153 (void)unusedserver
; // Unused
1154 mStatus err
= mStatus_NoError
;
1155 const char *errormsg
= "Unknown";
1156 domainname
*name
= (domainname
*)"";
1157 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1158 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1159 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1160 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1161 name
= &x
->s
.RR_SRV
.resrec
.name
;
1163 // Check other parameters
1164 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1165 unsigned int size
= sizeof(RDataBody
);
1166 if (size
< data_len
)
1169 // Find the record we're updating. NULL reference means update the primary TXT record
1170 AuthRecord
*rr
= &x
->s
.RR_TXT
;
1171 if (reference
) // Scan our list to make sure we're updating a valid record that was previously added
1173 ExtraResourceRecord
*e
= x
->s
.Extras
;
1174 while (e
&& e
!= (ExtraResourceRecord
*)reference
) e
= e
->next
;
1175 if (!e
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such record"; goto fail
; }
1179 // Allocate memory, and handle failure
1180 RData
*newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
1181 if (!newrdata
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1183 // Fill in new length, and data
1184 newrdata
->MaxRDLength
= size
;
1185 memcpy(&newrdata
->u
, data
, data_len
);
1188 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, new length %d)",
1189 client
, x
->s
.RR_SRV
.resrec
.name
.c
, reference
, data_len
);
1190 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, data_len
, newrdata
, UpdateCallback
);
1191 if (err
) { errormsg
= "mDNS_Update"; goto fail
; }
1193 // Succeeded: Wrap up and return
1194 return(mStatus_NoError
);
1197 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client
, name
->c
, reference
, data_len
, errormsg
, err
);
1201 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1202 natural_t reference
)
1204 // Check client parameter
1205 (void)unusedserver
; // Unused
1206 mStatus err
= mStatus_NoError
;
1207 const char *errormsg
= "Unknown";
1208 domainname
*name
= (domainname
*)"";
1209 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1210 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1211 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1212 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1213 name
= &x
->s
.RR_SRV
.resrec
.name
;
1216 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X)", client
, x
->s
.RR_SRV
.resrec
.name
.c
, reference
);
1217 ExtraResourceRecord
*extra
= (ExtraResourceRecord
*)reference
;
1218 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, &x
->s
, extra
);
1219 if (err
) { errormsg
= "mDNS_RemoveRecordFromService (No such record)"; goto fail
; }
1221 // Succeeded: Wrap up and return
1222 if (extra
->r
.resrec
.rdata
!= &extra
->r
.rdatastorage
)
1223 freeL("Extra RData", extra
->r
.resrec
.rdata
);
1224 freeL("ExtraResourceRecord", extra
);
1225 return(mStatus_NoError
);
1228 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s (%ld)", client
, name
->c
, reference
, errormsg
, err
);
1232 //*************************************************************************************************************
1235 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1237 mig_reply_error_t
*request
= msg
;
1238 mig_reply_error_t
*reply
;
1239 mach_msg_return_t mr
;
1241 (void)port
; // Unused
1242 (void)size
; // Unused
1243 (void)info
; // Unused
1245 /* allocate a reply buffer */
1246 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
1248 /* call the MiG server routine */
1249 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
1251 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
1253 if (reply
->RetCode
== MIG_NO_REPLY
)
1256 * This return code is a little tricky -- it appears that the
1257 * demux routine found an error of some sort, but since that
1258 * error would not normally get returned either to the local
1259 * user or the remote one, we pretend it's ok.
1261 CFAllocatorDeallocate(NULL
, reply
);
1266 * destroy any out-of-line data in the request buffer but don't destroy
1267 * the reply port right (since we need that to send an error message).
1269 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1270 mach_msg_destroy(&request
->Head
);
1273 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
1275 /* no reply port, so destroy the reply */
1276 if (reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
1277 mach_msg_destroy(&reply
->Head
);
1278 CFAllocatorDeallocate(NULL
, reply
);
1285 * We don't want to block indefinitely because the client
1286 * isn't receiving messages from the reply port.
1287 * If we have a send-once right for the reply port, then
1288 * this isn't a concern because the send won't block.
1289 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1290 * To avoid falling off the kernel's fast RPC path unnecessarily,
1291 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1294 options
= MACH_SEND_MSG
;
1295 if (MACH_MSGH_BITS_REMOTE(reply
->Head
.msgh_bits
) == MACH_MSG_TYPE_MOVE_SEND_ONCE
)
1296 options
|= MACH_SEND_TIMEOUT
;
1298 mr
= mach_msg(&reply
->Head
, /* msg */
1299 options
, /* option */
1300 reply
->Head
.msgh_size
, /* send_size */
1302 MACH_PORT_NULL
, /* rcv_name */
1303 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1304 MACH_PORT_NULL
); /* notify */
1306 /* Has a message error occurred? */
1309 case MACH_SEND_INVALID_DEST
:
1310 case MACH_SEND_TIMED_OUT
:
1311 /* the reply can't be delivered, so destroy it */
1312 mach_msg_destroy(&reply
->Head
);
1316 /* Includes success case. */
1320 CFAllocatorDeallocate(NULL
, reply
);
1323 mDNSlocal kern_return_t
registerBootstrapService()
1325 kern_return_t status
;
1326 mach_port_t service_send_port
, service_rcv_port
;
1328 debugf("Registering Bootstrap Service");
1331 * See if our service name is already registered and if we have privilege to check in.
1333 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1334 if (status
== KERN_SUCCESS
)
1337 * If so, we must be a followup instance of an already defined server. In that case,
1338 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1339 * that in case we have to unregister later (which requires the privilege port).
1341 server_priv_port
= bootstrap_port
;
1342 restarting_via_mach_init
= TRUE
;
1344 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
1346 status
= bootstrap_create_server(bootstrap_port
, "/usr/sbin/mDNSResponder", getuid(),
1347 FALSE
/* relaunch immediately, not on demand */, &server_priv_port
);
1348 if (status
!= KERN_SUCCESS
) return status
;
1350 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
1351 if (status
!= KERN_SUCCESS
)
1353 mach_port_deallocate(mach_task_self(), server_priv_port
);
1357 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1358 if (status
!= KERN_SUCCESS
)
1360 mach_port_deallocate(mach_task_self(), server_priv_port
);
1361 mach_port_deallocate(mach_task_self(), service_send_port
);
1364 assert(service_send_port
== service_rcv_port
);
1368 * We have no intention of responding to requests on the service port. We are not otherwise
1369 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1370 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1371 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1373 mach_port_destroy(mach_task_self(), service_rcv_port
);
1377 mDNSlocal kern_return_t
destroyBootstrapService()
1379 debugf("Destroying Bootstrap Service");
1380 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
1383 mDNSlocal
void ExitCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1385 (void)port
; // Unused
1386 (void)msg
; // Unused
1387 (void)size
; // Unused
1388 (void)info
; // Unused
1391 int rrcache_active = 0;
1392 for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++;
1393 debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active);
1396 LogMsg("%s stopping", mDNSResponderVersionString
);
1398 debugf("ExitCallback: destroyBootstrapService");
1400 destroyBootstrapService();
1402 debugf("ExitCallback: Aborting MIG clients");
1403 while (DNSServiceDomainEnumerationList
)
1404 AbortClient(DNSServiceDomainEnumerationList
->ClientMachPort
, DNSServiceDomainEnumerationList
);
1405 while (DNSServiceBrowserList
)
1406 AbortClient(DNSServiceBrowserList
->ClientMachPort
, DNSServiceBrowserList
);
1407 while (DNSServiceResolverList
)
1408 AbortClient(DNSServiceResolverList
->ClientMachPort
, DNSServiceResolverList
);
1409 while (DNSServiceRegistrationList
)
1410 AbortClient(DNSServiceRegistrationList
->ClientMachPort
, DNSServiceRegistrationList
);
1412 debugf("ExitCallback: mDNS_Close");
1413 mDNS_Close(&mDNSStorage
);
1415 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1420 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1421 mDNSlocal
void HandleSIGTERM(int signal
)
1423 (void)signal
; // Unused
1425 debugf("SIGINT/SIGTERM");
1426 mach_msg_header_t header
;
1427 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1428 header
.msgh_remote_port
= exit_m_port
;
1429 header
.msgh_local_port
= MACH_PORT_NULL
;
1430 header
.msgh_size
= sizeof(header
);
1432 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1433 { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
1436 mDNSlocal
void INFOCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1438 (void)port
; // Unused
1439 (void)msg
; // Unused
1440 (void)size
; // Unused
1441 (void)info
; // Unused
1442 DNSServiceDomainEnumeration
*e
;
1443 DNSServiceBrowser
*b
;
1444 DNSServiceResolver
*l
;
1445 DNSServiceRegistration
*r
;
1448 mDNSu32 CacheUsed
= 0, CacheActive
= 0;
1450 LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString
);
1452 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
1453 for (rr
= mDNSStorage
.rrcache_hash
[slot
]; rr
; rr
=rr
->next
)
1456 if (rr
->CRActiveQuestion
) CacheActive
++;
1457 LogMsg("%s %-5s%-6s%s", rr
->CRActiveQuestion
? "Active: " : "Inactive:", DNSTypeName(rr
->resrec
.rrtype
),
1458 ((NetworkInterfaceInfoOSX
*)rr
->resrec
.InterfaceID
)->ifa_name
, GetRRDisplayString(&mDNSStorage
, rr
));
1459 usleep(1000); // Limit rate a little so we don't flood syslog too fast
1461 if (mDNSStorage
.rrcache_totalused
!= CacheUsed
)
1462 LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage
.rrcache_totalused
, CacheUsed
);
1463 if (mDNSStorage
.rrcache_active
!= CacheActive
)
1464 LogMsg("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage
.rrcache_active
, CacheActive
);
1465 LogMsg("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed
, CacheActive
);
1467 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
1468 LogMsg("%5d: DomainEnumeration %##s", e
->ClientMachPort
, e
->dom
.qname
.c
);
1470 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
1471 LogMsg("%5d: ServiceBrowse %##s", b
->ClientMachPort
, b
->q
.qname
.c
);
1473 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1474 LogMsg("%5d: ServiceResolve %##s", l
->ClientMachPort
, l
->i
.name
.c
);
1476 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1477 LogMsg("%5d: ServiceRegistration %##s", r
->ClientMachPort
, r
->s
.RR_SRV
.resrec
.name
.c
);
1481 LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString
);
1484 mDNSlocal
void HandleSIGINFO(int signal
)
1486 (void)signal
; // Unused
1487 mach_msg_header_t header
;
1488 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1489 header
.msgh_remote_port
= info_m_port
;
1490 header
.msgh_local_port
= MACH_PORT_NULL
;
1491 header
.msgh_size
= sizeof(header
);
1493 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1494 LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated.");
1497 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
1500 CFMachPortRef d_port
= CFMachPortCreate(NULL
, ClientDeathCallback
, NULL
, NULL
);
1501 CFMachPortRef s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
1502 CFMachPortRef e_port
= CFMachPortCreate(NULL
, ExitCallback
, NULL
, NULL
);
1503 CFMachPortRef i_port
= CFMachPortCreate(NULL
, INFOCallback
, NULL
, NULL
);
1504 mach_port_t m_port
= CFMachPortGetPort(s_port
);
1505 char *MachServerName
= mDNSMacOSXSystemBuildNumber(NULL
) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
1506 kern_return_t status
= bootstrap_register(bootstrap_port
, MachServerName
, m_port
);
1507 CFRunLoopSourceRef d_rls
= CFMachPortCreateRunLoopSource(NULL
, d_port
, 0);
1508 CFRunLoopSourceRef s_rls
= CFMachPortCreateRunLoopSource(NULL
, s_port
, 0);
1509 CFRunLoopSourceRef e_rls
= CFMachPortCreateRunLoopSource(NULL
, e_port
, 0);
1510 CFRunLoopSourceRef i_rls
= CFMachPortCreateRunLoopSource(NULL
, i_port
, 0);
1515 LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1517 LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status
), status
);
1521 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
1522 rrcachestorage
, RR_CACHE_SIZE
,
1523 mDNS_Init_AdvertiseLocalAddresses
,
1524 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
1525 if (err
) { LogMsg("Daemon start: mDNS_Init failed %ld", err
); return(err
); }
1527 client_death_port
= CFMachPortGetPort(d_port
);
1528 exit_m_port
= CFMachPortGetPort(e_port
);
1529 info_m_port
= CFMachPortGetPort(i_port
);
1531 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls
, kCFRunLoopDefaultMode
);
1532 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls
, kCFRunLoopDefaultMode
);
1533 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls
, kCFRunLoopDefaultMode
);
1534 CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls
, kCFRunLoopDefaultMode
);
1539 if (debug_mode
) printf("Service registered with Mach Port %d\n", m_port
);
1541 err
= udsserver_init();
1542 if (err
) { LogMsg("Daemon start: udsserver_init failed"); return err
; }
1543 err
= udsserver_add_rl_source();
1544 if (err
) { LogMsg("Daemon start: udsserver_add_rl_source failed"); return err
; }
1549 mDNSlocal mDNSs32
mDNSDaemonIdle(void)
1551 // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
1552 mDNSs32 nextevent
= mDNS_Execute(&mDNSStorage
);
1554 mDNSs32 now
= mDNSPlatformTimeNow();
1556 // 2. Deliver any waiting browse messages to clients
1557 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
1561 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
1562 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
1563 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
1564 DNSServiceBrowser
*x
= b
;
1566 if (x
->results
) // Try to deliver the list of results
1570 DNSServiceBrowserResult
*const r
= x
->results
;
1572 domainname type
, domain
;
1573 DeconstructServiceName(&r
->result
, &name
, &type
, &domain
); // Don't need to check result; already validated in FoundInstance()
1574 char cname
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
1575 char ctype
[MAX_ESCAPED_DOMAIN_NAME
];
1576 char cdom
[MAX_ESCAPED_DOMAIN_NAME
];
1577 ConvertDomainLabelToCString_unescaped(&name
, cname
);
1578 ConvertDomainNameToCString(&type
, ctype
);
1579 ConvertDomainNameToCString(&domain
, cdom
);
1580 DNSServiceDiscoveryReplyFlags flags
= (r
->next
) ? DNSServiceDiscoverReplyFlagsMoreComing
: 0;
1581 kern_return_t status
= DNSServiceBrowserReply_rpc(x
->ClientMachPort
, r
->resultType
, cname
, ctype
, cdom
, flags
, 1);
1582 // If we failed to send the mach message, try again in one second
1583 if (status
== MACH_SEND_TIMED_OUT
)
1585 if (nextevent
- now
> mDNSPlatformOneSecond
)
1586 nextevent
= now
+ mDNSPlatformOneSecond
;
1591 x
->lastsuccess
= now
;
1592 x
->results
= x
->results
->next
;
1593 freeL("DNSServiceBrowserResult", r
);
1596 // If this client hasn't read a single message in the last 60 seconds, abort it
1597 if (now
- x
->lastsuccess
>= 60 * mDNSPlatformOneSecond
)
1598 AbortBlockedClient(x
->ClientMachPort
, "browse", x
);
1602 DNSServiceResolver
*l
;
1603 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1604 if (l
->ReportTime
&& now
- l
->ReportTime
>= 0)
1607 LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. "
1608 "This places considerable burden on the network.", l
->ClientMachPort
, l
->i
.name
.c
);
1614 mDNSexport
int main(int argc
, char **argv
)
1617 kern_return_t status
;
1620 for (i
=1; i
<argc
; i
++)
1622 if (!strcmp(argv
[i
], "-d")) debug_mode
= 1;
1625 signal(SIGINT
, HandleSIGTERM
); // SIGINT is what you get for a Ctrl-C
1626 signal(SIGTERM
, HandleSIGTERM
);
1627 signal(SIGINFO
, HandleSIGINFO
);
1629 // Register the server with mach_init for automatic restart only during debug mode
1631 registerBootstrapService();
1633 if (!debug_mode
&& !restarting_via_mach_init
)
1634 exit(0); /* mach_init will restart us immediately as a daemon */
1636 // Unlike deamon(), mach_init does redirect standard file descriptors to /dev/null
1639 int fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
1642 // Avoid to unnecessarily duplicate a file descriptor to itself
1643 if (fd
!= STDIN_FILENO
) (void)dup2(fd
, STDIN_FILENO
);
1644 if (fd
!= STDOUT_FILENO
) (void)dup2(fd
, STDOUT_FILENO
);
1645 if (fd
!= STDERR_FILENO
) (void)dup2(fd
, STDERR_FILENO
);
1646 if (fd
!= STDIN_FILENO
&& fd
!= STDOUT_FILENO
&& fd
!= STDERR_FILENO
)
1651 fp
= fopen(PID_FILE
, "w");
1654 fprintf(fp
, "%d\n", getpid());
1658 LogMsg("%s starting", mDNSResponderVersionString
);
1659 status
= mDNSDaemonInitialize();
1664 int RunLoopStatus
= kCFRunLoopRunTimedOut
;
1666 // This is the main work loop:
1667 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
1668 // (2) Then we make sure we've delivered all waiting browse messages to our clients
1669 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
1670 // (4) On wakeup we first process *all* events
1671 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
1672 while (RunLoopStatus
== kCFRunLoopRunTimedOut
)
1674 // 1. Before going into a blocking wait call and letting our process to go sleep,
1675 // call mDNSDaemonIdle to allow any deferred work to be completed.
1676 mDNSs32 nextevent
= mDNSDaemonIdle();
1678 nextevent
= udsserver_idle(nextevent
);
1681 // 2. Work out how long we expect to sleep before the next scheduled task
1682 mDNSs32 ticks
= nextevent
- mDNSPlatformTimeNow();
1683 if (ticks
< 1) ticks
= 1;
1684 CFAbsoluteTime interval
= (CFAbsoluteTime
)ticks
/ (CFAbsoluteTime
)mDNSPlatformOneSecond
;
1686 // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
1687 // (a) our next wakeup time, or (b) an event occurs.
1688 // The 'true' parameter makes it return after handling any event that occurs
1689 // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
1690 verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents
, ticks
);
1692 RunLoopStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, interval
, true);
1694 // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
1695 while (RunLoopStatus
== kCFRunLoopRunHandledSource
)
1698 RunLoopStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.0, true);
1702 LogMsg("ERROR: CFRunLoopRun Exiting.");
1703 mDNS_Close(&mDNSStorage
);
1706 destroyBootstrapService();
1711 // For convenience when using the "strings" command, this is the last thing in the file
1712 mDNSexport
const char mDNSResponderVersionString
[] = STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";