]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-66.3.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / daemon.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 *
25 * Formatting notes:
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
37
38 Change History (most recent first):
39
40 $Log: daemon.c,v $
41 Revision 1.175 2004/06/10 20:23:21 cheshire
42 Also list interfaces in SIGINFO output
43
44 Revision 1.174 2004/06/08 18:54:48 ksekar
45 <rdar://problem/3681378>: mDNSResponder leaks after exploring in Printer Setup Utility
46
47 Revision 1.173 2004/06/08 17:35:12 cheshire
48 <rdar://problem/3683988> Detect and report if mDNSResponder uses too much CPU
49
50 Revision 1.172 2004/06/05 00:04:26 cheshire
51 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
52
53 Revision 1.171 2004/06/04 08:58:30 ksekar
54 <rdar://problem/3668624>: Keychain integration for secure dynamic update
55
56 Revision 1.170 2004/05/30 20:01:50 ksekar
57 <rdar://problem/3668635>: wide-area default registrations should be in
58 .local too - fixed service registration when clients pass an explicit
59 domain (broken by previous checkin)
60
61 Revision 1.169 2004/05/30 01:30:16 ksekar
62 <rdar://problem/3668635>: wide-area default registrations should be in
63 .local too
64
65 Revision 1.168 2004/05/18 23:51:26 cheshire
66 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
67
68 Revision 1.167 2004/05/14 16:39:47 ksekar
69 Browse for iChat locally for now.
70
71 Revision 1.166 2004/05/13 21:33:52 ksekar
72 Clean up non-local registration control via config file. Force iChat
73 registrations to be local for now.
74
75 Revision 1.165 2004/05/13 04:54:20 ksekar
76 Unified list copy/free code. Added symetric list for
77
78 Revision 1.164 2004/05/12 22:03:08 ksekar
79 Made GetSearchDomainList a true platform-layer call (declaration moved
80 from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local"
81 only on non-OSX platforms. Changed call to return a copy of the list
82 to avoid shared memory issues. Added a routine to free the list.
83
84 Revision 1.163 2004/05/12 02:03:25 ksekar
85 Non-local domains will only be browsed by default, and show up in
86 _browse domain enumeration, if they contain an _browse._dns-sd ptr record.
87
88 Revision 1.162 2004/04/14 23:09:29 ksekar
89 Support for TSIG signed dynamic updates.
90
91 Revision 1.161 2004/04/07 01:20:04 cheshire
92 Hash slot value should be unsigned
93
94 Revision 1.160 2004/04/06 19:51:24 cheshire
95 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
96 After more discussion, we've decided to use userid -2 if "nobody" user doesn't exist.
97
98 Revision 1.159 2004/04/03 01:36:55 cheshire
99 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
100 If "nobody" user doesn't exist, log a message and continue as "root"
101
102 Revision 1.158 2004/04/02 21:39:05 cheshire
103 Fix errors in comments
104
105 Revision 1.157 2004/03/19 18:49:10 ksekar
106 Increased size check in freeL() to account for LargeCacheRecord
107 structs larger than 8k
108
109 Revision 1.156 2004/03/19 18:19:19 ksekar
110 Fixed daemon.c to compile with malloc debugging turned on.
111
112 Revision 1.155 2004/03/13 01:57:34 ksekar
113 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
114
115 Revision 1.154 2004/03/12 08:42:47 cheshire
116 <rdar://problem/3548256>: Should not allow empty string for resolve domain
117
118 Revision 1.153 2004/03/12 08:08:51 cheshire
119 Update comments
120
121 Revision 1.152 2004/02/05 19:39:29 cheshire
122 Move creation of /var/run/mDNSResponder.pid to uds_daemon.c,
123 so that all platforms get this functionality
124
125 Revision 1.151 2004/02/03 22:35:34 cheshire
126 <rdar://problem/3548256>: Should not allow empty string for resolve domain
127
128 Revision 1.150 2004/01/28 21:14:23 cheshire
129 Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode)
130
131 Revision 1.149 2004/01/28 02:30:08 ksekar
132 Added default Search Domains to unicast browsing, controlled via
133 Networking sharing prefs pane. Stopped sending unicast messages on
134 every interface. Fixed unicast resolving via mach-port API.
135
136 Revision 1.148 2004/01/25 00:03:20 cheshire
137 Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
138
139 Revision 1.147 2004/01/19 19:51:46 cheshire
140 Fix compiler error (mixed declarations and code) on some versions of Linux
141
142 Revision 1.146 2003/12/08 21:00:46 rpantos
143 Changes to support mDNSResponder on Linux.
144
145 Revision 1.145 2003/12/05 22:08:07 cheshire
146 Update version string to "mDNSResponder-61", including new mechanism to allow dots (e.g. 58.1)
147
148 Revision 1.144 2003/11/19 23:21:08 ksekar
149 <rdar://problem/3486646>: config change handler not called for dns-sd services
150
151 Revision 1.143 2003/11/14 21:18:32 cheshire
152 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
153 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
154
155 Revision 1.142 2003/11/08 22:18:29 cheshire
156 <rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message
157
158 Revision 1.141 2003/11/07 02:30:57 cheshire
159 Also check per-slot cache use counts in SIGINFO state log
160
161 Revision 1.140 2003/10/21 19:58:26 cheshire
162 <rdar://problem/3459037> Syslog messages should show TTL as signed (for overdue records)
163
164 Revision 1.139 2003/10/21 00:10:18 rpantos
165 <rdar://problem/3409401>: mDNSResponder should not run as root
166
167 Revision 1.138 2003/10/07 20:16:58 cheshire
168 Shorten syslog message a bit
169
170 Revision 1.137 2003/09/23 02:12:43 cheshire
171 Also include port number in list of services registered via new UDS API
172
173 Revision 1.136 2003/09/23 02:07:25 cheshire
174 Include port number in DNSServiceRegistration START/STOP messages
175
176 Revision 1.135 2003/09/23 01:34:02 cheshire
177 In SIGINFO state log, show remaining TTL on cache records, and port number on ServiceRegistrations
178
179 Revision 1.134 2003/08/21 20:01:37 cheshire
180 <rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
181
182 Revision 1.133 2003/08/20 23:39:31 cheshire
183 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
184
185 Revision 1.132 2003/08/20 01:44:56 cheshire
186 Fix errors in LogOperation() calls (only used for debugging)
187
188 Revision 1.131 2003/08/19 05:39:43 cheshire
189 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
190
191 Revision 1.130 2003/08/16 03:39:01 cheshire
192 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
193
194 Revision 1.129 2003/08/15 20:16:03 cheshire
195 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
196 We want to avoid touching the rdata pages, so we don't page them in.
197 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
198 Moved this from the RData to the ResourceRecord object.
199 2. To avoid unnecessarily touching the rdata just to compare it,
200 compute a hash of the rdata and store the hash in the ResourceRecord object.
201
202 Revision 1.128 2003/08/14 19:30:36 cheshire
203 <rdar://problem/3378473> Include list of cache records in SIGINFO output
204
205 Revision 1.127 2003/08/14 02:18:21 cheshire
206 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
207
208 Revision 1.126 2003/08/12 19:56:25 cheshire
209 Update to APSL 2.0
210
211 Revision 1.125 2003/08/08 18:36:04 cheshire
212 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
213
214 Revision 1.124 2003/07/25 18:28:23 cheshire
215 Minor fix to error messages in syslog: Display string parameters with quotes
216
217 Revision 1.123 2003/07/23 17:45:28 cheshire
218 <rdar://problem/3339388> mDNSResponder leaks a bit
219 Don't allocate memory for the reply until after we've verified that the reply is valid
220
221 Revision 1.122 2003/07/23 00:00:04 cheshire
222 Add comments
223
224 Revision 1.121 2003/07/20 03:38:51 ksekar
225 <rdar://problem/3320722> Completed support for Unix-domain socket based API.
226
227 Revision 1.120 2003/07/18 00:30:00 cheshire
228 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
229
230 Revision 1.119 2003/07/17 19:08:58 cheshire
231 <rdar://problem/3332153> Remove calls to enable obsolete UDS code
232
233 Revision 1.118 2003/07/15 21:12:28 cheshire
234 Added extra debugging checks in validatelists() (not used in final shipping version)
235
236 Revision 1.117 2003/07/15 01:55:15 cheshire
237 <rdar://problem/3315777> Need to implement service registration with subtypes
238
239 Revision 1.116 2003/07/02 21:19:51 cheshire
240 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
241
242 Revision 1.115 2003/07/02 02:41:24 cheshire
243 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
244
245 Revision 1.114 2003/07/01 21:10:20 cheshire
246 Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
247
248 Revision 1.113 2003/06/28 17:27:43 vlubet
249 <rdar://problem/3221246> Redirect standard input, standard output, and
250 standard error file descriptors to /dev/null just like any other
251 well behaved daemon
252
253 Revision 1.112 2003/06/25 23:42:19 ksekar
254 <rdar://problem/3249292>: Feature: New Rendezvous APIs (#7875)
255 Reviewed by: Stuart Cheshire
256 Added files necessary to implement Unix domain sockets based enhanced
257 Rendezvous APIs, and integrated with existing Mach-port based daemon.
258
259 Revision 1.111 2003/06/11 01:02:43 cheshire
260 <rdar://problem/3287858> mDNSResponder binary compatibility
261 Make single binary that can run on both Jaguar and Panther.
262
263 Revision 1.110 2003/06/10 01:14:11 cheshire
264 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
265
266 Revision 1.109 2003/06/06 19:53:43 cheshire
267 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
268 (Global search-and-replace; no functional change to code execution.)
269
270 Revision 1.108 2003/06/06 14:08:06 cheshire
271 For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
272
273 Revision 1.107 2003/05/29 05:44:55 cheshire
274 Minor fixes to log messages
275
276 Revision 1.106 2003/05/27 18:30:55 cheshire
277 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
278 Dean Reece suggested SIGINFO is more appropriate than SIGHUP
279
280 Revision 1.105 2003/05/26 03:21:29 cheshire
281 Tidy up address structure naming:
282 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
283 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
284 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
285
286 Revision 1.104 2003/05/26 00:42:06 cheshire
287 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
288
289 Revision 1.103 2003/05/23 23:07:44 cheshire
290 <rdar://problem/3268199> Must not write to stderr when running as daemon
291
292 Revision 1.102 2003/05/22 01:32:31 cheshire
293 Fix typo in Log message format string
294
295 Revision 1.101 2003/05/22 00:26:55 cheshire
296 <rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
297 Modify error message to explain that this is technically legal, but may indicate a bug.
298
299 Revision 1.100 2003/05/21 21:02:24 ksekar
300 <rdar://problem/3247035>: Service should be prefixed
301 Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
302 Mach message port to "com.apple.mDNSResponder.
303
304 Revision 1.99 2003/05/21 17:33:49 cheshire
305 Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
306
307 Revision 1.98 2003/05/20 00:33:07 cheshire
308 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
309 SIGHUP now writes state summary to syslog
310
311 Revision 1.97 2003/05/08 00:19:08 cheshire
312 <rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
313
314 Revision 1.96 2003/05/07 22:10:46 cheshire
315 <rdar://problem/3250330> Add a few more error logging messages
316
317 Revision 1.95 2003/05/07 19:20:17 cheshire
318 <rdar://problem/3251391> Add version number to mDNSResponder builds
319
320 Revision 1.94 2003/05/07 00:28:18 cheshire
321 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
322
323 Revision 1.93 2003/05/06 00:00:49 cheshire
324 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
325
326 Revision 1.92 2003/04/04 20:38:57 cheshire
327 Add $Log header
328
329 */
330
331 #include <mach/mach.h>
332 #include <mach/mach_error.h>
333 #include <servers/bootstrap.h>
334 #include <sys/types.h>
335 #include <unistd.h>
336 #include <paths.h>
337 #include <fcntl.h>
338 #include <pwd.h>
339
340 #include "DNSServiceDiscoveryRequestServer.h"
341 #include "DNSServiceDiscoveryReply.h"
342
343 #include "mDNSClientAPI.h" // Defines the interface to the client layer above
344 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
345
346 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
347
348 #include "GenLinkedList.h"
349
350 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
351
352 //*************************************************************************************************************
353 // Macros
354
355 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
356 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
357 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
358 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
359 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
360
361 // convenience definition
362 #define _UNUSED __attribute__ ((unused))
363
364 //*************************************************************************************************************
365 // Globals
366
367 #define LOCAL_DEFAULT_REG 1 // empty string means register in the local domain
368 #define DEFAULT_REG_DOMAIN "apple.com." // used if the above flag is turned off
369 mDNSexport mDNS mDNSStorage;
370 static mDNS_PlatformSupport PlatformStorage;
371 #define RR_CACHE_SIZE 64
372 static CacheRecord rrcachestorage[RR_CACHE_SIZE];
373
374 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
375 static mach_port_t client_death_port = MACH_PORT_NULL;
376 static mach_port_t exit_m_port = MACH_PORT_NULL;
377 static mach_port_t info_m_port = MACH_PORT_NULL;
378 static mach_port_t server_priv_port = MACH_PORT_NULL;
379
380 // mDNS Mach Message Timeout, in milliseconds.
381 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
382 // fails to service its mach message queue, but long enough to give a well-written
383 // client a chance to service its mach message queue without getting cut off.
384 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
385 // even extra-slow clients a fair chance before we cut them off.
386 #define MDNS_MM_TIMEOUT 250
387
388 static int restarting_via_mach_init = 0;
389
390 //*************************************************************************************************************
391 // Active client list structures
392
393 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
394 struct DNSServiceDomainEnumeration_struct
395 {
396 DNSServiceDomainEnumeration *next;
397 mach_port_t ClientMachPort;
398 DNSQuestion dom; // Question asking for domains
399 DNSQuestion def; // Question asking for default domain
400 };
401
402 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
403 struct DNSServiceBrowserResult_struct
404 {
405 DNSServiceBrowserResult *next;
406 int resultType;
407 domainname result;
408 };
409
410 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
411
412 typedef struct DNSServiceBrowserQuestion
413 {
414 struct DNSServiceBrowserQuestion *next;
415 DNSQuestion q;
416 } DNSServiceBrowserQuestion;
417
418 struct DNSServiceBrowser_struct
419 {
420 DNSServiceBrowser *next;
421 mach_port_t ClientMachPort;
422 DNSServiceBrowserQuestion *qlist;
423 DNSServiceBrowserResult *results;
424 mDNSs32 lastsuccess;
425 };
426
427 typedef struct DNSServiceResolver_struct DNSServiceResolver;
428 struct DNSServiceResolver_struct
429 {
430 DNSServiceResolver *next;
431 mach_port_t ClientMachPort;
432 ServiceInfoQuery q;
433 ServiceInfo i;
434 mDNSs32 ReportTime;
435 };
436
437
438 typedef struct ExtraRecordRef
439 {
440 ExtraResourceRecord *localRef; // extra added to .local service
441 ExtraResourceRecord *globalRef; // extra added to default global service (may be NULL)
442 struct ExtraRecordRef *next;
443 } ExtraRecordRef;
444
445 typedef struct DNSServiceRegistration_struct DNSServiceRegistration;
446 struct DNSServiceRegistration_struct
447 {
448 DNSServiceRegistration *next;
449 mach_port_t ClientMachPort;
450 mDNSBool autoname;
451 mDNSBool autorenameLS;
452 mDNSBool autorenameGS;
453 mDNSBool deallocate; // gs and ls (below) will receive separate MemFree callbacks,
454 // the latter of which must deallocate the wrapper structure.
455 // ls MemFree callback: if (!gs) free wrapper; else set deallocate flag
456 // gs callback: if (deallocate) free wrapper; else free (gs), gs = NULL
457 domainlabel name;
458 ExtraRecordRef *ExtraRefList;
459 ServiceRecordSet *gs; // default "global" (wide area) service (may be NULL)
460 ServiceRecordSet ls; // .local service (also used if client passes an explicit domain)
461 // Don't add any fields after ServiceRecordSet.
462 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
463 };
464
465 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
466 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
467 static DNSServiceResolver *DNSServiceResolverList = NULL;
468 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
469
470 //*************************************************************************************************************
471 // General Utility Functions
472
473 #if MACOSX_MDNS_MALLOC_DEBUGGING
474
475 char _malloc_options[] = "AXZ";
476
477 static void validatelists(mDNS *const m)
478 {
479 DNSServiceDomainEnumeration *e;
480 DNSServiceBrowser *b;
481 DNSServiceResolver *l;
482 DNSServiceRegistration *r;
483 AuthRecord *rr;
484 CacheRecord *cr;
485 DNSQuestion *q;
486 mDNSu32 slot;
487
488 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
489 if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
490 LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort);
491
492 for (b = DNSServiceBrowserList; b; b=b->next)
493 if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
494 LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort);
495
496 for (l = DNSServiceResolverList; l; l=l->next)
497 if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
498 LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort);
499
500 for (r = DNSServiceRegistrationList; r; r=r->next)
501 if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
502 LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort);
503
504 for (rr = m->ResourceRecords; rr; rr=rr->next)
505 if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
506 LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType);
507
508 for (rr = m->DuplicateRecords; rr; rr=rr->next)
509 if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
510 LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType);
511
512 for (q = m->Questions; q; q=q->next)
513 if (q->ThisQInterval == (mDNSs32)~0)
514 LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval);
515
516 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
517 for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next)
518 if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
519 LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType);
520 }
521
522 void *mallocL(char *msg, unsigned int size)
523 {
524 unsigned long *mem = malloc(size+8);
525 if (!mem)
526 {
527 LogMsg("malloc( %s : %d ) failed", msg, size);
528 return(NULL);
529 }
530 else
531 {
532 LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
533 mem[0] = 0xDEAD1234;
534 mem[1] = size;
535 //bzero(&mem[2], size);
536 memset(&mem[2], 0xFF, size);
537 validatelists(&mDNSStorage);
538 return(&mem[2]);
539 }
540 }
541
542 void freeL(char *msg, void *x)
543 {
544 if (!x)
545 LogMsg("free( %s @ NULL )!", msg);
546 else
547 {
548 unsigned long *mem = ((unsigned long *)x) - 2;
549 if (mem[0] != 0xDEAD1234)
550 { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
551 if (mem[1] > 24000)
552 { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; }
553 LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
554 //bzero(mem, mem[1]+8);
555 memset(mem, 0xFF, mem[1]+8);
556 validatelists(&mDNSStorage);
557 free(mem);
558 }
559 }
560
561 #endif
562
563 //*************************************************************************************************************
564 // Client Death Detection
565
566 mDNSlocal void FreeSRS(ServiceRecordSet *s)
567 {
568 while (s->Extras)
569 {
570 ExtraResourceRecord *extras = s->Extras;
571 s->Extras = s->Extras->next;
572 if (extras->r.resrec.rdata != &extras->r.rdatastorage)
573 freeL("Extra RData", extras->r.resrec.rdata);
574 freeL("ExtraResourceRecord", extras);
575 }
576
577 if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
578 freeL("TXT RData", s->RR_TXT.resrec.rdata);
579
580 if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
581 }
582
583 mDNSlocal void FreeDNSServiceRegistration(ServiceRecordSet *srs)
584 {
585 DNSServiceRegistration *x = srs->ServiceContext;
586 ExtraRecordRef *ref, *fptr;
587
588 FreeSRS(srs);
589 if (srs == x->gs)
590 {
591 freeL("DNSServiceRegistration GlobalService", srs);
592 x->gs = NULL;
593 }
594 else x->deallocate = mDNStrue;
595
596 if (x->deallocate && !x->gs)
597 {
598 ref = x->ExtraRefList;
599 while (ref)
600 { fptr = ref; ref = ref->next; freeL("ExtraRecordRef", fptr); }
601 freeL("DNSServiceRegistration", x);
602 }
603 }
604
605
606 // AbortClient finds whatever client is identified by the given Mach port,
607 // stops whatever operation that client was doing, and frees its memory.
608 // In the case of a service registration, the actual freeing may be deferred
609 // until we get the mStatus_MemFree message, if necessary
610 mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
611 {
612 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
613 DNSServiceBrowser **b = &DNSServiceBrowserList;
614 DNSServiceResolver **l = &DNSServiceResolverList;
615 DNSServiceRegistration **r = &DNSServiceRegistrationList;
616
617 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
618 if (*e)
619 {
620 DNSServiceDomainEnumeration *x = *e;
621 *e = (*e)->next;
622 if (m && m != x)
623 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
624 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
625 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
626 mDNS_StopGetDomains(&mDNSStorage, &x->def);
627 freeL("DNSServiceDomainEnumeration", x);
628 return;
629 }
630
631 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
632 if (*b)
633 {
634 DNSServiceBrowser *x = *b;
635 DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
636 *b = (*b)->next;
637 while (qptr)
638 {
639 if (m && m != x)
640 LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
641 else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c);
642 mDNS_StopBrowse(&mDNSStorage, &qptr->q);
643 freePtr = qptr;
644 qptr = qptr->next;
645 freeL("DNSServiceBrowserQuestion", freePtr);
646 }
647 while (x->results)
648 {
649 DNSServiceBrowserResult *r = x->results;
650 x->results = x->results->next;
651 freeL("DNSServiceBrowserResult", r);
652 }
653 freeL("DNSServiceBrowser", x);
654 return;
655 }
656
657 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
658 if (*l)
659 {
660 DNSServiceResolver *x = *l;
661 *l = (*l)->next;
662 if (m && m != x)
663 LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
664 else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort, x->i.name.c);
665 mDNS_StopResolveService(&mDNSStorage, &x->q);
666 freeL("DNSServiceResolver", x);
667 return;
668 }
669
670 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
671 if (*r)
672 {
673 DNSServiceRegistration *x = *r;
674 *r = (*r)->next;
675 x->autorenameLS = mDNSfalse;
676 x->autorenameGS = mDNSfalse;
677 if (m && m != x)
678 {
679 LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls), m, x);
680 if (x->gs) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs), m, x);
681 }
682 else
683 {
684 LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls));
685 if (x->gs) LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs));
686 }
687 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
688 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
689 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
690 // the list, so we should go ahead and free the memory right now
691 if (x->gs && mDNS_DeregisterService(&mDNSStorage, x->gs))
692 {
693 // Deregister returned an error, so we free immediately
694 FreeSRS(x->gs);
695 x->gs = NULL;
696 }
697 if (mDNS_DeregisterService(&mDNSStorage, &x->ls))
698 FreeDNSServiceRegistration(&x->ls);
699 return;
700 }
701
702 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
703 }
704
705 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
706
707 mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
708 {
709 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
710 DNSServiceBrowser *b = DNSServiceBrowserList;
711 DNSServiceResolver *l = DNSServiceResolverList;
712 DNSServiceRegistration *r = DNSServiceRegistrationList;
713 DNSServiceBrowserQuestion *qptr;
714
715 while (e && e->ClientMachPort != c) e = e->next;
716 while (b && b->ClientMachPort != c) b = b->next;
717 while (l && l->ClientMachPort != c) l = l->next;
718 while (r && r->ClientMachPort != c) r = r->next;
719 if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
720 else if (b)
721 {
722 for (qptr = b->qlist; qptr; qptr = qptr->next)
723 LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg);
724 }
725 else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
726 else if (r)
727 {
728 LogMsg("%5d: Registration(%##s) %s%s", c, r->ls.RR_SRV.resrec.name.c, reason, msg);
729 if (r->gs) LogMsg("%5d: Registration(%##s) %s%s", c, r->gs->RR_SRV.resrec.name.c, reason, msg);
730 }
731 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
732
733 AbortClient(c, m);
734 }
735
736 mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
737 {
738 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
739 DNSServiceBrowser *b = DNSServiceBrowserList;
740 DNSServiceResolver *l = DNSServiceResolverList;
741 DNSServiceRegistration *r = DNSServiceRegistrationList;
742 DNSServiceBrowserQuestion *qptr;
743
744 while (e && e->ClientMachPort != c) e = e->next;
745 while (b && b->ClientMachPort != c) b = b->next;
746 while (l && l->ClientMachPort != c) l = l->next;
747 while (r && r->ClientMachPort != c) r = r->next;
748 if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
749 if (b)
750 {
751 for (qptr = b->qlist; qptr; qptr = qptr->next)
752 LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c);
753 }
754 if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
755 if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->ls.RR_SRV.resrec.name.c);
756 return(e || b || l || r);
757 }
758
759 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
760 {
761 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
762 (void)unusedport; // Unused
763 (void)size; // Unused
764 (void)info; // Unused
765 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
766 {
767 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
768 AbortClient(deathMessage->not_port, NULL);
769
770 /* Deallocate the send right that came in the dead name notification */
771 mach_port_destroy(mach_task_self(), deathMessage->not_port);
772 }
773 }
774
775 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
776 {
777 mach_port_t prev;
778 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
779 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
780 // If the port already died while we were thinking about it, then abort the operation right away
781 if (r != KERN_SUCCESS)
782 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
783 }
784
785 //*************************************************************************************************************
786 // Domain Enumeration
787
788 mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
789 {
790 kern_return_t status;
791 #pragma unused(m)
792 char buffer[MAX_ESCAPED_DOMAIN_NAME];
793 DNSServiceDomainEnumerationReplyResultType rt;
794 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
795
796 debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
797 if (answer->rrtype != kDNSType_PTR) return;
798 if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
799
800 if (AddRecord)
801 {
802 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
803 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
804 }
805 else
806 {
807 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
808 else return;
809 }
810
811 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
812 x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
813 !AddRecord ? "RemoveDomain" :
814 question == &x->dom ? "AddDomain" : "AddDomainDefault");
815
816 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
817 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
818 if (status == MACH_SEND_TIMED_OUT)
819 AbortBlockedClient(x->ClientMachPort, "enumeration", x);
820 }
821
822 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
823 int regDom)
824 {
825 // Check client parameter
826 (void)unusedserver; // Unused
827 mStatus err = mStatus_NoError;
828 const char *errormsg = "Unknown";
829 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
830 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
831
832 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
833 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
834 const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
835
836 // Allocate memory, and handle failure
837 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
838 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
839
840 // Set up object, and link into list
841 x->ClientMachPort = client;
842 x->next = DNSServiceDomainEnumerationList;
843 DNSServiceDomainEnumerationList = x;
844
845 // Generate initial response
846 verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
847 // We always give local. as the initial default browse domain, and then look for more
848 kern_return_t status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT);
849 if (status == MACH_SEND_TIMED_OUT)
850 { AbortBlockedClient(x->ClientMachPort, "local enumeration", x); return(mStatus_UnknownErr); }
851
852 // Do the operation
853 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, FoundDomain, x);
854 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, FoundDomain, x);
855 if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
856
857 // Succeeded: Wrap up and return
858 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
859 EnableDeathNotificationForClient(client, x);
860 return(mStatus_NoError);
861
862 fail:
863 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err);
864 return(err);
865 }
866
867 //*************************************************************************************************************
868 // Browse for services
869
870 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
871 {
872 (void)m; // Unused
873
874 if (answer->rrtype != kDNSType_PTR)
875 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
876
877 domainlabel name;
878 domainname type, domain;
879 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
880 {
881 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
882 answer->name.c, answer->rdata->u.name.c);
883 return;
884 }
885
886 DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
887 if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
888
889 verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
890 AssignDomainName(x->result, answer->rdata->u.name);
891 if (AddRecord)
892 x->resultType = DNSServiceBrowserReplyAddInstance;
893 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
894 x->next = NULL;
895
896 DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
897 DNSServiceBrowserResult **p = &browser->results;
898 while (*p) p = &(*p)->next;
899 *p = x;
900 }
901
902 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
903 DNSCString regtype, DNSCString domain)
904 {
905 // Check client parameter
906 (void)unusedserver; // Unused
907 mStatus err = mStatus_NoError;
908 const char *errormsg = "Unknown";
909 DNameListElem *SearchDomains = NULL, *sdPtr;
910 DNSServiceBrowserQuestion *qptr;
911
912 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
913 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
914
915 // Check other parameters
916 domainname t, d;
917 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
918
919 // Allocate memory, and handle failure
920 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
921 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
922
923 // Set up object, and link into list
924 x->ClientMachPort = client;
925 x->results = NULL;
926 x->lastsuccess = 0;
927 x->qlist = NULL;
928 x->next = DNSServiceBrowserList;
929 DNSServiceBrowserList = x;
930
931 //!!!KRS browse locally for ichat
932 if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp.")))
933 domain = "local.";
934
935 if (domain[0])
936 {
937 // Start browser for an explicit domain
938 x->qlist = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
939 x->qlist->next = NULL;
940 if (!x->qlist) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; }
941
942 if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; }
943 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c);
944 err = mDNS_StartBrowse(&mDNSStorage, &x->qlist->q, &t, &d, mDNSInterface_Any, FoundInstance, x);
945 if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; }
946 }
947 else
948 {
949 // Start browser on all domains
950 SearchDomains = mDNSPlatformGetSearchDomainList();
951 if (!SearchDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
952 for (sdPtr = SearchDomains; sdPtr; sdPtr = sdPtr->next)
953 {
954 qptr = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
955 if (!qptr) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; }
956 qptr->next = x->qlist;
957 x->qlist = qptr;
958 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, sdPtr->name.c);
959 err = mDNS_StartBrowse(&mDNSStorage, &qptr->q, &t, &sdPtr->name, mDNSInterface_Any, FoundInstance, x);
960 if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; }
961 }
962 }
963 // Succeeded: Wrap up and return
964 EnableDeathNotificationForClient(client, x);
965 mDNS_FreeDNameList(SearchDomains);
966 return(mStatus_NoError);
967
968 badparam:
969 err = mStatus_BadParamErr;
970 fail:
971 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err);
972 if (SearchDomains) mDNS_FreeDNameList(SearchDomains);
973 return(err);
974 }
975
976 //*************************************************************************************************************
977 // Resolve Service Info
978
979 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
980 {
981 kern_return_t status;
982 DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
983 NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
984 if (query->info->InterfaceID == (mDNSInterfaceID)~0) ifx = mDNSNULL;
985 struct sockaddr_storage interface;
986 struct sockaddr_storage address;
987 char cstring[1024];
988 int i, pstrlen = query->info->TXTinfo[0];
989 (void)m; // Unused
990
991 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
992
993 if (query->info->TXTlen > sizeof(cstring)) return;
994
995 bzero(&interface, sizeof(interface));
996 bzero(&address, sizeof(address));
997
998 if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
999 {
1000 struct sockaddr_in *sin = (struct sockaddr_in*)&interface;
1001 sin->sin_len = sizeof(*sin);
1002 sin->sin_family = AF_INET;
1003 sin->sin_port = 0;
1004 sin->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
1005 }
1006 else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
1007 {
1008 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
1009 sin6->sin6_len = sizeof(*sin6);
1010 sin6->sin6_family = AF_INET6;
1011 sin6->sin6_flowinfo = 0;
1012 sin6->sin6_port = 0;
1013 sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
1014 sin6->sin6_scope_id = ifx->scope_id;
1015 }
1016
1017 if (query->info->ip.type == mDNSAddrType_IPv4)
1018 {
1019 struct sockaddr_in *sin = (struct sockaddr_in*)&address;
1020 sin->sin_len = sizeof(*sin);
1021 sin->sin_family = AF_INET;
1022 sin->sin_port = query->info->port.NotAnInteger;
1023 sin->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
1024 }
1025 else
1026 {
1027 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
1028 sin6->sin6_len = sizeof(*sin6);
1029 sin6->sin6_family = AF_INET6;
1030 sin6->sin6_port = query->info->port.NotAnInteger;
1031 sin6->sin6_flowinfo = 0;
1032 sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
1033 sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
1034 }
1035
1036 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
1037 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
1038 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
1039 // ASCII-1 characters are used in the C-string as boundary markers,
1040 // to indicate the boundaries between the original constituent P-strings.
1041 for (i=1; i<query->info->TXTlen; i++)
1042 {
1043 if (--pstrlen >= 0)
1044 cstring[i-1] = query->info->TXTinfo[i];
1045 else
1046 {
1047 cstring[i-1] = 1;
1048 pstrlen = query->info->TXTinfo[i];
1049 }
1050 }
1051 cstring[i-1] = 0; // Put the terminating NULL on the end
1052
1053 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
1054 x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
1055 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
1056 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
1057 if (status == MACH_SEND_TIMED_OUT)
1058 AbortBlockedClient(x->ClientMachPort, "resolve", x);
1059 }
1060
1061 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
1062 DNSCString name, DNSCString regtype, DNSCString domain)
1063 {
1064 // Check client parameter
1065 (void)unusedserver; // Unused
1066 mStatus err = mStatus_NoError;
1067 const char *errormsg = "Unknown";
1068 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1069 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1070
1071 // Check other parameters
1072 domainlabel n;
1073 domainname t, d, srv;
1074 if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1075 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1076 if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
1077 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1078
1079 // Allocate memory, and handle failure
1080 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
1081 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1082
1083 // Set up object, and link into list
1084 x->ClientMachPort = client;
1085 x->i.InterfaceID = mDNSInterface_Any;
1086 x->i.name = srv;
1087 x->ReportTime = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1;
1088 // Don't report errors for old iChat ("_ichat._tcp") service.
1089 // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead,
1090 // and so should other applications that have valid reasons to be doing ongoing record monitoring.
1091 if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0;
1092 x->next = DNSServiceResolverList;
1093 DNSServiceResolverList = x;
1094
1095 // Do the operation
1096 LogOperation("%5d: DNSServiceResolver(%##s) START", client, x->i.name.c);
1097 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
1098 if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
1099
1100 // Succeeded: Wrap up and return
1101 EnableDeathNotificationForClient(client, x);
1102 return(mStatus_NoError);
1103
1104 badparam:
1105 err = mStatus_BadParamErr;
1106 fail:
1107 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err);
1108 return(err);
1109 }
1110
1111 //*************************************************************************************************************
1112 // Registration
1113
1114 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
1115 {
1116 DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext;
1117
1118 if (result == mStatus_NoError)
1119 {
1120 kern_return_t status;
1121 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr));
1122 status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
1123 if (status == MACH_SEND_TIMED_OUT)
1124 AbortBlockedClient(x->ClientMachPort, "registration success", x);
1125 }
1126
1127 else if (result == mStatus_NameConflict)
1128 {
1129 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr));
1130 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1131 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1132 if (x->autoname)
1133 mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
1134 else
1135 {
1136 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1137 // of their registration in the usual way (which we will catch via client death notification).
1138 // If the Mach queue is full, we forcibly abort the client immediately.
1139 kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
1140 if (status == MACH_SEND_TIMED_OUT)
1141 AbortBlockedClient(x->ClientMachPort, "registration conflict", x);
1142 }
1143 }
1144
1145 else if (result == mStatus_MemFree)
1146 {
1147 mDNSBool *autorename = (sr == &x->ls ? &x->autorenameLS : &x->autorenameGS);
1148 if (*autorename)
1149 {
1150 debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c);
1151 *autorename = mDNSfalse;
1152 x->name = mDNSStorage.nicelabel;
1153 mDNS_RenameAndReregisterService(m, sr, &x->name);
1154 }
1155 else
1156 {
1157 // SANITY CHECK: Should only get mStatus_MemFree as a result of calling mDNS_DeregisterService()
1158 // and should only get it with x->autorename false if we've already removed the record from our
1159 // list, but this check is just to make sure...
1160 DNSServiceRegistration **r = &DNSServiceRegistrationList;
1161 while (*r && *r != x) r = &(*r)->next;
1162 if (*r)
1163 {
1164 LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c);
1165 *r = (*r)->next;
1166 }
1167 // END SANITY CHECK
1168 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr));
1169 FreeDNSServiceRegistration(sr);
1170 }
1171 }
1172
1173 else
1174 {
1175 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld",
1176 x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr), result);
1177 if (sr == x->gs) { freeL("RegCallback - ServiceRecordSet", x->gs); x->gs = NULL; }
1178 }
1179 }
1180
1181 mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port)
1182 {
1183 int count = 1; // Start with the one we're planning to register, then see if there are any more
1184 AuthRecord *rr;
1185 for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
1186 if (rr->resrec.rrtype == kDNSType_SRV &&
1187 rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger &&
1188 SameDomainName(&rr->resrec.name, srv))
1189 count++;
1190
1191 if (count > 1)
1192 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1193 x->ClientMachPort, count, srv->c, mDNSVal16(port));
1194 }
1195
1196 // Pass NULL for x to allocate the structure (for local service). Call again w/ initialized x to add a global service.
1197 mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString name, DNSCString regtype, DNSCString domain,
1198 int notAnIntPort, DNSCString txtRecord, DNSServiceRegistration *x)
1199 {
1200 ServiceRecordSet *srs = NULL; // record set to use in registration operation
1201 mStatus err = mStatus_NoError;
1202 const char *errormsg = "Unknown";
1203
1204 // Check for sub-types after the service type
1205 AuthRecord *SubTypes = mDNSNULL;
1206 mDNSu32 i, NumSubTypes = 0;
1207 char *comma = regtype;
1208 while (*comma && *comma != ',') comma++;
1209 if (*comma) // If we found a comma...
1210 {
1211 *comma = 0; // Overwrite the first comma with a nul
1212 char *p = comma + 1; // Start scanning from the next character
1213 while (*p)
1214 {
1215 if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; }
1216 while (*p && *p != ',') p++;
1217 if (*p) *p++ = 0;
1218 NumSubTypes++;
1219 }
1220 }
1221
1222 // Check other parameters
1223 domainlabel n;
1224 domainname t, d;
1225 domainname srv;
1226 if (!name[0]) n = mDNSStorage.nicelabel;
1227 else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1228 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1229
1230 if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
1231 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1232
1233 mDNSIPPort port;
1234 port.NotAnInteger = notAnIntPort;
1235
1236 unsigned char txtinfo[1024] = "";
1237 unsigned int data_len = 0;
1238 unsigned int size = sizeof(RDataBody);
1239 unsigned char *pstring = &txtinfo[data_len];
1240 char *ptr = txtRecord;
1241
1242 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1243 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1244 // Hence we have to convert the C-string to a P-string.
1245 // ASCII-1 characters are allowed in the C-string as boundary markers,
1246 // so that a single C-string can be used to represent one or more P-strings.
1247 while (*ptr)
1248 {
1249 if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
1250 if (*ptr == 1) // If this is our boundary marker, start a new P-string
1251 {
1252 pstring = &txtinfo[data_len];
1253 pstring[0] = 0;
1254 ptr++;
1255 }
1256 else
1257 {
1258 if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
1259 pstring[++pstring[0]] = *ptr++;
1260 }
1261 }
1262
1263 data_len++;
1264 if (size < data_len)
1265 size = data_len;
1266
1267 // Allocate memory, and handle failure
1268 if (!x)
1269 {
1270 x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size);
1271 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1272 bzero(x, sizeof(*x) - sizeof(RDataBody) + size);
1273 // Set up object, and link into list
1274 x->ClientMachPort = client;
1275 x->autoname = (!name[0]);
1276 x->name = n;
1277 x->next = DNSServiceRegistrationList;
1278 DNSServiceRegistrationList = x;
1279 srs = &x->ls;
1280 }
1281 else
1282 {
1283 x->gs = mallocL("DNSServiceRegistration GlobalService", sizeof(ServiceRecordSet) - sizeof(RDataBody) + size);
1284 if (!x->gs) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1285 srs = x->gs;
1286 bzero(srs, sizeof(ServiceRecordSet) - sizeof(RDataBody) + size);
1287 }
1288
1289 if (NumSubTypes)
1290 {
1291 SubTypes = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
1292 if (!SubTypes) { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1293 for (i = 0; i < NumSubTypes; i++)
1294 {
1295 comma++; // Advance over the nul character
1296 MakeDomainNameFromDNSNameString(&SubTypes[i].resrec.name, comma);
1297 while (*comma) comma++; // Advance comma to point to the next terminating nul
1298 }
1299 }
1300
1301 // Do the operation
1302 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1303 x->ClientMachPort, name, regtype, domain, mDNSVal16(port));
1304 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1305 // a port number of zero. When two instances of the protected client are allowed to run on one
1306 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1307 if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &srv, port);
1308
1309 err = mDNS_RegisterService(&mDNSStorage, srs,
1310 &x->name, &t, &d, // Name, type, domain
1311 mDNSNULL, port, // Host and port
1312 txtinfo, data_len, // TXT data, length
1313 SubTypes, NumSubTypes, // Subtypes
1314 mDNSInterface_Any, // Interface ID
1315 RegCallback, x); // Callback and context
1316
1317 if (err)
1318 {
1319 if (srs == &x->ls) AbortClient(client, x); // don't abort client for global service
1320 else FreeDNSServiceRegistration(x->gs);
1321 errormsg = "mDNS_RegisterService";
1322 goto fail;
1323 }
1324 return x;
1325
1326 badtxt:
1327 LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
1328 badparam:
1329 err = mStatus_BadParamErr;
1330 fail:
1331 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1332 client, name, regtype, domain, notAnIntPort, errormsg, err);
1333 return NULL;
1334 }
1335
1336 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1337 DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord)
1338 {
1339 // Check client parameter
1340 (void)unusedserver; // Unused
1341 mStatus err = mStatus_NoError;
1342 const char *errormsg = "Unknown";
1343 DNSServiceRegistration *x = NULL;
1344 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1345 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1346
1347 x = RegisterService(client, name, regtype, *domain ? domain : "local.", notAnIntPort, txtRecord, NULL);
1348 if (!x) { err = mStatus_UnknownErr; goto fail; }
1349
1350 //!!!KRS if we got a dynamic reg domain from the config file, use it for default (except for iChat)
1351 if (!*domain && mDNSStorage.uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp."))
1352 x = RegisterService(client, name, regtype, mDNSStorage.uDNS_info.ServiceRegDomain, notAnIntPort, txtRecord, x);
1353
1354 // Succeeded: Wrap up and return
1355 EnableDeathNotificationForClient(client, x);
1356 return(mStatus_NoError);
1357
1358 fail:
1359 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1360 client, name, regtype, domain, notAnIntPort, errormsg, err);
1361 return mStatus_UnknownErr;
1362 }
1363
1364 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
1365 {
1366 (void)m; // Unused
1367 if (result == mStatus_ConfigChanged)
1368 {
1369 DNSServiceRegistration *r;
1370 for (r = DNSServiceRegistrationList; r; r=r->next)
1371 if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c))
1372 {
1373 debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c);
1374 r->autorenameLS = mDNStrue;
1375 mDNS_DeregisterService(&mDNSStorage, &r->ls);
1376 if (r->gs) { mDNS_DeregisterService(&mDNSStorage, r->gs); r->autorenameGS = mDNStrue; }
1377 }
1378 udsserver_handle_configchange();
1379 }
1380 else if (result == mStatus_GrowCache)
1381 {
1382 // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore
1383 mDNSu32 numrecords = m->rrcache_size;
1384 CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords);
1385 if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords);
1386 }
1387 }
1388
1389 //*************************************************************************************************************
1390 // Add / Update / Remove records from existing Registration
1391
1392
1393 mDNSlocal ExtraResourceRecord *AddExtraRecord(DNSServiceRegistration *x, ServiceRecordSet *srs, mach_port_t client,
1394 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1395 {
1396 mStatus err = mStatus_NoError;
1397 const char *errormsg = "Unknown";
1398 domainname *name = (domainname *)"";
1399 name = &srs->RR_SRV.resrec.name;
1400
1401 (void)x; // unused
1402
1403 unsigned int size = sizeof(RDataBody);
1404 if (size < data_len)
1405 size = data_len;
1406
1407 // Allocate memory, and handle failure
1408 ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
1409 if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1410
1411 // Fill in type, length, and data of new record
1412 extra->r.resrec.rrtype = type;
1413 extra->r.rdatastorage.MaxRDLength = size;
1414 extra->r.resrec.rdlength = data_len;
1415 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
1416
1417 // Do the operation
1418 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1419 client, srs->RR_SRV.resrec.name.c, type, data_len, extra);
1420 err = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl);
1421
1422 if (err)
1423 {
1424 freeL("Extra Resource Record", extra);
1425 errormsg = "mDNS_AddRecordToService";
1426 goto fail;
1427 }
1428
1429 return extra;
1430
1431 fail:
1432 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err);
1433 return NULL;
1434 }
1435
1436
1437 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1438 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
1439 {
1440 // Check client parameter
1441 (void)unusedserver; // Unused
1442 mStatus err = mStatus_NoError;
1443 const char *errormsg = "Unknown";
1444 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1445 DNSServiceRegistration *x = DNSServiceRegistrationList;
1446 while (x && x->ClientMachPort != client) x = x->next;
1447 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1448
1449 // Check other parameters
1450 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1451
1452 ExtraRecordRef *ref = mallocL("ExtraRecordRef", sizeof(ExtraRecordRef));
1453 if (!ref) { LogMsg("ERROR: malloc"); return mStatus_NoMemoryErr; }
1454
1455 ref->localRef = AddExtraRecord(x, &x->ls, client, type, data, data_len, ttl);
1456 if (!ref->localRef) { freeL("ExtraRecordRef", ref); *reference = (natural_t)NULL; return mStatus_UnknownErr; }
1457
1458 if (x->gs) ref->globalRef = AddExtraRecord(x, x->gs, client, type, data, data_len, ttl); // return success even if global case fails
1459 else ref->globalRef = NULL;
1460
1461 // Succeeded: Wrap up and return
1462 ref->next = x->ExtraRefList;
1463 x->ExtraRefList = ref;
1464 *reference = (natural_t)ref;
1465 return mStatus_NoError;
1466
1467 fail:
1468 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, x->name.c, type, data_len, errormsg, err);
1469 return mStatus_UnknownErr;
1470 }
1471
1472 mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
1473 {
1474 (void)m; // Unused
1475 if (OldRData != &rr->rdatastorage)
1476 freeL("Old RData", OldRData);
1477 }
1478
1479 mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1480 {
1481 // Check client parameter
1482 mStatus err = mStatus_NoError;
1483 const char *errormsg = "Unknown";
1484 domainname *name = (domainname *)"";
1485
1486 name = &srs->RR_SRV.resrec.name;
1487
1488 unsigned int size = sizeof(RDataBody);
1489 if (size < data_len)
1490 size = data_len;
1491
1492 // Allocate memory, and handle failure
1493 RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
1494 if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1495
1496 // Fill in new length, and data
1497 newrdata->MaxRDLength = size;
1498 memcpy(&newrdata->u, data, data_len);
1499
1500 // Do the operation
1501 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1502 client, srs->RR_SRV.resrec.name.c, data_len);
1503
1504 err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
1505 if (err)
1506 {
1507 errormsg = "mDNS_Update";
1508 freeL("RData", newrdata);
1509 return err;
1510 }
1511 return(mStatus_NoError);
1512
1513 fail:
1514 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%ld)", client, name->c, data_len, errormsg, err);
1515 return(err);
1516 }
1517
1518
1519 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1520 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1521 {
1522 // Check client parameter
1523 mStatus err = mStatus_NoError;
1524 const char *errormsg = "Unknown";
1525 domainname *name = (domainname *)"";
1526 AuthRecord *gRR, *lRR;
1527
1528 (void)unusedserver; // unused
1529 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1530 DNSServiceRegistration *x = DNSServiceRegistrationList;
1531 while (x && x->ClientMachPort != client) x = x->next;
1532 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1533
1534 // Check other parameters
1535 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1536
1537 // Find the record we're updating. NULL reference means update the primary TXT record
1538 if (!reference)
1539 {
1540 lRR = &x->ls.RR_TXT;
1541 gRR = x->gs ? &x->gs->RR_TXT : NULL;
1542 }
1543 else
1544 {
1545 ExtraRecordRef *ref;
1546 for (ref = x->ExtraRefList; ref; ref= ref->next)
1547 if (ref == (ExtraRecordRef *)reference) break;
1548 if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
1549 lRR = &ref->localRef->r;
1550 gRR = ref->globalRef ? &ref->globalRef->r : NULL;
1551 }
1552
1553 err = UpdateRecord(&x->ls, client, lRR, data, data_len, ttl);
1554 if (err) goto fail;
1555
1556 if (gRR) UpdateRecord(x->gs, client, gRR, data, data_len, ttl); // don't return error if global fails
1557 return mStatus_NoError;
1558
1559 fail:
1560 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err);
1561 return(err);
1562 }
1563
1564 mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
1565 {
1566 domainname *name = &srs->RR_SRV.resrec.name;
1567 mStatus err = mStatus_NoError;
1568 const char *errormsg = "Unknown";
1569
1570 // Do the operation
1571 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name.c);
1572
1573 err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra);
1574 if (err) { errormsg = "mDNS_RemoveRecordFromService (No such record)"; goto fail; }
1575
1576 // Succeeded: Wrap up and return
1577 if (extra->r.resrec.rdata != &extra->r.rdatastorage)
1578 freeL("Extra RData", extra->r.resrec.rdata);
1579 freeL("ExtraResourceRecord", extra);
1580 return(mStatus_NoError);
1581
1582 fail:
1583 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s", client, name->c, errormsg, err);
1584 return(err);
1585 }
1586
1587 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1588 natural_t reference)
1589 {
1590 // Check client parameter
1591 (void)unusedserver; // Unused
1592 mStatus err = mStatus_NoError;
1593 const char *errormsg = "Unknown";
1594 ExtraRecordRef *ref, *prev = NULL;
1595 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1596 DNSServiceRegistration *x = DNSServiceRegistrationList;
1597 while (x && x->ClientMachPort != client) x = x->next;
1598 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1599
1600 ref = x->ExtraRefList;
1601 while (ref)
1602 {
1603 if (ref == (ExtraRecordRef *)ref) break;
1604 prev = ref;
1605 ref = ref->next;
1606 }
1607
1608 if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
1609 err = RemoveRecord(&x->ls, ref->localRef, client);
1610 if (x->gs && ref->globalRef) RemoveRecord(x->gs, ref->globalRef, client); // don't return error if this fails
1611
1612 // delete the ref struct
1613 if (prev) prev->next = ref->next;
1614 else x->ExtraRefList = ref->next;
1615 ref->next = NULL;
1616 freeL("ExtraRecordRef", ref);
1617 return err;
1618
1619 fail:
1620 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err);
1621 return(err);
1622 }
1623
1624 //*************************************************************************************************************
1625 // Support Code
1626
1627 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1628 {
1629 mig_reply_error_t *request = msg;
1630 mig_reply_error_t *reply;
1631 mach_msg_return_t mr;
1632 int options;
1633 (void)port; // Unused
1634 (void)size; // Unused
1635 (void)info; // Unused
1636
1637 /* allocate a reply buffer */
1638 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
1639
1640 /* call the MiG server routine */
1641 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
1642
1643 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
1644 {
1645 if (reply->RetCode == MIG_NO_REPLY)
1646 {
1647 /*
1648 * This return code is a little tricky -- it appears that the
1649 * demux routine found an error of some sort, but since that
1650 * error would not normally get returned either to the local
1651 * user or the remote one, we pretend it's ok.
1652 */
1653 CFAllocatorDeallocate(NULL, reply);
1654 return;
1655 }
1656
1657 /*
1658 * destroy any out-of-line data in the request buffer but don't destroy
1659 * the reply port right (since we need that to send an error message).
1660 */
1661 request->Head.msgh_remote_port = MACH_PORT_NULL;
1662 mach_msg_destroy(&request->Head);
1663 }
1664
1665 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
1666 {
1667 /* no reply port, so destroy the reply */
1668 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
1669 mach_msg_destroy(&reply->Head);
1670 CFAllocatorDeallocate(NULL, reply);
1671 return;
1672 }
1673
1674 /*
1675 * send reply.
1676 *
1677 * We don't want to block indefinitely because the client
1678 * isn't receiving messages from the reply port.
1679 * If we have a send-once right for the reply port, then
1680 * this isn't a concern because the send won't block.
1681 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1682 * To avoid falling off the kernel's fast RPC path unnecessarily,
1683 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1684 */
1685
1686 options = MACH_SEND_MSG;
1687 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
1688 options |= MACH_SEND_TIMEOUT;
1689
1690 mr = mach_msg(&reply->Head, /* msg */
1691 options, /* option */
1692 reply->Head.msgh_size, /* send_size */
1693 0, /* rcv_size */
1694 MACH_PORT_NULL, /* rcv_name */
1695 MACH_MSG_TIMEOUT_NONE, /* timeout */
1696 MACH_PORT_NULL); /* notify */
1697
1698 /* Has a message error occurred? */
1699 switch (mr)
1700 {
1701 case MACH_SEND_INVALID_DEST:
1702 case MACH_SEND_TIMED_OUT:
1703 /* the reply can't be delivered, so destroy it */
1704 mach_msg_destroy(&reply->Head);
1705 break;
1706
1707 default :
1708 /* Includes success case. */
1709 break;
1710 }
1711
1712 CFAllocatorDeallocate(NULL, reply);
1713 }
1714
1715 mDNSlocal kern_return_t registerBootstrapService()
1716 {
1717 kern_return_t status;
1718 mach_port_t service_send_port, service_rcv_port;
1719
1720 debugf("Registering Bootstrap Service");
1721
1722 /*
1723 * See if our service name is already registered and if we have privilege to check in.
1724 */
1725 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1726 if (status == KERN_SUCCESS)
1727 {
1728 /*
1729 * If so, we must be a followup instance of an already defined server. In that case,
1730 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1731 * that in case we have to unregister later (which requires the privilege port).
1732 */
1733 server_priv_port = bootstrap_port;
1734 restarting_via_mach_init = TRUE;
1735 }
1736 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
1737 {
1738 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
1739 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
1740 if (status != KERN_SUCCESS) return status;
1741
1742 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
1743 if (status != KERN_SUCCESS)
1744 {
1745 mach_port_deallocate(mach_task_self(), server_priv_port);
1746 return status;
1747 }
1748
1749 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1750 if (status != KERN_SUCCESS)
1751 {
1752 mach_port_deallocate(mach_task_self(), server_priv_port);
1753 mach_port_deallocate(mach_task_self(), service_send_port);
1754 return status;
1755 }
1756 assert(service_send_port == service_rcv_port);
1757 }
1758
1759 /*
1760 * We have no intention of responding to requests on the service port. We are not otherwise
1761 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1762 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1763 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1764 */
1765 mach_port_destroy(mach_task_self(), service_rcv_port);
1766 return status;
1767 }
1768
1769 mDNSlocal kern_return_t destroyBootstrapService()
1770 {
1771 debugf("Destroying Bootstrap Service");
1772 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
1773 }
1774
1775 mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1776 {
1777 (void)port; // Unused
1778 (void)msg; // Unused
1779 (void)size; // Unused
1780 (void)info; // Unused
1781 /*
1782 CacheRecord *rr;
1783 int rrcache_active = 0;
1784 for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++;
1785 debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active);
1786 */
1787
1788 LogMsgIdent(mDNSResponderVersionString, "stopping");
1789
1790 debugf("ExitCallback: destroyBootstrapService");
1791 if (!mDNS_DebugMode)
1792 destroyBootstrapService();
1793
1794 debugf("ExitCallback: Aborting MIG clients");
1795 while (DNSServiceDomainEnumerationList)
1796 AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
1797 while (DNSServiceBrowserList)
1798 AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
1799 while (DNSServiceResolverList)
1800 AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
1801 while (DNSServiceRegistrationList)
1802 AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
1803
1804 debugf("ExitCallback: mDNS_Close");
1805 mDNS_Close(&mDNSStorage);
1806 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1807 exit(0);
1808 }
1809
1810 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1811 mDNSlocal void HandleSIGTERM(int signal)
1812 {
1813 (void)signal; // Unused
1814 debugf(" ");
1815 debugf("SIGINT/SIGTERM");
1816 mach_msg_header_t header;
1817 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1818 header.msgh_remote_port = exit_m_port;
1819 header.msgh_local_port = MACH_PORT_NULL;
1820 header.msgh_size = sizeof(header);
1821 header.msgh_id = 0;
1822 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
1823 { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
1824 }
1825
1826 mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1827 {
1828 (void)port; // Unused
1829 (void)msg; // Unused
1830 (void)size; // Unused
1831 (void)info; // Unused
1832 DNSServiceDomainEnumeration *e;
1833 DNSServiceBrowser *b;
1834 DNSServiceResolver *l;
1835 DNSServiceRegistration *r;
1836 NetworkInterfaceInfoOSX *i;
1837 mDNSu32 slot;
1838 CacheRecord *rr;
1839 mDNSu32 CacheUsed = 0, CacheActive = 0;
1840 mDNSs32 now = mDNSPlatformTimeNow();
1841
1842 LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----");
1843
1844 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
1845 {
1846 mDNSu32 SlotUsed = 0;
1847 for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next)
1848 {
1849 mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond;
1850 CacheUsed++;
1851 SlotUsed++;
1852 if (rr->CRActiveQuestion) CacheActive++;
1853 LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, DNSTypeName(rr->resrec.rrtype),
1854 ((NetworkInterfaceInfoOSX *)rr->resrec.InterfaceID)->ifa_name, GetRRDisplayString(&mDNSStorage, rr));
1855 usleep(1000); // Limit rate a little so we don't flood syslog too fast
1856 }
1857 if (mDNSStorage.rrcache_used[slot] != SlotUsed)
1858 LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", mDNSStorage.rrcache_used[slot], SlotUsed);
1859 }
1860 if (mDNSStorage.rrcache_totalused != CacheUsed)
1861 LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed);
1862 if (mDNSStorage.rrcache_active != CacheActive)
1863 LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive);
1864 LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
1865
1866 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
1867 LogMsgNoIdent("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
1868
1869 for (b = DNSServiceBrowserList; b; b=b->next)
1870 {
1871 DNSServiceBrowserQuestion *qptr;
1872 for (qptr = b->qlist; qptr; qptr = qptr->next)
1873 LogMsgNoIdent("%5d: ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c);
1874 }
1875 for (l = DNSServiceResolverList; l; l=l->next)
1876 LogMsgNoIdent("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
1877
1878 for (r = DNSServiceRegistrationList; r; r=r->next)
1879 {
1880 LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->ls.RR_SRV.resrec.name.c, mDNSVal16(r->ls.RR_SRV.resrec.rdata->u.srv.port));
1881 if (r->gs) LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->gs->RR_SRV.resrec.name.c, mDNSVal16(r->gs->RR_SRV.resrec.rdata->u.srv.port));
1882 }
1883
1884 udsserver_info();
1885
1886 for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
1887 {
1888 if (!i->Exists)
1889 LogMsgNoIdent("Interface: %s %5s(%lu) DORMANT",
1890 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id);
1891 else
1892 LogMsgNoIdent("Interface: %s %5s(%lu) %s %s %2d %s %2d InterfaceID %p %s %s %#a",
1893 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id,
1894 i->ifinfo.InterfaceActive ? "Active" : " ",
1895 i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4,
1896 i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6,
1897 i->ifinfo.InterfaceID,
1898 i->ifinfo.Advertise ? "Adv" : " ",
1899 i->ifinfo.McastTxRx ? "TxRx" : " ",
1900 &i->ifinfo.ip);
1901 }
1902
1903 LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----");
1904 }
1905
1906 mDNSlocal void HandleSIGINFO(int signal)
1907 {
1908 (void)signal; // Unused
1909 mach_msg_header_t header;
1910 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1911 header.msgh_remote_port = info_m_port;
1912 header.msgh_local_port = MACH_PORT_NULL;
1913 header.msgh_size = sizeof(header);
1914 header.msgh_id = 0;
1915 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
1916 LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated.");
1917 }
1918
1919 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
1920 {
1921 mStatus err;
1922 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
1923 CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
1924 CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL);
1925 CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL);
1926 mach_port_t m_port = CFMachPortGetPort(s_port);
1927 char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
1928 kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
1929 CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
1930 CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
1931 CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0);
1932 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
1933
1934 if (status)
1935 {
1936 if (status == 1103)
1937 LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1938 else
1939 LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
1940 return(status);
1941 }
1942
1943 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
1944 rrcachestorage, RR_CACHE_SIZE,
1945 mDNS_Init_AdvertiseLocalAddresses,
1946 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
1947
1948 if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); }
1949
1950 client_death_port = CFMachPortGetPort(d_port);
1951 exit_m_port = CFMachPortGetPort(e_port);
1952 info_m_port = CFMachPortGetPort(i_port);
1953
1954 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode);
1955 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
1956 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode);
1957 CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode);
1958 CFRelease(d_rls);
1959 CFRelease(s_rls);
1960 CFRelease(e_rls);
1961 CFRelease(i_rls);
1962 if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
1963 err = udsserver_init(&mDNSStorage);
1964 if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; }
1965 return(err);
1966 }
1967
1968 mDNSlocal mDNSs32 mDNSDaemonIdle(void)
1969 {
1970 // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
1971 mDNSs32 nextevent = mDNS_Execute(&mDNSStorage);
1972
1973 mDNSs32 now = mDNSPlatformTimeNow();
1974
1975 // 2. Deliver any waiting browse messages to clients
1976 DNSServiceBrowser *b = DNSServiceBrowserList;
1977
1978 while (b)
1979 {
1980 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
1981 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
1982 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
1983 DNSServiceBrowser *x = b;
1984 b = b->next;
1985 if (x->results) // Try to deliver the list of results
1986 {
1987 while (x->results)
1988 {
1989 DNSServiceBrowserResult *const r = x->results;
1990 domainlabel name;
1991 domainname type, domain;
1992 DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance()
1993 char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
1994 char ctype[MAX_ESCAPED_DOMAIN_NAME];
1995 char cdom [MAX_ESCAPED_DOMAIN_NAME];
1996 ConvertDomainLabelToCString_unescaped(&name, cname);
1997 ConvertDomainNameToCString(&type, ctype);
1998 ConvertDomainNameToCString(&domain, cdom);
1999 DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
2000 kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
2001 // If we failed to send the mach message, try again in one second
2002 if (status == MACH_SEND_TIMED_OUT)
2003 {
2004 if (nextevent - now > mDNSPlatformOneSecond)
2005 nextevent = now + mDNSPlatformOneSecond;
2006 break;
2007 }
2008 else
2009 {
2010 x->lastsuccess = now;
2011 x->results = x->results->next;
2012 freeL("DNSServiceBrowserResult", r);
2013 }
2014 }
2015 // If this client hasn't read a single message in the last 60 seconds, abort it
2016 if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
2017 AbortBlockedClient(x->ClientMachPort, "browse", x);
2018 }
2019 }
2020
2021 DNSServiceResolver *l;
2022 for (l = DNSServiceResolverList; l; l=l->next)
2023 if (l->ReportTime && now - l->ReportTime >= 0)
2024 {
2025 l->ReportTime = 0;
2026 LogMsg("%5d: DNSServiceResolver(%##s) active for over two minutes. "
2027 "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c);
2028 }
2029
2030 return(nextevent);
2031 }
2032
2033 mDNSexport int main(int argc, char **argv)
2034 {
2035 int i;
2036 kern_return_t status;
2037
2038 for (i=1; i<argc; i++)
2039 {
2040 if (!strcmp(argv[i], "-d")) mDNS_DebugMode = mDNStrue;
2041 }
2042
2043 signal(SIGINT, HandleSIGTERM); // SIGINT is what you get for a Ctrl-C
2044 signal(SIGTERM, HandleSIGTERM);
2045 signal(SIGINFO, HandleSIGINFO);
2046
2047 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
2048 if (!mDNS_DebugMode) registerBootstrapService();
2049
2050 if (!mDNS_DebugMode && !restarting_via_mach_init)
2051 exit(0); /* mach_init will restart us immediately as a daemon */
2052
2053 if (!mDNS_DebugMode)
2054 {
2055 int fd = open(_PATH_DEVNULL, O_RDWR, 0);
2056 if (fd != -1)
2057 {
2058 // Avoid unnecessarily duplicating a file descriptor to itself
2059 if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
2060 if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
2061 if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
2062 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
2063 (void)close (fd);
2064 }
2065 }
2066
2067 // First do the all the initialization we need root privilege for, before we change to user "nobody"
2068 LogMsgIdent(mDNSResponderVersionString, "starting");
2069 status = mDNSDaemonInitialize();
2070
2071 // Now that we're finished with anything privileged, switch over to running as "nobody"
2072 const struct passwd *pw = getpwnam("nobody");
2073 if (pw != NULL)
2074 setuid(pw->pw_uid);
2075 else
2076 setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database
2077
2078 if (status == 0)
2079 {
2080 int numevents = 0;
2081 int RunLoopStatus = kCFRunLoopRunTimedOut;
2082
2083 // This is the main work loop:
2084 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2085 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2086 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2087 // (4) On wakeup we first process *all* events
2088 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2089 while (RunLoopStatus == kCFRunLoopRunTimedOut)
2090 {
2091 // 1. Before going into a blocking wait call and letting our process to go sleep,
2092 // call mDNSDaemonIdle to allow any deferred work to be completed.
2093 mDNSs32 nextevent = mDNSDaemonIdle();
2094 nextevent = udsserver_idle(nextevent);
2095
2096 // 2. Work out how long we expect to sleep before the next scheduled task
2097 mDNSs32 ticks = nextevent - mDNSPlatformTimeNow();
2098 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
2099 if (ticks > 1)
2100 RepeatedBusy = 0;
2101 else
2102 {
2103 ticks = 1;
2104 if (++RepeatedBusy >= mDNSPlatformOneSecond * 10)
2105 { LogMsg("Task Scheduling Error: Continuously busy for the last ten seconds"); RepeatedBusy = 0; }
2106 }
2107 CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond;
2108
2109 // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
2110 // (a) our next wakeup time, or (b) an event occurs.
2111 // The 'true' parameter makes it return after handling any event that occurs
2112 // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
2113 verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks);
2114 numevents = 0;
2115 RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true);
2116
2117 // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
2118 while (RunLoopStatus == kCFRunLoopRunHandledSource)
2119 {
2120 numevents++;
2121 RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
2122 }
2123 }
2124
2125 LogMsg("ERROR: CFRunLoopRun Exiting.");
2126 mDNS_Close(&mDNSStorage);
2127 }
2128
2129 if (!mDNS_DebugMode) destroyBootstrapService();
2130
2131 return(status);
2132 }
2133
2134 // uds_daemon.c support routines /////////////////////////////////////////////
2135
2136 // We keep a list of client-supplied event sources in PosixEventSource records
2137 struct CFSocketEventSource
2138 {
2139 udsEventCallback Callback;
2140 void *Context;
2141 int fd;
2142 struct CFSocketEventSource *Next;
2143 CFSocketRef SocketRef;
2144 CFRunLoopSourceRef RLS;
2145 };
2146 typedef struct CFSocketEventSource CFSocketEventSource;
2147
2148 static GenLinkedList gEventSources; // linked list of CFSocketEventSource's
2149
2150 static void cf_callback(CFSocketRef s _UNUSED, CFSocketCallBackType t _UNUSED, CFDataRef dr _UNUSED, const void *c _UNUSED, void *i)
2151 // Called by CFSocket when data appears on socket
2152 {
2153 CFSocketEventSource *source = (CFSocketEventSource*) i;
2154 source->Callback(source->Context);
2155 }
2156
2157 mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
2158 // Arrange things so that callback is called with context when data appears on fd
2159 {
2160 CFSocketEventSource *newSource;
2161 CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL };
2162
2163 if (gEventSources.LinkOffset == 0)
2164 InitLinkedList(&gEventSources, offsetof(CFSocketEventSource, Next));
2165
2166 if (fd >= FD_SETSIZE || fd < 0)
2167 return mStatus_UnsupportedErr;
2168 if (callback == NULL)
2169 return mStatus_BadParamErr;
2170
2171 newSource = (CFSocketEventSource*) calloc(1, sizeof *newSource);
2172 if (NULL == newSource)
2173 return mStatus_NoMemoryErr;
2174
2175 newSource->Callback = callback;
2176 newSource->Context = context;
2177 newSource->fd = fd;
2178
2179 cfContext.info = newSource;
2180 if ( NULL != (newSource->SocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack,
2181 cf_callback, &cfContext)) &&
2182 NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->SocketRef, 0)))
2183 {
2184 CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode);
2185 AddToTail(&gEventSources, newSource);
2186 }
2187 else
2188 {
2189 if (newSource->SocketRef)
2190 {
2191 CFSocketInvalidate(newSource->SocketRef); // automatically closes socket
2192 CFRelease(newSource->SocketRef);
2193 }
2194 return mStatus_NoMemoryErr;
2195 }
2196
2197 return mStatus_NoError;
2198 }
2199
2200 mStatus udsSupportRemoveFDFromEventLoop(int fd)
2201 // Reverse what was done in udsSupportAddFDToEventLoop().
2202 {
2203 CFSocketEventSource *iSource;
2204
2205 for (iSource=(CFSocketEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
2206 {
2207 if (fd == iSource->fd)
2208 {
2209 RemoveFromList(&gEventSources, iSource);
2210 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode);
2211 CFRunLoopSourceInvalidate(iSource->RLS);
2212 CFRelease(iSource->RLS);
2213 CFSocketInvalidate(iSource->SocketRef);
2214 CFRelease(iSource->SocketRef);
2215 free(iSource);
2216 return mStatus_NoError;
2217 }
2218 }
2219 return mStatus_NoSuchNameErr;
2220 }
2221
2222 // For convenience when using the "strings" command, this is the last thing in the file
2223 mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";