]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-214.3.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / daemon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Formatting notes:
18 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
19 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
20 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
21 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
22 * therefore common sense dictates that if they are part of a compound statement then they
23 * should be indented to the same level as everything else in that compound statement.
24 * Indenting curly braces at the same level as the "if" implies that curly braces are
25 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
26 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
27 * understand why variable y is not of type "char*" just proves the point that poor code
28 * layout leads people to unfortunate misunderstandings about how the C language really works.)
29
30 Change History (most recent first):
31
32 $Log: daemon.c,v $
33 Revision 1.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 extern mDNSBool StrictUnicastOrdering;
672
673 //*************************************************************************************************************
674 #if COMPILER_LIKES_PRAGMA_MARK
675 #pragma mark -
676 #pragma mark - Active client list structures
677 #endif
678
679 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
680 struct DNSServiceDomainEnumeration_struct
681 {
682 DNSServiceDomainEnumeration *next;
683 mach_port_t ClientMachPort;
684 DNSQuestion dom; // Question asking for domains
685 DNSQuestion def; // Question asking for default domain
686 };
687
688 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
689 struct DNSServiceBrowserResult_struct
690 {
691 DNSServiceBrowserResult *next;
692 int resultType;
693 domainname result;
694 };
695
696 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
697
698 typedef struct DNSServiceBrowserQuestion
699 {
700 struct DNSServiceBrowserQuestion *next;
701 DNSQuestion q;
702 domainname domain;
703 } DNSServiceBrowserQuestion;
704
705 struct DNSServiceBrowser_struct
706 {
707 DNSServiceBrowser *next;
708 mach_port_t ClientMachPort;
709 DNSServiceBrowserQuestion *qlist;
710 DNSServiceBrowserResult *results;
711 mDNSs32 lastsuccess;
712 mDNSBool DefaultDomain; // was the browse started on an explicit domain?
713 domainname type; // registration type
714 };
715
716 typedef struct DNSServiceResolver_struct DNSServiceResolver;
717 struct DNSServiceResolver_struct
718 {
719 DNSServiceResolver *next;
720 mach_port_t ClientMachPort;
721 ServiceInfoQuery q;
722 ServiceInfo i;
723 mDNSs32 ReportTime;
724 };
725
726 // A single registered service: ServiceRecordSet + bookkeeping
727 // Note that we duplicate some fields from parent DNSServiceRegistration object
728 // to facilitate cleanup, when instances and parent may be deallocated at different times.
729 typedef struct ServiceInstance
730 {
731 struct ServiceInstance *next;
732 mach_port_t ClientMachPort;
733 mDNSBool autoname; // Set if this name is tied to the Computer Name
734 mDNSBool renameonmemfree; // Set if we just got a name conflict and now need to automatically pick a new name
735 domainlabel name;
736 domainname domain;
737 ServiceRecordSet srs;
738 // Don't add any fields after ServiceRecordSet.
739 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
740 } ServiceInstance;
741
742 // A client-created service. May reference several ServiceInstance objects if default
743 // settings cause registration in multiple domains.
744 typedef struct DNSServiceRegistration
745 {
746 struct DNSServiceRegistration *next;
747 mach_port_t ClientMachPort;
748 mDNSBool DefaultDomain;
749 mDNSBool autoname;
750 size_t rdsize;
751 int NumSubTypes;
752 char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes
753 domainlabel name; // used only if autoname is false
754 domainname type;
755 mDNSIPPort port;
756 unsigned char txtinfo[1024];
757 size_t txt_len;
758 uint32_t NextRef;
759 ServiceInstance *regs;
760 } DNSServiceRegistration;
761
762 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
763 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
764 static DNSServiceResolver *DNSServiceResolverList = NULL;
765 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
766
767 // We keep a list of client-supplied event sources in KQSocketEventSource records
768 typedef struct KQSocketEventSource
769 {
770 struct KQSocketEventSource *next;
771 int fd;
772 KQueueEntry kqs;
773 } KQSocketEventSource;
774
775 static KQSocketEventSource *gEventSources;
776
777 //*************************************************************************************************************
778 #if COMPILER_LIKES_PRAGMA_MARK
779 #pragma mark -
780 #pragma mark - General Utility Functions
781 #endif
782
783 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
784
785 char _malloc_options[] = "AXZ";
786
787 mDNSexport void LogMemCorruption(const char *format, ...)
788 {
789 char buffer[512];
790 va_list ptr;
791 va_start(ptr,format);
792 buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
793 va_end(ptr);
794 LogMsg("!!!! %s !!!!", buffer);
795 NotifyOfElusiveBug("Memory Corruption", buffer);
796 #if ForceAlerts
797 *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want
798 #endif
799 }
800
801 mDNSlocal void validatelists(mDNS *const m)
802 {
803 // Check local lists
804 KQSocketEventSource *k;
805 for (k = gEventSources; k; k=k->next)
806 if (k->next == (KQSocketEventSource *)~0 || k->fd < 0)
807 LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd);
808
809 // Check Mach client lists
810 DNSServiceDomainEnumeration *e;
811 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
812 if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
813 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort);
814
815 DNSServiceBrowser *b;
816 for (b = DNSServiceBrowserList; b; b=b->next)
817 if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
818 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort);
819
820 DNSServiceResolver *l;
821 for (l = DNSServiceResolverList; l; l=l->next)
822 if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
823 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort);
824
825 DNSServiceRegistration *r;
826 for (r = DNSServiceRegistrationList; r; r=r->next)
827 if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
828 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort);
829
830 // Check Unix Domain Socket client lists (uds_daemon.c)
831 uds_validatelists();
832
833 // Check core mDNS lists
834 AuthRecord *rr;
835 for (rr = m->ResourceRecords; rr; rr=rr->next)
836 {
837 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
838 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
839 if (rr->resrec.name != &rr->namestorage)
840 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
841 rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c);
842 }
843
844 for (rr = m->DuplicateRecords; rr; rr=rr->next)
845 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
846 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
847
848 rr = m->NewLocalRecords;
849 if (rr)
850 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
851 LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType);
852
853 rr = m->CurrentRecord;
854 if (rr)
855 if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
856 LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType);
857
858 DNSQuestion *q;
859 for (q = m->Questions; q; q=q->next)
860 if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32)~0)
861 LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next);
862
863 CacheGroup *cg;
864 CacheRecord *cr;
865 mDNSu32 slot;
866 FORALL_CACHERECORDS(slot, cg, cr)
867 {
868 if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
869 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType);
870 if (cr->CRActiveQuestion)
871 {
872 for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break;
873 if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr));
874 }
875 }
876
877 // Check core uDNS lists
878 udns_validatelists(m);
879
880 // Check platform-layer lists
881 NetworkInterfaceInfoOSX *i;
882 for (i = m->p->InterfaceList; i; i = i->next)
883 if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0)
884 LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname);
885
886 ClientTunnel *t;
887 for (t = m->TunnelClients; t; t=t->next)
888 if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63)
889 LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]);
890 }
891
892 mDNSexport void *mallocL(char *msg, unsigned int size)
893 {
894 // Allocate space for two words of sanity checking data before the requested block
895 mDNSu32 *mem = malloc(sizeof(mDNSu32) * 2 + size);
896 if (!mem)
897 { LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); }
898 else
899 {
900 if (size > 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]);
901 else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
902 mem[0] = 0xDEAD1234;
903 mem[1] = size;
904 //mDNSPlatformMemZero(&mem[2], size);
905 memset(&mem[2], 0xFF, size);
906 validatelists(&mDNSStorage);
907 return(&mem[2]);
908 }
909 }
910
911 mDNSexport void freeL(char *msg, void *x)
912 {
913 if (!x)
914 LogMsg("free( %s @ NULL )!", msg);
915 else
916 {
917 mDNSu32 *mem = ((mDNSu32 *)x) - 2;
918 if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
919 if (mem[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]);
920 else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
921 //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]);
922 memset(mem, 0xFF, sizeof(mDNSu32) * 2 + mem[1]);
923 validatelists(&mDNSStorage);
924 free(mem);
925 }
926 }
927
928 #endif
929
930 //*************************************************************************************************************
931 #if COMPILER_LIKES_PRAGMA_MARK
932 #pragma mark -
933 #pragma mark - Mach client request handlers
934 #endif
935
936 //*************************************************************************************************************
937 // Client Death Detection
938
939 // This gets called after ALL constituent records of the Service Record Set have been deregistered
940 mDNSlocal void FreeServiceInstance(ServiceInstance *x)
941 {
942 ServiceRecordSet *s = &x->srs;
943 ExtraResourceRecord *e = x->srs.Extras, *tmp;
944
945 while (e)
946 {
947 e->r.RecordContext = e;
948 tmp = e;
949 e = e->next;
950 FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
951 }
952
953 if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
954 freeL("TXT RData", s->RR_TXT.resrec.rdata);
955
956 if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
957 freeL("ServiceInstance", x);
958 }
959
960 // AbortClient finds whatever client is identified by the given Mach port,
961 // stops whatever operation that client was doing, and frees its memory.
962 // In the case of a service registration, the actual freeing may be deferred
963 // until we get the mStatus_MemFree message, if necessary
964 mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
965 {
966 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
967 DNSServiceBrowser **b = &DNSServiceBrowserList;
968 DNSServiceResolver **l = &DNSServiceResolverList;
969 DNSServiceRegistration **r = &DNSServiceRegistrationList;
970
971 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
972 if (*e)
973 {
974 DNSServiceDomainEnumeration *x = *e;
975 *e = (*e)->next;
976 if (m && m != x)
977 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
978 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
979 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
980 mDNS_StopGetDomains(&mDNSStorage, &x->def);
981 freeL("DNSServiceDomainEnumeration", x);
982 return;
983 }
984
985 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
986 if (*b)
987 {
988 DNSServiceBrowser *x = *b;
989 DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
990 *b = (*b)->next;
991 while (qptr)
992 {
993 if (m && m != x)
994 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
995 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c);
996 mDNS_StopBrowse(&mDNSStorage, &qptr->q);
997 freePtr = qptr;
998 qptr = qptr->next;
999 freeL("DNSServiceBrowserQuestion", freePtr);
1000 }
1001 while (x->results)
1002 {
1003 DNSServiceBrowserResult *t = x->results;
1004 x->results = x->results->next;
1005 freeL("DNSServiceBrowserResult", t);
1006 }
1007 freeL("DNSServiceBrowser", x);
1008 return;
1009 }
1010
1011 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
1012 if (*l)
1013 {
1014 DNSServiceResolver *x = *l;
1015 *l = (*l)->next;
1016 if (m && m != x)
1017 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
1018 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c);
1019 mDNS_StopResolveService(&mDNSStorage, &x->q);
1020 freeL("DNSServiceResolver", x);
1021 return;
1022 }
1023
1024 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
1025 if (*r)
1026 {
1027 ServiceInstance *si = NULL;
1028 DNSServiceRegistration *x = *r;
1029 *r = (*r)->next;
1030
1031 si = x->regs;
1032 while (si)
1033 {
1034 ServiceInstance *instance = si;
1035 si = si->next;
1036 instance->renameonmemfree = mDNSfalse;
1037 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);
1038 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs));
1039
1040 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
1041 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
1042 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
1043 // the list, so we should go ahead and free the memory right now
1044 if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer
1045 }
1046 x->regs = NULL;
1047 freeL("DNSServiceRegistration", x);
1048 return;
1049 }
1050
1051 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
1052 }
1053
1054 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
1055
1056 mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
1057 {
1058 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
1059 DNSServiceBrowser *b = DNSServiceBrowserList;
1060 DNSServiceResolver *l = DNSServiceResolverList;
1061 DNSServiceRegistration *r = DNSServiceRegistrationList;
1062 DNSServiceBrowserQuestion *qptr;
1063
1064 while (e && e->ClientMachPort != c) e = e->next;
1065 while (b && b->ClientMachPort != c) b = b->next;
1066 while (l && l->ClientMachPort != c) l = l->next;
1067 while (r && r->ClientMachPort != c) r = r->next;
1068
1069 if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
1070 else if (b)
1071 {
1072 for (qptr = b->qlist; qptr; qptr = qptr->next)
1073 LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg);
1074 }
1075 else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
1076 else if (r)
1077 {
1078 ServiceInstance *si;
1079 for (si = r->regs; si; si = si->next)
1080 LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name->c, reason, msg);
1081 }
1082 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
1083
1084 AbortClient(c, m);
1085 }
1086
1087 mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
1088 {
1089 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
1090 DNSServiceBrowser *b = DNSServiceBrowserList;
1091 DNSServiceResolver *l = DNSServiceResolverList;
1092 DNSServiceRegistration *r = DNSServiceRegistrationList;
1093 DNSServiceBrowserQuestion *qptr;
1094
1095 while (e && e->ClientMachPort != c) e = e->next;
1096 while (b && b->ClientMachPort != c) b = b->next;
1097 while (l && l->ClientMachPort != c) l = l->next;
1098 while (r && r->ClientMachPort != c) r = r->next;
1099 if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
1100 if (b)
1101 {
1102 for (qptr = b->qlist; qptr; qptr = qptr->next)
1103 LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c);
1104 }
1105 if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
1106 if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL);
1107 return(e || b || l || r);
1108 }
1109
1110 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
1111 {
1112 KQueueLock(&mDNSStorage);
1113 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
1114 (void)unusedport; // Unused
1115 (void)size; // Unused
1116 (void)info; // Unused
1117 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
1118 {
1119 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
1120 AbortClient(deathMessage->not_port, NULL);
1121
1122 /* Deallocate the send right that came in the dead name notification */
1123 mach_port_destroy(mach_task_self(), deathMessage->not_port);
1124 }
1125 KQueueUnlock(&mDNSStorage, "Mach AbortClient");
1126 }
1127
1128 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
1129 {
1130 mach_port_t prev;
1131 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
1132 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
1133 // If the port already died while we were thinking about it, then abort the operation right away
1134 if (r != KERN_SUCCESS)
1135 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
1136 }
1137
1138 //*************************************************************************************************************
1139 // Domain Enumeration
1140
1141 mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
1142 {
1143 kern_return_t status;
1144 char buffer[MAX_ESCAPED_DOMAIN_NAME];
1145 DNSServiceDomainEnumerationReplyResultType rt;
1146 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
1147 (void)m; // Unused
1148
1149 debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
1150 if (answer->rrtype != kDNSType_PTR) return;
1151 if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
1152
1153 if (AddRecord)
1154 {
1155 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
1156 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
1157 }
1158 else
1159 {
1160 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
1161 else return;
1162 }
1163
1164 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
1165 x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
1166 !AddRecord ? "RemoveDomain" :
1167 question == &x->dom ? "AddDomain" : "AddDomainDefault");
1168
1169 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
1170 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
1171 if (status == MACH_SEND_TIMED_OUT)
1172 AbortBlockedClient(x->ClientMachPort, "enumeration", x);
1173 }
1174
1175 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1176 int regDom)
1177 {
1178 // Check client parameter
1179 (void)unusedserver; // Unused
1180 mStatus err = mStatus_NoError;
1181 const char *errormsg = "Unknown";
1182 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1183 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1184
1185 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
1186 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
1187
1188 // Allocate memory, and handle failure
1189 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
1190 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1191
1192 // Set up object, and link into list
1193 x->ClientMachPort = client;
1194 x->next = DNSServiceDomainEnumerationList;
1195 DNSServiceDomainEnumerationList = x;
1196
1197 verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
1198
1199 // Do the operation
1200 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
1201 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
1202 if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
1203
1204 // Succeeded: Wrap up and return
1205 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
1206 EnableDeathNotificationForClient(client, x);
1207 return(mStatus_NoError);
1208
1209 fail:
1210 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client, regDom, errormsg, err);
1211 return(err);
1212 }
1213
1214 //*************************************************************************************************************
1215 // Browse for services
1216
1217 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
1218 {
1219 (void)m; // Unused
1220
1221 if (answer->rrtype != kDNSType_PTR)
1222 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
1223
1224 domainlabel name;
1225 domainname type, domain;
1226 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
1227 {
1228 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
1229 answer->name->c, answer->rdata->u.name.c);
1230 return;
1231 }
1232
1233 DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
1234 if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
1235
1236 verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
1237 AssignDomainName(&x->result, &answer->rdata->u.name);
1238 if (AddRecord)
1239 x->resultType = DNSServiceBrowserReplyAddInstance;
1240 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
1241 x->next = NULL;
1242
1243 DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
1244 DNSServiceBrowserResult **p = &browser->results;
1245 while (*p) p = &(*p)->next;
1246 *p = x;
1247
1248 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
1249 browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
1250 }
1251
1252 mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d)
1253 {
1254 mStatus err = mStatus_NoError;
1255 DNSServiceBrowserQuestion *ptr, *question = NULL;
1256
1257 for (ptr = browser->qlist; ptr; ptr = ptr->next)
1258 {
1259 if (SameDomainName(&ptr->q.qname, d))
1260 { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; }
1261 }
1262
1263 question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
1264 if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; }
1265 AssignDomainName(&question->domain, d);
1266 question->next = browser->qlist;
1267 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
1268 err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser);
1269 if (!err)
1270 browser->qlist = question;
1271 else
1272 {
1273 LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err);
1274 freeL("DNSServiceBrowserQuestion", question);
1275 }
1276 return err;
1277 }
1278
1279 mDNSexport void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add)
1280 {
1281 DNSServiceBrowser *ptr;
1282 for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next)
1283 {
1284 if (ptr->DefaultDomain)
1285 {
1286 if (add)
1287 {
1288 mStatus err = AddDomainToBrowser(ptr, d);
1289 if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort);
1290 }
1291 else
1292 {
1293 DNSServiceBrowserQuestion **q = &ptr->qlist;
1294 while (*q)
1295 {
1296 if (SameDomainName(&(*q)->domain, d))
1297 {
1298 DNSServiceBrowserQuestion *rem = *q;
1299 *q = (*q)->next;
1300 mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
1301 freeL("DNSServiceBrowserQuestion", rem);
1302 return;
1303 }
1304 q = &(*q)->next;
1305 }
1306 LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort);
1307 }
1308 }
1309 }
1310 }
1311
1312 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1313 DNSCString regtype, DNSCString domain)
1314 {
1315 // Check client parameter
1316 (void)unusedserver; // Unused
1317 mStatus err = mStatus_NoError;
1318 const char *errormsg = "Unknown";
1319
1320 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1321 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1322
1323 // Check other parameters
1324 domainname t, d;
1325 t.c[0] = 0;
1326 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1327 if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; }
1328 if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
1329 { errormsg = "Bad Service SubType"; goto badparam; }
1330 if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
1331 domainname temp;
1332 if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
1333 if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local"
1334
1335 // Allocate memory, and handle failure
1336 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
1337 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1338
1339 // Set up object, and link into list
1340 AssignDomainName(&x->type, &t);
1341 x->ClientMachPort = client;
1342 x->results = NULL;
1343 x->lastsuccess = 0;
1344 x->qlist = NULL;
1345 x->next = DNSServiceBrowserList;
1346 DNSServiceBrowserList = x;
1347
1348 if (domain[0])
1349 {
1350 // Start browser for an explicit domain
1351 x->DefaultDomain = mDNSfalse;
1352 if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; }
1353 err = AddDomainToBrowser(x, &d);
1354 if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
1355 }
1356 else
1357 {
1358 DNameListElem *sdPtr;
1359 // Start browser on all domains
1360 x->DefaultDomain = mDNStrue;
1361 if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
1362 for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next)
1363 {
1364 err = AddDomainToBrowser(x, &sdPtr->name);
1365 if (err)
1366 {
1367 // only terminally bail if .local fails
1368 if (!SameDomainName(&localdomain, &sdPtr->name))
1369 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c);
1370 else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
1371 }
1372 }
1373 }
1374
1375 // Succeeded: Wrap up and return
1376 EnableDeathNotificationForClient(client, x);
1377 return(mStatus_NoError);
1378
1379 badparam:
1380 err = mStatus_BadParamErr;
1381 fail:
1382 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client, regtype, domain, errormsg, err);
1383 return(err);
1384 }
1385
1386 //*************************************************************************************************************
1387 // Resolve Service Info
1388
1389 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
1390 {
1391 kern_return_t status;
1392 DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
1393 NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID);
1394 if (query->info->InterfaceID == mDNSInterface_LocalOnly) ifx = mDNSNULL;
1395 struct sockaddr_storage interface;
1396 struct sockaddr_storage address;
1397 char cstring[1024];
1398 int i, pstrlen = query->info->TXTinfo[0];
1399 (void)m; // Unused
1400
1401 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
1402
1403 if (query->info->TXTlen > sizeof(cstring)) return;
1404
1405 mDNSPlatformMemZero(&interface, sizeof(interface));
1406 mDNSPlatformMemZero(&address, sizeof(address));
1407
1408 if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
1409 {
1410 struct sockaddr_in *s = (struct sockaddr_in*)&interface;
1411 s->sin_len = sizeof(*s);
1412 s->sin_family = AF_INET;
1413 s->sin_port = 0;
1414 s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
1415 }
1416 else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
1417 {
1418 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
1419 sin6->sin6_len = sizeof(*sin6);
1420 sin6->sin6_family = AF_INET6;
1421 sin6->sin6_flowinfo = 0;
1422 sin6->sin6_port = 0;
1423 sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
1424 sin6->sin6_scope_id = ifx->scope_id;
1425 }
1426
1427 if (query->info->ip.type == mDNSAddrType_IPv4)
1428 {
1429 struct sockaddr_in *s = (struct sockaddr_in*)&address;
1430 s->sin_len = sizeof(*s);
1431 s->sin_family = AF_INET;
1432 s->sin_port = query->info->port.NotAnInteger;
1433 s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
1434 }
1435 else
1436 {
1437 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
1438 sin6->sin6_len = sizeof(*sin6);
1439 sin6->sin6_family = AF_INET6;
1440 sin6->sin6_port = query->info->port.NotAnInteger;
1441 sin6->sin6_flowinfo = 0;
1442 sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
1443 sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
1444 }
1445
1446 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
1447 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
1448 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
1449 // ASCII-1 characters are used in the C-string as boundary markers,
1450 // to indicate the boundaries between the original constituent P-strings.
1451 for (i=1; i<query->info->TXTlen; i++)
1452 {
1453 if (--pstrlen >= 0)
1454 cstring[i-1] = query->info->TXTinfo[i];
1455 else
1456 {
1457 cstring[i-1] = 1;
1458 pstrlen = query->info->TXTinfo[i];
1459 }
1460 }
1461 cstring[i-1] = 0; // Put the terminating NULL on the end
1462
1463 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
1464 x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
1465 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
1466 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
1467 if (status == MACH_SEND_TIMED_OUT)
1468 AbortBlockedClient(x->ClientMachPort, "resolve", x);
1469 }
1470
1471 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
1472 DNSCString name, DNSCString regtype, DNSCString domain)
1473 {
1474 // Check client parameter
1475 (void)unusedserver; // Unused
1476 mStatus err = mStatus_NoError;
1477 const char *errormsg = "Unknown";
1478 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1479 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1480
1481 // Check other parameters
1482 domainlabel n;
1483 domainname t, d, srv;
1484 if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1485 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1486 if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
1487 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1488
1489 // Allocate memory, and handle failure
1490 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
1491 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1492
1493 // Set up object, and link into list
1494 x->ClientMachPort = client;
1495 x->i.InterfaceID = mDNSInterface_Any;
1496 x->i.name = srv;
1497 x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
1498 x->next = DNSServiceResolverList;
1499 DNSServiceResolverList = x;
1500
1501 // Do the operation
1502 LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c);
1503 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
1504 if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
1505
1506 // Succeeded: Wrap up and return
1507 EnableDeathNotificationForClient(client, x);
1508 return(mStatus_NoError);
1509
1510 badparam:
1511 err = mStatus_BadParamErr;
1512 fail:
1513 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err);
1514 return(err);
1515 }
1516
1517 //*************************************************************************************************************
1518 // Registration
1519
1520 mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
1521 {
1522 m->p->NotifyUser = NonZeroTime(m->timenow + delay);
1523 }
1524
1525 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
1526 {
1527 ServiceInstance *si = (ServiceInstance*)srs->ServiceContext;
1528
1529 if (result == mStatus_NoError)
1530 {
1531 kern_return_t status;
1532 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1533 status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1534 if (status == MACH_SEND_TIMED_OUT)
1535 AbortBlockedClient(si->ClientMachPort, "registration success", si);
1536 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1537 RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
1538 }
1539
1540 else if (result == mStatus_NameConflict)
1541 {
1542 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1543 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1544 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1545 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1546 {
1547 // On conflict for an autoname service, rename and reregister *all* autoname services
1548 IncrementLabelSuffix(&m->nicelabel, mDNStrue);
1549 mDNS_ConfigChanged(m);
1550 }
1551 else if (si->autoname)
1552 {
1553 mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
1554 return;
1555 }
1556 else
1557 {
1558 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1559 // of their registration in the usual way (which we will catch via client death notification).
1560 // If the Mach queue is full, we forcibly abort the client immediately.
1561 kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1562 if (status == MACH_SEND_TIMED_OUT)
1563 AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL);
1564 }
1565 }
1566
1567 else if (result == mStatus_MemFree)
1568 {
1569 if (si->renameonmemfree) // We intentionally terminated registration so we could re-register with new name
1570 {
1571 debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c);
1572 si->renameonmemfree = mDNSfalse;
1573 si->name = m->nicelabel;
1574 mDNS_RenameAndReregisterService(m, srs, &si->name);
1575 }
1576 else
1577 {
1578 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1579 DNSServiceRegistration *r;
1580 for (r = DNSServiceRegistrationList; r; r = r->next)
1581 {
1582 ServiceInstance **sp = &r->regs;
1583 while (*sp)
1584 {
1585 if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; }
1586 sp = &(*sp)->next;
1587 }
1588 }
1589 // END SANITY CHECK
1590 FreeServiceInstance(si);
1591 }
1592 }
1593
1594 else if (result != mStatus_NATTraversal)
1595 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result);
1596 }
1597
1598 mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain)
1599 {
1600 mStatus err = 0;
1601 ServiceInstance *si = NULL;
1602 AuthRecord *SubTypes = NULL;
1603
1604 for (si = x->regs; si; si = si->next)
1605 {
1606 if (SameDomainName(&si->domain, domain))
1607 { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
1608 }
1609
1610 SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype);
1611 if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
1612
1613 si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
1614 if (!si) return mStatus_NoMemoryErr;
1615
1616 si->ClientMachPort = x->ClientMachPort;
1617 si->renameonmemfree = mDNSfalse;
1618 si->autoname = x->autoname;
1619 si->name = x->autoname ? mDNSStorage.nicelabel : x->name;
1620 si->domain = *domain;
1621
1622 err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL,
1623 x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si);
1624 if (!err)
1625 {
1626 si->next = x->regs;
1627 x->regs = si;
1628 }
1629 else
1630 {
1631 LogMsg("Error %d for registration of service in domain %##s", err, domain->c);
1632 freeL("ServiceInstance", si);
1633 }
1634 return err;
1635 }
1636
1637 mDNSexport void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add)
1638 {
1639 DNSServiceRegistration *reg;
1640
1641 for (reg = DNSServiceRegistrationList; reg; reg = reg->next)
1642 {
1643 if (reg->DefaultDomain)
1644 {
1645 if (add)
1646 AddServiceInstance(reg, d);
1647 else
1648 {
1649 ServiceInstance **si = &reg->regs;
1650 while (*si)
1651 {
1652 if (SameDomainName(&(*si)->domain, d))
1653 {
1654 ServiceInstance *s = *si;
1655 *si = (*si)->next;
1656 if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s); // only free memory synchronously on error
1657 break;
1658 }
1659 si = &(*si)->next;
1660 }
1661 if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed
1662 }
1663 }
1664 }
1665 }
1666
1667 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1668 DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord)
1669 {
1670 (void)unusedserver; // Unused
1671 mStatus err = mStatus_NoError;
1672 const char *errormsg = "Unknown";
1673
1674 // older versions of this code passed the port via mach IPC as an int.
1675 // we continue to pass it as 4 bytes to maintain binary compatibility,
1676 // but now ensure that the network byte order is preserved by using a struct
1677 mDNSIPPort port;
1678 port.b[0] = IpPort.bytes[2];
1679 port.b[1] = IpPort.bytes[3];
1680
1681 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1682 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1683
1684 // Check for sub-types after the service type
1685 size_t reglen = strlen(regtype) + 1;
1686 if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
1687 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1688 if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }
1689
1690 // Check other parameters
1691 domainlabel n;
1692 domainname t, d;
1693 domainname srv;
1694 if (!name[0]) n = mDNSStorage.nicelabel;
1695 else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1696 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1697 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
1698 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1699
1700 unsigned char txtinfo[1024] = "";
1701 unsigned int data_len = 0;
1702 unsigned int size = sizeof(RDataBody);
1703 unsigned char *pstring = &txtinfo[data_len];
1704 char *ptr = txtRecord;
1705
1706 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1707 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1708 // Hence we have to convert the C-string to a P-string.
1709 // ASCII-1 characters are allowed in the C-string as boundary markers,
1710 // so that a single C-string can be used to represent one or more P-strings.
1711 while (*ptr)
1712 {
1713 if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
1714 if (*ptr == 1) // If this is our boundary marker, start a new P-string
1715 {
1716 pstring = &txtinfo[data_len];
1717 pstring[0] = 0;
1718 ptr++;
1719 }
1720 else
1721 {
1722 if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
1723 pstring[++pstring[0]] = *ptr++;
1724 }
1725 }
1726
1727 data_len++;
1728 if (size < data_len)
1729 size = data_len;
1730
1731 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1732 // a port number of zero. When two instances of the protected client are allowed to run on one
1733 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1734 if (!mDNSIPPortIsZero(port))
1735 {
1736 int count = CountExistingRegistrations(&srv, port);
1737 if (count)
1738 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1739 client, count+1, srv.c, mDNSVal16(port));
1740 }
1741
1742 // Allocate memory, and handle failure
1743 DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x));
1744 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1745 mDNSPlatformMemZero(x, sizeof(*x));
1746
1747 // Set up object, and link into list
1748 x->ClientMachPort = client;
1749 x->DefaultDomain = !domain[0];
1750 x->autoname = (!name[0]);
1751 x->rdsize = size;
1752 x->NumSubTypes = NumSubTypes;
1753 memcpy(x->regtype, regtype, reglen);
1754 x->name = n;
1755 x->type = t;
1756 x->port = port;
1757 memcpy(x->txtinfo, txtinfo, 1024);
1758 x->txt_len = data_len;
1759 x->NextRef = 0;
1760 x->regs = NULL;
1761
1762 x->next = DNSServiceRegistrationList;
1763 DNSServiceRegistrationList = x;
1764
1765 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1766 x->ClientMachPort, name, regtype, domain, mDNSVal16(port));
1767
1768 err = AddServiceInstance(x, &d);
1769 if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails
1770
1771 if (x->DefaultDomain)
1772 {
1773 DNameListElem *p;
1774 for (p = AutoRegistrationDomains; p; p = p->next)
1775 AddServiceInstance(x, &p->name);
1776 }
1777
1778 // Succeeded: Wrap up and return
1779 EnableDeathNotificationForClient(client, x);
1780 return(mStatus_NoError);
1781
1782 badtxt:
1783 LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
1784 badparam:
1785 err = mStatus_BadParamErr;
1786 fail:
1787 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)",
1788 client, name, regtype, domain, mDNSVal16(port), errormsg, err);
1789 return(err);
1790 }
1791
1792 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
1793 {
1794 (void)m; // Unused
1795 if (result == mStatus_NoError)
1796 {
1797 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
1798 LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
1799 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1800 RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond);
1801 }
1802 else if (result == mStatus_NameConflict)
1803 {
1804 LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c);
1805 if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow);
1806 else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond)
1807 {
1808 // Tell the helper we've given up
1809 mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, NULL);
1810 }
1811 }
1812 else if (result == mStatus_GrowCache)
1813 {
1814 // Allocate another chunk of cache storage
1815 CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE);
1816 //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
1817 if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
1818 }
1819 else if (result == mStatus_ConfigChanged)
1820 {
1821 // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed.
1822 mDNSPreferencesSetName(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
1823 mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
1824
1825 // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
1826 DNSServiceRegistration *r;
1827 for (r = DNSServiceRegistrationList; r; r=r->next)
1828 if (r->autoname)
1829 {
1830 ServiceInstance *si;
1831 for (si = r->regs; si; si = si->next)
1832 {
1833 if (!SameDomainLabelCS(si->name.c, m->nicelabel.c))
1834 {
1835 debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c);
1836 si->renameonmemfree = mDNStrue;
1837 if (mDNS_DeregisterService(m, &si->srs)) // If service deregistered already, we can re-register immediately
1838 RegCallback(m, &si->srs, mStatus_MemFree);
1839 }
1840 }
1841 }
1842
1843 // Then we call into the UDS daemon code, to let it do the same
1844 udsserver_handle_configchange(m);
1845 }
1846 }
1847
1848 //*************************************************************************************************************
1849 // Add / Update / Remove records from existing Registration
1850
1851 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1852 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
1853 {
1854 // Check client parameter
1855 uint32_t id;
1856 mStatus err = mStatus_NoError;
1857 const char *errormsg = "Unknown";
1858 DNSServiceRegistration *x = DNSServiceRegistrationList;
1859 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1860 ServiceInstance *si;
1861 size_t size;
1862 (void)unusedserver; // Unused
1863 while (x && x->ClientMachPort != client) x = x->next;
1864 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1865
1866 // Check other parameters
1867 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1868 if (data_len > sizeof(RDataBody)) size = data_len;
1869 else size = sizeof(RDataBody);
1870
1871 id = x->NextRef++;
1872 *reference = (natural_t)id;
1873 for (si = x->regs; si; si = si->next)
1874 {
1875 // Allocate memory, and handle failure
1876 ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
1877 if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1878
1879 // Fill in type, length, and data of new record
1880 extra->r.resrec.rrtype = type;
1881 extra->r.rdatastorage.MaxRDLength = size;
1882 extra->r.resrec.rdlength = data_len;
1883 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
1884
1885 // Do the operation
1886 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1887 client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
1888 err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl);
1889
1890 if (err)
1891 {
1892 freeL("Extra Resource Record", extra);
1893 errormsg = "mDNS_AddRecordToService";
1894 goto fail;
1895 }
1896
1897 extra->ClientID = id;
1898 }
1899
1900 return mStatus_NoError;
1901
1902 fail:
1903 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client, x ? x->name.c : (mDNSu8*)"\x8""«NULL»", type, data_len, errormsg, err);
1904 return mStatus_UnknownErr;
1905 }
1906
1907 mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
1908 {
1909 (void)m; // Unused
1910 if (OldRData != &rr->rdatastorage)
1911 freeL("Old RData", OldRData);
1912 }
1913
1914 mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1915 {
1916 // Check client parameter
1917 mStatus err = mStatus_NoError;
1918 const char *errormsg = "Unknown";
1919 const domainname *name = (const domainname *)"";
1920
1921 name = srs->RR_SRV.resrec.name;
1922
1923 unsigned int size = sizeof(RDataBody);
1924 if (size < data_len)
1925 size = data_len;
1926
1927 // Allocate memory, and handle failure
1928 RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
1929 if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1930
1931 // Fill in new length, and data
1932 newrdata->MaxRDLength = size;
1933 memcpy(&newrdata->u, data, data_len);
1934
1935 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1936 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1937 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1938 if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; }
1939
1940 // Do the operation
1941 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
1942 client, srs->RR_SRV.resrec.name->c, data_len);
1943
1944 err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
1945 if (err)
1946 {
1947 errormsg = "mDNS_Update";
1948 freeL("RData", newrdata);
1949 return err;
1950 }
1951 return(mStatus_NoError);
1952
1953 fail:
1954 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client, name->c, data_len, errormsg, err);
1955 return(err);
1956 }
1957
1958 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1959 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1960 {
1961 // Check client parameter
1962 mStatus err = mStatus_NoError;
1963 const char *errormsg = "Unknown";
1964 const domainname *name = (const domainname *)"";
1965 ServiceInstance *si;
1966
1967 (void)unusedserver; // unused
1968 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1969 DNSServiceRegistration *x = DNSServiceRegistrationList;
1970 while (x && x->ClientMachPort != client) x = x->next;
1971 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1972
1973 // Check other parameters
1974 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1975
1976 for (si = x->regs; si; si = si->next)
1977 {
1978 AuthRecord *r = NULL;
1979
1980 // Find the record we're updating. NULL reference means update the primary TXT record
1981 if (!reference) r = &si->srs.RR_TXT;
1982 else
1983 {
1984 ExtraResourceRecord *ptr;
1985 for (ptr = si->srs.Extras; ptr; ptr = ptr->next)
1986 {
1987 if ((natural_t)ptr->ClientID == reference)
1988 { r = &ptr->r; break; }
1989 }
1990 if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
1991 }
1992 err = UpdateRecord(&si->srs, client, r, data, data_len, ttl);
1993 if (err) goto fail; //!!!KRS this will cause failures for non-local defaults!
1994 }
1995
1996 return mStatus_NoError;
1997
1998 fail:
1999 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client, name->c, reference, data_len, errormsg, err);
2000 return(err);
2001 }
2002
2003 mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
2004 {
2005 const domainname *const name = srs->RR_SRV.resrec.name;
2006 mStatus err = mStatus_NoError;
2007
2008 // Do the operation
2009 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c);
2010
2011 err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra);
2012 if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err);
2013
2014 return err;
2015 }
2016
2017 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
2018 natural_t reference)
2019 {
2020 // Check client parameter
2021 (void)unusedserver; // Unused
2022 mStatus err = mStatus_NoError;
2023 const char *errormsg = "Unknown";
2024 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
2025 DNSServiceRegistration *x = DNSServiceRegistrationList;
2026 ServiceInstance *si;
2027
2028 while (x && x->ClientMachPort != client) x = x->next;
2029 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
2030
2031 for (si = x->regs; si; si = si->next)
2032 {
2033 ExtraResourceRecord *e;
2034 for (e = si->srs.Extras; e; e = e->next)
2035 {
2036 if ((natural_t)e->ClientID == reference)
2037 {
2038 err = RemoveRecord(&si->srs, e, client);
2039 break;
2040 }
2041 }
2042 if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
2043 }
2044
2045 return mStatus_NoError;
2046
2047 fail:
2048 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err);
2049 return(err);
2050 }
2051
2052 //*************************************************************************************************************
2053 #if COMPILER_LIKES_PRAGMA_MARK
2054 #pragma mark -
2055 #pragma mark - Startup, shutdown, and supporting code
2056 #endif
2057
2058 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
2059 {
2060 mig_reply_error_t *request = msg;
2061 mig_reply_error_t *reply;
2062 mach_msg_return_t mr;
2063 int options;
2064 (void)port; // Unused
2065 (void)size; // Unused
2066 (void)info; // Unused
2067
2068 KQueueLock(&mDNSStorage);
2069
2070 /* allocate a reply buffer */
2071 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
2072
2073 /* call the MiG server routine */
2074 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
2075
2076 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
2077 {
2078 if (reply->RetCode == MIG_NO_REPLY)
2079 {
2080 /*
2081 * This return code is a little tricky -- it appears that the
2082 * demux routine found an error of some sort, but since that
2083 * error would not normally get returned either to the local
2084 * user or the remote one, we pretend it's ok.
2085 */
2086 CFAllocatorDeallocate(NULL, reply);
2087 goto done;
2088 }
2089
2090 /*
2091 * destroy any out-of-line data in the request buffer but don't destroy
2092 * the reply port right (since we need that to send an error message).
2093 */
2094 request->Head.msgh_remote_port = MACH_PORT_NULL;
2095 mach_msg_destroy(&request->Head);
2096 }
2097
2098 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
2099 {
2100 /* no reply port, so destroy the reply */
2101 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
2102 mach_msg_destroy(&reply->Head);
2103 CFAllocatorDeallocate(NULL, reply);
2104 goto done;
2105 }
2106
2107 /*
2108 * send reply.
2109 *
2110 * We don't want to block indefinitely because the client
2111 * isn't receiving messages from the reply port.
2112 * If we have a send-once right for the reply port, then
2113 * this isn't a concern because the send won't block.
2114 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
2115 * To avoid falling off the kernel's fast RPC path unnecessarily,
2116 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
2117 */
2118
2119 options = MACH_SEND_MSG;
2120 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
2121 options |= MACH_SEND_TIMEOUT;
2122
2123 mr = mach_msg(&reply->Head, /* msg */
2124 options, /* option */
2125 reply->Head.msgh_size, /* send_size */
2126 0, /* rcv_size */
2127 MACH_PORT_NULL, /* rcv_name */
2128 MACH_MSG_TIMEOUT_NONE, /* timeout */
2129 MACH_PORT_NULL); /* notify */
2130
2131 /* Has a message error occurred? */
2132 switch (mr)
2133 {
2134 case MACH_SEND_INVALID_DEST:
2135 case MACH_SEND_TIMED_OUT:
2136 /* the reply can't be delivered, so destroy it */
2137 mach_msg_destroy(&reply->Head);
2138 break;
2139
2140 default :
2141 /* Includes success case. */
2142 break;
2143 }
2144
2145 CFAllocatorDeallocate(NULL, reply);
2146
2147 done:
2148 KQueueUnlock(&mDNSStorage, "Mach client event");
2149 }
2150
2151 mDNSlocal kern_return_t registerBootstrapService()
2152 {
2153 kern_return_t status;
2154 mach_port_t service_send_port, service_rcv_port;
2155
2156 debugf("Registering Bootstrap Service");
2157
2158 /*
2159 * See if our service name is already registered and if we have privilege to check in.
2160 */
2161 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
2162 if (status == KERN_SUCCESS)
2163 {
2164 /*
2165 * If so, we must be a followup instance of an already defined server. In that case,
2166 * the bootstrap port we inherited from our parent is the server's privilege port, so set
2167 * that in case we have to unregister later (which requires the privilege port).
2168 */
2169 server_priv_port = bootstrap_port;
2170 restarting_via_mach_init = TRUE;
2171 }
2172 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
2173 {
2174 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
2175 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
2176 if (status != KERN_SUCCESS) return status;
2177
2178 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
2179 if (status != KERN_SUCCESS)
2180 {
2181 mach_port_deallocate(mach_task_self(), server_priv_port);
2182 return status;
2183 }
2184
2185 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
2186 if (status != KERN_SUCCESS)
2187 {
2188 mach_port_deallocate(mach_task_self(), server_priv_port);
2189 mach_port_deallocate(mach_task_self(), service_send_port);
2190 return status;
2191 }
2192 assert(service_send_port == service_rcv_port);
2193 }
2194
2195 /*
2196 * We have no intention of responding to requests on the service port. We are not otherwise
2197 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
2198 * So, we can dispose of all the rights we have for the service port. We don't destroy the
2199 * send right for the server's privileged bootstrap port - in case we have to unregister later.
2200 */
2201 mach_port_destroy(mach_task_self(), service_rcv_port);
2202 return status;
2203 }
2204
2205 mDNSlocal kern_return_t destroyBootstrapService()
2206 {
2207 debugf("Destroying Bootstrap Service");
2208 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
2209 }
2210
2211 mDNSlocal void ExitCallback(int sig)
2212 {
2213 (void)sig; // Unused
2214 LogMsg("%s stopping", mDNSResponderVersionString);
2215
2216 debugf("ExitCallback");
2217 if (!mDNS_DebugMode && !started_via_launchdaemon)
2218 destroyBootstrapService();
2219
2220 debugf("ExitCallback: Aborting MIG clients");
2221 while (DNSServiceDomainEnumerationList)
2222 AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
2223 while (DNSServiceBrowserList)
2224 AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
2225 while (DNSServiceResolverList)
2226 AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
2227 while (DNSServiceRegistrationList)
2228 AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
2229
2230 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
2231
2232 debugf("ExitCallback: mDNS_StartExit");
2233 mDNS_StartExit(&mDNSStorage);
2234 }
2235
2236 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
2237 mDNSlocal void HandleSIG(int sig)
2238 {
2239 // WARNING: can't call syslog or fprintf from signal handler
2240 mach_msg_header_t header;
2241 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
2242 header.msgh_remote_port = signal_port;
2243 header.msgh_local_port = MACH_PORT_NULL;
2244 header.msgh_size = sizeof(header);
2245 header.msgh_id = sig;
2246 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
2247 if (sig == SIGTERM || sig == SIGINT) exit(-1);
2248 }
2249
2250 mDNSlocal void CatchABRT(int sig)
2251 {
2252 // WARNING: can't call syslog or fprintf from signal handler
2253 // We want a CrashReporter stack trace so we can find out what library called abort()
2254 // So that we will crash, unblock all signals (that abort() may have blocked)
2255 sigset_t mask;
2256 sigfillset(&mask);
2257 sigprocmask(SIG_UNBLOCK, &mask, NULL);
2258 (void)sig;
2259 while(1) *(long*)0 = 0;
2260 }
2261
2262 mDNSlocal void INFOCallback(void)
2263 {
2264 mDNSs32 utc = mDNSPlatformUTC();
2265 DNSServiceDomainEnumeration *e;
2266 DNSServiceBrowser *b;
2267 DNSServiceResolver *l;
2268 DNSServiceRegistration *r;
2269 NetworkInterfaceInfoOSX *i;
2270 DNSServer *s;
2271
2272 LogMsg("---- BEGIN STATE LOG ----");
2273
2274 udsserver_info(&mDNSStorage);
2275
2276 LogMsgNoIdent("--------- Mach Clients ---------");
2277 if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList)
2278 LogMsgNoIdent("<None>");
2279 else
2280 {
2281 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
2282 LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
2283
2284 for (b = DNSServiceBrowserList; b; b=b->next)
2285 {
2286 DNSServiceBrowserQuestion *qptr;
2287 for (qptr = b->qlist; qptr; qptr = qptr->next)
2288 LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c);
2289 }
2290 for (l = DNSServiceResolverList; l; l=l->next)
2291 LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
2292
2293 for (r = DNSServiceRegistrationList; r; r=r->next)
2294 {
2295 ServiceInstance *si;
2296 for (si = r->regs; si; si = si->next)
2297 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));
2298 }
2299 }
2300
2301 LogMsgNoIdent("----- KQSocketEventSources -----");
2302 if (!gEventSources) LogMsgNoIdent("<None>");
2303 else
2304 {
2305 KQSocketEventSource *k;
2306 for (k = gEventSources; k; k=k->next)
2307 LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask);
2308 }
2309
2310 LogMsgNoIdent("------ Network Interfaces ------");
2311 if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent("<None>");
2312 else
2313 {
2314 for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
2315 {
2316 // Allow six characters for interface name, for names like "vmnet8"
2317 if (!i->Exists)
2318 LogMsgNoIdent("%p %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
2319 i->ifinfo.InterfaceID,
2320 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
2321 &i->ifinfo.ip, utc - i->LastSeen);
2322 else
2323 {
2324 const CacheRecord *sps[3];
2325 FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps);
2326 LogMsgNoIdent("%p %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
2327 i->ifinfo.InterfaceID,
2328 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
2329 i->ifinfo.InterfaceActive ? "Active" : " ",
2330 i->ifinfo.IPv4Available ? "v4" : " ",
2331 i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr,
2332 i->ifinfo.IPv6Available ? "v6" : " ",
2333 i->ifinfo.Advertise ? "⊙" : " ",
2334 i->ifinfo.McastTxRx ? "⇆" : " ",
2335 !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀",
2336 &i->ifinfo.ip);
2337
2338 if (sps[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c);
2339 if (sps[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c);
2340 if (sps[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c);
2341 }
2342 }
2343 }
2344
2345 LogMsgNoIdent("--------- DNS Servers ----------");
2346 if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
2347 else
2348 {
2349 for (s = mDNSStorage.DNSServers; s; s = s->next)
2350 {
2351 NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface);
2352 LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s",
2353 s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
2354 s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0,
2355 s->teststate == DNSServer_Untested ? "(Untested)" :
2356 s->teststate == DNSServer_Passed ? "" :
2357 s->teststate == DNSServer_Failed ? "(Failed)" :
2358 s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)");
2359 }
2360 }
2361
2362 mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
2363 LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
2364
2365 LogMsg("---- END STATE LOG ----");
2366 }
2367
2368 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
2369 {
2370 (void)port; // Unused
2371 (void)size; // Unused
2372 (void)info; // Unused
2373 mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
2374 mDNS *const m = &mDNSStorage;
2375
2376 // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
2377 KQueueLock(m);
2378 switch(msg_header->msgh_id)
2379 {
2380 case SIGHUP: {
2381 mDNSu32 slot;
2382 CacheGroup *cg;
2383 CacheRecord *rr;
2384 LogMsg("SIGHUP: Purge cache");
2385 mDNS_Lock(m);
2386 FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
2387 // Restart unicast and multicast queries
2388 mDNSCoreRestartQueries(m);
2389 mDNS_Unlock(m);
2390 } break;
2391 case SIGINT:
2392 case SIGTERM: ExitCallback(msg_header->msgh_id); break;
2393 case SIGINFO: INFOCallback(); break;
2394 case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
2395 LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
2396 WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
2397 break;
2398 case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
2399 LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
2400 break;
2401 default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
2402 }
2403 KQueueUnlock(m, "Unix Signal");
2404 }
2405
2406 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2407 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2408
2409 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
2410 {
2411 mStatus err;
2412 CFMachPortRef s_port;
2413
2414 // If launchd already created our Mach port for us, then use that, else we create a new one of our own
2415 if (m_port != MACH_PORT_NULL)
2416 s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
2417 else
2418 {
2419 s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
2420 m_port = CFMachPortGetPort(s_port);
2421 char *MachServerName = OSXVers < OSXVers_10_3_Panther ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
2422 kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
2423
2424 if (status)
2425 {
2426 if (status == 1103)
2427 LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
2428 else
2429 LogMsg("bootstrap_register() failed: %s %d", mach_error_string(status), status);
2430 return(status);
2431 }
2432 }
2433
2434 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
2435 CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL);
2436 CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
2437 CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
2438 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
2439
2440 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
2441 rrcachestorage, RR_CACHE_SIZE,
2442 advertise,
2443 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
2444
2445 if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); }
2446
2447 client_death_port = CFMachPortGetPort(d_port);
2448 signal_port = CFMachPortGetPort(i_port);
2449
2450 CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode);
2451 CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode);
2452 CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode);
2453 CFRelease(d_rls);
2454 CFRelease(s_rls);
2455 CFRelease(i_rls);
2456 if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
2457 return(err);
2458 }
2459
2460 mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
2461 {
2462 mDNSs32 now = mDNS_TimeNow(m);
2463
2464 // 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
2465 // Detailed reason:
2466 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2467 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2468 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2469 // we then systematically lose our own looped-back packets.
2470 if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);
2471
2472 if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); }
2473
2474 // KeyChain frequently fails to notify clients of change events. To work around this
2475 // we set a timer and periodically poll to detect if any changes have occurred.
2476 // Without this Back To My Mac just does't work for a large number of users.
2477 // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
2478 if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
2479 {
2480 m->p->KeyChainBugInterval *= 2;
2481 m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
2482 if (m->p->KeyChainBugInterval > 2 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
2483 mDNS_Lock(m);
2484 SetDomainSecrets(m);
2485 mDNS_Unlock(m);
2486 }
2487
2488 // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
2489 mDNSs32 nextevent = mDNS_Execute(m);
2490
2491 if (m->p->NetworkChanged)
2492 if (nextevent - m->p->NetworkChanged > 0)
2493 nextevent = m->p->NetworkChanged;
2494
2495 if (m->p->KeyChainBugTimer)
2496 if (nextevent - m->p->KeyChainBugTimer > 0)
2497 nextevent = m->p->KeyChainBugTimer;
2498
2499 if (m->p->RequestReSleep)
2500 if (nextevent - m->p->RequestReSleep > 0)
2501 nextevent = m->p->RequestReSleep;
2502
2503 // 3. Deliver any waiting browse messages to clients
2504 DNSServiceBrowser *b = DNSServiceBrowserList;
2505
2506 while (b)
2507 {
2508 // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2509 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2510 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2511 DNSServiceBrowser *x = b;
2512 b = b->next;
2513 if (x->results) // Try to deliver the list of results
2514 {
2515 while (x->results)
2516 {
2517 DNSServiceBrowserResult *const r = x->results;
2518 domainlabel name;
2519 domainname type, domain;
2520 DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance()
2521 char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2522 char ctype[MAX_ESCAPED_DOMAIN_NAME];
2523 char cdom [MAX_ESCAPED_DOMAIN_NAME];
2524 ConvertDomainLabelToCString_unescaped(&name, cname);
2525 ConvertDomainNameToCString(&type, ctype);
2526 ConvertDomainNameToCString(&domain, cdom);
2527 DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
2528 kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
2529 // If we failed to send the mach message, try again in one second
2530 if (status == MACH_SEND_TIMED_OUT)
2531 {
2532 if (nextevent - now > mDNSPlatformOneSecond)
2533 nextevent = now + mDNSPlatformOneSecond;
2534 break;
2535 }
2536 else
2537 {
2538 x->lastsuccess = now;
2539 x->results = x->results->next;
2540 freeL("DNSServiceBrowserResult", r);
2541 }
2542 }
2543 // If this client hasn't read a single message in the last 60 seconds, abort it
2544 if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
2545 AbortBlockedClient(x->ClientMachPort, "browse", x);
2546 }
2547 }
2548
2549 DNSServiceResolver *l;
2550 for (l = DNSServiceResolverList; l; l=l->next)
2551 if (l->ReportTime && now - l->ReportTime >= 0)
2552 {
2553 l->ReportTime = 0;
2554 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2555 "This places considerable burden on the network.", l->i.name.c);
2556 }
2557
2558 if (m->p->NotifyUser)
2559 {
2560 if (m->p->NotifyUser - now < 0)
2561 {
2562 if (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c))
2563 {
2564 LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c);
2565 mDNSPreferencesSetName(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
2566 m->p->usernicelabel = m->nicelabel;
2567 }
2568 if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
2569 {
2570 LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
2571 mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
2572 m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful
2573 m->p->userhostlabel = m->hostlabel;
2574 }
2575 m->p->NotifyUser = 0;
2576 }
2577 else
2578 if (nextevent - m->p->NotifyUser > 0)
2579 nextevent = m->p->NotifyUser;
2580 }
2581
2582 return(nextevent);
2583 }
2584
2585 // Right now we consider *ALL* of our DHCP leases
2586 // It might make sense to be a bit more selective and only consider the leases on interfaces
2587 // (a) that are capable and enabled for wake-on-LAN, and
2588 // (b) where we have found (and successfully registered with) a Sleep Proxy
2589 // If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
2590 mDNSlocal mDNSu32 DHCPWakeTime(void)
2591 {
2592 mDNSu32 e = 24 * 3600; // Maximum maintenance wake interval is 24 hours
2593 const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
2594 if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
2595 else
2596 {
2597 const SCPreferencesRef prefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:DHCPWakeTime"), NULL);
2598 if (!prefs) LogMsg("DHCPWakeTime: SCPreferencesCreate failed");
2599 else
2600 {
2601 const SCNetworkSetRef currentset = SCNetworkSetCopyCurrent(prefs);
2602 if (!currentset) LogMsg("DHCPWakeTime: SCNetworkSetCopyCurrent failed");
2603 else
2604 {
2605 const CFArrayRef services = SCNetworkSetCopyServices(currentset);
2606 if (!services) LogMsg("DHCPWakeTime: SCNetworkSetCopyServices failed");
2607 else
2608 {
2609 int i;
2610 for (i = 0; i < CFArrayGetCount(services); i++)
2611 {
2612 const SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, i);
2613 if (!service) LogMsg("DHCPWakeTime: CFArrayGetValueAtIndex %d failed", i);
2614 else
2615 {
2616 const CFStringRef serviceid = SCNetworkServiceGetServiceID(service);
2617 if (!serviceid) LogMsg("DHCPWakeTime: SCNetworkServiceGetServiceID %d failed", i);
2618 else
2619 {
2620 // Note: It's normal for this call to return NULL, for interfaces not using DHCP
2621 const CFDictionaryRef dhcp = SCDynamicStoreCopyDHCPInfo(NULL, serviceid);
2622 if (dhcp)
2623 {
2624 const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp);
2625 const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51); // Option 51 = IP Address Lease Time
2626 if (!start || !lease || CFDataGetLength(lease) < 4)
2627 LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
2628 "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
2629 i, start, lease, lease ? CFDataGetLength(lease) : 0);
2630 else
2631 {
2632 const UInt8 *d = CFDataGetBytePtr(lease);
2633 if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", i);
2634 else
2635 {
2636 const mDNSu32 elapsed = now - CFDateGetAbsoluteTime(start);
2637 const mDNSu32 lifetime = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]);
2638 const mDNSu32 remaining = lifetime - elapsed;
2639 const mDNSu32 wake = remaining > 60 ? remaining - remaining/10 : 54; // Wake at 90% of the lease time
2640 LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake);
2641 if (e > wake) e = wake;
2642 }
2643 }
2644 CFRelease(dhcp);
2645 }
2646 }
2647 }
2648 }
2649 CFRelease(services);
2650 }
2651 CFRelease(currentset);
2652 }
2653 CFRelease(prefs);
2654 }
2655 }
2656 return(e);
2657 }
2658
2659 // We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
2660 // For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
2661 // If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
2662 // for a few seconds and then waking again is silly and annoying.
2663 // If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
2664 // Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
2665 // allowing us an adequate safety margin to renew our lease before we lose it.
2666
2667 mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now)
2668 {
2669 mDNSBool ready = mDNSCoreReadyForSleep(m, now);
2670 if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse);
2671
2672 m->p->WakeAtUTC = 0;
2673 int result = kIOReturnSuccess;
2674 CFDictionaryRef opts = NULL;
2675
2676 // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
2677 // do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
2678 if (!m->SleepState)
2679 LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now);
2680 else
2681 {
2682 if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m))
2683 LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
2684 m->SystemWakeOnLANEnabled ? "is" : "not",
2685 mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no");
2686 else
2687 {
2688 mDNSs32 dhcp = DHCPWakeTime();
2689 LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp);
2690 mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now) / mDNSPlatformOneSecond;
2691 if (interval > dhcp) interval = dhcp;
2692
2693 // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
2694 // transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
2695 // a single SPS failure could result in a remote machine falling permanently asleep, requiring
2696 // someone to go to the machine in person to wake it up again, which would be unacceptable.
2697 if (!ready && interval > 3600) interval = 3600;
2698
2699 //interval = 48; // For testing
2700
2701 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2702 if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that
2703 {
2704 const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval);
2705 if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed");
2706 else
2707 {
2708 const mDNSs32 reqs = kIOPMSystemPowerStateCapabilityNetwork;
2709 const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs);
2710 if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed");
2711 else
2712 {
2713 const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") };
2714 const void *OptionVals[2] = { WakeDate, Requirements };
2715 opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2716 if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
2717 CFRelease(Requirements);
2718 }
2719 CFRelease(WakeDate);
2720 }
2721 LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval);
2722 }
2723 else // else schedule the wakeup using the old API instead to
2724 #endif
2725 {
2726 // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
2727 // so we should put it back to sleep. To avoid frustrating the user, we always request at least
2728 // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
2729 // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
2730 if (interval < 60) interval = 60;
2731
2732 result = mDNSPowerRequest(1, interval);
2733
2734 if (result == kIOReturnNotReady)
2735 {
2736 LogMsg("Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
2737 // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
2738 // requested wake time is "too soon", but there's no API to find out what constitutes
2739 // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
2740 // we just have to iterate with successively longer intervals until it doesn't fail.
2741 // Additionally, if our power request is deemed "too soon" for the machine to get to
2742 // sleep and wake back up again, we attempt to cancel the sleep request, since the
2743 // implication is that the system won't manage to be awake again at the time we need it.
2744 do
2745 {
2746 interval += (interval < 20) ? 1 : ((interval+3) / 4);
2747 result = mDNSPowerRequest(1, interval);
2748 }
2749 while (result == kIOReturnNotReady);
2750 }
2751
2752 if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
2753 else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
2754 m->p->WakeAtUTC = mDNSPlatformUTC() + interval;
2755 }
2756 }
2757
2758 // Clear our interface list to empty state, ready to go to sleep
2759 // As a side effect of doing this, we'll also cancel any outstanding SPS Resolve calls that didn't complete
2760 m->SleepState = SleepState_Sleeping;
2761 mDNSMacOSXNetworkChanged(m);
2762 }
2763
2764 LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
2765 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2766 (m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" :
2767 #endif
2768 (result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange",
2769 m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now);
2770
2771 m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above
2772
2773 #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
2774 if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts);
2775 else
2776 #endif
2777 if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie);
2778 else IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie);
2779
2780 if (opts) CFRelease(opts);
2781 return(mDNStrue);
2782 }
2783
2784 mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context)
2785 {
2786 // Read all of the bytes so we won't wake again.
2787 char buffer[100];
2788 while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue;
2789 }
2790
2791 mDNSlocal void * KQueueLoop(void *m_param)
2792 {
2793 mDNS *m = m_param;
2794 int numevents = 0;
2795
2796 #if USE_SELECT_WITH_KQUEUEFD
2797 fd_set readfds;
2798 FD_ZERO(&readfds);
2799 const int multiplier = 1000000 / mDNSPlatformOneSecond;
2800 #else
2801 const int multiplier = 1000000000 / mDNSPlatformOneSecond;
2802 #endif
2803
2804 pthread_mutex_lock(&PlatformStorage.BigMutex);
2805 LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last);
2806
2807 // This is the main work loop:
2808 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2809 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2810 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2811 // (4) On wakeup we first process *all* events
2812 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2813 for ( ; ; )
2814 {
2815 #define kEventsToReadAtOnce 1
2816 struct kevent new_events[kEventsToReadAtOnce];
2817
2818 // Run mDNS_Execute to find out the time we next need to wake up
2819 mDNSs32 start = mDNSPlatformRawTime();
2820 mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
2821 mDNSs32 end = mDNSPlatformRawTime();
2822 if (end - start >= WatchDogReportingThreshold)
2823 LogInfo("WARNING: Idle task took %dms to complete", end - start);
2824
2825 mDNSs32 now = mDNS_TimeNow(m);
2826
2827 if (m->ShutdownTime)
2828 {
2829 if (mDNSStorage.ResourceRecords)
2830 {
2831 LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords));
2832 if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages
2833 }
2834 if (mDNSStorage.ServiceRegistrations)
2835 LogInfo("Cannot exit yet; ServiceRegistrations still exists: %s", ARDisplayString(m, &mDNSStorage.ServiceRegistrations->RR_SRV));
2836 if (mDNS_ExitNow(m, now))
2837 {
2838 if (!mDNSStorage.ResourceRecords && !mDNSStorage.ServiceRegistrations)
2839 safe_vproc_transaction_end();
2840 LogInfo("mDNS_FinalExit");
2841 mDNS_FinalExit(&mDNSStorage);
2842 usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages
2843 exit(0);
2844 }
2845 if (nextTimerEvent - m->ShutdownTime >= 0)
2846 nextTimerEvent = m->ShutdownTime;
2847 }
2848
2849 if (m->SleepLimit)
2850 if (!AllowSleepNow(m, now))
2851 if (nextTimerEvent - m->SleepLimit >= 0)
2852 nextTimerEvent = m->SleepLimit;
2853
2854 // Convert absolute wakeup time to a relative time from now
2855 mDNSs32 ticks = nextTimerEvent - now;
2856 if (ticks < 1) ticks = 1;
2857
2858 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
2859 if (ticks > 1)
2860 RepeatedBusy = 0;
2861 else
2862 {
2863 ticks = 1;
2864 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
2865 }
2866
2867 verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks);
2868 numevents = 0;
2869
2870 // Release the lock, and sleep until:
2871 // 1. Something interesting happens like a packet arriving, or
2872 // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
2873 // 3. The timeout expires
2874 pthread_mutex_unlock(&PlatformStorage.BigMutex);
2875
2876 #if USE_SELECT_WITH_KQUEUEFD
2877 struct timeval timeout;
2878 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
2879 timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier;
2880 FD_SET(KQueueFD, &readfds);
2881 if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0)
2882 { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
2883 #else
2884 struct timespec timeout;
2885 timeout.tv_sec = ticks / mDNSPlatformOneSecond;
2886 timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier;
2887 // In my opinion, you ought to be able to call kevent() with nevents set to zero,
2888 // and have it work similarly to the way it does with nevents non-zero --
2889 // i.e. it waits until either an event happens or the timeout expires, and then wakes up.
2890 // In fact, what happens if you do this is that it just returns immediately. So, we have
2891 // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
2892 if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0)
2893 { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
2894 #endif
2895
2896 pthread_mutex_lock(&PlatformStorage.BigMutex);
2897 // We have to ignore the event we may have been told about above, because that
2898 // was done without holding the lock, and between the time we woke up and the
2899 // time we reclaimed the lock the other thread could have done something that
2900 // makes the event no longer valid. Now we have the lock, we call kevent again
2901 // and this time we can safely process the events it tells us about.
2902
2903 static const struct timespec zero_timeout = { 0, 0 };
2904 int events_found;
2905 while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0)
2906 {
2907 if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR))
2908 {
2909 // Not sure what to do here, our kqueue has failed us - this isn't ideal
2910 LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno));
2911 exit(errno);
2912 }
2913
2914 numevents += events_found;
2915
2916 int i;
2917 for (i = 0; i < events_found; i++)
2918 {
2919 const KQueueEntry *const kqentry = new_events[i].udata;
2920 mDNSs32 stime = mDNSPlatformRawTime();
2921 const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task
2922 kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext);
2923 mDNSs32 etime = mDNSPlatformRawTime();
2924 if (etime - stime >= WatchDogReportingThreshold)
2925 LogInfo("WARNING: %s took %dms to complete", KQtask, etime - stime);
2926 }
2927 }
2928 }
2929
2930 return NULL;
2931 }
2932
2933 mDNSlocal void LaunchdCheckin(void)
2934 {
2935 launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
2936 launch_data_t resp = launch_msg(msg);
2937 launch_data_free(msg);
2938 if (!resp) { LogMsg("launch_msg returned NULL"); return; }
2939
2940 if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
2941 {
2942 int err = launch_data_get_errno(resp);
2943 // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
2944 if (err != EACCES) LogMsg("launch_msg returned %d", err);
2945 else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err);
2946 }
2947 else
2948 {
2949 launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
2950 if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
2951 else
2952 {
2953 launch_data_t skt = launch_data_dict_lookup(skts, "Listeners");
2954 if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL");
2955 else
2956 {
2957 launchd_fds_count = launch_data_array_get_count(skt);
2958 if (launchd_fds_count == 0) LogMsg("launch_data_array_get_count(skt) returned 0");
2959 else
2960 {
2961 launchd_fds = mallocL("LaunchdCheckin", sizeof(dnssd_sock_t) * launchd_fds_count);
2962 if (!launchd_fds) LogMsg("LaunchdCheckin: malloc failed");
2963 else
2964 {
2965 size_t i;
2966 for(i = 0; i < launchd_fds_count; i++)
2967 {
2968 launch_data_t s = launch_data_array_get_index(skt, i);
2969 if (!s)
2970 {
2971 launchd_fds[i] = dnssd_InvalidSocket;
2972 LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i);
2973 }
2974 else
2975 {
2976 launchd_fds[i] = launch_data_get_fd(s);
2977 LogInfo("Launchd Unix Domain Socket [%d]: %d", i, launchd_fds[i]);
2978 }
2979 }
2980 }
2981 // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
2982 chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH);
2983 }
2984 }
2985 }
2986
2987 launch_data_t ports = launch_data_dict_lookup(resp, "MachServices");
2988 if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL");
2989 else
2990 {
2991 launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder");
2992 if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL");
2993 else
2994 {
2995 m_port = launch_data_get_fd(p);
2996 LogInfo("Launchd Mach Port: %d", m_port);
2997 if (m_port == ~0U) m_port = MACH_PORT_NULL;
2998 }
2999 }
3000 }
3001 launch_data_free(resp);
3002 }
3003
3004 mDNSlocal void DropPrivileges(void)
3005 {
3006 static const char login[] = "_mdnsresponder";
3007 struct passwd *pwd = getpwnam(login);
3008 if (NULL == pwd)
3009 LogMsg("Could not find account name \"%s\". Running as root.", login);
3010 else
3011 {
3012 uid_t uid = pwd->pw_uid;
3013 gid_t gid = pwd->pw_gid;
3014
3015 LogMsg("Started as root. Switching to userid \"%s\".", login);
3016
3017 if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno));
3018 else
3019 {
3020 static char path[] = "/var/run/mdns/mDNSResponder";
3021 char *p = strrchr(path, '/');
3022 *p = '\0';
3023 if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno));
3024 else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno));
3025 else
3026 {
3027 *p = '/';
3028 if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno));
3029 else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno));
3030 else LogInfo("DropPrivileges: Created subdirectory and symlink");
3031 }
3032 }
3033
3034 if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login, (unsigned long)gid);
3035 if (0 != setgid(gid)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid());
3036 if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid);
3037 }
3038 }
3039
3040 extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));
3041
3042 mDNSexport int main(int argc, char **argv)
3043 {
3044 int i;
3045 kern_return_t status;
3046 pthread_t KQueueThread;
3047
3048 LogMsg("%s starting", mDNSResponderVersionString);
3049
3050 #if 0
3051 LogMsg("CacheRecord %d", sizeof(CacheRecord));
3052 LogMsg("CacheGroup %d", sizeof(CacheGroup));
3053 LogMsg("ResourceRecord %d", sizeof(ResourceRecord));
3054 LogMsg("RData_small %d", sizeof(RData_small));
3055
3056 LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity));
3057 LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE);
3058 LogMsg("block usage %d", sizeof(CacheEntity) * RR_CACHE_SIZE);
3059 LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE);
3060 #endif
3061
3062 safe_vproc_transaction_begin();
3063
3064 if (0 == geteuid()) DropPrivileges();
3065
3066 for (i=1; i<argc; i++)
3067 {
3068 if (!strcasecmp(argv[i], "-d" )) mDNS_DebugMode = mDNStrue;
3069 if (!strcasecmp(argv[i], "-launchd" )) started_via_launchdaemon = mDNStrue;
3070 if (!strcasecmp(argv[i], "-launchdaemon" )) started_via_launchdaemon = mDNStrue;
3071 if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) advertise = mDNS_Init_DontAdvertiseLocalAddresses;
3072 if (!strcasecmp(argv[i], "-DebugLogging" )) mDNS_LoggingEnabled = mDNStrue;
3073 if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue;
3074 if (!strcasecmp(argv[i], "-OfferSleepProxyService" ))
3075 OfferSleepProxyService = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 80;
3076 if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue;
3077 }
3078
3079 // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
3080 if (!advertise) LogMsg("Administratively prohibiting multicast advertisements");
3081
3082 OSXVers = mDNSMacOSXSystemBuildNumber(NULL);
3083
3084 signal(SIGHUP, HandleSIG); // (Debugging) Purge the cache to check for cache handling bugs
3085 signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
3086 // 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
3087 if (OSXVers <= OSXVers_10_4_Tiger)
3088 {
3089 LogInfo("Adding SIGABRT handler");
3090 signal(SIGABRT, CatchABRT); // For debugging -- SIGABRT should never happen
3091 }
3092 signal(SIGPIPE, SIG_IGN ); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
3093 signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
3094 signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog
3095 signal(SIGUSR1, HandleSIG); // (Debugging) Enable Logging
3096 signal(SIGUSR2, HandleSIG); // (Debugging) Enable Packet Logging
3097
3098 mDNSStorage.p = &PlatformStorage; // Make sure mDNSStorage.p is set up, because validatelists uses it
3099 LaunchdCheckin();
3100
3101 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
3102 if (!mDNS_DebugMode && !started_via_launchdaemon)
3103 {
3104 registerBootstrapService();
3105 if (!restarting_via_mach_init) exit(0); // mach_init will restart us immediately as a daemon
3106 int fd = open(_PATH_DEVNULL, O_RDWR, 0);
3107 if (fd < 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno, strerror(errno));
3108 else
3109 {
3110 // Avoid unnecessarily duplicating a file descriptor to itself
3111 if (fd != STDIN_FILENO) if (dup2(fd, STDIN_FILENO) < 0) LogMsg("dup2(fd, STDIN_FILENO) failed errno %d (%s)", errno, strerror(errno));
3112 if (fd != STDOUT_FILENO) if (dup2(fd, STDOUT_FILENO) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno, strerror(errno));
3113 if (fd != STDERR_FILENO) if (dup2(fd, STDERR_FILENO) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno, strerror(errno));
3114 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) (void)close(fd);
3115 }
3116 }
3117
3118 // Create the kqueue, mutex and thread to support KQSockets
3119 KQueueFD = kqueue();
3120 if (KQueueFD == -1) { LogMsg("kqueue() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
3121
3122 i = pthread_mutex_init(&PlatformStorage.BigMutex, NULL);
3123 if (i == -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
3124
3125 int fdpair[2] = {0, 0};
3126 i = socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair);
3127 if (i == -1) { LogMsg("socketpair() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
3128
3129 // Socket pair returned us two identical sockets connected to each other
3130 // We will use the first socket to send the second socket. The second socket
3131 // will be added to the kqueue so it will wake when data is sent.
3132 static const KQueueEntry wakeKQEntry = { KQWokenFlushBytes, NULL, "kqueue wakeup after CFRunLoop event" };
3133 PlatformStorage.WakeKQueueLoopFD = fdpair[0];
3134 KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
3135
3136 // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
3137 #if MDNS_NO_SANDBOX
3138 LogMsg("Note: Compiled without Apple Sandbox support");
3139 #else
3140 if (!sandbox_init)
3141 LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
3142 else
3143 {
3144 char *sandbox_msg;
3145 int sandbox_err = sandbox_init("mDNSResponder", SANDBOX_NAMED, &sandbox_msg);
3146 if (sandbox_err) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg); sandbox_free_error(sandbox_msg); }
3147 else LogInfo("Now running under Apple Sandbox restrictions");
3148 }
3149 #endif
3150
3151 status = mDNSDaemonInitialize();
3152 if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }
3153
3154 status = udsserver_init(launchd_fds, launchd_fds_count);
3155 if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
3156
3157 mDNSMacOSXNetworkChanged(&mDNSStorage);
3158
3159 // Start the kqueue thread
3160 i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
3161 if (i == -1) { LogMsg("pthread_create() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
3162
3163 if (status == 0)
3164 {
3165 CFRunLoopRun();
3166 LogMsg("ERROR: CFRunLoopRun Exiting.");
3167 mDNS_Close(&mDNSStorage);
3168 }
3169
3170 LogMsg("%s exiting", mDNSResponderVersionString);
3171
3172 exit:
3173 if (!mDNS_DebugMode && !started_via_launchdaemon) destroyBootstrapService();
3174 return(status);
3175 }
3176
3177 // uds_daemon.c support routines /////////////////////////////////////////////
3178
3179 // Arrange things so that when data appears on fd, callback is called with context
3180 mDNSexport mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
3181 {
3182 KQSocketEventSource **p = &gEventSources;
3183 while (*p && (*p)->fd != fd) p = &(*p)->next;
3184 if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; }
3185
3186 KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource);
3187 if (!newSource) return mStatus_NoMemoryErr;
3188
3189 newSource->next = mDNSNULL;
3190 newSource->fd = fd;
3191 newSource->kqs.KQcallback = callback;
3192 newSource->kqs.KQcontext = context;
3193 newSource->kqs.KQtask = "UDS client";
3194
3195 if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
3196 {
3197 *p = newSource;
3198 return mStatus_NoError;
3199 }
3200
3201 LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno));
3202 freeL("KQSocketEventSource", newSource);
3203 return mStatus_BadParamErr;
3204 }
3205
3206 mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor
3207 {
3208 KQSocketEventSource **p = &gEventSources;
3209 while (*p && (*p)->fd != fd) p = &(*p)->next;
3210 if (*p)
3211 {
3212 KQSocketEventSource *s = *p;
3213 *p = (*p)->next;
3214 // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
3215 // causes the kernel to automatically remove any associated kevents
3216 close(s->fd);
3217 freeL("KQSocketEventSource", s);
3218 return mStatus_NoError;
3219 }
3220 LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd);
3221 return mStatus_NoSuchNameErr;
3222 }
3223
3224 #if _BUILDING_XCODE_PROJECT_
3225 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
3226 const char *__crashreporter_info__ = mDNSResponderVersionString;
3227 asm(".desc ___crashreporter_info__, 0x10");
3228 #endif
3229
3230 // For convenience when using the "strings" command, this is the last thing in the file
3231 // The "@(#) " pattern is a special prefix the "what" command looks for
3232 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";