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