]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-176.3.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / daemon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Formatting notes:
18 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
19 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
20 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
21 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
22 * therefore common sense dictates that if they are part of a compound statement then they
23 * should be indented to the same level as everything else in that compound statement.
24 * Indenting curly braces at the same level as the "if" implies that curly braces are
25 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
26 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
27 * understand why variable y is not of type "char*" just proves the point that poor code
28 * layout leads people to unfortunate misunderstandings about how the C language really works.)
29
30 Change History (most recent first):
31
32 $Log: daemon.c,v $
33 Revision 1.360 2008/03/13 20:55:16 mcguire
34 <rdar://problem/5769316> fix deprecated warnings/errors
35 Additional cleanup: use a conditional macro instead of lots of #if
36
37 Revision 1.359 2008/03/12 23:02:58 mcguire
38 <rdar://problem/5769316> fix deprecated warnings/errors
39
40 Revision 1.358 2008/03/06 21:26:11 cheshire
41 Moved duplicated STRINGIFY macro from individual C files to DNSCommon.h
42
43 Revision 1.357 2008/02/13 17:40:43 cheshire
44 <rdar://problem/5740501> Investigate mysterious SIGABRTs in mDNSResponder
45
46 Revision 1.356 2007/12/18 00:28:56 cheshire
47 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
48 Error in ReadyForSleep() logic -- missing "not" in "!mDNSOpaque16IsZero(q->TargetQID)"
49
50 Revision 1.355 2007/12/17 22:29:22 cheshire
51 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
52 Log message indicating when we make IOAllowPowerChange call; make sure nextTimerEvent is set appropriately
53
54 Revision 1.354 2007/12/15 01:12:28 cheshire
55 <rdar://problem/5526796> Need to remove active LLQs from server upon question cancellation, on sleep, and on shutdown
56
57 Revision 1.353 2007/12/14 19:14:02 cheshire
58 Added (commented out) code for testing sleep/wake
59
60 Revision 1.352 2007/12/14 00:58:29 cheshire
61 <rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
62 Additional fixes: When going to sleep, mDNSResponder needs to postpone sleep
63 until TLS/TCP deregistrations have completed (up to five seconds maximum)
64
65 Revision 1.351 2007/12/12 21:34:18 cheshire
66 Now that <rdar://problem/5124399> "Not getting Keychain events" is apparently fixed,
67 it makes sense to reduce our workaround retry count from 5 to 2 retries. Once we've
68 confirmed that the bug is definitely fixed we'll remove the workaround altogether.
69
70 Revision 1.350 2007/12/07 00:45:58 cheshire
71 <rdar://problem/5526800> BTMM: Need to clean up registrations on shutdown
72
73 Revision 1.349 2007/12/04 22:00:54 cheshire
74 Fixed mistake in comment
75
76 Revision 1.348 2007/12/01 00:27:43 cheshire
77 Fixed compile warning: declaration of 'r' shadows a previous local
78
79 Revision 1.347 2007/11/02 22:00:13 cheshire
80 <rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
81 Need to hold the lock while calling SetDomainSecrets
82
83 Revision 1.346 2007/11/02 20:18:13 cheshire
84 <rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
85
86 Revision 1.345 2007/10/17 18:41:21 cheshire
87 For debugging, make SIGUSR1 simulate a KeychainChanged event as well as a NetworkChanged
88
89 Revision 1.344 2007/09/29 01:06:17 mcguire
90 <rdar://problem/5507862> 9A564: mDNSResponder crash in mDNS_Execute
91
92 Revision 1.343 2007/09/24 05:02:41 cheshire
93 Debugging: In SIGINFO output, indicate explicitly when a given section is empty
94
95 Revision 1.342 2007/09/18 19:09:02 cheshire
96 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
97
98 Revision 1.341 2007/09/12 01:22:13 cheshire
99 Improve validatelists() checking to detect when 'next' pointer gets smashed to ~0
100
101 Revision 1.340 2007/09/07 22:44:03 mcguire
102 <rdar://problem/5448420> Move CFUserNotification code to mDNSResponderHelper
103
104 Revision 1.339 2007/09/06 19:08:29 cheshire
105 LogAllOperations check needs to be "#if LogAllOperations || MDNS_DEBUGMSGS"
106
107 Revision 1.338 2007/09/05 23:34:27 mcguire
108 Revert logging change
109
110 Revision 1.337 2007/09/05 20:45:50 cheshire
111 Added list of KQSocketEventSources in SIGINFO output
112
113 Revision 1.336 2007/08/31 17:15:37 cheshire
114 Reordered startup log messages so that "mDNSResponder ... starting" is the first message
115
116 Revision 1.335 2007/08/31 02:00:16 cheshire
117 Added comment explaining use of zero-width non-breaking space character to tag literal strings
118
119 Revision 1.334 2007/08/24 23:40:24 cheshire
120 Added comment about FreeServiceInstance
121
122 Revision 1.333 2007/08/23 21:02:35 cheshire
123 SecKeychainSetPreferenceDomain() call should be in platform-support layer, not daemon.c
124
125 Revision 1.332 2007/08/22 23:54:54 mcguire
126 <rdar://problem/5422558> BTMM: mDNSResponder should be able to run from the cmdline
127
128 Revision 1.331 2007/08/18 01:02:03 mcguire
129 <rdar://problem/5415593> No Bonjour services are getting registered at boot
130
131 Revision 1.330 2007/08/10 22:25:57 mkrochma
132 <rdar://problem/5396302> mDNSResponder continually complains about slow UDP packet reception -- about 400 msecs
133
134 Revision 1.329 2007/08/08 22:34:58 mcguire
135 <rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root
136
137 Revision 1.328 2007/07/27 22:43:37 cheshire
138 Improved mallocL/freeL "suspiciously large" debugging messages
139
140 Revision 1.327 2007/07/27 19:30:41 cheshire
141 Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
142 to properly reflect tri-state nature of the possible responses
143
144 Revision 1.326 2007/07/24 17:23:33 cheshire
145 <rdar://problem/5357133> Add list validation checks for debugging
146
147 Revision 1.325 2007/07/11 23:43:43 cheshire
148 Rename PurgeCacheResourceRecord to mDNS_PurgeCacheResourceRecord
149
150 Revision 1.324 2007/07/11 22:44:40 cheshire
151 <rdar://problem/5328801> SIGHUP should purge the cache
152
153 Revision 1.323 2007/07/11 03:01:50 cheshire
154 <rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services
155
156 Revision 1.322 2007/07/06 18:58:16 cheshire
157 Check m->NextScheduledNATOp in ShowTaskSchedulingError()
158
159 Revision 1.321 2007/07/02 21:54:20 cheshire
160 Fix compile error in MACOSX_MDNS_MALLOC_DEBUGGING checks
161
162 Revision 1.320 2007/06/28 21:16:27 cheshire
163 Rename "m->nextevent" as more informative "m->NextuDNSEvent"
164
165 Revision 1.319 2007/06/22 20:47:08 cheshire
166 <rdar://problem/5285417> DOS charset changes from CP932 to CP850 after Computer Name conflict
167 Made a "SafeSCPreferencesSetComputerName" routine to set the Computer Name without changing the machine's default character set
168
169 Revision 1.318 2007/06/20 01:45:40 cheshire
170 When showing dormant interfaces, display last-seen IP address for that dormant interface
171
172 Revision 1.317 2007/06/20 01:10:12 cheshire
173 <rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code
174
175 Revision 1.316 2007/06/19 19:27:11 cheshire
176 <rdar://problem/5141540> Sandbox mDNSResponder
177 Weak-link sandbox_init, so mDNSResponder can be run on Tiger for regression testing
178
179 Revision 1.315 2007/06/15 21:54:51 cheshire
180 <rdar://problem/4883206> Add packet logging to help debugging private browsing over TLS
181
182 Revision 1.314 2007/06/15 19:23:17 cheshire
183 <rdar://problem/5254053> mDNSResponder renames my host without asking
184 Improve log messages, to distinguish user-initiated renames from automatic (name conflict) renames
185
186 Revision 1.313 2007/05/25 16:02:05 cheshire
187 When MACOSX_MDNS_MALLOC_DEBUGGING is enabled, log suspiciously large memory allocations
188
189 Revision 1.312 2007/05/22 19:07:21 cheshire
190 Add comment explaining RR_CACHE_SIZE calculation
191
192 Revision 1.311 2007/05/15 21:47:21 cheshire
193 Get rid of "#pragma unused(m)"
194
195 Revision 1.310 2007/05/08 00:56:17 cheshire
196 <rdar://problem/4118503> Share single socket instead of creating separate socket for each active interface
197
198 Revision 1.309 2007/04/30 21:33:38 cheshire
199 Fix crash when a callback unregisters a service while the UpdateSRVRecords() loop
200 is iterating through the m->ServiceRegistrations list
201
202 Revision 1.308 2007/04/28 01:31:59 cheshire
203 Improve debugging support for catching memory corruption problems
204
205 Revision 1.307 2007/04/24 18:32:00 cheshire
206 Grab a copy of KQtask string pointer in case KQcallback deletes the task
207
208 Revision 1.306 2007/04/22 19:11:51 cheshire
209 Some quirk of RemoveFromList (GenLinkedList.c) was corrupting the list and causing a crash
210 if the element being removed was not the last in the list. Fixed by removing GenLinkedList.c
211 from the project and just using simple vanilla C linked-list manipulation instead.
212
213 Revision 1.305 2007/04/22 06:02:03 cheshire
214 <rdar://problem/4615977> Query should immediately return failure when no server
215
216 Revision 1.304 2007/04/21 21:47:47 cheshire
217 <rdar://problem/4376383> Daemon: Add watchdog timer
218
219 Revision 1.303 2007/04/18 00:50:47 cheshire
220 <rdar://problem/5141540> Sandbox mDNSResponder
221
222 Revision 1.302 2007/04/07 01:01:48 cheshire
223 <rdar://problem/5095167> mDNSResponder periodically blocks in SSLRead
224
225 Revision 1.301 2007/04/05 19:13:48 cheshire
226 Use better name in SCPreferencesCreate
227
228 Revision 1.300 2007/04/04 21:22:18 cheshire
229 Suppress "Local Hostname changed" syslog message when name has not actually changed
230
231 Revision 1.299 2007/04/03 19:19:33 cheshire
232 Use mDNSIPPortIsZero() instead of peeking into 'NotAnInteger' field
233
234 Revision 1.298 2007/03/30 21:51:45 cheshire
235 Minor code tidying
236
237 Revision 1.297 2007/03/27 22:47:19 cheshire
238 On memory corruption, if ForceAlerts is set, force a crash to get a stack trace
239
240 Revision 1.296 2007/03/24 01:23:29 cheshire
241 Call validator for uDNS data structures
242
243 Revision 1.295 2007/03/20 23:32:49 cheshire
244 Minor textual tidying
245
246 Revision 1.294 2007/03/07 02:50:50 cheshire
247 <rdar://problem/4574528> Name conflict dialog doesn't appear if Bonjour is persistantly unable to find an available hostname
248
249 Revision 1.293 2007/03/06 22:59:01 cheshire
250 <rdar://problem/4157921> Security: Null dereference possible in daemon.c
251
252 Revision 1.292 2007/02/28 21:55:10 cheshire
253 <rdar://problem/3862944> UI: Name conflict notifications should be localized
254 Additional fix: We were not getting our NotificationCallBackDismissed messages
255 because we were scheduling our CFUserNotification RunLoopSource on the wrong runloop.
256 (We were incorrectly assuming CFRunLoopGetCurrent() would be the right runloop.)
257
258 Revision 1.291 2007/02/28 03:51:24 cheshire
259 <rdar://problem/3862944> UI: Name conflict notifications should be localized
260 Moved curly quotes out of the literal text and into the localized text, so they
261 can be replaced with alternate characters as appropriate for other languages.
262
263 Revision 1.290 2007/02/14 01:58:19 cheshire
264 <rdar://problem/4995831> Don't delete Unix Domain Socket on exit if we didn't create it on startup
265
266 Revision 1.289 2007/02/07 19:32:00 cheshire
267 <rdar://problem/4980353> All mDNSResponder components should contain version strings in SCCS-compatible format
268
269 Revision 1.288 2007/02/07 01:01:24 cheshire
270 <rdar://problem/3956518> Need to go native with launchd
271 Additional refinements -- was unnecessarily calling launch_data_free()
272
273 Revision 1.287 2007/02/06 19:06:48 cheshire
274 <rdar://problem/3956518> Need to go native with launchd
275
276 Revision 1.286 2007/01/06 01:00:33 cheshire
277 Improved SIGINFO output
278
279 Revision 1.285 2007/01/05 08:30:47 cheshire
280 Trim excessive "$Log" checkin history from before 2006
281 (checkin history still available via "cvs log ..." of course)
282
283 Revision 1.284 2007/01/05 05:44:35 cheshire
284 Move automatic browse/registration management from uDNS.c to mDNSShared/uds_daemon.c,
285 so that mDNSPosix embedded clients will compile again
286
287 Revision 1.283 2007/01/04 23:11:14 cheshire
288 <rdar://problem/4720673> uDNS: Need to start caching unicast records
289 When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries
290
291 Revision 1.282 2006/12/21 00:09:45 cheshire
292 Use mDNSPlatformMemZero instead of bzero
293
294 Revision 1.281 2006/11/18 05:01:32 cheshire
295 Preliminary support for unifying the uDNS and mDNS code,
296 including caching of uDNS answers
297
298 Revision 1.280 2006/11/10 00:54:16 cheshire
299 <rdar://problem/4816598> Changing case of Computer Name doesn't work
300
301 Revision 1.279 2006/11/02 17:44:01 cheshire
302 No longer have a separate uDNS ActiveQueries list
303
304 Revision 1.278 2006/10/05 04:04:24 herscher
305 Remove embedded uDNS_info struct from DNSQuestion_struct
306
307 Revision 1.277 2006/09/21 21:01:24 cheshire
308 Change 'autorename' to more accurate name 'renameonmemfree'
309
310 Revision 1.276 2006/09/17 19:12:02 cheshire
311 Further changes for removal of uDNS_info substructure from mDNS_struct
312
313 Revision 1.275 2006/09/15 21:20:16 cheshire
314 Remove uDNS_info substructure from mDNS_struct
315
316 Revision 1.274 2006/08/14 23:24:39 cheshire
317 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
318
319 Revision 1.273 2006/07/30 05:43:19 cheshire
320 <rdar://problem/4049048> Convert mDNSResponder to use kqueue
321 Problems using KQueueFD with select() -- for now we'll stick to pure kevent()
322
323 Revision 1.272 2006/07/27 03:24:35 cheshire
324 <rdar://problem/4049048> Convert mDNSResponder to use kqueue
325 Further refinement: Declare KQueueEntry parameter "const"
326
327 Revision 1.271 2006/07/27 02:59:26 cheshire
328 <rdar://problem/4049048> Convert mDNSResponder to use kqueue
329 Further refinements: CFRunLoop thread needs to explicitly wake the kqueue thread
330 after releasing BigMutex, in case actions it took have resulted in new work for the
331 kqueue thread (e.g. NetworkChanged events may result in the kqueue thread having to
332 add new active interfaces to its list, and consequently schedule queries to be sent).
333
334 Revision 1.270 2006/07/25 17:16:36 mkrochma
335 Quick fix to solve kqueue related crashes and hangs
336
337 Revision 1.269 2006/07/22 06:11:37 cheshire
338 <rdar://problem/4049048> Convert mDNSResponder to use kqueue
339
340 Revision 1.268 2006/07/15 02:01:32 cheshire
341 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
342 Fix broken "empty string" browsing
343
344 Revision 1.267 2006/07/07 01:09:10 cheshire
345 <rdar://problem/4472013> Add Private DNS server functionality to dnsextd
346 Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd
347
348 Revision 1.266 2006/07/05 23:34:53 cheshire
349 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
350
351 Revision 1.265 2006/06/29 07:32:08 cheshire
352 Added missing LogOperation logging for DNSServiceBrowse results
353
354 Revision 1.264 2006/06/29 05:33:30 cheshire
355 <rdar://problem/4607043> mDNSResponder conditional compilation options
356
357 Revision 1.263 2006/06/08 23:23:48 cheshire
358 Fix errant indentation of curly brace at the end of provide_DNSServiceBrowserCreate_rpc()
359
360 Revision 1.262 2006/03/18 21:49:11 cheshire
361 Added comment in ShowTaskSchedulingError(mDNS *const m)
362
363 Revision 1.261 2006/01/06 01:22:28 cheshire
364 <rdar://problem/4108164> Reword "mach_absolute_time went backwards" dialog
365
366 */
367
368 #include <mach/mach.h>
369 #include <mach/mach_error.h>
370 #include <servers/bootstrap.h>
371 #include <sys/types.h>
372 #include <unistd.h>
373 #include <paths.h>
374 #include <fcntl.h>
375 #include <launch.h>
376 #include <pwd.h>
377 #include <sys/event.h>
378 #include <pthread.h>
379 #include <sandbox.h>
380 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
381
382 #if TARGET_OS_EMBEDDED
383 #include <bootstrap_priv.h>
384
385 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
386 #endif
387
388 #include "DNSServiceDiscoveryRequestServer.h"
389 #include "DNSServiceDiscoveryReply.h"
390
391 #include "uDNS.h"
392 #include "DNSCommon.h"
393 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
394
395 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
396
397 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
398 #include "helper.h"
399
400 //*************************************************************************************************************
401 // Globals
402
403 static mDNS_PlatformSupport PlatformStorage;
404
405 // Start off with a default cache of 16K (99 records)
406 // Each time we grow the cache we add another 99 records
407 // 99 * 164 = 16236 bytes.
408 // This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead
409 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
410 static CacheEntity rrcachestorage[RR_CACHE_SIZE];
411
412 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
413 static mach_port_t m_port = MACH_PORT_NULL;
414 static mach_port_t client_death_port = MACH_PORT_NULL;
415 static mach_port_t signal_port = MACH_PORT_NULL;
416 static mach_port_t server_priv_port = MACH_PORT_NULL;
417
418 static dnssd_sock_t launchd_fd = dnssd_InvalidSocket;
419
420 // mDNS Mach Message Timeout, in milliseconds.
421 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
422 // fails to service its mach message queue, but long enough to give a well-written
423 // client a chance to service its mach message queue without getting cut off.
424 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
425 // even extra-slow clients a fair chance before we cut them off.
426 #define MDNS_MM_TIMEOUT 250
427
428 static int restarting_via_mach_init = 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism
429 static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd
430
431 static int OSXVers;
432
433 static CFRunLoopRef CFRunLoop;
434
435 //*************************************************************************************************************
436 // Active client list structures
437
438 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
439 struct DNSServiceDomainEnumeration_struct
440 {
441 DNSServiceDomainEnumeration *next;
442 mach_port_t ClientMachPort;
443 DNSQuestion dom; // Question asking for domains
444 DNSQuestion def; // Question asking for default domain
445 };
446
447 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
448 struct DNSServiceBrowserResult_struct
449 {
450 DNSServiceBrowserResult *next;
451 int resultType;
452 domainname result;
453 };
454
455 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
456
457 typedef struct DNSServiceBrowserQuestion
458 {
459 struct DNSServiceBrowserQuestion *next;
460 DNSQuestion q;
461 domainname domain;
462 } DNSServiceBrowserQuestion;
463
464 struct DNSServiceBrowser_struct
465 {
466 DNSServiceBrowser *next;
467 mach_port_t ClientMachPort;
468 DNSServiceBrowserQuestion *qlist;
469 DNSServiceBrowserResult *results;
470 mDNSs32 lastsuccess;
471 mDNSBool DefaultDomain; // was the browse started on an explicit domain?
472 domainname type; // registration type
473 };
474
475 typedef struct DNSServiceResolver_struct DNSServiceResolver;
476 struct DNSServiceResolver_struct
477 {
478 DNSServiceResolver *next;
479 mach_port_t ClientMachPort;
480 ServiceInfoQuery q;
481 ServiceInfo i;
482 mDNSs32 ReportTime;
483 };
484
485 // A single registered service: ServiceRecordSet + bookkeeping
486 // Note that we duplicate some fields from parent DNSServiceRegistration object
487 // to facilitate cleanup, when instances and parent may be deallocated at different times.
488 typedef struct ServiceInstance
489 {
490 struct ServiceInstance *next;
491 mach_port_t ClientMachPort;
492 mDNSBool autoname; // Set if this name is tied to the Computer Name
493 mDNSBool renameonmemfree; // Set if we just got a name conflict and now need to automatically pick a new name
494 domainlabel name;
495 domainname domain;
496 ServiceRecordSet srs;
497 // Don't add any fields after ServiceRecordSet.
498 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
499 } ServiceInstance;
500
501 // A client-created service. May reference several ServiceInstance objects if default
502 // settings cause registration in multiple domains.
503 typedef struct DNSServiceRegistration
504 {
505 struct DNSServiceRegistration *next;
506 mach_port_t ClientMachPort;
507 mDNSBool DefaultDomain;
508 mDNSBool autoname;
509 size_t rdsize;
510 int NumSubTypes;
511 char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes
512 domainlabel name; // used only if autoname is false
513 domainname type;
514 mDNSIPPort port;
515 unsigned char txtinfo[1024];
516 size_t txt_len;
517 uint32_t NextRef;
518 ServiceInstance *regs;
519 } DNSServiceRegistration;
520
521 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
522 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
523 static DNSServiceResolver *DNSServiceResolverList = NULL;
524 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
525
526 // We keep a list of client-supplied event sources in KQSocketEventSource records
527 typedef struct KQSocketEventSource
528 {
529 struct KQSocketEventSource *next;
530 int fd;
531 KQueueEntry kqs;
532 } KQSocketEventSource;
533
534 static KQSocketEventSource *gEventSources;
535
536 //*************************************************************************************************************
537 // General Utility Functions
538
539 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
540
541 char _malloc_options[] = "AXZ";
542
543 mDNSexport void LogMemCorruption(const char *format, ...)
544 {
545 char buffer[512];
546 va_list ptr;
547 va_start(ptr,format);
548 buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
549 va_end(ptr);
550 LogMsg("!!!! %s !!!!", buffer);
551 NotifyOfElusiveBug("Memory Corruption", buffer);
552 #if ForceAlerts
553 *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
554 #endif
555 }
556
557 mDNSlocal void validatelists(mDNS *const m)
558 {
559 // Check local lists
560 KQSocketEventSource *k;
561 for (k = gEventSources; k; k=k->next)
562 if (k->next == (KQSocketEventSource *)~0 || k->fd < 0)
563 LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd);
564
565 // Check Mach client lists
566 DNSServiceDomainEnumeration *e;
567 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
568 if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
569 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort);
570
571 DNSServiceBrowser *b;
572 for (b = DNSServiceBrowserList; b; b=b->next)
573 if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
574 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort);
575
576 DNSServiceResolver *l;
577 for (l = DNSServiceResolverList; l; l=l->next)
578 if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
579 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort);
580
581 DNSServiceRegistration *r;
582 for (r = DNSServiceRegistrationList; r; r=r->next)
583 if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
584 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort);
585
586 // Check Unix Domain Socket client lists (uds_daemon.c)
587 uds_validatelists();
588
589 // Check core mDNS lists
590 AuthRecord *rr;
591 for (rr = m->ResourceRecords; rr; rr=rr->next)
592 {
593 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
594 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
595 if (rr->resrec.name != &rr->namestorage)
596 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
597 rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c);
598 }
599
600 for (rr = m->DuplicateRecords; rr; rr=rr->next)
601 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
602 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
603
604 rr = m->NewLocalRecords;
605 if (rr)
606 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
607 LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType);
608
609 rr = m->CurrentRecord;
610 if (rr)
611 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
612 LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType);
613
614 DNSQuestion *q;
615 for (q = m->Questions; q; q=q->next)
616 if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32)~0)
617 LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next);
618
619 CacheGroup *cg;
620 CacheRecord *cr;
621 mDNSu32 slot;
622 FORALL_CACHERECORDS(slot, cg, cr)
623 {
624 if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
625 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType);
626 if (cr->CRActiveQuestion)
627 {
628 for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break;
629 if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr));
630 }
631 }
632
633 // Check core uDNS lists
634 udns_validatelists(m);
635
636 // Check platform-layer lists
637 NetworkInterfaceInfoOSX *i;
638 for (i = m->p->InterfaceList; i; i = i->next)
639 if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->ifa_name || i->ifa_name == (char *)~0)
640 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifa_name);
641
642 ClientTunnel *t;
643 for (t = m->TunnelClients; t; t=t->next)
644 if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63)
645 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]);
646 }
647
648 void *mallocL(char *msg, unsigned int size)
649 {
650 unsigned long *mem = malloc(size+8);
651 if (!mem)
652 {
653 LogMsg("malloc( %s : %d ) failed", msg, size);
654 return(NULL);
655 }
656 else
657 {
658 if (size > 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]);
659 else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
660 mem[0] = 0xDEAD1234;
661 mem[1] = size;
662 //mDNSPlatformMemZero(&mem[2], size);
663 memset(&mem[2], 0xFF, size);
664 validatelists(&mDNSStorage);
665 return(&mem[2]);
666 }
667 }
668
669 void freeL(char *msg, void *x)
670 {
671 if (!x)
672 LogMsg("free( %s @ NULL )!", msg);
673 else
674 {
675 unsigned long *mem = ((unsigned long *)x) - 2;
676 if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
677 if (mem[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]);
678 else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
679 //mDNSPlatformMemZero(mem, mem[1]+8);
680 memset(mem, 0xFF, mem[1]+8);
681 validatelists(&mDNSStorage);
682 free(mem);
683 }
684 }
685
686 #endif
687
688 //*************************************************************************************************************
689 // Client Death Detection
690
691 // This gets called after ALL constituent records of the Service Record Set have been deregistered
692 mDNSlocal void FreeServiceInstance(ServiceInstance *x)
693 {
694 ServiceRecordSet *s = &x->srs;
695 ExtraResourceRecord *e = x->srs.Extras, *tmp;
696
697 while (e)
698 {
699 e->r.RecordContext = e;
700 tmp = e;
701 e = e->next;
702 FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
703 }
704
705 if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
706 freeL("TXT RData", s->RR_TXT.resrec.rdata);
707
708 if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
709 freeL("ServiceInstance", x);
710 }
711
712 // AbortClient finds whatever client is identified by the given Mach port,
713 // stops whatever operation that client was doing, and frees its memory.
714 // In the case of a service registration, the actual freeing may be deferred
715 // until we get the mStatus_MemFree message, if necessary
716 mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
717 {
718 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
719 DNSServiceBrowser **b = &DNSServiceBrowserList;
720 DNSServiceResolver **l = &DNSServiceResolverList;
721 DNSServiceRegistration **r = &DNSServiceRegistrationList;
722
723 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
724 if (*e)
725 {
726 DNSServiceDomainEnumeration *x = *e;
727 *e = (*e)->next;
728 if (m && m != x)
729 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
730 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
731 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
732 mDNS_StopGetDomains(&mDNSStorage, &x->def);
733 freeL("DNSServiceDomainEnumeration", x);
734 return;
735 }
736
737 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
738 if (*b)
739 {
740 DNSServiceBrowser *x = *b;
741 DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
742 *b = (*b)->next;
743 while (qptr)
744 {
745 if (m && m != x)
746 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
747 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c);
748 mDNS_StopBrowse(&mDNSStorage, &qptr->q);
749 freePtr = qptr;
750 qptr = qptr->next;
751 freeL("DNSServiceBrowserQuestion", freePtr);
752 }
753 while (x->results)
754 {
755 DNSServiceBrowserResult *t = x->results;
756 x->results = x->results->next;
757 freeL("DNSServiceBrowserResult", t);
758 }
759 freeL("DNSServiceBrowser", x);
760 return;
761 }
762
763 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
764 if (*l)
765 {
766 DNSServiceResolver *x = *l;
767 *l = (*l)->next;
768 if (m && m != x)
769 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
770 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c);
771 mDNS_StopResolveService(&mDNSStorage, &x->q);
772 freeL("DNSServiceResolver", x);
773 return;
774 }
775
776 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
777 if (*r)
778 {
779 ServiceInstance *si = NULL;
780 DNSServiceRegistration *x = *r;
781 *r = (*r)->next;
782
783 si = x->regs;
784 while (si)
785 {
786 ServiceInstance *instance = si;
787 si = si->next;
788 instance->renameonmemfree = mDNSfalse;
789 if (m && m != x) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs), m, x);
790 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs));
791
792 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
793 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
794 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
795 // the list, so we should go ahead and free the memory right now
796 if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer
797 }
798 x->regs = NULL;
799 freeL("DNSServiceRegistration", x);
800 return;
801 }
802
803 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
804 }
805
806 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
807
808 mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
809 {
810 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
811 DNSServiceBrowser *b = DNSServiceBrowserList;
812 DNSServiceResolver *l = DNSServiceResolverList;
813 DNSServiceRegistration *r = DNSServiceRegistrationList;
814 DNSServiceBrowserQuestion *qptr;
815
816 while (e && e->ClientMachPort != c) e = e->next;
817 while (b && b->ClientMachPort != c) b = b->next;
818 while (l && l->ClientMachPort != c) l = l->next;
819 while (r && r->ClientMachPort != c) r = r->next;
820
821 if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
822 else if (b)
823 {
824 for (qptr = b->qlist; qptr; qptr = qptr->next)
825 LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg);
826 }
827 else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
828 else if (r)
829 {
830 ServiceInstance *si;
831 for (si = r->regs; si; si = si->next)
832 LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name->c, reason, msg);
833 }
834 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
835
836 AbortClient(c, m);
837 }
838
839 mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
840 {
841 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
842 DNSServiceBrowser *b = DNSServiceBrowserList;
843 DNSServiceResolver *l = DNSServiceResolverList;
844 DNSServiceRegistration *r = DNSServiceRegistrationList;
845 DNSServiceBrowserQuestion *qptr;
846
847 while (e && e->ClientMachPort != c) e = e->next;
848 while (b && b->ClientMachPort != c) b = b->next;
849 while (l && l->ClientMachPort != c) l = l->next;
850 while (r && r->ClientMachPort != c) r = r->next;
851 if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
852 if (b)
853 {
854 for (qptr = b->qlist; qptr; qptr = qptr->next)
855 LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c);
856 }
857 if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
858 if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL);
859 return(e || b || l || r);
860 }
861
862 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
863 {
864 KQueueLock(&mDNSStorage);
865 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
866 (void)unusedport; // Unused
867 (void)size; // Unused
868 (void)info; // Unused
869 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
870 {
871 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
872 AbortClient(deathMessage->not_port, NULL);
873
874 /* Deallocate the send right that came in the dead name notification */
875 mach_port_destroy(mach_task_self(), deathMessage->not_port);
876 }
877 KQueueUnlock(&mDNSStorage, "Mach AbortClient");
878 }
879
880 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
881 {
882 mach_port_t prev;
883 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
884 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
885 // If the port already died while we were thinking about it, then abort the operation right away
886 if (r != KERN_SUCCESS)
887 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
888 }
889
890 //*************************************************************************************************************
891 // Domain Enumeration
892
893 mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
894 {
895 kern_return_t status;
896 char buffer[MAX_ESCAPED_DOMAIN_NAME];
897 DNSServiceDomainEnumerationReplyResultType rt;
898 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
899 (void)m; // Unused
900
901 debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
902 if (answer->rrtype != kDNSType_PTR) return;
903 if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
904
905 if (AddRecord)
906 {
907 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
908 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
909 }
910 else
911 {
912 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
913 else return;
914 }
915
916 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
917 x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
918 !AddRecord ? "RemoveDomain" :
919 question == &x->dom ? "AddDomain" : "AddDomainDefault");
920
921 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
922 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
923 if (status == MACH_SEND_TIMED_OUT)
924 AbortBlockedClient(x->ClientMachPort, "enumeration", x);
925 }
926
927 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
928 int regDom)
929 {
930 // Check client parameter
931 (void)unusedserver; // Unused
932 mStatus err = mStatus_NoError;
933 const char *errormsg = "Unknown";
934 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
935 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
936
937 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
938 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
939
940 // Allocate memory, and handle failure
941 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
942 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
943
944 // Set up object, and link into list
945 x->ClientMachPort = client;
946 x->next = DNSServiceDomainEnumerationList;
947 DNSServiceDomainEnumerationList = x;
948
949 verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
950
951 // Do the operation
952 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
953 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
954 if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
955
956 // Succeeded: Wrap up and return
957 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
958 EnableDeathNotificationForClient(client, x);
959 return(mStatus_NoError);
960
961 fail:
962 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err);
963 return(err);
964 }
965
966 //*************************************************************************************************************
967 // Browse for services
968
969 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
970 {
971 (void)m; // Unused
972
973 if (answer->rrtype != kDNSType_PTR)
974 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
975
976 domainlabel name;
977 domainname type, domain;
978 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
979 {
980 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
981 answer->name->c, answer->rdata->u.name.c);
982 return;
983 }
984
985 DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
986 if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
987
988 verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
989 AssignDomainName(&x->result, &answer->rdata->u.name);
990 if (AddRecord)
991 x->resultType = DNSServiceBrowserReplyAddInstance;
992 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
993 x->next = NULL;
994
995 DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
996 DNSServiceBrowserResult **p = &browser->results;
997 while (*p) p = &(*p)->next;
998 *p = x;
999
1000 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
1001 browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
1002 }
1003
1004 mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d)
1005 {
1006 mStatus err = mStatus_NoError;
1007 DNSServiceBrowserQuestion *ptr, *question = NULL;
1008
1009 for (ptr = browser->qlist; ptr; ptr = ptr->next)
1010 {
1011 if (SameDomainName(&ptr->q.qname, d))
1012 { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; }
1013 }
1014
1015 question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
1016 if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; }
1017 AssignDomainName(&question->domain, d);
1018 question->next = browser->qlist;
1019 browser->qlist = question;
1020 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
1021 err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser);
1022 if (err) LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err);
1023 return err;
1024 }
1025
1026 mDNSexport void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add)
1027 {
1028 DNSServiceBrowser *ptr;
1029 for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next)
1030 {
1031 if (ptr->DefaultDomain)
1032 {
1033 if (add)
1034 {
1035 mStatus err = AddDomainToBrowser(ptr, d);
1036 if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort);
1037 }
1038 else
1039 {
1040 DNSServiceBrowserQuestion **q = &ptr->qlist;
1041 while (*q)
1042 {
1043 if (SameDomainName(&(*q)->domain, d))
1044 {
1045 DNSServiceBrowserQuestion *rem = *q;
1046 *q = (*q)->next;
1047 mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
1048 freeL("DNSServiceBrowserQuestion", rem);
1049 return;
1050 }
1051 q = &(*q)->next;
1052 }
1053 LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort);
1054 }
1055 }
1056 }
1057 }
1058
1059 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1060 DNSCString regtype, DNSCString domain)
1061 {
1062 // Check client parameter
1063 (void)unusedserver; // Unused
1064 mStatus err = mStatus_NoError;
1065 const char *errormsg = "Unknown";
1066
1067 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1068 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1069
1070 // Check other parameters
1071 domainname t, d;
1072 t.c[0] = 0;
1073 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1074 if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; }
1075 if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
1076 { errormsg = "Bad Service SubType"; goto badparam; }
1077 if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
1078 domainname temp;
1079 if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
1080 if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local"
1081
1082 // Allocate memory, and handle failure
1083 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
1084 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1085
1086 // Set up object, and link into list
1087 AssignDomainName(&x->type, &t);
1088 x->ClientMachPort = client;
1089 x->results = NULL;
1090 x->lastsuccess = 0;
1091 x->qlist = NULL;
1092 x->next = DNSServiceBrowserList;
1093 DNSServiceBrowserList = x;
1094
1095 if (domain[0])
1096 {
1097 // Start browser for an explicit domain
1098 x->DefaultDomain = mDNSfalse;
1099 if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; }
1100 err = AddDomainToBrowser(x, &d);
1101 if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
1102 }
1103 else
1104 {
1105 DNameListElem *sdPtr;
1106 // Start browser on all domains
1107 x->DefaultDomain = mDNStrue;
1108 if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
1109 for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next)
1110 {
1111 err = AddDomainToBrowser(x, &sdPtr->name);
1112 if (err)
1113 {
1114 // only terminally bail if .local fails
1115 if (!SameDomainName(&localdomain, &sdPtr->name))
1116 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c);
1117 else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
1118 }
1119 }
1120 }
1121
1122 // Succeeded: Wrap up and return
1123 EnableDeathNotificationForClient(client, x);
1124 return(mStatus_NoError);
1125
1126 badparam:
1127 err = mStatus_BadParamErr;
1128 fail:
1129 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err);
1130 return(err);
1131 }
1132
1133 //*************************************************************************************************************
1134 // Resolve Service Info
1135
1136 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
1137 {
1138 kern_return_t status;
1139 DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
1140 NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
1141 if (query->info->InterfaceID == mDNSInterface_LocalOnly) ifx = mDNSNULL;
1142 struct sockaddr_storage interface;
1143 struct sockaddr_storage address;
1144 char cstring[1024];
1145 int i, pstrlen = query->info->TXTinfo[0];
1146 (void)m; // Unused
1147
1148 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
1149
1150 if (query->info->TXTlen > sizeof(cstring)) return;
1151
1152 mDNSPlatformMemZero(&interface, sizeof(interface));
1153 mDNSPlatformMemZero(&address, sizeof(address));
1154
1155 if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
1156 {
1157 struct sockaddr_in *s = (struct sockaddr_in*)&interface;
1158 s->sin_len = sizeof(*s);
1159 s->sin_family = AF_INET;
1160 s->sin_port = 0;
1161 s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
1162 }
1163 else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
1164 {
1165 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
1166 sin6->sin6_len = sizeof(*sin6);
1167 sin6->sin6_family = AF_INET6;
1168 sin6->sin6_flowinfo = 0;
1169 sin6->sin6_port = 0;
1170 sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
1171 sin6->sin6_scope_id = ifx->scope_id;
1172 }
1173
1174 if (query->info->ip.type == mDNSAddrType_IPv4)
1175 {
1176 struct sockaddr_in *s = (struct sockaddr_in*)&address;
1177 s->sin_len = sizeof(*s);
1178 s->sin_family = AF_INET;
1179 s->sin_port = query->info->port.NotAnInteger;
1180 s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
1181 }
1182 else
1183 {
1184 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
1185 sin6->sin6_len = sizeof(*sin6);
1186 sin6->sin6_family = AF_INET6;
1187 sin6->sin6_port = query->info->port.NotAnInteger;
1188 sin6->sin6_flowinfo = 0;
1189 sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
1190 sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
1191 }
1192
1193 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
1194 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
1195 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
1196 // ASCII-1 characters are used in the C-string as boundary markers,
1197 // to indicate the boundaries between the original constituent P-strings.
1198 for (i=1; i<query->info->TXTlen; i++)
1199 {
1200 if (--pstrlen >= 0)
1201 cstring[i-1] = query->info->TXTinfo[i];
1202 else
1203 {
1204 cstring[i-1] = 1;
1205 pstrlen = query->info->TXTinfo[i];
1206 }
1207 }
1208 cstring[i-1] = 0; // Put the terminating NULL on the end
1209
1210 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
1211 x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
1212 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
1213 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
1214 if (status == MACH_SEND_TIMED_OUT)
1215 AbortBlockedClient(x->ClientMachPort, "resolve", x);
1216 }
1217
1218 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
1219 DNSCString name, DNSCString regtype, DNSCString domain)
1220 {
1221 // Check client parameter
1222 (void)unusedserver; // Unused
1223 mStatus err = mStatus_NoError;
1224 const char *errormsg = "Unknown";
1225 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1226 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1227
1228 // Check other parameters
1229 domainlabel n;
1230 domainname t, d, srv;
1231 if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1232 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1233 if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
1234 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1235
1236 // Allocate memory, and handle failure
1237 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
1238 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1239
1240 // Set up object, and link into list
1241 x->ClientMachPort = client;
1242 x->i.InterfaceID = mDNSInterface_Any;
1243 x->i.name = srv;
1244 x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
1245 x->next = DNSServiceResolverList;
1246 DNSServiceResolverList = x;
1247
1248 // Do the operation
1249 LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c);
1250 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
1251 if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
1252
1253 // Succeeded: Wrap up and return
1254 EnableDeathNotificationForClient(client, x);
1255 return(mStatus_NoError);
1256
1257 badparam:
1258 err = mStatus_BadParamErr;
1259 fail:
1260 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err);
1261 return(err);
1262 }
1263
1264 //*************************************************************************************************************
1265 // Registration
1266
1267 mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
1268 {
1269 m->p->NotifyUser = NonZeroTime(m->timenow + delay);
1270 }
1271
1272 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
1273 {
1274 ServiceInstance *si = (ServiceInstance*)srs->ServiceContext;
1275
1276 if (result == mStatus_NoError)
1277 {
1278 kern_return_t status;
1279 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1280 status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1281 if (status == MACH_SEND_TIMED_OUT)
1282 AbortBlockedClient(si->ClientMachPort, "registration success", si);
1283 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1284 RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
1285 }
1286
1287 else if (result == mStatus_NameConflict)
1288 {
1289 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1290 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1291 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1292 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1293 {
1294 // On conflict for an autoname service, rename and reregister *all* autoname services
1295 IncrementLabelSuffix(&m->nicelabel, mDNStrue);
1296 m->MainCallback(m, mStatus_ConfigChanged);
1297 }
1298 else if (si->autoname)
1299 {
1300 mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
1301 return;
1302 }
1303 else
1304 {
1305 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1306 // of their registration in the usual way (which we will catch via client death notification).
1307 // If the Mach queue is full, we forcibly abort the client immediately.
1308 kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1309 if (status == MACH_SEND_TIMED_OUT)
1310 AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL);
1311 }
1312 }
1313
1314 else if (result == mStatus_MemFree)
1315 {
1316 if (si->renameonmemfree) // We intentionally terminated registration so we could re-register with new name
1317 {
1318 debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c);
1319 si->renameonmemfree = mDNSfalse;
1320 si->name = m->nicelabel;
1321 mDNS_RenameAndReregisterService(m, srs, &si->name);
1322 }
1323 else
1324 {
1325 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1326 DNSServiceRegistration *r;
1327 for (r = DNSServiceRegistrationList; r; r = r->next)
1328 {
1329 ServiceInstance **sp = &r->regs;
1330 while (*sp)
1331 {
1332 if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; }
1333 sp = &(*sp)->next;
1334 }
1335 }
1336 // END SANITY CHECK
1337 FreeServiceInstance(si);
1338 }
1339 }
1340
1341 else if (result != mStatus_NATTraversal)
1342 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result);
1343 }
1344
1345 mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain)
1346 {
1347 mStatus err = 0;
1348 ServiceInstance *si = NULL;
1349 AuthRecord *SubTypes = NULL;
1350
1351 for (si = x->regs; si; si = si->next)
1352 {
1353 if (SameDomainName(&si->domain, domain))
1354 { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
1355 }
1356
1357 SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype);
1358 if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
1359
1360 si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
1361 if (!si) return mStatus_NoMemoryErr;
1362
1363 si->ClientMachPort = x->ClientMachPort;
1364 si->renameonmemfree = mDNSfalse;
1365 si->autoname = x->autoname;
1366 si->name = x->autoname ? mDNSStorage.nicelabel : x->name;
1367 si->domain = *domain;
1368
1369 err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL,
1370 x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si);
1371 if (!err)
1372 {
1373 si->next = x->regs;
1374 x->regs = si;
1375 }
1376 else
1377 {
1378 LogMsg("Error %d for registration of service in domain %##s", err, domain->c);
1379 freeL("ServiceInstance", si);
1380 }
1381 return err;
1382 }
1383
1384 mDNSexport void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add)
1385 {
1386 DNSServiceRegistration *reg;
1387
1388 for (reg = DNSServiceRegistrationList; reg; reg = reg->next)
1389 {
1390 if (reg->DefaultDomain)
1391 {
1392 if (add)
1393 AddServiceInstance(reg, d);
1394 else
1395 {
1396 ServiceInstance **si = &reg->regs;
1397 while (*si)
1398 {
1399 if (SameDomainName(&(*si)->domain, d))
1400 {
1401 ServiceInstance *s = *si;
1402 *si = (*si)->next;
1403 if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s); // only free memory synchronously on error
1404 break;
1405 }
1406 si = &(*si)->next;
1407 }
1408 if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed
1409 }
1410 }
1411 }
1412 }
1413
1414 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1415 DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord)
1416 {
1417 (void)unusedserver; // Unused
1418 mStatus err = mStatus_NoError;
1419 const char *errormsg = "Unknown";
1420
1421 // older versions of this code passed the port via mach IPC as an int.
1422 // we continue to pass it as 4 bytes to maintain binary compatibility,
1423 // but now ensure that the network byte order is preserved by using a struct
1424 mDNSIPPort port;
1425 port.b[0] = IpPort.bytes[2];
1426 port.b[1] = IpPort.bytes[3];
1427
1428 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1429 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1430
1431 // Check for sub-types after the service type
1432 size_t reglen = strlen(regtype) + 1;
1433 if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
1434 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1435 if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }
1436
1437 // Check other parameters
1438 domainlabel n;
1439 domainname t, d;
1440 domainname srv;
1441 if (!name[0]) n = mDNSStorage.nicelabel;
1442 else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1443 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1444 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
1445 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1446
1447 unsigned char txtinfo[1024] = "";
1448 unsigned int data_len = 0;
1449 unsigned int size = sizeof(RDataBody);
1450 unsigned char *pstring = &txtinfo[data_len];
1451 char *ptr = txtRecord;
1452
1453 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1454 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1455 // Hence we have to convert the C-string to a P-string.
1456 // ASCII-1 characters are allowed in the C-string as boundary markers,
1457 // so that a single C-string can be used to represent one or more P-strings.
1458 while (*ptr)
1459 {
1460 if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
1461 if (*ptr == 1) // If this is our boundary marker, start a new P-string
1462 {
1463 pstring = &txtinfo[data_len];
1464 pstring[0] = 0;
1465 ptr++;
1466 }
1467 else
1468 {
1469 if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
1470 pstring[++pstring[0]] = *ptr++;
1471 }
1472 }
1473
1474 data_len++;
1475 if (size < data_len)
1476 size = data_len;
1477
1478 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1479 // a port number of zero. When two instances of the protected client are allowed to run on one
1480 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1481 if (!mDNSIPPortIsZero(port))
1482 {
1483 int count = CountExistingRegistrations(&srv, port);
1484 if (count)
1485 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1486 client, count+1, srv.c, mDNSVal16(port));
1487 }
1488
1489 // Allocate memory, and handle failure
1490 DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x));
1491 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1492 mDNSPlatformMemZero(x, sizeof(*x));
1493
1494 // Set up object, and link into list
1495 x->ClientMachPort = client;
1496 x->DefaultDomain = !domain[0];
1497 x->autoname = (!name[0]);
1498 x->rdsize = size;
1499 x->NumSubTypes = NumSubTypes;
1500 memcpy(x->regtype, regtype, reglen);
1501 x->name = n;
1502 x->type = t;
1503 x->port = port;
1504 memcpy(x->txtinfo, txtinfo, 1024);
1505 x->txt_len = data_len;
1506 x->NextRef = 0;
1507 x->regs = NULL;
1508
1509 x->next = DNSServiceRegistrationList;
1510 DNSServiceRegistrationList = x;
1511
1512 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1513 x->ClientMachPort, name, regtype, domain, mDNSVal16(port));
1514
1515 err = AddServiceInstance(x, &d);
1516 if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails
1517
1518 if (x->DefaultDomain)
1519 {
1520 DNameListElem *p;
1521 for (p = AutoRegistrationDomains; p; p = p->next)
1522 AddServiceInstance(x, &p->name);
1523 }
1524
1525 // Succeeded: Wrap up and return
1526 EnableDeathNotificationForClient(client, x);
1527 return(mStatus_NoError);
1528
1529 badtxt:
1530 LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
1531 badparam:
1532 err = mStatus_BadParamErr;
1533 fail:
1534 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1535 client, name, regtype, domain, mDNSVal16(port), errormsg, err);
1536 return(err);
1537 }
1538
1539 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
1540 {
1541 (void)m; // Unused
1542 if (result == mStatus_NoError)
1543 {
1544 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
1545 LogOperation("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
1546 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1547 RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond);
1548 }
1549 else if (result == mStatus_NameConflict)
1550 {
1551 LogOperation("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c);
1552 if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow);
1553 else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond)
1554 {
1555 // Tell the helper we've given up
1556 mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, NULL);
1557 }
1558 }
1559 else if (result == mStatus_GrowCache)
1560 {
1561 // Allocate another chunk of cache storage
1562 CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE);
1563 //LogOperation("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
1564 if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
1565 }
1566 else if (result == mStatus_ConfigChanged)
1567 {
1568 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
1569 mDNSPreferencesSetName(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
1570 mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
1571
1572 // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
1573 DNSServiceRegistration *r;
1574 for (r = DNSServiceRegistrationList; r; r=r->next)
1575 if (r->autoname)
1576 {
1577 ServiceInstance *si;
1578 for (si = r->regs; si; si = si->next)
1579 {
1580 if (!SameDomainLabelCS(si->name.c, m->nicelabel.c))
1581 {
1582 debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c);
1583 si->renameonmemfree = mDNStrue;
1584 if (mDNS_DeregisterService(m, &si->srs)) // If service deregistered already, we can re-register immediately
1585 RegCallback(m, &si->srs, mStatus_MemFree);
1586 }
1587 }
1588 }
1589
1590 // Then we call into the UDS daemon code, to let it do the same
1591 udsserver_handle_configchange(m);
1592 }
1593 }
1594
1595 //*************************************************************************************************************
1596 // Add / Update / Remove records from existing Registration
1597
1598 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1599 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
1600 {
1601 // Check client parameter
1602 uint32_t id;
1603 mStatus err = mStatus_NoError;
1604 const char *errormsg = "Unknown";
1605 DNSServiceRegistration *x = DNSServiceRegistrationList;
1606 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1607 ServiceInstance *si;
1608 size_t size;
1609 (void)unusedserver; // Unused
1610 while (x && x->ClientMachPort != client) x = x->next;
1611 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1612
1613 // Check other parameters
1614 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1615 if (data_len > sizeof(RDataBody)) size = data_len;
1616 else size = sizeof(RDataBody);
1617
1618 id = x->NextRef++;
1619 *reference = (natural_t)id;
1620 for (si = x->regs; si; si = si->next)
1621 {
1622 // Allocate memory, and handle failure
1623 ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
1624 if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1625
1626 // Fill in type, length, and data of new record
1627 extra->r.resrec.rrtype = type;
1628 extra->r.rdatastorage.MaxRDLength = size;
1629 extra->r.resrec.rdlength = data_len;
1630 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
1631
1632 // Do the operation
1633 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1634 client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
1635 err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl);
1636
1637 if (err)
1638 {
1639 freeL("Extra Resource Record", extra);
1640 errormsg = "mDNS_AddRecordToService";
1641 goto fail;
1642 }
1643
1644 extra->ClientID = id;
1645 }
1646
1647 return mStatus_NoError;
1648
1649 fail:
1650 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, x ? x->name.c : (mDNSu8*)"\x8""«NULL»", type, data_len, errormsg, err);
1651 return mStatus_UnknownErr;
1652 }
1653
1654 mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
1655 {
1656 (void)m; // Unused
1657 if (OldRData != &rr->rdatastorage)
1658 freeL("Old RData", OldRData);
1659 }
1660
1661 mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1662 {
1663 // Check client parameter
1664 mStatus err = mStatus_NoError;
1665 const char *errormsg = "Unknown";
1666 const domainname *name = (const domainname *)"";
1667
1668 name = srs->RR_SRV.resrec.name;
1669
1670 unsigned int size = sizeof(RDataBody);
1671 if (size < data_len)
1672 size = data_len;
1673
1674 // Allocate memory, and handle failure
1675 RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
1676 if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1677
1678 // Fill in new length, and data
1679 newrdata->MaxRDLength = size;
1680 memcpy(&newrdata->u, data, data_len);
1681
1682 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1683 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1684 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1685 if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; }
1686
1687 // Do the operation
1688 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1689 client, srs->RR_SRV.resrec.name->c, data_len);
1690
1691 err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
1692 if (err)
1693 {
1694 errormsg = "mDNS_Update";
1695 freeL("RData", newrdata);
1696 return err;
1697 }
1698 return(mStatus_NoError);
1699
1700 fail:
1701 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%ld)", client, name->c, data_len, errormsg, err);
1702 return(err);
1703 }
1704
1705 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1706 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1707 {
1708 // Check client parameter
1709 mStatus err = mStatus_NoError;
1710 const char *errormsg = "Unknown";
1711 const domainname *name = (const domainname *)"";
1712 ServiceInstance *si;
1713
1714 (void)unusedserver; // unused
1715 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1716 DNSServiceRegistration *x = DNSServiceRegistrationList;
1717 while (x && x->ClientMachPort != client) x = x->next;
1718 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1719
1720 // Check other parameters
1721 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1722
1723 for (si = x->regs; si; si = si->next)
1724 {
1725 AuthRecord *r = NULL;
1726
1727 // Find the record we're updating. NULL reference means update the primary TXT record
1728 if (!reference) r = &si->srs.RR_TXT;
1729 else
1730 {
1731 ExtraResourceRecord *ptr;
1732 for (ptr = si->srs.Extras; ptr; ptr = ptr->next)
1733 {
1734 if ((natural_t)ptr->ClientID == reference)
1735 { r = &ptr->r; break; }
1736 }
1737 if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
1738 }
1739 err = UpdateRecord(&si->srs, client, r, data, data_len, ttl);
1740 if (err) goto fail; //!!!KRS this will cause failures for non-local defaults!
1741 }
1742
1743 return mStatus_NoError;
1744
1745 fail:
1746 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err);
1747 return(err);
1748 }
1749
1750 mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
1751 {
1752 const domainname *const name = srs->RR_SRV.resrec.name;
1753 mStatus err = mStatus_NoError;
1754
1755 // Do the operation
1756 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c);
1757
1758 err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra);
1759 if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err);
1760
1761 return err;
1762 }
1763
1764 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1765 natural_t reference)
1766 {
1767 // Check client parameter
1768 (void)unusedserver; // Unused
1769 mStatus err = mStatus_NoError;
1770 const char *errormsg = "Unknown";
1771 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1772 DNSServiceRegistration *x = DNSServiceRegistrationList;
1773 ServiceInstance *si;
1774
1775 while (x && x->ClientMachPort != client) x = x->next;
1776 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1777
1778 for (si = x->regs; si; si = si->next)
1779 {
1780 ExtraResourceRecord *e;
1781 for (e = si->srs.Extras; e; e = e->next)
1782 {
1783 if ((natural_t)e->ClientID == reference)
1784 {
1785 err = RemoveRecord(&si->srs, e, client);
1786 break;
1787 }
1788 }
1789 if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
1790 }
1791
1792 return mStatus_NoError;
1793
1794 fail:
1795 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err);
1796 return(err);
1797 }
1798
1799 //*************************************************************************************************************
1800 // Support Code
1801
1802 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1803 {
1804 mig_reply_error_t *request = msg;
1805 mig_reply_error_t *reply;
1806 mach_msg_return_t mr;
1807 int options;
1808 (void)port; // Unused
1809 (void)size; // Unused
1810 (void)info; // Unused
1811
1812 KQueueLock(&mDNSStorage);
1813
1814 /* allocate a reply buffer */
1815 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
1816
1817 /* call the MiG server routine */
1818 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
1819
1820 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
1821 {
1822 if (reply->RetCode == MIG_NO_REPLY)
1823 {
1824 /*
1825 * This return code is a little tricky -- it appears that the
1826 * demux routine found an error of some sort, but since that
1827 * error would not normally get returned either to the local
1828 * user or the remote one, we pretend it's ok.
1829 */
1830 CFAllocatorDeallocate(NULL, reply);
1831 goto done;
1832 }
1833
1834 /*
1835 * destroy any out-of-line data in the request buffer but don't destroy
1836 * the reply port right (since we need that to send an error message).
1837 */
1838 request->Head.msgh_remote_port = MACH_PORT_NULL;
1839 mach_msg_destroy(&request->Head);
1840 }
1841
1842 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
1843 {
1844 /* no reply port, so destroy the reply */
1845 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
1846 mach_msg_destroy(&reply->Head);
1847 CFAllocatorDeallocate(NULL, reply);
1848 goto done;
1849 }
1850
1851 /*
1852 * send reply.
1853 *
1854 * We don't want to block indefinitely because the client
1855 * isn't receiving messages from the reply port.
1856 * If we have a send-once right for the reply port, then
1857 * this isn't a concern because the send won't block.
1858 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1859 * To avoid falling off the kernel's fast RPC path unnecessarily,
1860 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1861 */
1862
1863 options = MACH_SEND_MSG;
1864 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
1865 options |= MACH_SEND_TIMEOUT;
1866
1867 mr = mach_msg(&reply->Head, /* msg */
1868 options, /* option */
1869 reply->Head.msgh_size, /* send_size */
1870 0, /* rcv_size */
1871 MACH_PORT_NULL, /* rcv_name */
1872 MACH_MSG_TIMEOUT_NONE, /* timeout */
1873 MACH_PORT_NULL); /* notify */
1874
1875 /* Has a message error occurred? */
1876 switch (mr)
1877 {
1878 case MACH_SEND_INVALID_DEST:
1879 case MACH_SEND_TIMED_OUT:
1880 /* the reply can't be delivered, so destroy it */
1881 mach_msg_destroy(&reply->Head);
1882 break;
1883
1884 default :
1885 /* Includes success case. */
1886 break;
1887 }
1888
1889 CFAllocatorDeallocate(NULL, reply);
1890
1891 done:
1892 KQueueUnlock(&mDNSStorage, "Mach client event");
1893 }
1894
1895 mDNSlocal kern_return_t registerBootstrapService()
1896 {
1897 kern_return_t status;
1898 mach_port_t service_send_port, service_rcv_port;
1899
1900 debugf("Registering Bootstrap Service");
1901
1902 /*
1903 * See if our service name is already registered and if we have privilege to check in.
1904 */
1905 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1906 if (status == KERN_SUCCESS)
1907 {
1908 /*
1909 * If so, we must be a followup instance of an already defined server. In that case,
1910 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1911 * that in case we have to unregister later (which requires the privilege port).
1912 */
1913 server_priv_port = bootstrap_port;
1914 restarting_via_mach_init = TRUE;
1915 }
1916 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
1917 {
1918 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
1919 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
1920 if (status != KERN_SUCCESS) return status;
1921
1922 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
1923 if (status != KERN_SUCCESS)
1924 {
1925 mach_port_deallocate(mach_task_self(), server_priv_port);
1926 return status;
1927 }
1928
1929 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1930 if (status != KERN_SUCCESS)
1931 {
1932 mach_port_deallocate(mach_task_self(), server_priv_port);
1933 mach_port_deallocate(mach_task_self(), service_send_port);
1934 return status;
1935 }
1936 assert(service_send_port == service_rcv_port);
1937 }
1938
1939 /*
1940 * We have no intention of responding to requests on the service port. We are not otherwise
1941 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1942 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1943 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1944 */
1945 mach_port_destroy(mach_task_self(), service_rcv_port);
1946 return status;
1947 }
1948
1949 mDNSlocal kern_return_t destroyBootstrapService()
1950 {
1951 debugf("Destroying Bootstrap Service");
1952 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
1953 }
1954
1955 mDNSlocal void ExitCallback(int sig)
1956 {
1957 (void)sig; // Unused
1958 LogMsgIdent(mDNSResponderVersionString, "stopping");
1959
1960 debugf("ExitCallback");
1961 if (!mDNS_DebugMode && !started_via_launchdaemon)
1962 destroyBootstrapService();
1963
1964 debugf("ExitCallback: Aborting MIG clients");
1965 while (DNSServiceDomainEnumerationList)
1966 AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
1967 while (DNSServiceBrowserList)
1968 AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
1969 while (DNSServiceResolverList)
1970 AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
1971 while (DNSServiceRegistrationList)
1972 AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
1973
1974 if (udsserver_exit(launchd_fd) < 0) LogMsg("ExitCallback: udsserver_exit failed");
1975
1976 debugf("ExitCallback: mDNS_StartExit");
1977 mDNS_StartExit(&mDNSStorage);
1978 }
1979
1980 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1981 mDNSlocal void HandleSIG(int sig)
1982 {
1983 debugf(" ");
1984 debugf("HandleSIG %d", sig);
1985 mach_msg_header_t header;
1986 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1987 header.msgh_remote_port = signal_port;
1988 header.msgh_local_port = MACH_PORT_NULL;
1989 header.msgh_size = sizeof(header);
1990 header.msgh_id = sig;
1991 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
1992 {
1993 LogMsg("HandleSIG %d: mach_msg_send failed", sig);
1994 if (sig == SIGTERM || sig == SIGINT) exit(-1);
1995 }
1996 }
1997
1998 mDNSlocal void CatchABRT(int sig)
1999 {
2000 LogMsg("Received SIGABRT %d", sig);
2001 sleep(1); // Pause to make sure syslog gets the message
2002 while(1) *(long*)0 = 0; // Generate a CrashReporter stack trace so we can find out what library called abort();
2003 }
2004
2005 mDNSlocal void INFOCallback(void)
2006 {
2007 mDNSs32 utc = mDNSPlatformUTC();
2008 DNSServiceDomainEnumeration *e;
2009 DNSServiceBrowser *b;
2010 DNSServiceResolver *l;
2011 DNSServiceRegistration *r;
2012 NetworkInterfaceInfoOSX *i;
2013 DNSServer *s;
2014
2015 LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----");
2016
2017 udsserver_info(&mDNSStorage);
2018
2019 LogMsgNoIdent("--------- Mach Clients ---------");
2020 if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList)
2021 LogMsgNoIdent("<None>");
2022 else
2023 {
2024 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
2025 LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
2026
2027 for (b = DNSServiceBrowserList; b; b=b->next)
2028 {
2029 DNSServiceBrowserQuestion *qptr;
2030 for (qptr = b->qlist; qptr; qptr = qptr->next)
2031 LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c);
2032 }
2033 for (l = DNSServiceResolverList; l; l=l->next)
2034 LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
2035
2036 for (r = DNSServiceRegistrationList; r; r=r->next)
2037 {
2038 ServiceInstance *si;
2039 for (si = r->regs; si; si = si->next)
2040 LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name->c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port));
2041 }
2042 }
2043
2044 LogMsgNoIdent("----- KQSocketEventSources -----");
2045 if (!gEventSources) LogMsgNoIdent("<None>");
2046 else
2047 {
2048 KQSocketEventSource *k;
2049 for (k = gEventSources; k; k=k->next)
2050 LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask);
2051 }
2052
2053 LogMsgNoIdent("------ Network Interfaces ------");
2054 if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent("<None>");
2055 else
2056 {
2057 for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
2058 {
2059 if (!i->Exists)
2060 LogMsgNoIdent("Interface: %s %5s(%lu) %.6a %#a dormant for %d seconds",
2061 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID,
2062 &i->ifinfo.ip, utc - i->LastSeen);
2063 else
2064 LogMsgNoIdent("Interface: %s %5s(%lu) %.6a %s %s %-15.4a %s InterfaceID %p %s %s %#a",
2065 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID,
2066 i->ifinfo.InterfaceActive ? "Active" : " ",
2067 i->ifinfo.IPv4Available ? "v4" : " ",
2068 i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr,
2069 i->ifinfo.IPv6Available ? "v6" : " ",
2070 i->ifinfo.InterfaceID,
2071 i->ifinfo.Advertise ? "Adv" : " ",
2072 i->ifinfo.McastTxRx ? "TxRx" : " ",
2073 &i->ifinfo.ip);
2074 }
2075 }
2076
2077 LogMsgNoIdent("--------- DNS Servers ----------");
2078 if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
2079 else
2080 {
2081 for (s = mDNSStorage.DNSServers; s; s = s->next)
2082 {
2083 NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)s->interface;
2084 LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %s",
2085 s->domain.c, ifx ? ifx->ifa_name : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
2086 s->teststate == DNSServer_Untested ? "(Untested)" :
2087 s->teststate == DNSServer_Passed ? "" :
2088 s->teststate == DNSServer_Failed ? "(Failed)" :
2089 s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)");
2090 }
2091 }
2092
2093 mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
2094 LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
2095
2096 LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----");
2097 }
2098
2099 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
2100 {
2101 (void)port; // Unused
2102 (void)size; // Unused
2103 (void)info; // Unused
2104 mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
2105 mDNS *const m = &mDNSStorage;
2106
2107 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
2108 KQueueLock(m);
2109 switch(msg_header->msgh_id)
2110 {
2111 case SIGHUP: {
2112 mDNSu32 slot;
2113 CacheGroup *cg;
2114 CacheRecord *rr;
2115 LogMsg("SIGHUP: Purge cache");
2116 FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
2117 } break;
2118 case SIGINT:
2119 case SIGTERM: ExitCallback(msg_header->msgh_id); break;
2120 case SIGINFO: INFOCallback(); break;
2121 case SIGUSR1: // mDNSCoreMachineSleep(m, !m->SleepState); break;
2122 LogMsg("SIGUSR1: Simulate Network Configuration Change Event");
2123 mDNSMacOSXNetworkChanged(m);
2124
2125 // Simulate KeychainChanged
2126 mDNS_Lock(m);
2127 SetDomainSecrets(m);
2128 mDNS_Unlock(m);
2129
2130 break;
2131 case SIGUSR2: SigLogLevel(); break;
2132 default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
2133 }
2134 KQueueUnlock(m, "Unix Signal");
2135 }
2136
2137 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2138 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2139
2140 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
2141 {
2142 mStatus err;
2143 CFMachPortRef s_port;
2144
2145 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
2146 if (m_port != MACH_PORT_NULL)
2147 s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
2148 else
2149 {
2150 s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
2151 m_port = CFMachPortGetPort(s_port);
2152 char *MachServerName = OSXVers < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
2153 kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
2154
2155 if (status)
2156 {
2157 if (status == 1103)
2158 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
2159 else
2160 LogMsg("bootstrap_register() failed: %s %d", mach_error_string(status), status);
2161 return(status);
2162 }
2163 }
2164
2165 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
2166 CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL);
2167 CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
2168 CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
2169 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
2170
2171 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
2172 rrcachestorage, RR_CACHE_SIZE,
2173 mDNS_Init_AdvertiseLocalAddresses,
2174 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
2175
2176 if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); }
2177
2178 client_death_port = CFMachPortGetPort(d_port);
2179 signal_port = CFMachPortGetPort(i_port);
2180
2181 CFRunLoop = CFRunLoopGetCurrent();
2182 CFRunLoopAddSource(CFRunLoop, d_rls, kCFRunLoopDefaultMode);
2183 CFRunLoopAddSource(CFRunLoop, s_rls, kCFRunLoopDefaultMode);
2184 CFRunLoopAddSource(CFRunLoop, i_rls, kCFRunLoopDefaultMode);
2185 CFRelease(d_rls);
2186 CFRelease(s_rls);
2187 CFRelease(i_rls);
2188 if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
2189 return(err);
2190 }
2191
2192 mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
2193 {
2194 mDNSs32 now = mDNS_TimeNow(m);
2195
2196 // 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
2197 // Detailed reason:
2198 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2199 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2200 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2201 // we then systematically lose our own looped-back packets.
2202 if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);
2203
2204 // KeyChain frequently fails to notify clients of change events. To work around this
2205 // we set a timer and periodically poll to detect if any changes have occurred.
2206 // Without this Back To My Mac just does't work for a large number of users.
2207 // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
2208 if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
2209 {
2210 m->p->KeyChainBugInterval *= 2;
2211 m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
2212 if (m->p->KeyChainBugInterval > 2 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
2213 mDNS_Lock(m);
2214 SetDomainSecrets(m);
2215 mDNS_Unlock(m);
2216 }
2217
2218 // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
2219 mDNSs32 nextevent = mDNS_Execute(m);
2220
2221 if (m->p->NetworkChanged)
2222 if (nextevent - m->p->NetworkChanged > 0)
2223 nextevent = m->p->NetworkChanged;
2224
2225 if (m->p->KeyChainBugTimer)
2226 if (nextevent - m->p->KeyChainBugTimer > 0)
2227 nextevent = m->p->KeyChainBugTimer;
2228
2229 // 3. Deliver any waiting browse messages to clients
2230 DNSServiceBrowser *b = DNSServiceBrowserList;
2231
2232 while (b)
2233 {
2234 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2235 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2236 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2237 DNSServiceBrowser *x = b;
2238 b = b->next;
2239 if (x->results) // Try to deliver the list of results
2240 {
2241 while (x->results)
2242 {
2243 DNSServiceBrowserResult *const r = x->results;
2244 domainlabel name;
2245 domainname type, domain;
2246 DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance()
2247 char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2248 char ctype[MAX_ESCAPED_DOMAIN_NAME];
2249 char cdom [MAX_ESCAPED_DOMAIN_NAME];
2250 ConvertDomainLabelToCString_unescaped(&name, cname);
2251 ConvertDomainNameToCString(&type, ctype);
2252 ConvertDomainNameToCString(&domain, cdom);
2253 DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
2254 kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
2255 // If we failed to send the mach message, try again in one second
2256 if (status == MACH_SEND_TIMED_OUT)
2257 {
2258 if (nextevent - now > mDNSPlatformOneSecond)
2259 nextevent = now + mDNSPlatformOneSecond;
2260 break;
2261 }
2262 else
2263 {
2264 x->lastsuccess = now;
2265 x->results = x->results->next;
2266 freeL("DNSServiceBrowserResult", r);
2267 }
2268 }
2269 // If this client hasn't read a single message in the last 60 seconds, abort it
2270 if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
2271 AbortBlockedClient(x->ClientMachPort, "browse", x);
2272 }
2273 }
2274
2275 DNSServiceResolver *l;
2276 for (l = DNSServiceResolverList; l; l=l->next)
2277 if (l->ReportTime && now - l->ReportTime >= 0)
2278 {
2279 l->ReportTime = 0;
2280 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2281 "This places considerable burden on the network.", l->i.name.c);
2282 }
2283
2284 if (m->p->NotifyUser)
2285 {
2286 if (m->p->NotifyUser - now < 0)
2287 {
2288 if (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c))
2289 {
2290 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c);
2291 mDNSPreferencesSetName(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
2292 m->p->usernicelabel = m->nicelabel;
2293 }
2294 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
2295 {
2296 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
2297 mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
2298 m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful
2299 m->p->userhostlabel = m->hostlabel;
2300 }
2301 m->p->NotifyUser = 0;
2302 }
2303 else
2304 if (nextevent - m->p->NotifyUser > 0)
2305 nextevent = m->p->NotifyUser;
2306 }
2307
2308 return(nextevent);
2309 }
2310
2311 mDNSlocal void ShowTaskSchedulingError(mDNS *const m)
2312 {
2313 mDNS_Lock(m);
2314
2315 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
2316
2317 // NOTE: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent
2318
2319 if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
2320 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
2321 m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
2322 if (m->NewLocalOnlyQuestions)
2323 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
2324 m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
2325 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords))
2326 LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, m->NewLocalRecords));
2327 if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
2328 LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending);
2329 #ifndef UNICAST_DISABLED
2330 if (m->timenow - m->NextuDNSEvent >= 0)
2331 LogMsg("Task Scheduling Error: NextuDNSEvent %d", m->timenow - m->NextuDNSEvent);
2332 #endif
2333 if (m->timenow - m->NextCacheCheck >= 0)
2334 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck);
2335 if (m->timenow - m->NextScheduledQuery >= 0)
2336 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery);
2337 if (m->timenow - m->NextScheduledProbe >= 0)
2338 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe);
2339 if (m->timenow - m->NextScheduledResponse >= 0)
2340 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
2341 if (m->timenow - m->NextScheduledNATOp >= 0)
2342 LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp);
2343
2344 mDNS_Unlock(&mDNSStorage);
2345 }
2346
2347 mDNSlocal mDNSBool ReadyForSleep(mDNS *m)
2348 {
2349 (void)m;
2350
2351 // 1. Scan list of private LLQs, and make sure they've all completed their handshake with the server
2352 DNSQuestion *q;
2353 for (q = m->Questions; q; q = q->next)
2354 if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp) return(mDNSfalse);
2355
2356 // 2. Scan list of registered records
2357 AuthRecord *rr;
2358 for (rr = m->ResourceRecords; rr; rr = rr->next)
2359 if (rr->state == regState_Refresh && rr->tcp) return(mDNSfalse);
2360
2361 // 2. Scan list of registered services
2362 ServiceRecordSet *srs;
2363 for (srs = m->ServiceRegistrations; srs; srs = srs->uDNS_next)
2364 if (srs->state == regState_NoTarget && srs->tcp) return(mDNSfalse);
2365
2366 return(mDNStrue);
2367 }
2368
2369 mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context)
2370 {
2371 // Read all of the bytes so we won't wake again.
2372 char buffer[100];
2373 while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue;
2374 }
2375
2376 mDNSlocal void * KQueueLoop(void *m_param)
2377 {
2378 mDNS *m = m_param;
2379 int numevents = 0;
2380
2381 #if USE_SELECT_WITH_KQUEUEFD
2382 fd_set readfds;
2383 FD_ZERO(&readfds);
2384 const int multiplier = 1000000 / mDNSPlatformOneSecond;
2385 #else
2386 const int multiplier = 1000000000 / mDNSPlatformOneSecond;
2387 #endif
2388
2389 pthread_mutex_lock(&PlatformStorage.BigMutex);
2390 LogOperation("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last);
2391
2392 // This is the main work loop:
2393 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2394 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2395 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2396 // (4) On wakeup we first process *all* events
2397 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2398 for ( ; ; )
2399 {
2400 #define kEventsToReadAtOnce 1
2401 struct kevent new_events[kEventsToReadAtOnce];
2402
2403 // Run mDNS_Execute to find out the time we next need to wake up
2404 mDNSs32 start = mDNSPlatformRawTime();
2405 mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
2406 mDNSs32 end = mDNSPlatformRawTime();
2407 if (end - start >= WatchDogReportingThreshold)
2408 LogOperation("WARNING: Idle task took %dms to complete", end - start);
2409
2410 mDNSs32 now = mDNS_TimeNow(m);
2411
2412 if (m->ShutdownTime)
2413 {
2414 if (mDNS_ExitNow(m, now))
2415 {
2416 LogOperation("mDNS_FinalExit");
2417 mDNS_FinalExit(&mDNSStorage);
2418 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2419 exit(0);
2420 }
2421 if (nextTimerEvent - m->ShutdownTime >= 0)
2422 nextTimerEvent = m->ShutdownTime;
2423 }
2424
2425 if (m->p->SleepLimit)
2426 {
2427 mDNSBool ready = ReadyForSleep(m);
2428 if (ready || now - m->p->SleepLimit >= 0)
2429 {
2430 LogOperation("IOAllowPowerChange(%lX) %s at %ld (%d ticks remaining)", m->p->SleepCookie,
2431 ready ? "ready for sleep" : "giving up", now, m->p->SleepLimit - now);
2432 m->p->SleepLimit = 0;
2433 IOAllowPowerChange(m->p->PowerConnection, m->p->SleepCookie);
2434 }
2435 else
2436 if (nextTimerEvent - m->p->SleepLimit >= 0)
2437 nextTimerEvent = m->p->SleepLimit;
2438 }
2439
2440 // Convert absolute wakeup time to a relative time from now
2441 mDNSs32 ticks = nextTimerEvent - now;
2442 if (ticks < 1) ticks = 1;
2443
2444 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
2445 if (ticks > 1)
2446 RepeatedBusy = 0;
2447 else
2448 {
2449 ticks = 1;
2450 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
2451 }
2452
2453 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks);
2454 numevents = 0;
2455
2456 // Release the lock, and sleep until:
2457 // 1. Something interesting happens like a packet arriving, or
2458 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
2459 // 3. The timeout expires
2460 pthread_mutex_unlock(&PlatformStorage.BigMutex);
2461
2462 #if USE_SELECT_WITH_KQUEUEFD
2463 struct timeval timeout;
2464 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
2465 timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier;
2466 FD_SET(KQueueFD, &readfds);
2467 if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0)
2468 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
2469 #else
2470 struct timespec timeout;
2471 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
2472 timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier;
2473 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
2474 // and have it work similarly to the way it does with nevents non-zero --
2475 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
2476 // In fact, what happens if you do this is that it just returns immediately. So, we have
2477 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
2478 if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0)
2479 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
2480 #endif
2481
2482 pthread_mutex_lock(&PlatformStorage.BigMutex);
2483 // We have to ignore the event we may have been told about above, because that
2484 // was done without holding the lock, and between the time we woke up and the
2485 // time we reclaimed the lock the other thread could have done something that
2486 // makes the event no longer valid. Now we have the lock, we call kevent again
2487 // and this time we can safely process the events it tells us about.
2488
2489 static const struct timespec zero_timeout = { 0, 0 };
2490 int events_found;
2491 while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0)
2492 {
2493 if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR))
2494 {
2495 // Not sure what to do here, our kqueue has failed us - this isn't ideal
2496 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno));
2497 exit(errno);
2498 }
2499
2500 numevents += events_found;
2501
2502 int i;
2503 for (i = 0; i < events_found; i++)
2504 {
2505 const KQueueEntry *const kqentry = new_events[i].udata;
2506 mDNSs32 stime = mDNSPlatformRawTime();
2507 #if LogAllOperations || MDNS_DEBUGMSGS
2508 const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task
2509 #endif
2510 kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext);
2511 mDNSs32 etime = mDNSPlatformRawTime();
2512 if (etime - stime >= WatchDogReportingThreshold)
2513 LogOperation("WARNING: %s took %dms to complete", KQtask, etime - stime);
2514 }
2515 }
2516 }
2517
2518 return NULL;
2519 }
2520
2521 mDNSlocal void LaunchdCheckin(void)
2522 {
2523 launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
2524 launch_data_t resp = launch_msg(msg);
2525 launch_data_free(msg);
2526 if (!resp) { LogMsg("launch_msg returned NULL"); return; }
2527
2528 if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
2529 {
2530 int err = launch_data_get_errno(resp);
2531 // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
2532 if (err != EACCES) LogMsg("launch_msg returned %d", err);
2533 else LogOperation("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err);
2534 }
2535 else
2536 {
2537 launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
2538 if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
2539 else
2540 {
2541 launch_data_t skt = launch_data_dict_lookup(skts, "Listeners");
2542 if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL");
2543 else
2544 {
2545 launch_data_t s = launch_data_array_get_index(skt, 0);
2546 if (!s) LogMsg("launch_data_array_get_index(skt, 0) returned NULL");
2547 else
2548 {
2549 launchd_fd = launch_data_get_fd(s);
2550 LogOperation("Launchd Unix Domain Socket: %d", launchd_fd);
2551 // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
2552 chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH);
2553 }
2554 }
2555 }
2556
2557 launch_data_t ports = launch_data_dict_lookup(resp, "MachServices");
2558 if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL");
2559 else
2560 {
2561 launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder");
2562 if (!p) LogOperation("launch_data_array_get_index(ports, 0) returned NULL");
2563 else
2564 {
2565 m_port = launch_data_get_fd(p);
2566 LogOperation("Launchd Mach Port: %d", m_port);
2567 if (m_port == ~0U) m_port = MACH_PORT_NULL;
2568 }
2569 }
2570 }
2571 launch_data_free(resp);
2572 }
2573
2574 mDNSlocal void DropPrivileges(void)
2575 {
2576 static const char login[] = "_mdnsresponder";
2577 struct passwd *pwd = getpwnam(login);
2578 if (NULL == pwd)
2579 LogMsg("Could not find account name \"%s\". Running as root.", login);
2580 else
2581 {
2582 uid_t uid = pwd->pw_uid;
2583 gid_t gid = pwd->pw_gid;
2584
2585 LogMsg("Started as root. Switching to userid \"%s\".", login);
2586
2587 if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno));
2588 else
2589 {
2590 static char path[] = "/var/run/mdns/mDNSResponder";
2591 char *p = strrchr(path, '/');
2592 *p = '\0';
2593 if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno));
2594 else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno));
2595 else
2596 {
2597 *p = '/';
2598 if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno));
2599 else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno));
2600 else LogOperation("DropPrivileges: Created subdirectory and symlink");
2601 }
2602 }
2603
2604 if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login, (unsigned long)gid);
2605 if (0 != setgid(gid)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid());
2606 if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid);
2607 }
2608 }
2609
2610 extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));
2611
2612 mDNSexport int main(int argc, char **argv)
2613 {
2614 int i;
2615 kern_return_t status;
2616 pthread_t KQueueThread;
2617
2618 LogMsgIdent(mDNSResponderVersionString, "starting");
2619
2620 if (0 == geteuid()) DropPrivileges();
2621
2622 for (i=1; i<argc; i++)
2623 {
2624 if (!strcasecmp(argv[i], "-d" )) mDNS_DebugMode = mDNStrue;
2625 if (!strcasecmp(argv[i], "-launchd" )) started_via_launchdaemon = mDNStrue;
2626 if (!strcasecmp(argv[i], "-launchdaemon")) started_via_launchdaemon = mDNStrue;
2627 }
2628
2629 signal(SIGHUP, HandleSIG); // (Debugging) Purge the cache to check for cache handling bugs
2630 signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
2631 signal(SIGABRT, CatchABRT); // For debugging -- SIGABRT should never happen
2632 signal(SIGPIPE, SIG_IGN ); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
2633 signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
2634 signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog
2635 signal(SIGUSR1, HandleSIG); // (Debugging) Simulate network change notification from System Configuration Framework
2636 signal(SIGUSR2, HandleSIG); // (Debugging) Change log level
2637
2638 mDNSStorage.p = &PlatformStorage; // Make sure mDNSStorage.p is set up, because validatelists uses it
2639 LaunchdCheckin();
2640
2641 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
2642 if (!mDNS_DebugMode && !started_via_launchdaemon)
2643 {
2644 registerBootstrapService();
2645 if (!restarting_via_mach_init) exit(0); // mach_init will restart us immediately as a daemon
2646 int fd = open(_PATH_DEVNULL, O_RDWR, 0);
2647 if (fd < 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno, strerror(errno));
2648 else
2649 {
2650 // Avoid unnecessarily duplicating a file descriptor to itself
2651 if (fd != STDIN_FILENO) if (dup2(fd, STDIN_FILENO) < 0) LogMsg("dup2(fd, STDIN_FILENO) failed errno %d (%s)", errno, strerror(errno));
2652 if (fd != STDOUT_FILENO) if (dup2(fd, STDOUT_FILENO) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno, strerror(errno));
2653 if (fd != STDERR_FILENO) if (dup2(fd, STDERR_FILENO) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno, strerror(errno));
2654 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) (void)close(fd);
2655 }
2656 }
2657
2658 // Create the kqueue, mutex and thread to support KQSockets
2659 KQueueFD = kqueue();
2660 if (KQueueFD == -1) { LogMsg("kqueue() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2661
2662 i = pthread_mutex_init(&PlatformStorage.BigMutex, NULL);
2663 if (i == -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2664
2665 int fdpair[2] = {0, 0};
2666 i = socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair);
2667 if (i == -1) { LogMsg("socketpair() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2668
2669 // Socket pair returned us two identical sockets connected to each other
2670 // We will use the first socket to send the second socket. The second socket
2671 // will be added to the kqueue so it will wake when data is sent.
2672 static const KQueueEntry wakeKQEntry = { KQWokenFlushBytes, NULL, "kqueue wakeup after CFRunLoop event" };
2673 PlatformStorage.WakeKQueueLoopFD = fdpair[0];
2674 KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
2675
2676 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
2677 #if MDNS_NO_SANDBOX
2678 LogMsg("Note: Compiled without Apple Sandbox support");
2679 #else
2680 if (!sandbox_init)
2681 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
2682 else
2683 {
2684 char *sandbox_msg;
2685 int sandbox_err = sandbox_init("mDNSResponder", SANDBOX_NAMED, &sandbox_msg);
2686 if (sandbox_err) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg); sandbox_free_error(sandbox_msg); }
2687 else LogOperation("Now running under Apple Sandbox restrictions");
2688 }
2689 #endif
2690
2691 OSXVers = mDNSMacOSXSystemBuildNumber(NULL);
2692 status = mDNSDaemonInitialize();
2693 if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }
2694 status = udsserver_init(launchd_fd);
2695 if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
2696
2697 // Start the kqueue thread
2698 i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
2699 if (i == -1) { LogMsg("pthread_create() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
2700
2701 if (status == 0)
2702 {
2703 CFRunLoopRun();
2704 LogMsg("ERROR: CFRunLoopRun Exiting.");
2705 mDNS_Close(&mDNSStorage);
2706 }
2707
2708 LogMsgIdent(mDNSResponderVersionString, "exiting");
2709
2710 exit:
2711 if (!mDNS_DebugMode && !started_via_launchdaemon) destroyBootstrapService();
2712 return(status);
2713 }
2714
2715 // uds_daemon.c support routines /////////////////////////////////////////////
2716
2717 // Arrange things so that callback is called with context when data appears on fd
2718 mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
2719 {
2720 KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource);
2721 if (!newSource) return mStatus_NoMemoryErr;
2722
2723 mDNSPlatformMemZero(newSource, sizeof(*newSource));
2724 newSource->fd = fd;
2725 newSource->kqs.KQcallback = callback;
2726 newSource->kqs.KQcontext = context;
2727 newSource->kqs.KQtask = "UDS client";
2728
2729 if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
2730 {
2731 KQSocketEventSource **p = &gEventSources;
2732 while (*p) p = &(*p)->next;
2733 *p = newSource;
2734 return mStatus_NoError;
2735 }
2736 else
2737 {
2738 close(fd);
2739 free(newSource);
2740 return mStatus_NoMemoryErr;
2741 }
2742 }
2743
2744 mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor
2745 {
2746 KQSocketEventSource **p = &gEventSources;
2747 while (*p && (*p)->fd != fd) p = &(*p)->next;
2748 if (*p)
2749 {
2750 KQSocketEventSource *s = *p;
2751 *p = (*p)->next;
2752 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
2753 // causes the kernel to automatically remove any associated kevents
2754 close(s->fd);
2755 freeL("KQSocketEventSource", s);
2756 return mStatus_NoError;
2757 }
2758 return mStatus_NoSuchNameErr;
2759 }
2760
2761 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
2762 const char *__crashreporter_info__ = mDNSResponderVersionString;
2763 asm(".desc ___crashreporter_info__, 0x10");
2764
2765 // For convenience when using the "strings" command, this is the last thing in the file
2766 // The "@(#) " pattern is a special prefix the "what" command looks for
2767 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";