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.6 2004/04/06 19:50:36 cheshire
40 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
41 After more discussion, we've decided to use userid -2 if "nobody" user doesn't exist.
43 Revision 1.134.2.5 2004/04/03 01:29:07 cheshire
44 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
45 If "nobody" user doesn't exist, log a message and continue as "root"
47 Revision 1.134.2.4 2004/04/02 21:50:21 cheshire
48 Fix errors in comments
50 Revision 1.134.2.3 2003/12/12 01:21:30 cheshire
51 <rdar://problem/3491108> mDNSResponder should not run as root
53 Revision 1.134.2.2 2003/12/05 00:03:35 cheshire
54 <rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
56 Revision 1.134.2.1 2003/12/03 11:00:09 cheshire
57 Update "mDNSResponderVersion" mechanism to allow dots so we can do mDNSResponder-58.1 for SUPan
59 Revision 1.134 2003/08/21 20:01:37 cheshire
60 <rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
62 Revision 1.133 2003/08/20 23:39:31 cheshire
63 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
65 Revision 1.132 2003/08/20 01:44:56 cheshire
66 Fix errors in LogOperation() calls (only used for debugging)
68 Revision 1.131 2003/08/19 05:39:43 cheshire
69 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
71 Revision 1.130 2003/08/16 03:39:01 cheshire
72 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
74 Revision 1.129 2003/08/15 20:16:03 cheshire
75 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
76 We want to avoid touching the rdata pages, so we don't page them in.
77 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
78 Moved this from the RData to the ResourceRecord object.
79 2. To avoid unnecessarily touching the rdata just to compare it,
80 compute a hash of the rdata and store the hash in the ResourceRecord object.
82 Revision 1.128 2003/08/14 19:30:36 cheshire
83 <rdar://problem/3378473> Include list of cache records in SIGINFO output
85 Revision 1.127 2003/08/14 02:18:21 cheshire
86 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
88 Revision 1.126 2003/08/12 19:56:25 cheshire
91 Revision 1.125 2003/08/08 18:36:04 cheshire
92 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
94 Revision 1.124 2003/07/25 18:28:23 cheshire
95 Minor fix to error messages in syslog: Display string parameters with quotes
97 Revision 1.123 2003/07/23 17:45:28 cheshire
98 <rdar://problem/3339388> mDNSResponder leaks a bit
99 Don't allocate memory for the reply until after we've verified that the reply is valid
101 Revision 1.122 2003/07/23 00:00:04 cheshire
104 Revision 1.121 2003/07/20 03:38:51 ksekar
106 Completed support for Unix-domain socket based API.
108 Revision 1.120 2003/07/18 00:30:00 cheshire
109 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
111 Revision 1.119 2003/07/17 19:08:58 cheshire
112 <rdar://problem/3332153> Remove calls to enable obsolete UDS code
114 Revision 1.118 2003/07/15 21:12:28 cheshire
115 Added extra debugging checks in validatelists() (not used in final shipping version)
117 Revision 1.117 2003/07/15 01:55:15 cheshire
118 <rdar://problem/3315777> Need to implement service registration with subtypes
120 Revision 1.116 2003/07/02 21:19:51 cheshire
121 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
123 Revision 1.115 2003/07/02 02:41:24 cheshire
124 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
126 Revision 1.114 2003/07/01 21:10:20 cheshire
127 Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
129 Revision 1.113 2003/06/28 17:27:43 vlubet
130 <rdar://problem/3221246> Redirect standard input, standard output, and
131 standard error file descriptors to /dev/null just like any other
134 Revision 1.112 2003/06/25 23:42:19 ksekar
135 Bug #: <rdar://problem/3249292>: Feature: New Rendezvous APIs (#7875)
136 Reviewed by: Stuart Cheshire
137 Added files necessary to implement Unix domain sockets based enhanced
138 Rendezvous APIs, and integrated with existing Mach-port based daemon.
140 Revision 1.111 2003/06/11 01:02:43 cheshire
141 <rdar://problem/3287858> mDNSResponder binary compatibility
142 Make single binary that can run on both Jaguar and Panther.
144 Revision 1.110 2003/06/10 01:14:11 cheshire
145 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
147 Revision 1.109 2003/06/06 19:53:43 cheshire
148 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
149 (Global search-and-replace; no functional change to code execution.)
151 Revision 1.108 2003/06/06 14:08:06 cheshire
152 For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
154 Revision 1.107 2003/05/29 05:44:55 cheshire
155 Minor fixes to log messages
157 Revision 1.106 2003/05/27 18:30:55 cheshire
158 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
159 Dean Reece suggested SIGINFO is more appropriate than SIGHUP
161 Revision 1.105 2003/05/26 03:21:29 cheshire
162 Tidy up address structure naming:
163 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
164 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
165 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
167 Revision 1.104 2003/05/26 00:42:06 cheshire
168 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
170 Revision 1.103 2003/05/23 23:07:44 cheshire
171 <rdar://problem/3268199> Must not write to stderr when running as daemon
173 Revision 1.102 2003/05/22 01:32:31 cheshire
174 Fix typo in Log message format string
176 Revision 1.101 2003/05/22 00:26:55 cheshire
177 <rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
178 Modify error message to explain that this is technically legal, but may indicate a bug.
180 Revision 1.100 2003/05/21 21:02:24 ksekar
181 Bug #: <rdar://problem/3247035>: Service should be prefixed
182 Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
183 Mach message port to "com.apple.mDNSResponder.
185 Revision 1.99 2003/05/21 17:33:49 cheshire
186 Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
188 Revision 1.98 2003/05/20 00:33:07 cheshire
189 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
190 SIGHUP now writes state summary to syslog
192 Revision 1.97 2003/05/08 00:19:08 cheshire
193 <rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
195 Revision 1.96 2003/05/07 22:10:46 cheshire
196 <rdar://problem/3250330> Add a few more error logging messages
198 Revision 1.95 2003/05/07 19:20:17 cheshire
199 <rdar://problem/3251391> Add version number to mDNSResponder builds
201 Revision 1.94 2003/05/07 00:28:18 cheshire
202 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
204 Revision 1.93 2003/05/06 00:00:49 cheshire
205 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
207 Revision 1.92 2003/04/04 20:38:57 cheshire
212 #include <mach/mach.h>
213 #include <mach/mach_error.h>
214 #include <servers/bootstrap.h>
215 #include <sys/types.h>
221 #include "DNSServiceDiscoveryRequestServer.h"
222 #include "DNSServiceDiscoveryReply.h"
224 #include "mDNSClientAPI.h" // Defines the interface to the client layer above
225 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
227 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
231 //*************************************************************************************************************
234 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
235 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
236 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
237 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
238 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
240 //*************************************************************************************************************
243 mDNSexport mDNS mDNSStorage
;
244 static mDNS_PlatformSupport PlatformStorage
;
245 #define RR_CACHE_SIZE 64
246 static CacheRecord rrcachestorage
[RR_CACHE_SIZE
];
247 static const char PID_FILE
[] = "/var/run/mDNSResponder.pid";
249 static const char kmDNSBootstrapName
[] = "com.apple.mDNSResponderRestart";
250 static mach_port_t client_death_port
= MACH_PORT_NULL
;
251 static mach_port_t exit_m_port
= MACH_PORT_NULL
;
252 static mach_port_t info_m_port
= MACH_PORT_NULL
;
253 static mach_port_t server_priv_port
= MACH_PORT_NULL
;
255 // mDNS Mach Message Timeout, in milliseconds.
256 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
257 // fails to service its mach message queue, but long enough to give a well-written
258 // client a chance to service its mach message queue without getting cut off.
259 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
260 // even extra-slow clients a fair chance before we cut them off.
261 #define MDNS_MM_TIMEOUT 250
263 static int restarting_via_mach_init
= 0;
271 //*************************************************************************************************************
272 // Active client list structures
274 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
275 struct DNSServiceDomainEnumeration_struct
277 DNSServiceDomainEnumeration
*next
;
278 mach_port_t ClientMachPort
;
279 DNSQuestion dom
; // Question asking for domains
280 DNSQuestion def
; // Question asking for default domain
283 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult
;
284 struct DNSServiceBrowserResult_struct
286 DNSServiceBrowserResult
*next
;
291 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
292 struct DNSServiceBrowser_struct
294 DNSServiceBrowser
*next
;
295 mach_port_t ClientMachPort
;
297 DNSServiceBrowserResult
*results
;
301 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
302 struct DNSServiceResolver_struct
304 DNSServiceResolver
*next
;
305 mach_port_t ClientMachPort
;
311 typedef struct DNSServiceRegistration_struct DNSServiceRegistration
;
312 struct DNSServiceRegistration_struct
314 DNSServiceRegistration
*next
;
315 mach_port_t ClientMachPort
;
320 // Don't add any fields after ServiceRecordSet.
321 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
324 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
325 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
326 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
327 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
329 //*************************************************************************************************************
330 // General Utility Functions
332 #if MACOSX_MDNS_MALLOC_DEBUGGING
334 char _malloc_options
[] = "AXZ";
336 static void validatelists(mDNS
*const m
)
338 DNSServiceDomainEnumeration
*e
;
339 DNSServiceBrowser
*b
;
340 DNSServiceResolver
*l
;
341 DNSServiceRegistration
*r
;
347 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
348 if (e
->ClientMachPort
== 0 || e
->ClientMachPort
== (mach_port_t
)~0)
349 LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e
, e
->ClientMachPort
);
351 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
352 if (b
->ClientMachPort
== 0 || b
->ClientMachPort
== (mach_port_t
)~0)
353 LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b
, b
->ClientMachPort
);
355 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
356 if (l
->ClientMachPort
== 0 || l
->ClientMachPort
== (mach_port_t
)~0)
357 LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l
, l
->ClientMachPort
);
359 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
360 if (r
->ClientMachPort
== 0 || r
->ClientMachPort
== (mach_port_t
)~0)
361 LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r
, r
->ClientMachPort
);
363 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
364 if (rr
->RecordType
== 0 || rr
->RecordType
== 0xFF)
365 LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr
, rr
->RecordType
);
367 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
)
368 if (rr
->RecordType
== 0 || rr
->RecordType
== 0xFF)
369 LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr
, rr
->RecordType
);
371 for (q
= m
->Questions
; q
; q
=q
->next
)
372 if (q
->ThisQInterval
== (mDNSs32
)~0)
373 LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q
, q
->ThisQInterval
);
375 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
376 for (cr
= mDNSStorage
.rrcache_hash
[slot
]; cr
; cr
=cr
->next
)
377 if (cr
->RecordType
== 0 || cr
->RecordType
== 0xFF)
378 LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot
, rr
, rr
->RecordType
);
381 void *mallocL(char *msg
, unsigned int size
)
383 unsigned long *mem
= malloc(size
+8);
386 LogMsg("malloc( %s : %d ) failed", msg
, size
);
391 LogMalloc("malloc( %s : %lu ) = %p", msg
, size
, &mem
[2]);
394 //bzero(&mem[2], size);
395 memset(&mem
[2], 0xFF, size
);
396 validatelists(&mDNSStorage
);
401 void freeL(char *msg
, void *x
)
404 LogMsg("free( %s @ NULL )!", msg
);
407 unsigned long *mem
= ((unsigned long *)x
) - 2;
408 if (mem
[0] != 0xDEAD1234)
409 { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
411 { LogMsg("free( %s : %ld @ %p) too big!", msg
, mem
[1], &mem
[2]); return; }
412 LogMalloc("free( %s : %ld @ %p)", msg
, mem
[1], &mem
[2]);
413 //bzero(mem, mem[1]+8);
414 memset(mem
, 0xFF, mem
[1]+8);
415 validatelists(&mDNSStorage
);
422 //*************************************************************************************************************
423 // Client Death Detection
425 mDNSlocal
void FreeDNSServiceRegistration(DNSServiceRegistration
*x
)
429 ExtraResourceRecord
*extras
= x
->s
.Extras
;
430 x
->s
.Extras
= x
->s
.Extras
->next
;
431 if (extras
->r
.resrec
.rdata
!= &extras
->r
.rdatastorage
)
432 freeL("Extra RData", extras
->r
.resrec
.rdata
);
433 freeL("ExtraResourceRecord", extras
);
436 if (x
->s
.RR_TXT
.resrec
.rdata
!= &x
->s
.RR_TXT
.rdatastorage
)
437 freeL("TXT RData", x
->s
.RR_TXT
.resrec
.rdata
);
439 if (x
->s
.SubTypes
) freeL("ServiceSubTypes", x
->s
.SubTypes
);
441 freeL("DNSServiceRegistration", x
);
444 // AbortClient finds whatever client is identified by the given Mach port,
445 // stops whatever operation that client was doing, and frees its memory.
446 // In the case of a service registration, the actual freeing may be deferred
447 // until we get the mStatus_MemFree message, if necessary
448 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
, void *m
)
450 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
451 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
452 DNSServiceResolver
**l
= &DNSServiceResolverList
;
453 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
455 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
458 DNSServiceDomainEnumeration
*x
= *e
;
461 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->dom
.qname
.c
, m
, x
);
462 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort
, x
->dom
.qname
.c
);
463 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
464 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
465 freeL("DNSServiceDomainEnumeration", x
);
469 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
472 DNSServiceBrowser
*x
= *b
;
475 LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->q
.qname
.c
, m
, x
);
476 else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort
, x
->q
.qname
.c
);
477 mDNS_StopBrowse(&mDNSStorage
, &x
->q
);
480 DNSServiceBrowserResult
*r
= x
->results
;
481 x
->results
= x
->results
->next
;
482 freeL("DNSServiceBrowserResult", r
);
484 freeL("DNSServiceBrowser", x
);
488 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
491 DNSServiceResolver
*x
= *l
;
494 LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->i
.name
.c
, m
, x
);
495 else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort
, x
->i
.name
.c
);
496 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
497 freeL("DNSServiceResolver", x
);
501 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
504 DNSServiceRegistration
*x
= *r
;
506 x
->autorename
= mDNSfalse
;
508 LogMsg("%5d: DNSServiceRegistration(%##s) STOP; WARNING m %p != x %p", ClientMachPort
, x
->s
.RR_SRV
.resrec
.name
.c
, m
, x
);
509 else LogOperation("%5d: DNSServiceRegistration(%##s) STOP", ClientMachPort
, x
->s
.RR_SRV
.resrec
.name
.c
);
510 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
511 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
512 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
513 // the list, so we should go ahead and free the memory right now
514 if (mDNS_DeregisterService(&mDNSStorage
, &x
->s
) != mStatus_NoError
)
515 FreeDNSServiceRegistration(x
);
519 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort
);
522 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
524 mDNSlocal
void AbortClientWithLogMessage(mach_port_t c
, char *reason
, char *msg
, void *m
)
526 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
527 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
528 DNSServiceResolver
*l
= DNSServiceResolverList
;
529 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
530 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
531 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
532 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
533 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
534 if (e
) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c
, e
->dom
.qname
.c
, reason
, msg
);
535 else if (b
) LogMsg("%5d: Browser(%##s) %s%s", c
, b
->q
.qname
.c
, reason
, msg
);
536 else if (l
) LogMsg("%5d: Resolver(%##s) %s%s", c
, l
->i
.name
.c
, reason
, msg
);
537 else if (r
) LogMsg("%5d: Registration(%##s) %s%s", c
, r
->s
.RR_SRV
.resrec
.name
.c
, reason
, msg
);
538 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c
, reason
, msg
);
543 mDNSlocal mDNSBool
CheckForExistingClient(mach_port_t c
)
545 DNSServiceDomainEnumeration
*e
= DNSServiceDomainEnumerationList
;
546 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
547 DNSServiceResolver
*l
= DNSServiceResolverList
;
548 DNSServiceRegistration
*r
= DNSServiceRegistrationList
;
549 while (e
&& e
->ClientMachPort
!= c
) e
= e
->next
;
550 while (b
&& b
->ClientMachPort
!= c
) b
= b
->next
;
551 while (l
&& l
->ClientMachPort
!= c
) l
= l
->next
;
552 while (r
&& r
->ClientMachPort
!= c
) r
= r
->next
;
553 if (e
) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c
, e
->dom
.qname
.c
);
554 if (b
) LogMsg("%5d: Browser(%##s) already exists!", c
, b
->q
.qname
.c
);
555 if (l
) LogMsg("%5d: Resolver(%##s) already exists!", c
, l
->i
.name
.c
);
556 if (r
) LogMsg("%5d: Registration(%##s) already exists!", c
, r
->s
.RR_SRV
.resrec
.name
.c
);
557 return(e
|| b
|| l
|| r
);
560 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
562 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
563 (void)unusedport
; // Unused
564 (void)size
; // Unused
565 (void)info
; // Unused
566 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
568 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
569 AbortClient(deathMessage
->not_port
, NULL
);
571 /* Deallocate the send right that came in the dead name notification */
572 mach_port_destroy( mach_task_self(), deathMessage
->not_port
);
576 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
, void *m
)
579 kern_return_t r
= mach_port_request_notification(mach_task_self(), ClientMachPort
, MACH_NOTIFY_DEAD_NAME
, 0,
580 client_death_port
, MACH_MSG_TYPE_MAKE_SEND_ONCE
, &prev
);
581 // If the port already died while we were thinking about it, then abort the operation right away
582 if (r
!= KERN_SUCCESS
)
583 AbortClientWithLogMessage(ClientMachPort
, "died/deallocated before we could enable death notification", "", m
);
586 //*************************************************************************************************************
587 // Domain Enumeration
589 mDNSlocal
void FoundDomain(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
591 kern_return_t status
;
593 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
594 DNSServiceDomainEnumerationReplyResultType rt
;
595 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->QuestionContext
;
597 debugf("FoundDomain: %##s PTR %##s", answer
->name
.c
, answer
->rdata
->u
.name
.c
);
598 if (answer
->rrtype
!= kDNSType_PTR
) return;
599 if (!x
) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
603 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
604 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
608 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
612 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
613 x
->ClientMachPort
, x
->dom
.qname
.c
, answer
->rdata
->u
.name
.c
,
614 !AddRecord
? "RemoveDomain" :
615 question
== &x
->dom
? "AddDomain" : "AddDomainDefault");
617 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, buffer
);
618 status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, buffer
, 0, MDNS_MM_TIMEOUT
);
619 if (status
== MACH_SEND_TIMED_OUT
)
620 AbortBlockedClient(x
->ClientMachPort
, "enumeration", x
);
623 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
626 // Check client parameter
627 (void)unusedserver
; // Unused
628 mStatus err
= mStatus_NoError
;
629 const char *errormsg
= "Unknown";
630 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
631 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
633 mDNS_DomainType dt1
= regDom
? mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
634 mDNS_DomainType dt2
= regDom
? mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
635 const DNSServiceDomainEnumerationReplyResultType rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
637 // Allocate memory, and handle failure
638 DNSServiceDomainEnumeration
*x
= mallocL("DNSServiceDomainEnumeration", sizeof(*x
));
639 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
641 // Set up object, and link into list
642 x
->ClientMachPort
= client
;
643 x
->next
= DNSServiceDomainEnumerationList
;
644 DNSServiceDomainEnumerationList
= x
;
646 // Generate initial response
647 verbosedebugf("%5d: Enumerate %s Domains", client
, regDom
? "Registration" : "Browsing");
648 // We always give local. as the initial default browse domain, and then look for more
649 kern_return_t status
= DNSServiceDomainEnumerationReply_rpc(x
->ClientMachPort
, rt
, "local.", 0, MDNS_MM_TIMEOUT
);
650 if (status
== MACH_SEND_TIMED_OUT
)
651 { AbortBlockedClient(x
->ClientMachPort
, "local enumeration", x
); return(mStatus_UnknownErr
); }
654 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, mDNSInterface_Any
, FoundDomain
, x
);
655 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, mDNSInterface_Any
, FoundDomain
, x
);
656 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_GetDomains"; goto fail
; }
658 // Succeeded: Wrap up and return
659 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client
, x
->dom
.qname
.c
);
660 EnableDeathNotificationForClient(client
, x
);
661 return(mStatus_NoError
);
664 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client
, regDom
, errormsg
, err
);
668 //*************************************************************************************************************
669 // Browse for services
671 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
675 if (answer
->rrtype
!= kDNSType_PTR
)
676 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
); return; }
679 domainname type
, domain
;
680 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
682 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
683 answer
->name
.c
, answer
->rdata
->u
.name
.c
);
687 DNSServiceBrowserResult
*x
= mallocL("DNSServiceBrowserResult", sizeof(*x
));
688 if (!x
) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer
->rdata
->u
.name
.c
); return; }
690 verbosedebugf("FoundInstance: %s %##s", AddRecord
? "Add" : "Rmv", answer
->rdata
->u
.name
.c
);
691 AssignDomainName(x
->result
, answer
->rdata
->u
.name
);
693 x
->resultType
= DNSServiceBrowserReplyAddInstance
;
694 else x
->resultType
= DNSServiceBrowserReplyRemoveInstance
;
697 DNSServiceBrowser
*browser
= (DNSServiceBrowser
*)question
->QuestionContext
;
698 DNSServiceBrowserResult
**p
= &browser
->results
;
699 while (*p
) p
= &(*p
)->next
;
703 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
704 DNSCString regtype
, DNSCString domain
)
706 // Check client parameter
707 (void)unusedserver
; // Unused
708 mStatus err
= mStatus_NoError
;
709 const char *errormsg
= "Unknown";
710 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
711 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
713 // Check other parameters
715 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Illegal regtype"; goto badparam
; }
716 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Illegal domain"; goto badparam
; }
718 // Allocate memory, and handle failure
719 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
720 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
722 // Set up object, and link into list
723 x
->ClientMachPort
= client
;
726 x
->next
= DNSServiceBrowserList
;
727 DNSServiceBrowserList
= x
;
730 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client
, t
.c
, d
.c
);
731 err
= mDNS_StartBrowse(&mDNSStorage
, &x
->q
, &t
, &d
, mDNSInterface_Any
, FoundInstance
, x
);
732 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartBrowse"; goto fail
; }
734 // Succeeded: Wrap up and return
735 EnableDeathNotificationForClient(client
, x
);
736 return(mStatus_NoError
);
739 err
= mStatus_BadParamErr
;
741 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client
, regtype
, domain
, errormsg
, err
);
745 //*************************************************************************************************************
746 // Resolve Service Info
748 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
750 kern_return_t status
;
751 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->ServiceInfoQueryContext
;
752 NetworkInterfaceInfoOSX
*ifx
= (NetworkInterfaceInfoOSX
*)query
->info
->InterfaceID
;
753 if (query
->info
->InterfaceID
== (mDNSInterfaceID
)~0) ifx
= mDNSNULL
;
754 struct sockaddr_storage interface
;
755 struct sockaddr_storage address
;
757 int i
, pstrlen
= query
->info
->TXTinfo
[0];
760 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
762 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
764 bzero(&interface
, sizeof(interface
));
765 bzero(&address
, sizeof(address
));
767 if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv4
)
769 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&interface
;
770 sin
->sin_len
= sizeof(*sin
);
771 sin
->sin_family
= AF_INET
;
773 sin
->sin_addr
.s_addr
= ifx
->ifinfo
.ip
.ip
.v4
.NotAnInteger
;
775 else if (ifx
&& ifx
->ifinfo
.ip
.type
== mDNSAddrType_IPv6
)
777 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&interface
;
778 sin6
->sin6_len
= sizeof(*sin6
);
779 sin6
->sin6_family
= AF_INET6
;
780 sin6
->sin6_flowinfo
= 0;
782 sin6
->sin6_addr
= *(struct in6_addr
*)&ifx
->ifinfo
.ip
.ip
.v6
;
783 sin6
->sin6_scope_id
= ifx
->scope_id
;
786 if (query
->info
->ip
.type
== mDNSAddrType_IPv4
)
788 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&address
;
789 sin
->sin_len
= sizeof(*sin
);
790 sin
->sin_family
= AF_INET
;
791 sin
->sin_port
= query
->info
->port
.NotAnInteger
;
792 sin
->sin_addr
.s_addr
= query
->info
->ip
.ip
.v4
.NotAnInteger
;
796 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&address
;
797 sin6
->sin6_len
= sizeof(*sin6
);
798 sin6
->sin6_family
= AF_INET6
;
799 sin6
->sin6_port
= query
->info
->port
.NotAnInteger
;
800 sin6
->sin6_flowinfo
= 0;
801 sin6
->sin6_addr
= *(struct in6_addr
*)&query
->info
->ip
.ip
.v6
;
802 sin6
->sin6_scope_id
= ifx
? ifx
->scope_id
: 0;
805 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
806 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
807 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
808 // ASCII-1 characters are used in the C-string as boundary markers,
809 // to indicate the boundaries between the original constituent P-strings.
810 for (i
=1; i
<query
->info
->TXTlen
; i
++)
813 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
817 pstrlen
= query
->info
->TXTinfo
[i
];
820 cstring
[i
-1] = 0; // Put the terminating NULL on the end
822 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%d", x
->ClientMachPort
,
823 x
->i
.name
.c
, &query
->info
->ip
, (int)query
->info
->port
.b
[0] << 8 | query
->info
->port
.b
[1]);
824 status
= DNSServiceResolverReply_rpc(x
->ClientMachPort
,
825 (char*)&interface
, (char*)&address
, cstring
, 0, MDNS_MM_TIMEOUT
);
826 if (status
== MACH_SEND_TIMED_OUT
)
827 AbortBlockedClient(x
->ClientMachPort
, "resolve", x
);
830 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
831 DNSCString name
, DNSCString regtype
, DNSCString domain
)
833 // Check client parameter
834 (void)unusedserver
; // Unused
835 mStatus err
= mStatus_NoError
;
836 const char *errormsg
= "Unknown";
837 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
838 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
840 // Check other parameters
842 domainname t
, d
, srv
;
843 if (!name
[0] || !MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
844 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
845 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
846 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
848 // Allocate memory, and handle failure
849 DNSServiceResolver
*x
= mallocL("DNSServiceResolver", sizeof(*x
));
850 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
852 // Set up object, and link into list
853 x
->ClientMachPort
= client
;
854 x
->i
.InterfaceID
= mDNSInterface_Any
;
856 x
->ReportTime
= (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond
) | 1;
857 // Don't report errors for old iChat ("_ichat._tcp") service.
858 // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead,
859 // and so should other applications that have valid reasons to be doing ongoing record monitoring.
860 if (SameDomainLabel(t
.c
, (mDNSu8
*)"\x6_ichat")) x
->ReportTime
= 0;
861 x
->next
= DNSServiceResolverList
;
862 DNSServiceResolverList
= x
;
865 LogOperation("%5d: DNSServiceResolver(%##s) START", client
, x
->i
.name
.c
);
866 err
= mDNS_StartResolveService(&mDNSStorage
, &x
->q
, &x
->i
, FoundInstanceInfo
, x
);
867 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_StartResolveService"; goto fail
; }
869 // Succeeded: Wrap up and return
870 EnableDeathNotificationForClient(client
, x
);
871 return(mStatus_NoError
);
874 err
= mStatus_BadParamErr
;
876 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client
, name
, regtype
, domain
, errormsg
, err
);
880 //*************************************************************************************************************
883 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
885 DNSServiceRegistration
*x
= (DNSServiceRegistration
*)sr
->ServiceContext
;
887 if (result
== mStatus_NoError
)
889 kern_return_t status
;
890 LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
891 status
= DNSServiceRegistrationReply_rpc(x
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
892 if (status
== MACH_SEND_TIMED_OUT
)
893 AbortBlockedClient(x
->ClientMachPort
, "registration success", x
);
896 else if (result
== mStatus_NameConflict
)
898 LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
899 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
900 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
902 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
905 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
906 // of their registration in the usual way (which we will catch via client death notification).
907 // If the Mach queue is full, we forcibly abort the client immediately.
908 kern_return_t status
= DNSServiceRegistrationReply_rpc(x
->ClientMachPort
, result
, MDNS_MM_TIMEOUT
);
909 if (status
== MACH_SEND_TIMED_OUT
)
910 AbortBlockedClient(x
->ClientMachPort
, "registration conflict", x
);
914 else if (result
== mStatus_MemFree
)
918 debugf("RegCallback renaming %#s to %#s", x
->name
.c
, mDNSStorage
.nicelabel
.c
);
919 x
->autorename
= mDNSfalse
;
920 x
->name
= mDNSStorage
.nicelabel
;
921 mDNS_RenameAndReregisterService(m
, &x
->s
, &x
->name
);
925 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
926 while (*r
&& *r
!= x
) r
= &(*r
)->next
;
929 LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr
->RR_SRV
.resrec
.name
.c
);
932 LogOperation("%5d: DNSServiceRegistration(%##s) Memory Free", x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
);
933 FreeDNSServiceRegistration(x
);
938 LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld",
939 x
->ClientMachPort
, sr
->RR_SRV
.resrec
.name
.c
, result
);
942 mDNSlocal
void CheckForDuplicateRegistrations(DNSServiceRegistration
*x
, domainname
*srv
, mDNSIPPort port
)
944 int count
= 1; // Start with the one we're planning to register, then see if there are any more
946 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
947 if (rr
->resrec
.rrtype
== kDNSType_SRV
&&
948 rr
->resrec
.rdata
->u
.srv
.port
.NotAnInteger
== port
.NotAnInteger
&&
949 SameDomainName(&rr
->resrec
.name
, srv
))
953 LogMsg("%5d: Client application registered %d identical instances of service %##s port %d.",
954 x
->ClientMachPort
, count
, srv
->c
, (int)port
.b
[0] << 8 | port
.b
[1]);
957 mDNSexport kern_return_t
provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
958 DNSCString name
, DNSCString regtype
, DNSCString domain
, int notAnIntPort
, DNSCString txtRecord
)
960 // Check client parameter
961 (void)unusedserver
; // Unused
962 mStatus err
= mStatus_NoError
;
963 const char *errormsg
= "Unknown";
964 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
965 if (CheckForExistingClient(client
)) { err
= mStatus_Invalid
; errormsg
= "Client id already in use"; goto fail
; }
967 // Check for sub-types after the service type
968 AuthRecord
*SubTypes
= mDNSNULL
;
969 mDNSu32 i
, NumSubTypes
= 0;
970 char *comma
= regtype
;
971 while (*comma
&& *comma
!= ',') comma
++;
972 if (*comma
) // If we found a comma...
974 *comma
= 0; // Overwrite the first comma with a nul
975 char *p
= comma
+ 1; // Start scanning from the next character
978 if ( !(*p
&& *p
!= ',')) { errormsg
= "Bad Service SubType"; goto badparam
; }
979 while (*p
&& *p
!= ',') p
++;
985 // Check other parameters
989 if (!name
[0]) n
= mDNSStorage
.nicelabel
;
990 else if (!MakeDomainLabelFromLiteralString(&n
, name
)) { errormsg
= "Bad Instance Name"; goto badparam
; }
991 if (!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) { errormsg
= "Bad Service Type"; goto badparam
; }
992 if (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) { errormsg
= "Bad Domain"; goto badparam
; }
993 if (!ConstructServiceName(&srv
, &n
, &t
, &d
)) { errormsg
= "Bad Name"; goto badparam
; }
996 port
.NotAnInteger
= notAnIntPort
;
998 unsigned char txtinfo
[1024] = "";
999 unsigned int data_len
= 0;
1000 unsigned int size
= sizeof(RDataBody
);
1001 unsigned char *pstring
= &txtinfo
[data_len
];
1002 char *ptr
= txtRecord
;
1004 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1005 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1006 // Hence we have to convert the C-string to a P-string.
1007 // ASCII-1 characters are allowed in the C-string as boundary markers,
1008 // so that a single C-string can be used to represent one or more P-strings.
1011 if (++data_len
>= sizeof(txtinfo
)) { errormsg
= "TXT record too long"; goto badtxt
; }
1012 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
1014 pstring
= &txtinfo
[data_len
];
1020 if (pstring
[0] == 255) { errormsg
= "TXT record invalid (component longer than 255)"; goto badtxt
; }
1021 pstring
[++pstring
[0]] = *ptr
++;
1026 if (size
< data_len
)
1029 // Allocate memory, and handle failure
1030 DNSServiceRegistration
*x
= mallocL("DNSServiceRegistration", sizeof(*x
) - sizeof(RDataBody
) + size
);
1031 if (!x
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1035 SubTypes
= mallocL("ServiceSubTypes", NumSubTypes
* sizeof(AuthRecord
));
1036 if (!SubTypes
) { freeL("DNSServiceRegistration", x
); err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1037 for (i
= 0; i
< NumSubTypes
; i
++)
1039 comma
++; // Advance over the nul character
1040 MakeDomainNameFromDNSNameString(&SubTypes
[i
].resrec
.name
, comma
);
1041 while (*comma
) comma
++; // Advance comma to point to the next terminating nul
1045 // Set up object, and link into list
1046 x
->ClientMachPort
= client
;
1047 x
->autoname
= (!name
[0]);
1048 x
->autorename
= mDNSfalse
;
1050 x
->next
= DNSServiceRegistrationList
;
1051 DNSServiceRegistrationList
= x
;
1054 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x
->ClientMachPort
, name
, regtype
, domain
);
1055 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1056 // a port number of zero. When two instances of the protected client are allowed to run on one
1057 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1058 if (port
.NotAnInteger
) CheckForDuplicateRegistrations(x
, &srv
, port
);
1060 err
= mDNS_RegisterService(&mDNSStorage
, &x
->s
,
1061 &x
->name
, &t
, &d
, // Name, type, domain
1062 mDNSNULL
, port
, // Host and port
1063 txtinfo
, data_len
, // TXT data, length
1064 SubTypes
, NumSubTypes
, // Subtypes
1065 mDNSInterface_Any
, // Interace ID
1066 RegCallback
, x
); // Callback and context
1068 if (err
) { AbortClient(client
, x
); errormsg
= "mDNS_RegisterService"; goto fail
; }
1070 // Succeeded: Wrap up and return
1071 EnableDeathNotificationForClient(client
, x
);
1072 return(mStatus_NoError
);
1075 LogMsg("%5d: TXT record: %.100s...", client
, txtRecord
);
1077 err
= mStatus_BadParamErr
;
1079 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1080 client
, name
, regtype
, domain
, notAnIntPort
, errormsg
, err
);
1084 mDNSlocal
void mDNS_StatusCallback(mDNS
*const m
, mStatus result
)
1087 if (result
== mStatus_ConfigChanged
)
1089 DNSServiceRegistration
*r
;
1090 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1091 if (r
->autoname
&& !SameDomainLabel(r
->name
.c
, mDNSStorage
.nicelabel
.c
))
1093 debugf("NetworkChanged renaming %#s to %#s", r
->name
.c
, mDNSStorage
.nicelabel
.c
);
1094 r
->autorename
= mDNStrue
;
1095 mDNS_DeregisterService(&mDNSStorage
, &r
->s
);
1098 else if (result
== mStatus_GrowCache
)
1100 // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore
1101 mDNSu32 numrecords
= m
->rrcache_size
;
1102 CacheRecord
*storage
= mallocL("mStatus_GrowCache", sizeof(CacheRecord
) * numrecords
);
1103 if (storage
) mDNS_GrowCache(&mDNSStorage
, storage
, numrecords
);
1107 //*************************************************************************************************************
1108 // Add / Update / Remove records from existing Registration
1110 mDNSexport kern_return_t
provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1111 int type
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
, natural_t
*reference
)
1113 // Check client parameter
1114 (void)unusedserver
; // Unused
1115 mStatus err
= mStatus_NoError
;
1116 const char *errormsg
= "Unknown";
1117 domainname
*name
= (domainname
*)"";
1118 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1119 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1120 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1121 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1122 name
= &x
->s
.RR_SRV
.resrec
.name
;
1124 // Check other parameters
1125 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1126 unsigned int size
= sizeof(RDataBody
);
1127 if (size
< data_len
)
1130 // Allocate memory, and handle failure
1131 ExtraResourceRecord
*extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
1132 if (!extra
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1134 // Fill in type, length, and data of new record
1135 extra
->r
.resrec
.rrtype
= type
;
1136 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1137 extra
->r
.resrec
.rdlength
= data_len
;
1138 memcpy(&extra
->r
.rdatastorage
.u
.data
, data
, data_len
);
1141 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1142 client
, x
->s
.RR_SRV
.resrec
.name
.c
, type
, data_len
, extra
);
1143 err
= mDNS_AddRecordToService(&mDNSStorage
, &x
->s
, extra
, &extra
->r
.rdatastorage
, ttl
);
1144 *reference
= (natural_t
)extra
;
1145 if (err
) { errormsg
= "mDNS_AddRecordToService"; goto fail
; }
1147 // Succeeded: Wrap up and return
1148 return(mStatus_NoError
);
1151 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client
, name
->c
, type
, data_len
, errormsg
, err
);
1155 mDNSlocal
void UpdateCallback(mDNS
*const m
, AuthRecord
*const rr
, RData
*OldRData
)
1158 if (OldRData
!= &rr
->rdatastorage
)
1159 freeL("Old RData", OldRData
);
1162 mDNSexport kern_return_t
provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1163 natural_t reference
, const char *data
, mach_msg_type_number_t data_len
, uint32_t ttl
)
1165 // Check client parameter
1166 (void)unusedserver
; // Unused
1167 mStatus err
= mStatus_NoError
;
1168 const char *errormsg
= "Unknown";
1169 domainname
*name
= (domainname
*)"";
1170 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1171 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1172 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1173 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1174 name
= &x
->s
.RR_SRV
.resrec
.name
;
1176 // Check other parameters
1177 if (data_len
> 8192) { err
= mStatus_BadParamErr
; errormsg
= "data_len > 8K"; goto fail
; }
1178 unsigned int size
= sizeof(RDataBody
);
1179 if (size
< data_len
)
1182 // Find the record we're updating. NULL reference means update the primary TXT record
1183 AuthRecord
*rr
= &x
->s
.RR_TXT
;
1184 if (reference
) // Scan our list to make sure we're updating a valid record that was previously added
1186 ExtraResourceRecord
*e
= x
->s
.Extras
;
1187 while (e
&& e
!= (ExtraResourceRecord
*)reference
) e
= e
->next
;
1188 if (!e
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such record"; goto fail
; }
1192 // Allocate memory, and handle failure
1193 RData
*newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
1194 if (!newrdata
) { err
= mStatus_NoMemoryErr
; errormsg
= "No memory"; goto fail
; }
1196 // Fill in new length, and data
1197 newrdata
->MaxRDLength
= size
;
1198 memcpy(&newrdata
->u
, data
, data_len
);
1201 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, new length %d)",
1202 client
, x
->s
.RR_SRV
.resrec
.name
.c
, reference
, data_len
);
1203 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, data_len
, newrdata
, UpdateCallback
);
1204 if (err
) { errormsg
= "mDNS_Update"; goto fail
; }
1206 // Succeeded: Wrap up and return
1207 return(mStatus_NoError
);
1210 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client
, name
->c
, reference
, data_len
, errormsg
, err
);
1214 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
1215 natural_t reference
)
1217 // Check client parameter
1218 (void)unusedserver
; // Unused
1219 mStatus err
= mStatus_NoError
;
1220 const char *errormsg
= "Unknown";
1221 domainname
*name
= (domainname
*)"";
1222 if (client
== (mach_port_t
)-1) { err
= mStatus_Invalid
; errormsg
= "Client id -1 invalid"; goto fail
; }
1223 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
1224 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
1225 if (!x
) { err
= mStatus_BadReferenceErr
; errormsg
= "No such client"; goto fail
; }
1226 name
= &x
->s
.RR_SRV
.resrec
.name
;
1229 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X)", client
, x
->s
.RR_SRV
.resrec
.name
.c
, reference
);
1230 ExtraResourceRecord
*extra
= (ExtraResourceRecord
*)reference
;
1231 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, &x
->s
, extra
);
1232 if (err
) { errormsg
= "mDNS_RemoveRecordFromService (No such record)"; goto fail
; }
1234 // Succeeded: Wrap up and return
1235 if (extra
->r
.resrec
.rdata
!= &extra
->r
.rdatastorage
)
1236 freeL("Extra RData", extra
->r
.resrec
.rdata
);
1237 freeL("ExtraResourceRecord", extra
);
1238 return(mStatus_NoError
);
1241 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s (%ld)", client
, name
->c
, reference
, errormsg
, err
);
1245 //*************************************************************************************************************
1248 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1250 mig_reply_error_t
*request
= msg
;
1251 mig_reply_error_t
*reply
;
1252 mach_msg_return_t mr
;
1254 (void)port
; // Unused
1255 (void)size
; // Unused
1256 (void)info
; // Unused
1258 /* allocate a reply buffer */
1259 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
1261 /* call the MiG server routine */
1262 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
1264 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
1266 if (reply
->RetCode
== MIG_NO_REPLY
)
1269 * This return code is a little tricky -- it appears that the
1270 * demux routine found an error of some sort, but since that
1271 * error would not normally get returned either to the local
1272 * user or the remote one, we pretend it's ok.
1274 CFAllocatorDeallocate(NULL
, reply
);
1279 * destroy any out-of-line data in the request buffer but don't destroy
1280 * the reply port right (since we need that to send an error message).
1282 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
1283 mach_msg_destroy(&request
->Head
);
1286 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
1288 /* no reply port, so destroy the reply */
1289 if (reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
1290 mach_msg_destroy(&reply
->Head
);
1291 CFAllocatorDeallocate(NULL
, reply
);
1298 * We don't want to block indefinitely because the client
1299 * isn't receiving messages from the reply port.
1300 * If we have a send-once right for the reply port, then
1301 * this isn't a concern because the send won't block.
1302 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1303 * To avoid falling off the kernel's fast RPC path unnecessarily,
1304 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1307 options
= MACH_SEND_MSG
;
1308 if (MACH_MSGH_BITS_REMOTE(reply
->Head
.msgh_bits
) == MACH_MSG_TYPE_MOVE_SEND_ONCE
)
1309 options
|= MACH_SEND_TIMEOUT
;
1311 mr
= mach_msg(&reply
->Head
, /* msg */
1312 options
, /* option */
1313 reply
->Head
.msgh_size
, /* send_size */
1315 MACH_PORT_NULL
, /* rcv_name */
1316 MACH_MSG_TIMEOUT_NONE
, /* timeout */
1317 MACH_PORT_NULL
); /* notify */
1319 /* Has a message error occurred? */
1322 case MACH_SEND_INVALID_DEST
:
1323 case MACH_SEND_TIMED_OUT
:
1324 /* the reply can't be delivered, so destroy it */
1325 mach_msg_destroy(&reply
->Head
);
1329 /* Includes success case. */
1333 CFAllocatorDeallocate(NULL
, reply
);
1336 mDNSlocal kern_return_t
registerBootstrapService()
1338 kern_return_t status
;
1339 mach_port_t service_send_port
, service_rcv_port
;
1341 debugf("Registering Bootstrap Service");
1344 * See if our service name is already registered and if we have privilege to check in.
1346 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1347 if (status
== KERN_SUCCESS
)
1350 * If so, we must be a followup instance of an already defined server. In that case,
1351 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1352 * that in case we have to unregister later (which requires the privilege port).
1354 server_priv_port
= bootstrap_port
;
1355 restarting_via_mach_init
= TRUE
;
1357 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
1359 status
= bootstrap_create_server(bootstrap_port
, "/usr/sbin/mDNSResponder", getuid(),
1360 FALSE
/* relaunch immediately, not on demand */, &server_priv_port
);
1361 if (status
!= KERN_SUCCESS
) return status
;
1363 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
1364 if (status
!= KERN_SUCCESS
)
1366 mach_port_deallocate(mach_task_self(), server_priv_port
);
1370 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
1371 if (status
!= KERN_SUCCESS
)
1373 mach_port_deallocate(mach_task_self(), server_priv_port
);
1374 mach_port_deallocate(mach_task_self(), service_send_port
);
1377 assert(service_send_port
== service_rcv_port
);
1381 * We have no intention of responding to requests on the service port. We are not otherwise
1382 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1383 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1384 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1386 mach_port_destroy(mach_task_self(), service_rcv_port
);
1390 mDNSlocal kern_return_t
destroyBootstrapService()
1392 debugf("Destroying Bootstrap Service");
1393 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
1396 mDNSlocal
void ExitCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1398 (void)port
; // Unused
1399 (void)msg
; // Unused
1400 (void)size
; // Unused
1401 (void)info
; // Unused
1404 int rrcache_active = 0;
1405 for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++;
1406 debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active);
1409 LogMsg("%s stopping", mDNSResponderVersionString
);
1411 debugf("ExitCallback: destroyBootstrapService");
1413 destroyBootstrapService();
1415 debugf("ExitCallback: Aborting MIG clients");
1416 while (DNSServiceDomainEnumerationList
)
1417 AbortClient(DNSServiceDomainEnumerationList
->ClientMachPort
, DNSServiceDomainEnumerationList
);
1418 while (DNSServiceBrowserList
)
1419 AbortClient(DNSServiceBrowserList
->ClientMachPort
, DNSServiceBrowserList
);
1420 while (DNSServiceResolverList
)
1421 AbortClient(DNSServiceResolverList
->ClientMachPort
, DNSServiceResolverList
);
1422 while (DNSServiceRegistrationList
)
1423 AbortClient(DNSServiceRegistrationList
->ClientMachPort
, DNSServiceRegistrationList
);
1425 debugf("ExitCallback: mDNS_Close");
1426 mDNS_Close(&mDNSStorage
);
1428 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1433 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1434 mDNSlocal
void HandleSIGTERM(int signal
)
1436 (void)signal
; // Unused
1438 debugf("SIGINT/SIGTERM");
1439 mach_msg_header_t header
;
1440 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1441 header
.msgh_remote_port
= exit_m_port
;
1442 header
.msgh_local_port
= MACH_PORT_NULL
;
1443 header
.msgh_size
= sizeof(header
);
1445 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1446 { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
1449 mDNSlocal
void INFOCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1451 (void)port
; // Unused
1452 (void)msg
; // Unused
1453 (void)size
; // Unused
1454 (void)info
; // Unused
1455 DNSServiceDomainEnumeration
*e
;
1456 DNSServiceBrowser
*b
;
1457 DNSServiceResolver
*l
;
1458 DNSServiceRegistration
*r
;
1461 mDNSu32 CacheUsed
= 0, CacheActive
= 0;
1463 LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString
);
1465 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
1466 for (rr
= mDNSStorage
.rrcache_hash
[slot
]; rr
; rr
=rr
->next
)
1469 if (rr
->CRActiveQuestion
) CacheActive
++;
1470 LogMsg("%s %-5s%-6s%s", rr
->CRActiveQuestion
? "Active: " : "Inactive:", DNSTypeName(rr
->resrec
.rrtype
),
1471 ((NetworkInterfaceInfoOSX
*)rr
->resrec
.InterfaceID
)->ifa_name
, GetRRDisplayString(&mDNSStorage
, rr
));
1472 usleep(1000); // Limit rate a little so we don't flood syslog too fast
1474 if (mDNSStorage
.rrcache_totalused
!= CacheUsed
)
1475 LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage
.rrcache_totalused
, CacheUsed
);
1476 if (mDNSStorage
.rrcache_active
!= CacheActive
)
1477 LogMsg("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage
.rrcache_active
, CacheActive
);
1478 LogMsg("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed
, CacheActive
);
1480 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
1481 LogMsg("%5d: DomainEnumeration %##s", e
->ClientMachPort
, e
->dom
.qname
.c
);
1483 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
1484 LogMsg("%5d: ServiceBrowse %##s", b
->ClientMachPort
, b
->q
.qname
.c
);
1486 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1487 LogMsg("%5d: ServiceResolve %##s", l
->ClientMachPort
, l
->i
.name
.c
);
1489 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
1490 LogMsg("%5d: ServiceRegistration %##s", r
->ClientMachPort
, r
->s
.RR_SRV
.resrec
.name
.c
);
1494 LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString
);
1497 mDNSlocal
void HandleSIGINFO(int signal
)
1499 (void)signal
; // Unused
1500 mach_msg_header_t header
;
1501 header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND
, 0);
1502 header
.msgh_remote_port
= info_m_port
;
1503 header
.msgh_local_port
= MACH_PORT_NULL
;
1504 header
.msgh_size
= sizeof(header
);
1506 if (mach_msg_send(&header
) != MACH_MSG_SUCCESS
)
1507 LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated.");
1510 mDNSlocal kern_return_t
mDNSDaemonInitialize(void)
1513 CFMachPortRef d_port
= CFMachPortCreate(NULL
, ClientDeathCallback
, NULL
, NULL
);
1514 CFMachPortRef s_port
= CFMachPortCreate(NULL
, DNSserverCallback
, NULL
, NULL
);
1515 CFMachPortRef e_port
= CFMachPortCreate(NULL
, ExitCallback
, NULL
, NULL
);
1516 CFMachPortRef i_port
= CFMachPortCreate(NULL
, INFOCallback
, NULL
, NULL
);
1517 mach_port_t m_port
= CFMachPortGetPort(s_port
);
1518 char *MachServerName
= mDNSMacOSXSystemBuildNumber(NULL
) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
1519 kern_return_t status
= bootstrap_register(bootstrap_port
, MachServerName
, m_port
);
1520 CFRunLoopSourceRef d_rls
= CFMachPortCreateRunLoopSource(NULL
, d_port
, 0);
1521 CFRunLoopSourceRef s_rls
= CFMachPortCreateRunLoopSource(NULL
, s_port
, 0);
1522 CFRunLoopSourceRef e_rls
= CFMachPortCreateRunLoopSource(NULL
, e_port
, 0);
1523 CFRunLoopSourceRef i_rls
= CFMachPortCreateRunLoopSource(NULL
, i_port
, 0);
1528 LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1530 LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status
), status
);
1534 err
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
1535 rrcachestorage
, RR_CACHE_SIZE
,
1536 mDNS_Init_AdvertiseLocalAddresses
,
1537 mDNS_StatusCallback
, mDNS_Init_NoInitCallbackContext
);
1538 if (err
) { LogMsg("Daemon start: mDNS_Init failed %ld", err
); return(err
); }
1540 client_death_port
= CFMachPortGetPort(d_port
);
1541 exit_m_port
= CFMachPortGetPort(e_port
);
1542 info_m_port
= CFMachPortGetPort(i_port
);
1544 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls
, kCFRunLoopDefaultMode
);
1545 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls
, kCFRunLoopDefaultMode
);
1546 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls
, kCFRunLoopDefaultMode
);
1547 CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls
, kCFRunLoopDefaultMode
);
1552 if (debug_mode
) printf("Service registered with Mach Port %d\n", m_port
);
1554 err
= udsserver_init();
1555 if (err
) { LogMsg("Daemon start: udsserver_init failed"); return err
; }
1556 err
= udsserver_add_rl_source();
1557 if (err
) { LogMsg("Daemon start: udsserver_add_rl_source failed"); return err
; }
1562 mDNSlocal mDNSs32
mDNSDaemonIdle(void)
1564 // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
1565 mDNSs32 nextevent
= mDNS_Execute(&mDNSStorage
);
1567 mDNSs32 now
= mDNSPlatformTimeNow();
1569 // 2. Deliver any waiting browse messages to clients
1570 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
1574 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
1575 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
1576 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
1577 DNSServiceBrowser
*x
= b
;
1579 if (x
->results
) // Try to deliver the list of results
1583 DNSServiceBrowserResult
*const r
= x
->results
;
1585 domainname type
, domain
;
1586 DeconstructServiceName(&r
->result
, &name
, &type
, &domain
); // Don't need to check result; already validated in FoundInstance()
1587 char cname
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
1588 char ctype
[MAX_ESCAPED_DOMAIN_NAME
];
1589 char cdom
[MAX_ESCAPED_DOMAIN_NAME
];
1590 ConvertDomainLabelToCString_unescaped(&name
, cname
);
1591 ConvertDomainNameToCString(&type
, ctype
);
1592 ConvertDomainNameToCString(&domain
, cdom
);
1593 DNSServiceDiscoveryReplyFlags flags
= (r
->next
) ? DNSServiceDiscoverReplyFlagsMoreComing
: 0;
1594 kern_return_t status
= DNSServiceBrowserReply_rpc(x
->ClientMachPort
, r
->resultType
, cname
, ctype
, cdom
, flags
, 1);
1595 // If we failed to send the mach message, try again in one second
1596 if (status
== MACH_SEND_TIMED_OUT
)
1598 if (nextevent
- now
> mDNSPlatformOneSecond
)
1599 nextevent
= now
+ mDNSPlatformOneSecond
;
1604 x
->lastsuccess
= now
;
1605 x
->results
= x
->results
->next
;
1606 freeL("DNSServiceBrowserResult", r
);
1609 // If this client hasn't read a single message in the last 60 seconds, abort it
1610 if (now
- x
->lastsuccess
>= 60 * mDNSPlatformOneSecond
)
1611 AbortBlockedClient(x
->ClientMachPort
, "browse", x
);
1615 DNSServiceResolver
*l
;
1616 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
1617 if (l
->ReportTime
&& now
- l
->ReportTime
>= 0)
1620 LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. "
1621 "This places considerable burden on the network.", l
->ClientMachPort
, l
->i
.name
.c
);
1627 mDNSexport
int main(int argc
, char **argv
)
1630 kern_return_t status
;
1633 for (i
=1; i
<argc
; i
++)
1635 if (!strcmp(argv
[i
], "-d")) debug_mode
= 1;
1638 signal(SIGINT
, HandleSIGTERM
); // SIGINT is what you get for a Ctrl-C
1639 signal(SIGTERM
, HandleSIGTERM
);
1640 signal(SIGINFO
, HandleSIGINFO
);
1642 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
1644 registerBootstrapService();
1646 if (!debug_mode
&& !restarting_via_mach_init
)
1647 exit(0); /* mach_init will restart us immediately as a daemon */
1649 // Unlike deamon(), mach_init does redirect standard file descriptors to /dev/null
1652 int fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
1655 // Avoid unnecessarily duplicating a file descriptor to itself
1656 if (fd
!= STDIN_FILENO
) (void)dup2(fd
, STDIN_FILENO
);
1657 if (fd
!= STDOUT_FILENO
) (void)dup2(fd
, STDOUT_FILENO
);
1658 if (fd
!= STDERR_FILENO
) (void)dup2(fd
, STDERR_FILENO
);
1659 if (fd
!= STDIN_FILENO
&& fd
!= STDOUT_FILENO
&& fd
!= STDERR_FILENO
)
1664 fp
= fopen(PID_FILE
, "w");
1667 fprintf(fp
, "%d\n", getpid());
1671 LogMsg("%s starting", mDNSResponderVersionString
);
1672 status
= mDNSDaemonInitialize();
1674 // Now that we're finished with anything privileged, switch over to running as "nobody"
1675 const struct passwd
*pw
= getpwnam( "nobody");
1677 setuid( pw
->pw_uid
);
1679 setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database
1684 int RunLoopStatus
= kCFRunLoopRunTimedOut
;
1686 // This is the main work loop:
1687 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
1688 // (2) Then we make sure we've delivered all waiting browse messages to our clients
1689 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
1690 // (4) On wakeup we first process *all* events
1691 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
1692 while (RunLoopStatus
== kCFRunLoopRunTimedOut
)
1694 // 1. Before going into a blocking wait call and letting our process to go sleep,
1695 // call mDNSDaemonIdle to allow any deferred work to be completed.
1696 mDNSs32 nextevent
= mDNSDaemonIdle();
1698 nextevent
= udsserver_idle(nextevent
);
1701 // 2. Work out how long we expect to sleep before the next scheduled task
1702 mDNSs32 ticks
= nextevent
- mDNSPlatformTimeNow();
1703 if (ticks
< 1) ticks
= 1;
1704 CFAbsoluteTime interval
= (CFAbsoluteTime
)ticks
/ (CFAbsoluteTime
)mDNSPlatformOneSecond
;
1706 // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
1707 // (a) our next wakeup time, or (b) an event occurs.
1708 // The 'true' parameter makes it return after handling any event that occurs
1709 // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
1710 verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents
, ticks
);
1712 RunLoopStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, interval
, true);
1714 // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
1715 while (RunLoopStatus
== kCFRunLoopRunHandledSource
)
1718 RunLoopStatus
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 0.0, true);
1722 LogMsg("ERROR: CFRunLoopRun Exiting.");
1723 mDNS_Close(&mDNSStorage
);
1726 destroyBootstrapService();
1731 // For convenience when using the "strings" command, this is the last thing in the file
1732 mDNSexport
const char mDNSResponderVersionString
[] = STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";