2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
25 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
26 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
27 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
28 * therefore common sense dictates that if they are part of a compound statement then they
29 * should be indented to the same level as everything else in that compound statement.
30 * Indenting curly braces at the same level as the "if" implies that curly braces are
31 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
32 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
33 * understand why variable y is not of type "char*" just proves the point that poor code
34 * layout leads people to unfortunate misunderstandings about how the C language really works.)
36 Change History (most recent first):
39 Revision 1.134.2.8 2005/01/28 04:03:24 cheshire
40 <rdar://problem/3759302> SUPan: Current method of doing subtypes causes name collisions
41 Summary: Pulled in ConstructServiceName, CountSubTypes and AllocateSubTypes from Tiger version.
43 Revision 1.134.2.7 2004/06/18 17:28:19 cheshire
44 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
46 Revision 1.134.2.6 2004/04/06 19:50:36 cheshire
47 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
48 After more discussion, we've decided to use userid -2 if "nobody" user doesn't exist.
50 Revision 1.134.2.5 2004/04/03 01:29:07 cheshire
51 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
52 If "nobody" user doesn't exist, log a message and continue as "root"
54 Revision 1.134.2.4 2004/04/02 21:50:21 cheshire
55 Fix errors in comments
57 Revision 1.134.2.3 2003/12/12 01:21:30 cheshire
58 <rdar://problem/3491108> mDNSResponder should not run as root
60 Revision 1.134.2.2 2003/12/05 00:03:35 cheshire
61 <rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
63 Revision 1.134.2.1 2003/12/03 11:00:09 cheshire
64 Update "mDNSResponderVersion" mechanism to allow dots so we can do mDNSResponder-58.1 for SUPan
66 Revision 1.134 2003/08/21 20:01:37 cheshire
67 <rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
69 Revision 1.133 2003/08/20 23:39:31 cheshire
70 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
72 Revision 1.132 2003/08/20 01:44:56 cheshire
73 Fix errors in LogOperation() calls (only used for debugging)
75 Revision 1.131 2003/08/19 05:39:43 cheshire
76 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
78 Revision 1.130 2003/08/16 03:39:01 cheshire
79 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
81 Revision 1.129 2003/08/15 20:16:03 cheshire
82 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
83 We want to avoid touching the rdata pages, so we don't page them in.
84 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
85 Moved this from the RData to the ResourceRecord object.
86 2. To avoid unnecessarily touching the rdata just to compare it,
87 compute a hash of the rdata and store the hash in the ResourceRecord object.
89 Revision 1.128 2003/08/14 19:30:36 cheshire
90 <rdar://problem/3378473> Include list of cache records in SIGINFO output
92 Revision 1.127 2003/08/14 02:18:21 cheshire
93 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
95 Revision 1.126 2003/08/12 19:56:25 cheshire
98 Revision 1.125 2003/08/08 18:36:04 cheshire
99 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
101 Revision 1.124 2003/07/25 18:28:23 cheshire
102 Minor fix to error messages in syslog: Display string parameters with quotes
104 Revision 1.123 2003/07/23 17:45:28 cheshire
105 <rdar://problem/3339388> mDNSResponder leaks a bit
106 Don't allocate memory for the reply until after we've verified that the reply is valid
108 Revision 1.122 2003/07/23 00:00:04 cheshire
111 Revision 1.121 2003/07/20 03:38:51 ksekar
113 Completed support for Unix-domain socket based API.
115 Revision 1.120 2003/07/18 00:30:00 cheshire
116 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
118 Revision 1.119 2003/07/17 19:08:58 cheshire
119 <rdar://problem/3332153> Remove calls to enable obsolete UDS code
121 Revision 1.118 2003/07/15 21:12:28 cheshire
122 Added extra debugging checks in validatelists() (not used in final shipping version)
124 Revision 1.117 2003/07/15 01:55:15 cheshire
125 <rdar://problem/3315777> Need to implement service registration with subtypes
127 Revision 1.116 2003/07/02 21:19:51 cheshire
128 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
130 Revision 1.115 2003/07/02 02:41:24 cheshire
131 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
133 Revision 1.114 2003/07/01 21:10:20 cheshire
134 Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
136 Revision 1.113 2003/06/28 17:27:43 vlubet
137 <rdar://problem/3221246> Redirect standard input, standard output, and
138 standard error file descriptors to /dev/null just like any other
141 Revision 1.112 2003/06/25 23:42:19 ksekar
142 Bug #: <rdar://problem/3249292>: Feature: New Rendezvous APIs (#7875)
143 Reviewed by: Stuart Cheshire
144 Added files necessary to implement Unix domain sockets based enhanced
145 Rendezvous APIs, and integrated with existing Mach-port based daemon.
147 Revision 1.111 2003/06/11 01:02:43 cheshire
148 <rdar://problem/3287858> mDNSResponder binary compatibility
149 Make single binary that can run on both Jaguar and Panther.
151 Revision 1.110 2003/06/10 01:14:11 cheshire
152 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
154 Revision 1.109 2003/06/06 19:53:43 cheshire
155 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
156 (Global search-and-replace; no functional change to code execution.)
158 Revision 1.108 2003/06/06 14:08:06 cheshire
159 For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
161 Revision 1.107 2003/05/29 05:44:55 cheshire
162 Minor fixes to log messages
164 Revision 1.106 2003/05/27 18:30:55 cheshire
165 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
166 Dean Reece suggested SIGINFO is more appropriate than SIGHUP
168 Revision 1.105 2003/05/26 03:21:29 cheshire
169 Tidy up address structure naming:
170 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
171 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
172 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
174 Revision 1.104 2003/05/26 00:42:06 cheshire
175 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
177 Revision 1.103 2003/05/23 23:07:44 cheshire
178 <rdar://problem/3268199> Must not write to stderr when running as daemon
180 Revision 1.102 2003/05/22 01:32:31 cheshire
181 Fix typo in Log message format string
183 Revision 1.101 2003/05/22 00:26:55 cheshire
184 <rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
185 Modify error message to explain that this is technically legal, but may indicate a bug.
187 Revision 1.100 2003/05/21 21:02:24 ksekar
188 Bug #: <rdar://problem/3247035>: Service should be prefixed
189 Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
190 Mach message port to "com.apple.mDNSResponder.
192 Revision 1.99 2003/05/21 17:33:49 cheshire
193 Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
195 Revision 1.98 2003/05/20 00:33:07 cheshire
196 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
197 SIGHUP now writes state summary to syslog
199 Revision 1.97 2003/05/08 00:19:08 cheshire
200 <rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
202 Revision 1.96 2003/05/07 22:10:46 cheshire
203 <rdar://problem/3250330> Add a few more error logging messages
205 Revision 1.95 2003/05/07 19:20:17 cheshire
206 <rdar://problem/3251391> Add version number to mDNSResponder builds
208 Revision 1.94 2003/05/07 00:28:18 cheshire
209 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
211 Revision 1.93 2003/05/06 00:00:49 cheshire
212 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
214 Revision 1.92 2003/04/04 20:38:57 cheshire
219 #include <mach/mach.h>
220 #include <mach/mach_error.h>
221 #include <servers/bootstrap.h>
222 #include <sys/types.h>
228 #include "DNSServiceDiscoveryRequestServer.h"
229 #include "DNSServiceDiscoveryReply.h"
231 #include "mDNSClientAPI.h" // Defines the interface to the client layer above
232 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
234 extern mDNSs32
CountSubTypes(char *regtype
);
235 extern AuthRecord
*AllocateSubTypes(mDNSs32 NumSubTypes
, char *p
);
237 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
241 //*************************************************************************************************************
244 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
245 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
246 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
247 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
248 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
250 //*************************************************************************************************************
253 mDNSexport mDNS mDNSStorage
;
254 static mDNS_PlatformSupport PlatformStorage
;
255 #define RR_CACHE_SIZE 64
256 static CacheRecord rrcachestorage
[RR_CACHE_SIZE
];
257 static const char PID_FILE
[] = "/var/run/mDNSResponder.pid";
259 static const char kmDNSBootstrapName
[] = "com.apple.mDNSResponderRestart";
260 static mach_port_t client_death_port
= MACH_PORT_NULL
;
261 static mach_port_t exit_m_port
= MACH_PORT_NULL
;
262 static mach_port_t info_m_port
= MACH_PORT_NULL
;
263 static mach_port_t server_priv_port
= MACH_PORT_NULL
;
265 // mDNS Mach Message Timeout, in milliseconds.
266 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
267 // fails to service its mach message queue, but long enough to give a well-written
268 // client a chance to service its mach message queue without getting cut off.
269 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
270 // even extra-slow clients a fair chance before we cut them off.
271 #define MDNS_MM_TIMEOUT 250
273 static int restarting_via_mach_init
= 0;
281 //*************************************************************************************************************
282 // Active client list structures
284 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
285 struct DNSServiceDomainEnumeration_struct
287 DNSServiceDomainEnumeration
*next
;
288 mach_port_t ClientMachPort
;
289 DNSQuestion dom
; // Question asking for domains
290 DNSQuestion def
; // Question asking for default domain
293 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult
;
294 struct DNSServiceBrowserResult_struct
296 DNSServiceBrowserResult
*next
;
301 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
302 struct DNSServiceBrowser_struct
304 DNSServiceBrowser
*next
;
305 mach_port_t ClientMachPort
;
307 DNSServiceBrowserResult
*results
;
311 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
312 struct DNSServiceResolver_struct
314 DNSServiceResolver
*next
;
315 mach_port_t ClientMachPort
;
321 typedef struct DNSServiceRegistration_struct DNSServiceRegistration
;
322 struct DNSServiceRegistration_struct
324 DNSServiceRegistration
*next
;
325 mach_port_t ClientMachPort
;
330 // Don't add any fields after ServiceRecordSet.
331 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
334 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
335 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
336 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
337 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
339 //*************************************************************************************************************
340 // General Utility Functions
342 #if MACOSX_MDNS_MALLOC_DEBUGGING
344 char _malloc_options
[] = "AXZ";
346 static void validatelists(mDNS
*const m
)
348 DNSServiceDomainEnumeration
*e
;
349 DNSServiceBrowser
*b
;
350 DNSServiceResolver
*l
;
351 DNSServiceRegistration
*r
;
357 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
358 if (e
->ClientMachPort
== 0 || e
->ClientMachPort
== (mach_port_t
)~0)
359 LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e
, e
->ClientMachPort
);
361 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
362 if (b
->ClientMachPort
== 0 || b
->ClientMachPort
== (mach_port_t
)~0)
363 LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b
, b
->ClientMachPort
);
365 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
366 if (l
->ClientMachPort
== 0 || l
->ClientMachPort
== (mach_port_t
)~0)
367 LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l
, l
->ClientMachPort
);
369 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
370 if (r
->ClientMachPort
== 0 || r
->ClientMachPort
== (mach_port_t
)~0)
371 LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r
, r
->ClientMachPort
);
373 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
374 if (rr
->RecordType
== 0 || rr
->RecordType
== 0xFF)
375 LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr
, rr
->RecordType
);
377 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
)
378 if (rr
->RecordType
== 0 || rr
->RecordType
== 0xFF)
379 LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr
, rr
->RecordType
);
381 for (q
= m
->Questions
; q
; q
=q
->next
)
382 if (q
->ThisQInterval
== (mDNSs32
)~0)
383 LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q
, q
->ThisQInterval
);
385 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
386 for (cr
= mDNSStorage
.rrcache_hash
[slot
]; cr
; cr
=cr
->next
)
387 if (cr
->RecordType
== 0 || cr
->RecordType
== 0xFF)
388 LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot
, rr
, rr
->RecordType
);
391 void *mallocL(char *msg
, unsigned int size
)
393 unsigned long *mem
= malloc(size
+8);
396 LogMsg("malloc( %s : %d ) failed", msg
, size
);
401 LogMalloc("malloc( %s : %lu ) = %p", msg
, size
, &mem
[2]);
404 //bzero(&mem[2], size);
405 memset(&mem
[2], 0xFF, size
);
406 validatelists(&mDNSStorage
);
411 void freeL(char *msg
, void *x
)
414 LogMsg("free( %s @ NULL )!", msg
);
417 unsigned long *mem
= ((unsigned long *)x
) - 2;
418 if (mem
[0] != 0xDEAD1234)
419 { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
421 { LogMsg("free( %s : %ld @ %p) too big!", msg
, mem
[1], &mem
[2]); return; }
422 LogMalloc("free( %s : %ld @ %p)", msg
, mem
[1], &mem
[2]);
423 //bzero(mem, mem[1]+8);
424 memset(mem
, 0xFF, mem
[1]+8);
425 validatelists(&mDNSStorage
);
432 //*************************************************************************************************************
433 // Client Death Detection
435 mDNSlocal
void FreeDNSServiceRegistration(DNSServiceRegistration
*x
)
439 ExtraResourceRecord
*extras
= x
->s
.Extras
;
440 x
->s
.Extras
= x
->s
.Extras
->next
;
441 if (extras
->r
.resrec
.rdata
!= &extras
->r
.rdatastorage
)
442 freeL("Extra RData", extras
->r
.resrec
.rdata
);
443 freeL("ExtraResourceRecord", extras
);
446 if (x
->s
.RR_TXT
.resrec
.rdata
!= &x
->s
.RR_TXT
.rdatastorage
)
447 freeL("TXT RData", x
->s
.RR_TXT
.resrec
.rdata
);
449 if (x
->s
.SubTypes
) freeL("ServiceSubTypes", x
->s
.SubTypes
);
451 freeL("DNSServiceRegistration", x
);
454 // AbortClient finds whatever client is identified by the given Mach port,
455 // stops whatever operation that client was doing, and frees its memory.
456 // In the case of a service registration, the actual freeing may be deferred
457 // until we get the mStatus_MemFree message, if necessary
458 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
, void *m
)
460 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
461 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
462 DNSServiceResolver
**l
= &DNSServiceResolverList
;
463 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
465 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
468 DNSServiceDomainEnumeration
*x
= *e
;
471 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->dom
.qname
.c
, m
, x
);
472 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort
, x
->dom
.qname
.c
);
473 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
474 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
475 freeL("DNSServiceDomainEnumeration", x
);
479 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
482 DNSServiceBrowser
*x
= *b
;
485 LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->q
.qname
.c
, m
, x
);
486 else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort
, x
->q
.qname
.c
);
487 mDNS_StopBrowse(&mDNSStorage
, &x
->q
);
490 DNSServiceBrowserResult
*r
= x
->results
;
491 x
->results
= x
->results
->next
;
492 freeL("DNSServiceBrowserResult", r
);
494 freeL("DNSServiceBrowser", x
);
498 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
501 DNSServiceResolver
*x
= *l
;
504 LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->i
.name
.c
, m
, x
);
505 else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort
, x
->i
.name
.c
);
506 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
507 freeL("DNSServiceResolver", x
);
511 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
514 DNSServiceRegistration
*x
= *r
;
516 x
->autorename
= mDNSfalse
;
518 LogMsg("%5d: DNSServiceRegistration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->s
.RR_SRV
.resrec
.name
.c
, m
, x
);
519 else LogOperation("%5d: DNSServiceRegistration(%##s) STOP", ClientMachPort
, x
->s
.RR_SRV
.resrec
.name
.c
);
520 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
521 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
522 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
523 // the list, so we should go ahead and free the memory right now
524 if (mDNS_DeregisterService(&mDNSStorage
, &x
->s
) != mStatus_NoError
)
525 FreeDNSServiceRegistration(x
);
529 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort
);
532 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
534 mDNSlocal
void AbortClientWithLogMessage(mach_port_t c
, char *reason
, char *msg
, void *m
)
536 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
537 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
538 DNSServiceResolver
*l
= DNSServiceResolverList
;
539 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
540 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
541 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
542 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
543 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
544 if (e
) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c
, e
->dom
.qname
.c
, reason
, msg
);
545 else if (b
) LogMsg("%5d: Browser(%##s) %s%s", c
, b
->q
.qname
.c
, reason
, msg
);
546 else if (l
) LogMsg("%5d: Resolver(%##s) %s%s", c
, l
->i
.name
.c
, reason
, msg
);
547 else if (r
) LogMsg("%5d: Registration(%##s) %s%s", c
, r
->s
.RR_SRV
.resrec
.name
.c
, reason
, msg
);
548 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c
, reason
, msg
);
553 mDNSlocal mDNSBool
CheckForExistingClient(mach_port_t c
)
555 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
556 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
557 DNSServiceResolver
*l
= DNSServiceResolverList
;
558 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
559 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
560 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
561 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
562 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
563 if (e
) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c
, e
->dom
.qname
.c
);
564 if (b
) LogMsg("%5d: Browser(%##s) already exists!", c
, b
->q
.qname
.c
);
565 if (l
) LogMsg("%5d: Resolver(%##s) already exists!", c
, l
->i
.name
.c
);
566 if (r
) LogMsg("%5d: Registration(%##s) already exists!", c
, r
->s
.RR_SRV
.resrec
.name
.c
);
567 return(e
|| b
|| l
|| r
);
570 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
572 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
573 (void)unusedport
; // Unused
574 (void)size
; // Unused
575 (void)info
; // Unused
576 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
578 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
579 AbortClient(deathMessage
->not_port
, NULL
);
581 /* Deallocate the send right that came in the dead name notification */
582 mach_port_destroy( mach_task_self(), deathMessage
->not_port
);
586 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
, void *m
)
589 kern_return_t r
= mach_port_request_notification(mach_task_self(), ClientMachPort
, MACH_NOTIFY_DEAD_NAME
, 0,
590 client_death_port
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &prev
);
591 // If the port already died while we were thinking about it, then abort the operation right away
592 if (r
!= KERN_SUCCESS
)
593 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
596 //*************************************************************************************************************
597 // Domain Enumeration
599 mDNSlocal
void FoundDomain(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
601 kern_return_t status
;
603 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
604 DNSServiceDomainEnumerationReplyResultType rt
;
605 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->QuestionContext
;
607 debugf("FoundDomain: %##s PTR %##s", answer
->name
.c
, answer
->rdata
->u
.name
.c
);
608 if (answer
->rrtype
!= kDNSType_PTR
) return;
609 if (!x
) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
613 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
614 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
618 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
622 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
623 x
->ClientMachPort
, x
->dom
.qname
.c
, answer
->rdata
->u
.name
.c
,
624 !AddRecord
? "RemoveDomain" :
625 question
== &x
->dom
? "AddDomain" : "AddDomainDefault");
627 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, buffer
);
628 status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, buffer
, 0, MDNS_MM_TIMEOUT
);
629 if (status
== MACH_SEND_TIMED_OUT
)
630 AbortBlockedClient(x
->ClientMachPort
, "enumeration", x
);
633 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
636 // Check client parameter
637 (void)unusedserver
; // Unused
638 mStatus err
= mStatus_NoError
;
639 const char *errormsg
= "Unknown";
640 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
641 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
643 mDNS_DomainType dt1
= regDom
? mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
644 mDNS_DomainType dt2
= regDom
? mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
645 const DNSServiceDomainEnumerationReplyResultType rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
647 // Allocate memory, and handle failure
648 DNSServiceDomainEnumeration
*x
= mallocL("DNSServiceDomainEnumeration", sizeof(*x
));
649 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
651 // Set up object, and link into list
652 x
->ClientMachPort
= client
;
653 x
->next
= DNSServiceDomainEnumerationList
;
654 DNSServiceDomainEnumerationList
= x
;
656 // Generate initial response
657 verbosedebugf("%5d: Enumerate %s Domains", client
, regDom
? "Registration" : "Browsing");
658 // We always give local. as the initial default browse domain, and then look for more
659 kern_return_t status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, "local.", 0, MDNS_MM_TIMEOUT
);
660 if (status
== MACH_SEND_TIMED_OUT
)
661 { AbortBlockedClient(x
->ClientMachPort
, "local enumeration", x
); return(mStatus_UnknownErr
); }
664 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, mDNSInterface_Any
, FoundDomain
, x
);
665 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, mDNSInterface_Any
, FoundDomain
, x
);
666 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_GetDomains"; goto fail
; }
668 // Succeeded: Wrap up and return
669 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client
, x
->dom
.qname
.c
);
670 EnableDeathNotificationForClient(client
, x
);
671 return(mStatus_NoError
);
674 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client
, regDom
, errormsg
, err
);
678 //*************************************************************************************************************
679 // Browse for services
681 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
685 if (answer
->rrtype
!= kDNSType_PTR
)
686 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
); return; }
689 domainname type
, domain
;
690 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
692 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
693 answer
->name
.c
, answer
->rdata
->u
.name
.c
);
697 DNSServiceBrowserResult
*x
= mallocL("DNSServiceBrowserResult", sizeof(*x
));
698 if (!x
) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer
->rdata
->u
.name
.c
); return; }
700 verbosedebugf("FoundInstance: %s %##s", AddRecord
? "Add" : "Rmv", answer
->rdata
->u
.name
.c
);
701 AssignDomainName(x
->result
, answer
->rdata
->u
.name
);
703 x
->resultType
= DNSServiceBrowserReplyAddInstance
;
704 else x
->resultType
= DNSServiceBrowserReplyRemoveInstance
;
707 DNSServiceBrowser
*browser
= (DNSServiceBrowser
*)question
->QuestionContext
;
708 DNSServiceBrowserResult
**p
= &browser
->results
;
709 while (*p
) p
= &(*p
)->next
;
713 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
714 DNSCString regtype
, DNSCString domain
)
716 // Check client parameter
717 (void)unusedserver
; // Unused
718 mStatus err
= mStatus_NoError
;
719 const char *errormsg
= "Unknown";
720 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
721 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
723 // Check other parameters
726 mDNSs32 NumSubTypes
= CountSubTypes(regtype
);
727 if (NumSubTypes
< 0 || NumSubTypes
> 1) { errormsg
= "Bad Service SubType"; goto badparam
; }
728 if (NumSubTypes
== 1 && !AppendDNSNameString(&t
, regtype
+ strlen(regtype
) + 1))
729 { errormsg
= "Bad Service SubType"; goto badparam
; }
730 if (!regtype
[0] || !AppendDNSNameString(&t
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
731 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Illegal domain"; goto badparam
; }
733 // Allocate memory, and handle failure
734 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
735 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
737 // Set up object, and link into list
738 x
->ClientMachPort
= client
;
741 x
->next
= DNSServiceBrowserList
;
742 DNSServiceBrowserList
= x
;
745 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client
, t
.c
, d
.c
);
746 err
= mDNS_StartBrowse(&mDNSStorage
, &x
->q
, &t
, &d
, mDNSInterface_Any
, FoundInstance
, x
);
747 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartBrowse"; goto fail
; }
749 // Succeeded: Wrap up and return
750 EnableDeathNotificationForClient(client
, x
);
751 return(mStatus_NoError
);
754 err
= mStatus_BadParamErr
;
756 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client
, regtype
, domain
, errormsg
, err
);
760 //*************************************************************************************************************
761 // Resolve Service Info
763 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
765 kern_return_t status
;
766 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->ServiceInfoQueryContext
;
767 NetworkInterfaceInfoOSX
*ifx
= (NetworkInterfaceInfoOSX
*)query
->info
->InterfaceID
;
768 if (query
->info
->InterfaceID
== (mDNSInterfaceID
)~0) ifx
= mDNSNULL
;
769 struct sockaddr_storage interface
;
770 struct sockaddr_storage address
;
772 int i
, pstrlen
= query
->info
->TXTinfo
[0];
775 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
777 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
779 bzero(&interface
, sizeof(interface
));
780 bzero(&address
, sizeof(address
));
782 if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
)
784 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&interface
;
785 sin
->sin_len
= sizeof(*sin
);
786 sin
->sin_family
= AF_INET
;
788 sin
->sin_addr
.s_addr
= ifx
->ifinfo
.ip
.ip
.v4
.NotAnInteger
;
790 else if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
)
792 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&interface
;
793 sin6
->sin6_len
= sizeof(*sin6
);
794 sin6
->sin6_family
= AF_INET6
;
795 sin6
->sin6_flowinfo
= 0;
797 sin6
->sin6_addr
= *(struct in6_addr
*)&ifx
->ifinfo
.ip
.ip
.v6
;
798 sin6
->sin6_scope_id
= ifx
->scope_id
;
801 if (query
->info
->ip
.type
== mDNSAddrType_IPv4
)
803 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&address
;
804 sin
->sin_len
= sizeof(*sin
);
805 sin
->sin_family
= AF_INET
;
806 sin
->sin_port
= query
->info
->port
.NotAnInteger
;
807 sin
->sin_addr
.s_addr
= query
->info
->ip
.ip
.v4
.NotAnInteger
;
811 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&address
;
812 sin6
->sin6_len
= sizeof(*sin6
);
813 sin6
->sin6_family
= AF_INET6
;
814 sin6
->sin6_port
= query
->info
->port
.NotAnInteger
;
815 sin6
->sin6_flowinfo
= 0;
816 sin6
->sin6_addr
= *(struct in6_addr
*)&query
->info
->ip
.ip
.v6
;
817 sin6
->sin6_scope_id
= ifx
? ifx
->scope_id
: 0;
820 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
821 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
822 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
823 // ASCII-1 characters are used in the C-string as boundary markers,
824 // to indicate the boundaries between the original constituent P-strings.
825 for (i
=1; i
<query
->info
->TXTlen
; i
++)
828 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
832 pstrlen
= query
->info
->TXTinfo
[i
];
835 cstring
[i
-1] = 0; // Put the terminating NULL on the end
837 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%d", x
->ClientMachPort
,
838 x
->i
.name
.c
, &query
->info
->ip
, (int)query
->info
->port
.b
[0] << 8 | query
->info
->port
.b
[1]);
839 status
= DNSServiceResolverReply_rpc(x
->ClientMachPort
,
840 (char*)&interface
, (char*)&address
, cstring
, 0, MDNS_MM_TIMEOUT
);
841 if (status
== MACH_SEND_TIMED_OUT
)
842 AbortBlockedClient(x
->ClientMachPort
, "resolve", x
);
845 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
846 DNSCString name
, DNSCString regtype
, DNSCString domain
)
848 // Check client parameter
849 (void)unusedserver
; // Unused
850 mStatus err
= mStatus_NoError
;
851 const char *errormsg
= "Unknown";
852 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
853 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
855 // Check other parameters
857 domainname t
, d
, srv
;
858 if (!name
[0] || !MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
859 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
860 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
861 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
863 // Allocate memory, and handle failure
864 DNSServiceResolver
*x
= mallocL("DNSServiceResolver", sizeof(*x
));
865 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
867 // Set up object, and link into list
868 x
->ClientMachPort
= client
;
869 x
->i
.InterfaceID
= mDNSInterface_Any
;
871 x
->ReportTime
= (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond
) | 1;
872 // Don't report errors for old iChat ("_ichat._tcp") service.
873 // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead,
874 // and so should other applications that have valid reasons to be doing ongoing record monitoring.
875 if (SameDomainLabel(t
.c
, (mDNSu8
*)"\x6_ichat")) x
->ReportTime
= 0;
876 x
->next
= DNSServiceResolverList
;
877 DNSServiceResolverList
= x
;
880 LogOperation("%5d: DNSServiceResolver(%##s) START", client
, x
->i
.name
.c
);
881 err
= mDNS_StartResolveService(&mDNSStorage
, &x
->q
, &x
->i
, FoundInstanceInfo
, x
);
882 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartResolveService"; goto fail
; }
884 // Succeeded: Wrap up and return
885 EnableDeathNotificationForClient(client
, x
);
886 return(mStatus_NoError
);
889 err
= mStatus_BadParamErr
;
891 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client
, name
, regtype
, domain
, errormsg
, err
);
895 //*************************************************************************************************************
898 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
900 DNSServiceRegistration
*x
= (DNSServiceRegistration
*)sr
->ServiceContext
;
902 if (result
== mStatus_NoError
)
904 kern_return_t status
;
905 LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
906 status
= DNSServiceRegistrationReply_rpc(x
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
907 if (status
== MACH_SEND_TIMED_OUT
)
908 AbortBlockedClient(x
->ClientMachPort
, "registration success", x
);
911 else if (result
== mStatus_NameConflict
)
913 LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
914 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
915 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
917 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
920 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
921 // of their registration in the usual way (which we will catch via client death notification).
922 // If the Mach queue is full, we forcibly abort the client immediately.
923 kern_return_t status
= DNSServiceRegistrationReply_rpc(x
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
924 if (status
== MACH_SEND_TIMED_OUT
)
925 AbortBlockedClient(x
->ClientMachPort
, "registration conflict", x
);
929 else if (result
== mStatus_MemFree
)
933 debugf("RegCallback renaming %#s to %#s", x
->name
.c
, mDNSStorage
.nicelabel
.c
);
934 x
->autorename
= mDNSfalse
;
935 x
->name
= mDNSStorage
.nicelabel
;
936 mDNS_RenameAndReregisterService(m
, &x
->s
, &x
->name
);
940 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
941 while (*r
&& *r
!= x
) r
= &(*r
)->next
;
944 LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr
->RR_SRV
.resrec
.name
.c
);
947 LogOperation("%5d: DNSServiceRegistration(%##s) Memory Free", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
948 FreeDNSServiceRegistration(x
);
953 LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld",
954 x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
, result
);
957 mDNSlocal
void CheckForDuplicateRegistrations(DNSServiceRegistration
*x
, domainname
*srv
, mDNSIPPort port
)
959 int count
= 1; // Start with the one we're planning to register, then see if there are any more
961 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
962 if (rr
->resrec
.rrtype
== kDNSType_SRV
&&
963 rr
->resrec
.rdata
->u
.srv
.port
.NotAnInteger
== port
.NotAnInteger
&&
964 SameDomainName(&rr
->resrec
.name
, srv
))
968 LogMsg("%5d: Client application registered %d identical instances of service %##s port %d.",
969 x
->ClientMachPort
, count
, srv
->c
, (int)port
.b
[0] << 8 | port
.b
[1]);
972 mDNSexport kern_return_t
provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
973 DNSCString name
, DNSCString regtype
, DNSCString domain
, int notAnIntPort
, DNSCString txtRecord
)
975 // Check client parameter
976 (void)unusedserver
; // Unused
977 mStatus err
= mStatus_NoError
;
978 const char *errormsg
= "Unknown";
979 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
980 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
982 // Check for sub-types after the service type
983 mDNSs32 NumSubTypes
= CountSubTypes(regtype
);
984 if (NumSubTypes
< 0) { errormsg
= "Bad Service SubType"; goto badparam
; }
986 // Check other parameters
990 if (!name
[0]) n
= mDNSStorage
.nicelabel
;
991 else if (!MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
992 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
993 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
994 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
997 port
.NotAnInteger
= notAnIntPort
;
999 unsigned char txtinfo
[1024] = "";
1000 unsigned int data_len
= 0;
1001 unsigned int size
= sizeof(RDataBody
);
1002 unsigned char *pstring
= &txtinfo
[data_len
];
1003 char *ptr
= txtRecord
;
1005 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1006 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1007 // Hence we have to convert the C-string to a P-string.
1008 // ASCII-1 characters are allowed in the C-string as boundary markers,
1009 // so that a single C-string can be used to represent one or more P-strings.
1012 if (++data_len
>= sizeof(txtinfo
)) { errormsg
= "TXT record too long"; goto badtxt
; }
1013 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
1015 pstring
= &txtinfo
[data_len
];
1021 if (pstring
[0] == 255) { errormsg
= "TXT record invalid (component longer than 255)"; goto badtxt
; }
1022 pstring
[++pstring
[0]] = *ptr
++;
1027 if (size
< data_len
)
1030 // Allocate memory, and handle failure
1031 DNSServiceRegistration
*x
= mallocL("DNSServiceRegistration", sizeof(*x
) - sizeof(RDataBody
) + size
);
1032 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1034 AuthRecord
*SubTypes
= AllocateSubTypes(NumSubTypes
, regtype
);
1035 if (NumSubTypes
&& !SubTypes
)
1036 { freeL("DNSServiceRegistration", x
); err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1038 // Set up object, and link into list
1039 x
->ClientMachPort
= client
;
1040 x
->autoname
= (!name
[0]);
1041 x
->autorename
= mDNSfalse
;
1043 x
->next
= DNSServiceRegistrationList
;
1044 DNSServiceRegistrationList
= x
;
1047 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x
->ClientMachPort
, name
, regtype
, domain
);
1048 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1049 // a port number of zero. When two instances of the protected client are allowed to run on one
1050 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1051 if (port
.NotAnInteger
) CheckForDuplicateRegistrations(x
, &srv
, port
);
1053 err
= mDNS_RegisterService(&mDNSStorage
, &x
->s
,
1054 &x
->name
, &t
, &d
, // Name, type, domain
1055 mDNSNULL
, port
, // Host and port
1056 txtinfo
, data_len
, // TXT data, length
1057 SubTypes
, NumSubTypes
, // Subtypes
1058 mDNSInterface_Any
, // Interface ID
1059 RegCallback
, x
); // Callback and context
1061 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_RegisterService"; goto fail
; }
1063 // Succeeded: Wrap up and return
1064 EnableDeathNotificationForClient(client
, x
);
1065 return(mStatus_NoError
);
1068 LogMsg("%5d: TXT record: %.100s...", client
, txtRecord
);
1070 err
= mStatus_BadParamErr
;
1072 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1073 client
, name
, regtype
, domain
, notAnIntPort
, errormsg
, err
);
1077 mDNSlocal
void mDNS_StatusCallback(mDNS
*const m
, mStatus result
)
1080 if (result
== mStatus_ConfigChanged
)
1082 DNSServiceRegistration
*r
;
1083 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1084 if (r
->autoname
&& !SameDomainLabel(r
->name
.c
, mDNSStorage
.nicelabel
.c
))
1086 debugf("NetworkChanged renaming %#s to %#s", r
->name
.c
, mDNSStorage
.nicelabel
.c
);
1087 r
->autorename
= mDNStrue
;
1088 mDNS_DeregisterService(&mDNSStorage
, &r
->s
);
1090 udsserver_handle_configchange();
1092 else if (result
== mStatus_GrowCache
)
1094 // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore
1095 mDNSu32 numrecords
= m
->rrcache_size
;
1096 CacheRecord
*storage
= mallocL("mStatus_GrowCache", sizeof(CacheRecord
) * numrecords
);
1097 if (storage
) mDNS_GrowCache(&mDNSStorage
, storage
, numrecords
);
1101 //*************************************************************************************************************
1102 // Add / Update / Remove records from existing Registration
1104 mDNSexport kern_return_t
provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1105 int type
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
, natural_t
*reference
)
1107 // Check client parameter
1108 (void)unusedserver
; // Unused
1109 mStatus err
= mStatus_NoError
;
1110 const char *errormsg
= "Unknown";
1111 domainname
*name
= (domainname
*)"";
1112 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1113 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1114 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1115 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1116 name
= &x
->s
.RR_SRV
.resrec
.name
;
1118 // Check other parameters
1119 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1120 unsigned int size
= sizeof(RDataBody
);
1121 if (size
< data_len
)
1124 // Allocate memory, and handle failure
1125 ExtraResourceRecord
*extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
1126 if (!extra
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1128 // Fill in type, length, and data of new record
1129 extra
->r
.resrec
.rrtype
= type
;
1130 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1131 extra
->r
.resrec
.rdlength
= data_len
;
1132 memcpy(&extra
->r
.rdatastorage
.u
.data
, data
, data_len
);
1135 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1136 client
, x
->s
.RR_SRV
.resrec
.name
.c
, type
, data_len
, extra
);
1137 err
= mDNS_AddRecordToService(&mDNSStorage
, &x
->s
, extra
, &extra
->r
.rdatastorage
, ttl
);
1138 *reference
= (natural_t
)extra
;
1139 if (err
) { errormsg
= "mDNS_AddRecordToService"; goto fail
; }
1141 // Succeeded: Wrap up and return
1142 return(mStatus_NoError
);
1145 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client
, name
->c
, type
, data_len
, errormsg
, err
);
1149 mDNSlocal
void UpdateCallback(mDNS
*const m
, AuthRecord
*const rr
, RData
*OldRData
)
1152 if (OldRData
!= &rr
->rdatastorage
)
1153 freeL("Old RData", OldRData
);
1156 mDNSexport kern_return_t
provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1157 natural_t reference
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1159 // Check client parameter
1160 (void)unusedserver
; // Unused
1161 mStatus err
= mStatus_NoError
;
1162 const char *errormsg
= "Unknown";
1163 domainname
*name
= (domainname
*)"";
1164 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1165 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1166 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1167 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1168 name
= &x
->s
.RR_SRV
.resrec
.name
;
1170 // Check other parameters
1171 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1172 unsigned int size
= sizeof(RDataBody
);
1173 if (size
< data_len
)
1176 // Find the record we're updating. NULL reference means update the primary TXT record
1177 AuthRecord
*rr
= &x
->s
.RR_TXT
;
1178 if (reference
) // Scan our list to make sure we're updating a valid record that was previously added
1180 ExtraResourceRecord
*e
= x
->s
.Extras
;
1181 while (e
&& e
!= (ExtraResourceRecord
*)reference
) e
= e
->next
;
1182 if (!e
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such record"; goto fail
; }
1186 // Allocate memory, and handle failure
1187 RData
*newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
1188 if (!newrdata
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1190 // Fill in new length, and data
1191 newrdata
->MaxRDLength
= size
;
1192 memcpy(&newrdata
->u
, data
, data_len
);
1195 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, new length %d)",
1196 client
, x
->s
.RR_SRV
.resrec
.name
.c
, reference
, data_len
);
1197 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, data_len
, newrdata
, UpdateCallback
);
1198 if (err
) { errormsg
= "mDNS_Update"; goto fail
; }
1200 // Succeeded: Wrap up and return
1201 return(mStatus_NoError
);
1204 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client
, name
->c
, reference
, data_len
, errormsg
, err
);
1208 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1209 natural_t reference
)
1211 // Check client parameter
1212 (void)unusedserver
; // Unused
1213 mStatus err
= mStatus_NoError
;
1214 const char *errormsg
= "Unknown";
1215 domainname
*name
= (domainname
*)"";
1216 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1217 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1218 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1219 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1220 name
= &x
->s
.RR_SRV
.resrec
.name
;
1223 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X)", client
, x
->s
.RR_SRV
.resrec
.name
.c
, reference
);
1224 ExtraResourceRecord
*extra
= (ExtraResourceRecord
*)reference
;
1225 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, &x
->s
, extra
);
1226 if (err
) { errormsg
= "mDNS_RemoveRecordFromService (No such record)"; goto fail
; }
1228 // Succeeded: Wrap up and return
1229 if (extra
->r
.resrec
.rdata
!= &extra
->r
.rdatastorage
)
1230 freeL("Extra RData", extra
->r
.resrec
.rdata
);
1231 freeL("ExtraResourceRecord", extra
);
1232 return(mStatus_NoError
);
1235 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s (%ld)", client
, name
->c
, reference
, errormsg
, err
);
1239 //*************************************************************************************************************
1242 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1244 mig_reply_error_t
*request
= msg
;
1245 mig_reply_error_t
*reply
;
1246 mach_msg_return_t mr
;
1248 (void)port
; // Unused
1249 (void)size
; // Unused
1250 (void)info
; // Unused
1252 /* allocate a reply buffer */
1253 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
1255 /* call the MiG server routine */
1256 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
1258 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
1260 if (reply
->RetCode
== MIG_NO_REPLY
)
1263 * This return code is a little tricky -- it appears that the
1264 * demux routine found an error of some sort, but since that
1265 * error would not normally get returned either to the local
1266 * user or the remote one, we pretend it's ok.
1268 CFAllocatorDeallocate(NULL
, reply
);
1273 * destroy any out-of-line data in the request buffer but don't destroy
1274 * the reply port right (since we need that to send an error message).
1276 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1277 mach_msg_destroy(&request
->Head
);
1280 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
1282 /* no reply port, so destroy the reply */
1283 if (reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
1284 mach_msg_destroy(&reply
->Head
);
1285 CFAllocatorDeallocate(NULL
, reply
);
1292 * We don't want to block indefinitely because the client
1293 * isn't receiving messages from the reply port.
1294 * If we have a send-once right for the reply port, then
1295 * this isn't a concern because the send won't block.
1296 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1297 * To avoid falling off the kernel's fast RPC path unnecessarily,
1298 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1301 options
= MACH_SEND_MSG
;
1302 if (MACH_MSGH_BITS_REMOTE(reply
->Head
.msgh_bits
) == MACH_MSG_TYPE_MOVE_SEND_ONCE
)
1303 options
|= MACH_SEND_TIMEOUT
;
1305 mr
= mach_msg(&reply
->Head
, /* msg */
1306 options
, /* option */
1307 reply
->Head
.msgh_size
, /* send_size */
1309 MACH_PORT_NULL
, /* rcv_name */
1310 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1311 MACH_PORT_NULL
); /* notify */
1313 /* Has a message error occurred? */
1316 case MACH_SEND_INVALID_DEST
:
1317 case MACH_SEND_TIMED_OUT
:
1318 /* the reply can't be delivered, so destroy it */
1319 mach_msg_destroy(&reply
->Head
);
1323 /* Includes success case. */
1327 CFAllocatorDeallocate(NULL
, reply
);
1330 mDNSlocal kern_return_t
registerBootstrapService()
1332 kern_return_t status
;
1333 mach_port_t service_send_port
, service_rcv_port
;
1335 debugf("Registering Bootstrap Service");
1338 * See if our service name is already registered and if we have privilege to check in.
1340 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1341 if (status
== KERN_SUCCESS
)
1344 * If so, we must be a followup instance of an already defined server. In that case,
1345 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1346 * that in case we have to unregister later (which requires the privilege port).
1348 server_priv_port
= bootstrap_port
;
1349 restarting_via_mach_init
= TRUE
;
1351 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
1353 status
= bootstrap_create_server(bootstrap_port
, "/usr/sbin/mDNSResponder", getuid(),
1354 FALSE
/* relaunch immediately, not on demand */, &server_priv_port
);
1355 if (status
!= KERN_SUCCESS
) return status
;
1357 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
1358 if (status
!= KERN_SUCCESS
)
1360 mach_port_deallocate(mach_task_self(), server_priv_port
);
1364 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1365 if (status
!= KERN_SUCCESS
)
1367 mach_port_deallocate(mach_task_self(), server_priv_port
);
1368 mach_port_deallocate(mach_task_self(), service_send_port
);
1371 assert(service_send_port
== service_rcv_port
);
1375 * We have no intention of responding to requests on the service port. We are not otherwise
1376 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1377 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1378 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1380 mach_port_destroy(mach_task_self(), service_rcv_port
);
1384 mDNSlocal kern_return_t
destroyBootstrapService()
1386 debugf("Destroying Bootstrap Service");
1387 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
1390 mDNSlocal
void ExitCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1392 (void)port
; // Unused
1393 (void)msg
; // Unused
1394 (void)size
; // Unused
1395 (void)info
; // Unused
1398 int rrcache_active = 0;
1399 for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++;
1400 debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active);
1403 LogMsg("%s stopping", mDNSResponderVersionString
);
1405 debugf("ExitCallback: destroyBootstrapService");
1407 destroyBootstrapService();
1409 debugf("ExitCallback: Aborting MIG clients");
1410 while (DNSServiceDomainEnumerationList
)
1411 AbortClient(DNSServiceDomainEnumerationList
->ClientMachPort
, DNSServiceDomainEnumerationList
);
1412 while (DNSServiceBrowserList
)
1413 AbortClient(DNSServiceBrowserList
->ClientMachPort
, DNSServiceBrowserList
);
1414 while (DNSServiceResolverList
)
1415 AbortClient(DNSServiceResolverList
->ClientMachPort
, DNSServiceResolverList
);
1416 while (DNSServiceRegistrationList
)
1417 AbortClient(DNSServiceRegistrationList
->ClientMachPort
, DNSServiceRegistrationList
);
1419 debugf("ExitCallback: mDNS_Close");
1420 mDNS_Close(&mDNSStorage
);
1422 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1427 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1428 mDNSlocal
void HandleSIGTERM(int signal
)
1430 (void)signal
; // Unused
1432 debugf("SIGINT/SIGTERM");
1433 mach_msg_header_t header
;
1434 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1435 header
.msgh_remote_port
= exit_m_port
;
1436 header
.msgh_local_port
= MACH_PORT_NULL
;
1437 header
.msgh_size
= sizeof(header
);
1439 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1440 { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
1443 mDNSlocal
void INFOCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1445 (void)port
; // Unused
1446 (void)msg
; // Unused
1447 (void)size
; // Unused
1448 (void)info
; // Unused
1449 DNSServiceDomainEnumeration
*e
;
1450 DNSServiceBrowser
*b
;
1451 DNSServiceResolver
*l
;
1452 DNSServiceRegistration
*r
;
1455 mDNSu32 CacheUsed
= 0, CacheActive
= 0;
1457 LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString
);
1459 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
1460 for (rr
= mDNSStorage
.rrcache_hash
[slot
]; rr
; rr
=rr
->next
)
1463 if (rr
->CRActiveQuestion
) CacheActive
++;
1464 LogMsg("%s %-5s%-6s%s", rr
->CRActiveQuestion
? "Active: " : "Inactive:", DNSTypeName(rr
->resrec
.rrtype
),
1465 ((NetworkInterfaceInfoOSX
*)rr
->resrec
.InterfaceID
)->ifa_name
, GetRRDisplayString(&mDNSStorage
, rr
));
1466 usleep(1000); // Limit rate a little so we don't flood syslog too fast
1468 if (mDNSStorage
.rrcache_totalused
!= CacheUsed
)
1469 LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage
.rrcache_totalused
, CacheUsed
);
1470 if (mDNSStorage
.rrcache_active
!= CacheActive
)
1471 LogMsg("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage
.rrcache_active
, CacheActive
);
1472 LogMsg("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed
, CacheActive
);
1474 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
1475 LogMsg("%5d: DomainEnumeration %##s", e
->ClientMachPort
, e
->dom
.qname
.c
);
1477 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
1478 LogMsg("%5d: ServiceBrowse %##s", b
->ClientMachPort
, b
->q
.qname
.c
);
1480 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1481 LogMsg("%5d: ServiceResolve %##s", l
->ClientMachPort
, l
->i
.name
.c
);
1483 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1484 LogMsg("%5d: ServiceRegistration %##s", r
->ClientMachPort
, r
->s
.RR_SRV
.resrec
.name
.c
);
1488 LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString
);
1491 mDNSlocal
void HandleSIGINFO(int signal
)
1493 (void)signal
; // Unused
1494 mach_msg_header_t header
;
1495 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1496 header
.msgh_remote_port
= info_m_port
;
1497 header
.msgh_local_port
= MACH_PORT_NULL
;
1498 header
.msgh_size
= sizeof(header
);
1500 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1501 LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated.");
1504 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
1507 CFMachPortRef d_port
= CFMachPortCreate(NULL
, ClientDeathCallback
, NULL
, NULL
);
1508 CFMachPortRef s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
1509 CFMachPortRef e_port
= CFMachPortCreate(NULL
, ExitCallback
, NULL
, NULL
);
1510 CFMachPortRef i_port
= CFMachPortCreate(NULL
, INFOCallback
, NULL
, NULL
);
1511 mach_port_t m_port
= CFMachPortGetPort(s_port
);
1512 char *MachServerName
= mDNSMacOSXSystemBuildNumber(NULL
) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
1513 kern_return_t status
= bootstrap_register(bootstrap_port
, MachServerName
, m_port
);
1514 CFRunLoopSourceRef d_rls
= CFMachPortCreateRunLoopSource(NULL
, d_port
, 0);
1515 CFRunLoopSourceRef s_rls
= CFMachPortCreateRunLoopSource(NULL
, s_port
, 0);
1516 CFRunLoopSourceRef e_rls
= CFMachPortCreateRunLoopSource(NULL
, e_port
, 0);
1517 CFRunLoopSourceRef i_rls
= CFMachPortCreateRunLoopSource(NULL
, i_port
, 0);
1522 LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1524 LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status
), status
);
1528 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
1529 rrcachestorage
, RR_CACHE_SIZE
,
1530 mDNS_Init_AdvertiseLocalAddresses
,
1531 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
1532 if (err
) { LogMsg("Daemon start: mDNS_Init failed %ld", err
); return(err
); }
1534 client_death_port
= CFMachPortGetPort(d_port
);
1535 exit_m_port
= CFMachPortGetPort(e_port
);
1536 info_m_port
= CFMachPortGetPort(i_port
);
1538 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls
, kCFRunLoopDefaultMode
);
1539 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls
, kCFRunLoopDefaultMode
);
1540 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls
, kCFRunLoopDefaultMode
);
1541 CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls
, kCFRunLoopDefaultMode
);
1546 if (debug_mode
) printf("Service registered with Mach Port %d\n", m_port
);
1548 err
= udsserver_init();
1549 if (err
) { LogMsg("Daemon start: udsserver_init failed"); return err
; }
1550 err
= udsserver_add_rl_source();
1551 if (err
) { LogMsg("Daemon start: udsserver_add_rl_source failed"); return err
; }
1556 mDNSlocal mDNSs32
mDNSDaemonIdle(void)
1558 // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
1559 mDNSs32 nextevent
= mDNS_Execute(&mDNSStorage
);
1561 mDNSs32 now
= mDNSPlatformTimeNow();
1563 // 2. Deliver any waiting browse messages to clients
1564 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
1568 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
1569 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
1570 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
1571 DNSServiceBrowser
*x
= b
;
1573 if (x
->results
) // Try to deliver the list of results
1577 DNSServiceBrowserResult
*const r
= x
->results
;
1579 domainname type
, domain
;
1580 DeconstructServiceName(&r
->result
, &name
, &type
, &domain
); // Don't need to check result; already validated in FoundInstance()
1581 char cname
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
1582 char ctype
[MAX_ESCAPED_DOMAIN_NAME
];
1583 char cdom
[MAX_ESCAPED_DOMAIN_NAME
];
1584 ConvertDomainLabelToCString_unescaped(&name
, cname
);
1585 ConvertDomainNameToCString(&type
, ctype
);
1586 ConvertDomainNameToCString(&domain
, cdom
);
1587 DNSServiceDiscoveryReplyFlags flags
= (r
->next
) ? DNSServiceDiscoverReplyFlagsMoreComing
: 0;
1588 kern_return_t status
= DNSServiceBrowserReply_rpc(x
->ClientMachPort
, r
->resultType
, cname
, ctype
, cdom
, flags
, 1);
1589 // If we failed to send the mach message, try again in one second
1590 if (status
== MACH_SEND_TIMED_OUT
)
1592 if (nextevent
- now
> mDNSPlatformOneSecond
)
1593 nextevent
= now
+ mDNSPlatformOneSecond
;
1598 x
->lastsuccess
= now
;
1599 x
->results
= x
->results
->next
;
1600 freeL("DNSServiceBrowserResult", r
);
1603 // If this client hasn't read a single message in the last 60 seconds, abort it
1604 if (now
- x
->lastsuccess
>= 60 * mDNSPlatformOneSecond
)
1605 AbortBlockedClient(x
->ClientMachPort
, "browse", x
);
1609 DNSServiceResolver
*l
;
1610 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1611 if (l
->ReportTime
&& now
- l
->ReportTime
>= 0)
1614 LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. "
1615 "This places considerable burden on the network.", l
->ClientMachPort
, l
->i
.name
.c
);
1621 mDNSexport
int main(int argc
, char **argv
)
1624 kern_return_t status
;
1627 for (i
=1; i
<argc
; i
++)
1629 if (!strcmp(argv
[i
], "-d")) debug_mode
= 1;
1632 signal(SIGINT
, HandleSIGTERM
); // SIGINT is what you get for a Ctrl-C
1633 signal(SIGTERM
, HandleSIGTERM
);
1634 signal(SIGINFO
, HandleSIGINFO
);
1636 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
1638 registerBootstrapService();
1640 if (!debug_mode
&& !restarting_via_mach_init
)
1641 exit(0); /* mach_init will restart us immediately as a daemon */
1643 // Unlike deamon(), mach_init does redirect standard file descriptors to /dev/null
1646 int fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
1649 // Avoid unnecessarily duplicating a file descriptor to itself
1650 if (fd
!= STDIN_FILENO
) (void)dup2(fd
, STDIN_FILENO
);
1651 if (fd
!= STDOUT_FILENO
) (void)dup2(fd
, STDOUT_FILENO
);
1652 if (fd
!= STDERR_FILENO
) (void)dup2(fd
, STDERR_FILENO
);
1653 if (fd
!= STDIN_FILENO
&& fd
!= STDOUT_FILENO
&& fd
!= STDERR_FILENO
)
1658 fp
= fopen(PID_FILE
, "w");
1661 fprintf(fp
, "%d\n", getpid());
1665 LogMsg("%s starting", mDNSResponderVersionString
);
1666 status
= mDNSDaemonInitialize();
1668 // Now that we're finished with anything privileged, switch over to running as "nobody"
1669 const struct passwd
*pw
= getpwnam( "nobody");
1671 setuid( pw
->pw_uid
);
1673 setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database
1678 int RunLoopStatus
= kCFRunLoopRunTimedOut
;
1680 // This is the main work loop:
1681 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
1682 // (2) Then we make sure we've delivered all waiting browse messages to our clients
1683 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
1684 // (4) On wakeup we first process *all* events
1685 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
1686 while (RunLoopStatus
== kCFRunLoopRunTimedOut
)
1688 // 1. Before going into a blocking wait call and letting our process to go sleep,
1689 // call mDNSDaemonIdle to allow any deferred work to be completed.
1690 mDNSs32 nextevent
= mDNSDaemonIdle();
1692 nextevent
= udsserver_idle(nextevent
);
1695 // 2. Work out how long we expect to sleep before the next scheduled task
1696 mDNSs32 ticks
= nextevent
- mDNSPlatformTimeNow();
1697 if (ticks
< 1) ticks
= 1;
1698 CFAbsoluteTime interval
= (CFAbsoluteTime
)ticks
/ (CFAbsoluteTime
)mDNSPlatformOneSecond
;
1700 // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
1701 // (a) our next wakeup time, or (b) an event occurs.
1702 // The 'true' parameter makes it return after handling any event that occurs
1703 // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
1704 verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents
, ticks
);
1706 RunLoopStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, interval
, true);
1708 // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
1709 while (RunLoopStatus
== kCFRunLoopRunHandledSource
)
1712 RunLoopStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.0, true);
1716 LogMsg("ERROR: CFRunLoopRun Exiting.");
1717 mDNS_Close(&mDNSStorage
);
1720 destroyBootstrapService();
1725 // For convenience when using the "strings" command, this is the last thing in the file
1726 mDNSexport
const char mDNSResponderVersionString
[] = STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";