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