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