]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-107.6.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.265.2.1 2006/08/29 06:24:30 cheshire
34 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
35
36 Revision 1.265 2006/06/29 07:32:08 cheshire
37 Added missing LogOperation logging for DNSServiceBrowse results
38
39 Revision 1.264 2006/06/29 05:33:30 cheshire
40 <rdar://problem/4607043> mDNSResponder conditional compilation options
41
42 Revision 1.263 2006/06/08 23:23:48 cheshire
43 Fix errant indentation of curly brace at the end of provide_DNSServiceBrowserCreate_rpc()
44
45 Revision 1.262 2006/03/18 21:49:11 cheshire
46 Added comment in ShowTaskSchedulingError(mDNS *const m)
47
48 Revision 1.261 2006/01/06 01:22:28 cheshire
49 <rdar://problem/4108164> Reword "mach_absolute_time went backwards" dialog
50
51 Revision 1.260 2005/11/07 01:51:58 cheshire
52 <rdar://problem/4331591> Include list of configured DNS servers in SIGINFO output
53
54 Revision 1.259 2005/07/22 21:50:55 ksekar
55 Fix GCC 4.0/Intel compiler warnings
56
57 Revision 1.258 2005/07/04 22:40:26 cheshire
58 Additional debugging code to help catch memory corruption
59
60 Revision 1.257 2005/03/28 19:28:55 cheshire
61 Fix minor typos in LogOperation() messages
62
63 Revision 1.256 2005/03/17 22:01:22 cheshire
64 Tidy up alignment of lines to make code more readable
65
66 Revision 1.255 2005/03/09 00:48:43 cheshire
67 <rdar://problem/4015157> QU packets getting sent too early on wake from sleep
68 Move "m->p->NetworkChanged = 0;" line from caller to callee
69
70 Revision 1.254 2005/03/03 04:34:19 cheshire
71 <rdar://problem/4025973> Bonjour name conflict dialog appears during MacBuddy
72
73 Revision 1.253 2005/03/03 03:55:09 cheshire
74 <rdar://problem/3862944> Name collision notifications should be localized
75
76 Revision 1.252 2005/02/23 02:29:17 cheshire
77 <rdar://problem/4005191> "Local Hostname is already in use..." dialogue shows for only 60 seconds before being removed
78 Minor refinements, better variable names, improved comments
79
80 Revision 1.251 2005/02/21 21:31:24 ksekar
81 <rdar://problem/4015162> changed LogMsg to debugf
82
83 Revision 1.250 2005/02/19 01:25:04 cheshire
84 <rdar://problem/4005191> "Local Hostname is already in use..." dialogue shows for only 60 seconds before being removed
85 Further refinements
86
87 Revision 1.249 2005/02/19 00:28:45 cheshire
88 <rdar://problem/4005191> "Local Hostname is already in use..." dialogue shows for only 60 seconds before being removed
89
90 Revision 1.248 2005/02/19 00:18:34 cheshire
91 Confusing variable name -- alertMessage should be called alertHeader
92
93 Revision 1.247 2005/02/15 02:13:49 cheshire
94 If we did registerBootstrapService() when starting, then we must do
95 destroyBootstrapService() before exiting, or Mach init will keep restarting us.
96
97 Revision 1.246 2005/02/03 00:44:37 cheshire
98 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
99
100 Revision 1.245 2005/02/01 19:56:47 ksekar
101 Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording
102
103 Revision 1.244 2005/01/28 00:34:49 cheshire
104 Turn off "Starting time value" log message
105
106 Revision 1.243 2005/01/27 17:46:58 cheshire
107 Added comment about CFSocketInvalidate closing the underlying socket
108
109 Revision 1.242 2005/01/27 00:10:58 cheshire
110 <rdar://problem/3967867> Name change log messages every time machine boots
111
112 Revision 1.241 2005/01/25 17:28:06 ksekar
113 <rdar://problem/3971467> Should not return "local" twice for domain enumeration
114
115 Revision 1.240 2005/01/21 02:39:18 cheshire
116 Rename FoundDomain() to DomainEnumFound() to avoid order-file symbol clash with other routine called FoundDomain()
117
118 Revision 1.239 2005/01/20 00:25:01 cheshire
119 Improve validatelists() log message generation
120
121 Revision 1.238 2005/01/19 19:15:35 ksekar
122 Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
123
124 Revision 1.237 2005/01/19 03:33:09 cheshire
125 <rdar://problem/3945652> When changing Computer Name, we drop our own Goobye Packets
126
127 Revision 1.236 2005/01/19 03:16:38 cheshire
128 <rdar://problem/3961051> CPU Spin in mDNSResponder
129 Improve detail of "Task Scheduling Error" diagnostic messages
130
131 Revision 1.235 2005/01/15 00:56:41 ksekar
132 <rdar://problem/3954575> Unicast services don't disappear when logging
133 out of VPN
134
135 Revision 1.234 2005/01/10 03:42:30 ksekar
136 Clarify debugf
137
138 Revision 1.233 2004/12/18 00:53:46 cheshire
139 Use symbolic constant mDNSInterface_LocalOnly instead of (mDNSInterfaceID)~0
140
141 Revision 1.232 2004/12/17 23:37:48 cheshire
142 <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
143 (and other repetitive configuration changes)
144
145 Revision 1.231 2004/12/17 04:13:38 cheshire
146 Removed debugging check
147
148 Revision 1.230 2004/12/17 04:09:30 cheshire
149 <rdar://problem/3191011> Switch mDNSResponder to launchd
150
151 Revision 1.229 2004/12/16 21:51:36 cheshire
152 Remove some startup messages
153
154 Revision 1.228 2004/12/16 20:13:01 cheshire
155 <rdar://problem/3324626> Cache memory management improvements
156
157 Revision 1.227 2004/12/10 13:52:57 cheshire
158 <rdar://problem/3909995> Turn off SIGPIPE signals
159
160 Revision 1.226 2004/12/10 05:27:26 cheshire
161 <rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine
162
163 Revision 1.225 2004/12/10 04:28:29 cheshire
164 <rdar://problem/3914406> User not notified of name changes for services using new UDS API
165
166 Revision 1.224 2004/12/10 00:41:05 cheshire
167 Adjust alignment of log messages
168
169 Revision 1.223 2004/12/07 20:42:34 cheshire
170 Add explicit context parameter to mDNS_RemoveRecordFromService()
171
172 Revision 1.222 2004/12/06 21:15:23 ksekar
173 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
174
175 Revision 1.221 2004/11/30 03:24:04 cheshire
176 <rdar://problem/3854544> Defer processing network configuration changes until configuration has stabilized
177
178 Revision 1.220 2004/11/29 23:34:31 cheshire
179 On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
180 is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
181 only nudges the time value to 1 if the interval calculation happens to result in the value zero.
182
183 Revision 1.219 2004/11/25 01:00:56 cheshire
184 Checkin 1.217 not necessary
185
186 Revision 1.218 2004/11/24 20:27:19 cheshire
187 Add missing "err" parameter in LogMsg() call
188
189 Revision 1.217 2004/11/24 17:55:01 ksekar
190 Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal
191
192 Revision 1.216 2004/11/24 00:10:44 cheshire
193 <rdar://problem/3869241> For unicast operations, verify that service types are legal
194
195 Revision 1.215 2004/11/23 22:33:01 cheshire
196 <rdar://problem/3654910> Remove temporary workaround code for iChat
197
198 Revision 1.214 2004/11/23 22:13:59 cheshire
199 <rdar://problem/3886293> Subtype advertising broken for Mach API
200
201 Revision 1.213 2004/11/23 06:12:55 cheshire
202 <rdar://problem/3871405> Update wording for name conflict dialogs
203
204 Revision 1.212 2004/11/23 05:15:37 cheshire
205 <rdar://problem/3875830> Computer Name in use message garbled
206
207 Revision 1.211 2004/11/23 05:00:41 cheshire
208 <rdar://problem/3874629> Name conflict log message should not have ".local" appended
209
210 Revision 1.210 2004/11/03 03:45:17 cheshire
211 <rdar://problem/3863627> mDNSResponder does not inform user of Computer Name collisions
212
213 Revision 1.209 2004/11/03 02:25:50 cheshire
214 <rdar://problem/3324137> Conflict for Computer Name should update *all* empty string services, not just the one with the conflict
215
216 Revision 1.208 2004/11/03 01:54:14 cheshire
217 Update debugging messages
218
219 Revision 1.207 2004/11/02 23:58:19 cheshire
220 <rdar://problem/2974905> mDNSResponder does not inform user of name collisions
221
222 Revision 1.206 2004/10/28 02:40:47 cheshire
223 Add log message to confirm receipt of SIGUSR1 (simulate network configuration change event)
224
225 Revision 1.205 2004/10/28 02:21:01 cheshire
226 <rdar://problem/3856500> Improve mDNSResponder signal handling
227 Added SIGHUP as a way to do a forced restart of the daemon (better than kill -9)
228 Added SIGUSR1 to simulate a network change notification from System Configuration Framework
229
230 Revision 1.204 2004/10/27 01:57:21 cheshire
231 Add check of m->p->InterfaceList
232
233 Revision 1.203 2004/10/26 04:31:44 cheshire
234 Rename CountSubTypes() as ChopSubTypes()
235
236 Revision 1.202 2004/10/26 01:29:18 cheshire
237 Use "#if 0" instead of commenting out code
238
239 Revision 1.201 2004/10/25 21:41:39 ksekar
240 <rdar://problem/3852958> wide-area name conflicts can cause crash
241
242 Revision 1.200 2004/10/22 01:03:55 cheshire
243 <rdar://problem/3375328> select() says data is waiting; recvfrom() says there is no data
244 Log error message if attempt to remap stdin/stdout/stderr to /dev/null fails
245
246 Revision 1.199 2004/10/19 21:33:19 cheshire
247 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
248 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
249 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
250
251 Revision 1.198 2004/10/15 23:00:18 ksekar
252 <rdar://problem/3799242> Need to update LLQs on location changes
253
254 Revision 1.197 2004/10/12 23:38:59 ksekar
255 <rdar://problem/3837065> remove unnecessary log message
256
257 Revision 1.196 2004/10/04 05:56:04 cheshire
258 <rdar://problem/3824730> mDNSResponder doesn't respond to certain AirPort changes
259
260 Revision 1.195 2004/09/30 00:24:59 ksekar
261 <rdar://problem/3695802> Dynamically update default registration domains on config change
262
263 Revision 1.194 2004/09/26 23:20:35 ksekar
264 <rdar://problem/3813108> Allow default registrations in multiple wide-area domains
265
266 Revision 1.193 2004/09/23 23:35:27 cheshire
267 Update error message
268
269 Revision 1.192 2004/09/21 23:40:12 ksekar
270 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
271
272 Revision 1.191 2004/09/21 21:05:12 cheshire
273 Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
274 into mDNSShared/uds_daemon.c
275
276 Revision 1.190 2004/09/21 19:51:15 cheshire
277 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
278
279 Revision 1.189 2004/09/21 18:17:23 cheshire
280 <rdar://problem/3785400> Add version info to mDNSResponder
281
282 Revision 1.188 2004/09/20 21:45:27 ksekar
283 Mach IPC cleanup
284
285 Revision 1.187 2004/09/17 01:08:52 cheshire
286 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
287 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
288 declared in that file are ONLY appropriate to single-address-space embedded applications.
289 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
290
291 Revision 1.186 2004/09/16 00:24:49 cheshire
292 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
293
294 Revision 1.185 2004/08/25 02:01:45 cheshire
295 <rdar://problem/3774777> Need to be able to get status of Dynamic DNS Host Name Update
296
297 Revision 1.184 2004/08/19 19:04:12 ksekar
298 <rdar://problem/3767546>: mDNSResponder crashes when adding a record to a service
299
300 Revision 1.183 2004/08/14 03:22:42 cheshire
301 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
302 Add GetUserSpecifiedDDNSName() routine
303 Convert ServiceRegDomain to domainname instead of C string
304 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
305
306 Revision 1.182 2004/08/13 23:57:59 cheshire
307 Get rid of non-portable "_UNUSED"
308
309 Revision 1.181 2004/08/11 02:02:26 cheshire
310 Remove "mDNS *globalInstance" parameter from udsserver_init();
311 Move CheckForDuplicateRegistrations to uds_daemon.c
312
313 Revision 1.180 2004/07/13 21:24:25 rpantos
314 Fix for <rdar://problem/3701120>.
315
316 Revision 1.179 2004/06/19 00:02:54 cheshire
317 Restore fix for <rdar://problem/3548256> Should not allow empty string for resolve domain
318
319 Revision 1.178 2004/06/18 19:10:00 cheshire
320 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
321
322 Revision 1.177 2004/06/16 23:14:46 ksekar
323 <rdar://problem/3693816> Remove fix for <rdar://problem/3548256> Should not allow empty string for resolve domain
324
325 Revision 1.176 2004/06/11 20:27:42 cheshire
326 Rename "SocketRef" as "cfs" to avoid conflict with other plaforms
327
328 Revision 1.175 2004/06/10 20:23:21 cheshire
329 Also list interfaces in SIGINFO output
330
331 Revision 1.174 2004/06/08 18:54:48 ksekar
332 <rdar://problem/3681378>: mDNSResponder leaks after exploring in Printer Setup Utility
333
334 Revision 1.173 2004/06/08 17:35:12 cheshire
335 <rdar://problem/3683988> Detect and report if mDNSResponder uses too much CPU
336
337 Revision 1.172 2004/06/05 00:04:26 cheshire
338 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
339
340 Revision 1.171 2004/06/04 08:58:30 ksekar
341 <rdar://problem/3668624>: Keychain integration for secure dynamic update
342
343 Revision 1.170 2004/05/30 20:01:50 ksekar
344 <rdar://problem/3668635>: wide-area default registrations should be in
345 .local too - fixed service registration when clients pass an explicit
346 domain (broken by previous checkin)
347
348 Revision 1.169 2004/05/30 01:30:16 ksekar
349 <rdar://problem/3668635>: wide-area default registrations should be in
350 .local too
351
352 Revision 1.168 2004/05/18 23:51:26 cheshire
353 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
354
355 Revision 1.167 2004/05/14 16:39:47 ksekar
356 Browse for iChat locally for now.
357
358 Revision 1.166 2004/05/13 21:33:52 ksekar
359 Clean up non-local registration control via config file. Force iChat
360 registrations to be local for now.
361
362 Revision 1.165 2004/05/13 04:54:20 ksekar
363 Unified list copy/free code. Added symetric list for
364
365 Revision 1.164 2004/05/12 22:03:08 ksekar
366 Made GetSearchDomainList a true platform-layer call (declaration moved
367 from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local"
368 only on non-OSX platforms. Changed call to return a copy of the list
369 to avoid shared memory issues. Added a routine to free the list.
370
371 Revision 1.163 2004/05/12 02:03:25 ksekar
372 Non-local domains will only be browsed by default, and show up in
373 _browse domain enumeration, if they contain an _browse._dns-sd ptr record.
374
375 Revision 1.162 2004/04/14 23:09:29 ksekar
376 Support for TSIG signed dynamic updates.
377
378 Revision 1.161 2004/04/07 01:20:04 cheshire
379 Hash slot value should be unsigned
380
381 Revision 1.160 2004/04/06 19:51:24 cheshire
382 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
383 After more discussion, we've decided to use userid -2 if "nobody" user doesn't exist.
384
385 Revision 1.159 2004/04/03 01:36:55 cheshire
386 <rdar://problem/3605898> mDNSResponder will not launch if "nobody" user doesn't exist.
387 If "nobody" user doesn't exist, log a message and continue as "root"
388
389 Revision 1.158 2004/04/02 21:39:05 cheshire
390 Fix errors in comments
391
392 Revision 1.157 2004/03/19 18:49:10 ksekar
393 Increased size check in freeL() to account for LargeCacheRecord
394 structs larger than 8k
395
396 Revision 1.156 2004/03/19 18:19:19 ksekar
397 Fixed daemon.c to compile with malloc debugging turned on.
398
399 Revision 1.155 2004/03/13 01:57:34 ksekar
400 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
401
402 Revision 1.154 2004/03/12 08:42:47 cheshire
403 <rdar://problem/3548256>: Should not allow empty string for resolve domain
404
405 Revision 1.153 2004/03/12 08:08:51 cheshire
406 Update comments
407
408 Revision 1.152 2004/02/05 19:39:29 cheshire
409 Move creation of /var/run/mDNSResponder.pid to uds_daemon.c,
410 so that all platforms get this functionality
411
412 Revision 1.151 2004/02/03 22:35:34 cheshire
413 <rdar://problem/3548256>: Should not allow empty string for resolve domain
414
415 Revision 1.150 2004/01/28 21:14:23 cheshire
416 Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode)
417
418 Revision 1.149 2004/01/28 02:30:08 ksekar
419 Added default Search Domains to unicast browsing, controlled via
420 Networking sharing prefs pane. Stopped sending unicast messages on
421 every interface. Fixed unicast resolving via mach-port API.
422
423 Revision 1.148 2004/01/25 00:03:20 cheshire
424 Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
425
426 Revision 1.147 2004/01/19 19:51:46 cheshire
427 Fix compiler error (mixed declarations and code) on some versions of Linux
428
429 Revision 1.146 2003/12/08 21:00:46 rpantos
430 Changes to support mDNSResponder on Linux.
431
432 Revision 1.145 2003/12/05 22:08:07 cheshire
433 Update version string to "mDNSResponder-61", including new mechanism to allow dots (e.g. 58.1)
434
435 Revision 1.144 2003/11/19 23:21:08 ksekar
436 <rdar://problem/3486646>: config change handler not called for dns-sd services
437
438 Revision 1.143 2003/11/14 21:18:32 cheshire
439 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
440 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
441
442 Revision 1.142 2003/11/08 22:18:29 cheshire
443 <rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message
444
445 Revision 1.141 2003/11/07 02:30:57 cheshire
446 Also check per-slot cache use counts in SIGINFO state log
447
448 Revision 1.140 2003/10/21 19:58:26 cheshire
449 <rdar://problem/3459037> Syslog messages should show TTL as signed (for overdue records)
450
451 Revision 1.139 2003/10/21 00:10:18 rpantos
452 <rdar://problem/3409401>: mDNSResponder should not run as root
453
454 Revision 1.138 2003/10/07 20:16:58 cheshire
455 Shorten syslog message a bit
456
457 Revision 1.137 2003/09/23 02:12:43 cheshire
458 Also include port number in list of services registered via new UDS API
459
460 Revision 1.136 2003/09/23 02:07:25 cheshire
461 Include port number in DNSServiceRegistration START/STOP messages
462
463 Revision 1.135 2003/09/23 01:34:02 cheshire
464 In SIGINFO state log, show remaining TTL on cache records, and port number on ServiceRegistrations
465
466 Revision 1.134 2003/08/21 20:01:37 cheshire
467 <rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
468
469 Revision 1.133 2003/08/20 23:39:31 cheshire
470 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
471
472 Revision 1.132 2003/08/20 01:44:56 cheshire
473 Fix errors in LogOperation() calls (only used for debugging)
474
475 Revision 1.131 2003/08/19 05:39:43 cheshire
476 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
477
478 Revision 1.130 2003/08/16 03:39:01 cheshire
479 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
480
481 Revision 1.129 2003/08/15 20:16:03 cheshire
482 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
483 We want to avoid touching the rdata pages, so we don't page them in.
484 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
485 Moved this from the RData to the ResourceRecord object.
486 2. To avoid unnecessarily touching the rdata just to compare it,
487 compute a hash of the rdata and store the hash in the ResourceRecord object.
488
489 Revision 1.128 2003/08/14 19:30:36 cheshire
490 <rdar://problem/3378473> Include list of cache records in SIGINFO output
491
492 Revision 1.127 2003/08/14 02:18:21 cheshire
493 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
494
495 Revision 1.126 2003/08/12 19:56:25 cheshire
496 Update to APSL 2.0
497
498 Revision 1.125 2003/08/08 18:36:04 cheshire
499 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
500
501 Revision 1.124 2003/07/25 18:28:23 cheshire
502 Minor fix to error messages in syslog: Display string parameters with quotes
503
504 Revision 1.123 2003/07/23 17:45:28 cheshire
505 <rdar://problem/3339388> mDNSResponder leaks a bit
506 Don't allocate memory for the reply until after we've verified that the reply is valid
507
508 Revision 1.122 2003/07/23 00:00:04 cheshire
509 Add comments
510
511 Revision 1.121 2003/07/20 03:38:51 ksekar
512 <rdar://problem/3320722> Completed support for Unix-domain socket based API.
513
514 Revision 1.120 2003/07/18 00:30:00 cheshire
515 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
516
517 Revision 1.119 2003/07/17 19:08:58 cheshire
518 <rdar://problem/3332153> Remove calls to enable obsolete UDS code
519
520 Revision 1.118 2003/07/15 21:12:28 cheshire
521 Added extra debugging checks in validatelists() (not used in final shipping version)
522
523 Revision 1.117 2003/07/15 01:55:15 cheshire
524 <rdar://problem/3315777> Need to implement service registration with subtypes
525
526 Revision 1.116 2003/07/02 21:19:51 cheshire
527 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
528
529 Revision 1.115 2003/07/02 02:41:24 cheshire
530 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
531
532 Revision 1.114 2003/07/01 21:10:20 cheshire
533 Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
534
535 Revision 1.113 2003/06/28 17:27:43 vlubet
536 <rdar://problem/3221246> Redirect standard input, standard output, and
537 standard error file descriptors to /dev/null just like any other
538 well behaved daemon
539
540 Revision 1.112 2003/06/25 23:42:19 ksekar
541 <rdar://problem/3249292>: Feature: New DNS-SD APIs (#7875)
542 Reviewed by: Stuart Cheshire
543 Added files necessary to implement Unix domain sockets based enhanced
544 DNS-SD APIs, and integrated with existing Mach-port based daemon.
545
546 Revision 1.111 2003/06/11 01:02:43 cheshire
547 <rdar://problem/3287858> mDNSResponder binary compatibility
548 Make single binary that can run on both Jaguar and Panther.
549
550 Revision 1.110 2003/06/10 01:14:11 cheshire
551 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
552
553 Revision 1.109 2003/06/06 19:53:43 cheshire
554 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
555 (Global search-and-replace; no functional change to code execution.)
556
557 Revision 1.108 2003/06/06 14:08:06 cheshire
558 For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
559
560 Revision 1.107 2003/05/29 05:44:55 cheshire
561 Minor fixes to log messages
562
563 Revision 1.106 2003/05/27 18:30:55 cheshire
564 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
565 Dean Reece suggested SIGINFO is more appropriate than SIGHUP
566
567 Revision 1.105 2003/05/26 03:21:29 cheshire
568 Tidy up address structure naming:
569 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
570 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
571 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
572
573 Revision 1.104 2003/05/26 00:42:06 cheshire
574 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
575
576 Revision 1.103 2003/05/23 23:07:44 cheshire
577 <rdar://problem/3268199> Must not write to stderr when running as daemon
578
579 Revision 1.102 2003/05/22 01:32:31 cheshire
580 Fix typo in Log message format string
581
582 Revision 1.101 2003/05/22 00:26:55 cheshire
583 <rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
584 Modify error message to explain that this is technically legal, but may indicate a bug.
585
586 Revision 1.100 2003/05/21 21:02:24 ksekar
587 <rdar://problem/3247035>: Service should be prefixed
588 Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
589 Mach message port to "com.apple.mDNSResponder.
590
591 Revision 1.99 2003/05/21 17:33:49 cheshire
592 Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
593
594 Revision 1.98 2003/05/20 00:33:07 cheshire
595 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
596 SIGHUP now writes state summary to syslog
597
598 Revision 1.97 2003/05/08 00:19:08 cheshire
599 <rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
600
601 Revision 1.96 2003/05/07 22:10:46 cheshire
602 <rdar://problem/3250330> Add a few more error logging messages
603
604 Revision 1.95 2003/05/07 19:20:17 cheshire
605 <rdar://problem/3251391> Add version number to mDNSResponder builds
606
607 Revision 1.94 2003/05/07 00:28:18 cheshire
608 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
609
610 Revision 1.93 2003/05/06 00:00:49 cheshire
611 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
612
613 Revision 1.92 2003/04/04 20:38:57 cheshire
614 Add $Log header
615
616 */
617
618 #include <mach/mach.h>
619 #include <mach/mach_error.h>
620 #include <servers/bootstrap.h>
621 #include <sys/types.h>
622 #include <unistd.h>
623 #include <paths.h>
624 #include <fcntl.h>
625 #include <pwd.h>
626 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
627
628 #include "DNSServiceDiscoveryRequestServer.h"
629 #include "DNSServiceDiscoveryReply.h"
630
631 #include "DNSCommon.h"
632 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
633
634 #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
635
636 #include "GenLinkedList.h"
637
638 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
639
640 //*************************************************************************************************************
641 // Macros
642
643 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
644 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
645 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
646 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
647 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
648
649 //*************************************************************************************************************
650 // Globals
651
652 #define LOCAL_DEFAULT_REG 1 // empty string means register in the local domain
653 #define DEFAULT_REG_DOMAIN "apple.com." // used if the above flag is turned off
654 static mDNS_PlatformSupport PlatformStorage;
655
656 // Start off with a default cache of 16K (about 100 records)
657 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
658 static CacheEntity rrcachestorage[RR_CACHE_SIZE];
659
660 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
661 static mach_port_t client_death_port = MACH_PORT_NULL;
662 static mach_port_t signal_port = MACH_PORT_NULL;
663 static mach_port_t server_priv_port = MACH_PORT_NULL;
664
665 // mDNS Mach Message Timeout, in milliseconds.
666 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
667 // fails to service its mach message queue, but long enough to give a well-written
668 // client a chance to service its mach message queue without getting cut off.
669 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
670 // even extra-slow clients a fair chance before we cut them off.
671 #define MDNS_MM_TIMEOUT 250
672
673 static int restarting_via_mach_init = 0;
674 static int started_via_launchdaemon = 0;
675
676 static int OSXVers;
677
678 //*************************************************************************************************************
679 // Active client list structures
680
681 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
682 struct DNSServiceDomainEnumeration_struct
683 {
684 DNSServiceDomainEnumeration *next;
685 mach_port_t ClientMachPort;
686 DNSQuestion dom; // Question asking for domains
687 DNSQuestion def; // Question asking for default domain
688 };
689
690 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
691 struct DNSServiceBrowserResult_struct
692 {
693 DNSServiceBrowserResult *next;
694 int resultType;
695 domainname result;
696 };
697
698 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
699
700 typedef struct DNSServiceBrowserQuestion
701 {
702 struct DNSServiceBrowserQuestion *next;
703 DNSQuestion q;
704 domainname domain;
705 } DNSServiceBrowserQuestion;
706
707 struct DNSServiceBrowser_struct
708 {
709 DNSServiceBrowser *next;
710 mach_port_t ClientMachPort;
711 DNSServiceBrowserQuestion *qlist;
712 DNSServiceBrowserResult *results;
713 mDNSs32 lastsuccess;
714 mDNSBool DefaultDomain; // was the browse started on an explicit domain?
715 domainname type; // registration type
716 };
717
718 typedef struct DNSServiceResolver_struct DNSServiceResolver;
719 struct DNSServiceResolver_struct
720 {
721 DNSServiceResolver *next;
722 mach_port_t ClientMachPort;
723 ServiceInfoQuery q;
724 ServiceInfo i;
725 mDNSs32 ReportTime;
726 };
727
728 // A single registered service: ServiceRecordSet + bookkeeping
729 // Note that we duplicate some fields from parent DNSServiceRegistration object
730 // to facilitate cleanup, when instances and parent may be deallocated at different times.
731 typedef struct ServiceInstance
732 {
733 struct ServiceInstance *next;
734 mach_port_t ClientMachPort;
735 mDNSBool autoname; // Set if this name is tied to the Computer Name
736 mDNSBool autorename; // Set if we just got a name conflict and now need to automatically pick a new name
737 domainlabel name;
738 domainname domain;
739 ServiceRecordSet srs;
740 // Don't add any fields after ServiceRecordSet.
741 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
742 } ServiceInstance;
743
744 // A client-created service. May reference several ServiceInstance objects if default
745 // settings cause registration in multiple domains.
746 typedef struct DNSServiceRegistration
747 {
748 struct DNSServiceRegistration *next;
749 mach_port_t ClientMachPort;
750 mDNSBool DefaultDomain;
751 mDNSBool autoname;
752 size_t rdsize;
753 int NumSubTypes;
754 char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes
755 domainlabel name; // used only if autoname is false
756 domainname type;
757 mDNSIPPort port;
758 unsigned char txtinfo[1024];
759 size_t txt_len;
760 uint32_t NextRef;
761 ServiceInstance *regs;
762 } DNSServiceRegistration;
763
764 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
765 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
766 static DNSServiceResolver *DNSServiceResolverList = NULL;
767 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
768
769 //*************************************************************************************************************
770 // General Utility Functions
771
772 #if MACOSX_MDNS_MALLOC_DEBUGGING
773
774 char _malloc_options[] = "AXZ";
775
776 mDNSexport void LogMemCorruption(const char *format, ...)
777 {
778 char buffer[512];
779 va_list ptr;
780 va_start(ptr,format);
781 buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
782 va_end(ptr);
783 LogMsg("!!!! %s !!!!", buffer);
784 NotifyOfElusiveBug("Memory Corruption", buffer);
785 //*(long*)0 = -1; // Trick to crash and get a stack trace right here, if that's what we want
786 }
787
788 mDNSlocal void validatelists(mDNS *const m)
789 {
790 // Check Mach client lists
791
792 DNSServiceDomainEnumeration *e;
793 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
794 if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
795 LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort);
796
797 DNSServiceBrowser *b;
798 for (b = DNSServiceBrowserList; b; b=b->next)
799 if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
800 LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort);
801
802 DNSServiceResolver *l;
803 for (l = DNSServiceResolverList; l; l=l->next)
804 if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
805 LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort);
806
807 DNSServiceRegistration *r;
808 for (r = DNSServiceRegistrationList; r; r=r->next)
809 if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
810 LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort);
811
812 // Check UDS client lists
813 uds_validatelists();
814
815 // Check core mDNS lists
816 AuthRecord *rr;
817 for (rr = m->ResourceRecords; rr; rr=rr->next)
818 {
819 if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
820 LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
821 if (rr->resrec.name != &rr->namestorage)
822 LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
823 rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c);
824 }
825
826 for (rr = m->DuplicateRecords; rr; rr=rr->next)
827 if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
828 LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
829
830 DNSQuestion *q;
831 for (q = m->Questions; q; q=q->next)
832 if (q->ThisQInterval == (mDNSs32)~0)
833 LogMemCorruption("Questions list: %p is garbage (%lX)", q, q->ThisQInterval);
834
835 CacheGroup *cg;
836 CacheRecord *cr;
837 mDNSu32 slot;
838 FORALL_CACHERECORDS(slot, cg, cr)
839 if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
840 LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, rr, rr->resrec.RecordType);
841
842 // Check platform-layer lists
843
844 NetworkInterfaceInfoOSX *i;
845 for (i = m->p->InterfaceList; i; i = i->next)
846 if (!i->ifa_name)
847 LogMemCorruption("InterfaceList: %p is garbage", i);
848
849 // Check uDNS lists
850
851 for (q = m->uDNS_info.ActiveQueries; q; q=q->next)
852 if (*(long*)q == (mDNSs32)~0)
853 LogMemCorruption("uDNS_info.ActiveQueries: %p is garbage (%lX)", q, *(long*)q);
854
855 ServiceRecordSet *s;
856 for (s = m->uDNS_info.ServiceRegistrations; s; s=s->next)
857 if (s->next == (ServiceRecordSet*)~0)
858 LogMemCorruption("uDNS_info.ServiceRegistrations: %p is garbage (%lX)", s, s->next);
859
860 for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next)
861 {
862 if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
863 LogMemCorruption("uDNS_info.RecordRegistrations: %p is garbage (%X)", rr, rr->resrec.RecordType);
864 if (rr->resrec.name != &rr->namestorage)
865 LogMemCorruption("uDNS_info.RecordRegistrations: %p name %p does not point to namestorage %p %##s",
866 rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c);
867 }
868
869 NATTraversalInfo *n;
870 for (n = m->uDNS_info.NATTraversals; n; n=n->next)
871 if (n->op > 2) LogMemCorruption("uDNS_info.NATTraversals: %p is garbage", n);
872
873 for (n = m->uDNS_info.LLQNatInfo; n; n=n->next)
874 if (n->op > 2) LogMemCorruption("uDNS_info.LLQNatInfo: %p is garbage", n);
875 }
876
877 void *mallocL(char *msg, unsigned int size)
878 {
879 unsigned long *mem = malloc(size+8);
880 if (!mem)
881 {
882 LogMsg("malloc( %s : %d ) failed", msg, size);
883 return(NULL);
884 }
885 else
886 {
887 LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
888 mem[0] = 0xDEAD1234;
889 mem[1] = size;
890 //bzero(&mem[2], size);
891 memset(&mem[2], 0xFF, size);
892 validatelists(&mDNSStorage);
893 return(&mem[2]);
894 }
895 }
896
897 void freeL(char *msg, void *x)
898 {
899 if (!x)
900 LogMsg("free( %s @ NULL )!", msg);
901 else
902 {
903 unsigned long *mem = ((unsigned long *)x) - 2;
904 if (mem[0] != 0xDEAD1234)
905 { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
906 if (mem[1] > 24000)
907 { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; }
908 LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
909 //bzero(mem, mem[1]+8);
910 memset(mem, 0xFF, mem[1]+8);
911 validatelists(&mDNSStorage);
912 free(mem);
913 }
914 }
915
916 #endif
917
918 //*************************************************************************************************************
919 // Client Death Detection
920
921 mDNSlocal void FreeServiceInstance(ServiceInstance *x)
922 {
923 ServiceRecordSet *s = &x->srs;
924 ExtraResourceRecord *e = x->srs.Extras, *tmp;
925
926 while(e)
927 {
928 e->r.RecordContext = e;
929 tmp = e;
930 e = e->next;
931 FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
932 }
933
934 if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
935 freeL("TXT RData", s->RR_TXT.resrec.rdata);
936
937 if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
938 freeL("ServiceInstance", x);
939 }
940
941 // AbortClient finds whatever client is identified by the given Mach port,
942 // stops whatever operation that client was doing, and frees its memory.
943 // In the case of a service registration, the actual freeing may be deferred
944 // until we get the mStatus_MemFree message, if necessary
945 mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
946 {
947 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
948 DNSServiceBrowser **b = &DNSServiceBrowserList;
949 DNSServiceResolver **l = &DNSServiceResolverList;
950 DNSServiceRegistration **r = &DNSServiceRegistrationList;
951
952 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
953 if (*e)
954 {
955 DNSServiceDomainEnumeration *x = *e;
956 *e = (*e)->next;
957 if (m && m != x)
958 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
959 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
960 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
961 mDNS_StopGetDomains(&mDNSStorage, &x->def);
962 freeL("DNSServiceDomainEnumeration", x);
963 return;
964 }
965
966 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
967 if (*b)
968 {
969 DNSServiceBrowser *x = *b;
970 DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
971 *b = (*b)->next;
972 while (qptr)
973 {
974 if (m && m != x)
975 LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
976 else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c);
977 mDNS_StopBrowse(&mDNSStorage, &qptr->q);
978 freePtr = qptr;
979 qptr = qptr->next;
980 freeL("DNSServiceBrowserQuestion", freePtr);
981 }
982 while (x->results)
983 {
984 DNSServiceBrowserResult *r = x->results;
985 x->results = x->results->next;
986 freeL("DNSServiceBrowserResult", r);
987 }
988 freeL("DNSServiceBrowser", x);
989 return;
990 }
991
992 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
993 if (*l)
994 {
995 DNSServiceResolver *x = *l;
996 *l = (*l)->next;
997 if (m && m != x)
998 LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
999 else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c);
1000 mDNS_StopResolveService(&mDNSStorage, &x->q);
1001 freeL("DNSServiceResolver", x);
1002 return;
1003 }
1004
1005 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
1006 if (*r)
1007 {
1008 ServiceInstance *si = NULL;
1009 DNSServiceRegistration *x = *r;
1010 *r = (*r)->next;
1011
1012 si = x->regs;
1013 while (si)
1014 {
1015 ServiceInstance *instance = si;
1016 si = si->next;
1017 instance->autorename = mDNSfalse;
1018 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);
1019 else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs));
1020
1021 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
1022 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
1023 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
1024 // the list, so we should go ahead and free the memory right now
1025 if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer
1026 }
1027 x->regs = NULL;
1028 freeL("DNSServiceRegistration", x);
1029 return;
1030 }
1031
1032 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
1033 }
1034
1035 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
1036
1037 mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
1038 {
1039 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
1040 DNSServiceBrowser *b = DNSServiceBrowserList;
1041 DNSServiceResolver *l = DNSServiceResolverList;
1042 DNSServiceRegistration *r = DNSServiceRegistrationList;
1043 DNSServiceBrowserQuestion *qptr;
1044
1045 while (e && e->ClientMachPort != c) e = e->next;
1046 while (b && b->ClientMachPort != c) b = b->next;
1047 while (l && l->ClientMachPort != c) l = l->next;
1048 while (r && r->ClientMachPort != c) r = r->next;
1049
1050 if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
1051 else if (b)
1052 {
1053 for (qptr = b->qlist; qptr; qptr = qptr->next)
1054 LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg);
1055 }
1056 else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
1057 else if (r)
1058 {
1059 ServiceInstance *si;
1060 for (si = r->regs; si; si = si->next)
1061 LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name->c, reason, msg);
1062 }
1063 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
1064
1065 AbortClient(c, m);
1066 }
1067
1068 mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
1069 {
1070 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
1071 DNSServiceBrowser *b = DNSServiceBrowserList;
1072 DNSServiceResolver *l = DNSServiceResolverList;
1073 DNSServiceRegistration *r = DNSServiceRegistrationList;
1074 DNSServiceBrowserQuestion *qptr;
1075
1076 while (e && e->ClientMachPort != c) e = e->next;
1077 while (b && b->ClientMachPort != c) b = b->next;
1078 while (l && l->ClientMachPort != c) l = l->next;
1079 while (r && r->ClientMachPort != c) r = r->next;
1080 if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
1081 if (b)
1082 {
1083 for (qptr = b->qlist; qptr; qptr = qptr->next)
1084 LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c);
1085 }
1086 if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
1087 if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL);
1088 return(e || b || l || r);
1089 }
1090
1091 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
1092 {
1093 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
1094 (void)unusedport; // Unused
1095 (void)size; // Unused
1096 (void)info; // Unused
1097 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
1098 {
1099 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
1100 AbortClient(deathMessage->not_port, NULL);
1101
1102 /* Deallocate the send right that came in the dead name notification */
1103 mach_port_destroy(mach_task_self(), deathMessage->not_port);
1104 }
1105 }
1106
1107 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
1108 {
1109 mach_port_t prev;
1110 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
1111 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
1112 // If the port already died while we were thinking about it, then abort the operation right away
1113 if (r != KERN_SUCCESS)
1114 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
1115 }
1116
1117 //*************************************************************************************************************
1118 // Domain Enumeration
1119
1120 mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
1121 {
1122 kern_return_t status;
1123 #pragma unused(m)
1124 char buffer[MAX_ESCAPED_DOMAIN_NAME];
1125 DNSServiceDomainEnumerationReplyResultType rt;
1126 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
1127
1128 debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
1129 if (answer->rrtype != kDNSType_PTR) return;
1130 if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }
1131
1132 if (AddRecord)
1133 {
1134 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
1135 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
1136 }
1137 else
1138 {
1139 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
1140 else return;
1141 }
1142
1143 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
1144 x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
1145 !AddRecord ? "RemoveDomain" :
1146 question == &x->dom ? "AddDomain" : "AddDomainDefault");
1147
1148 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
1149 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
1150 if (status == MACH_SEND_TIMED_OUT)
1151 AbortBlockedClient(x->ClientMachPort, "enumeration", x);
1152 }
1153
1154 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1155 int regDom)
1156 {
1157 // Check client parameter
1158 (void)unusedserver; // Unused
1159 mStatus err = mStatus_NoError;
1160 const char *errormsg = "Unknown";
1161 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1162 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1163
1164 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
1165 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
1166
1167 // Allocate memory, and handle failure
1168 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
1169 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1170
1171 // Set up object, and link into list
1172 x->ClientMachPort = client;
1173 x->next = DNSServiceDomainEnumerationList;
1174 DNSServiceDomainEnumerationList = x;
1175
1176 verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
1177
1178 // Do the operation
1179 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
1180 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
1181 if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
1182
1183 // Succeeded: Wrap up and return
1184 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
1185 EnableDeathNotificationForClient(client, x);
1186 return(mStatus_NoError);
1187
1188 fail:
1189 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err);
1190 return(err);
1191 }
1192
1193 //*************************************************************************************************************
1194 // Browse for services
1195
1196 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
1197 {
1198 (void)m; // Unused
1199
1200 if (answer->rrtype != kDNSType_PTR)
1201 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
1202
1203 domainlabel name;
1204 domainname type, domain;
1205 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
1206 {
1207 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
1208 answer->name->c, answer->rdata->u.name.c);
1209 return;
1210 }
1211
1212 DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
1213 if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
1214
1215 verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
1216 AssignDomainName(&x->result, &answer->rdata->u.name);
1217 if (AddRecord)
1218 x->resultType = DNSServiceBrowserReplyAddInstance;
1219 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
1220 x->next = NULL;
1221
1222 DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
1223 DNSServiceBrowserResult **p = &browser->results;
1224 while (*p) p = &(*p)->next;
1225 *p = x;
1226
1227 LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
1228 browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
1229 }
1230
1231 mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d)
1232 {
1233 mStatus err = mStatus_NoError;
1234 DNSServiceBrowserQuestion *ptr, *question = NULL;
1235
1236 for (ptr = browser->qlist; ptr; ptr = ptr->next)
1237 {
1238 if (SameDomainName(&ptr->q.qname, d))
1239 { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; }
1240 }
1241
1242 question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
1243 if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; }
1244 AssignDomainName(&question->domain, d);
1245 question->next = browser->qlist;
1246 browser->qlist = question;
1247 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
1248 err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser);
1249 if (err) LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err);
1250 return err;
1251 }
1252
1253 mDNSexport void DefaultBrowseDomainChanged(const domainname *d, mDNSBool add)
1254 {
1255 DNSServiceBrowser *ptr;
1256
1257 debugf("DefaultBrowseDomainChanged: %s default browse domain %##s", add ? "Adding" : "Removing", d->c);
1258 for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next)
1259 {
1260 if (ptr->DefaultDomain)
1261 {
1262 if (add)
1263 {
1264 mStatus err = AddDomainToBrowser(ptr, d);
1265 if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort);
1266 }
1267 else
1268 {
1269 DNSServiceBrowserQuestion **q = &ptr->qlist;
1270 while (*q)
1271 {
1272 if (SameDomainName(&(*q)->domain, d))
1273 {
1274 DNSServiceBrowserQuestion *remove = *q;
1275 *q = (*q)->next;
1276 if (remove->q.LongLived)
1277 {
1278 // Give goodbyes for known answers.
1279 // Note that this a special case where we know that the QuestionCallback function is our own
1280 // code (it's FoundInstance), and that callback routine doesn't ever cancel its operation, so we
1281 // don't need to guard against the question being cancelled mid-loop the way the mDNSCore routines do.
1282 CacheRecord *ka = remove->q.uDNS_info.knownAnswers;
1283 while (ka) { remove->q.QuestionCallback(&mDNSStorage, &remove->q, &ka->resrec, mDNSfalse); ka = ka->next; }
1284 }
1285 mDNS_StopBrowse(&mDNSStorage, &remove->q);
1286 freeL("DNSServiceBrowserQuestion", remove );
1287 return;
1288 }
1289 q = &(*q)->next;
1290 }
1291 LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort);
1292 }
1293 }
1294 }
1295 }
1296
1297 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1298 DNSCString regtype, DNSCString domain)
1299 {
1300 // Check client parameter
1301 (void)unusedserver; // Unused
1302 mStatus err = mStatus_NoError;
1303 const char *errormsg = "Unknown";
1304 DNameListElem *SearchDomains = NULL, *sdPtr;
1305
1306 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1307 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1308
1309 // Check other parameters
1310 domainname t, d;
1311 t.c[0] = 0;
1312 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1313 if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; }
1314 if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
1315 { errormsg = "Bad Service SubType"; goto badparam; }
1316 if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
1317 domainname temp;
1318 if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
1319 if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local"
1320
1321 // Allocate memory, and handle failure
1322 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
1323 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1324
1325 // Set up object, and link into list
1326 AssignDomainName(&x->type, &t);
1327 x->ClientMachPort = client;
1328 x->results = NULL;
1329 x->lastsuccess = 0;
1330 x->qlist = NULL;
1331 x->next = DNSServiceBrowserList;
1332 DNSServiceBrowserList = x;
1333
1334 if (domain[0])
1335 {
1336 // Start browser for an explicit domain
1337 x->DefaultDomain = mDNSfalse;
1338 if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; }
1339 err = AddDomainToBrowser(x, &d);
1340 if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
1341 }
1342 else
1343 {
1344 // Start browser on all domains
1345 x->DefaultDomain = mDNStrue;
1346 SearchDomains = mDNSPlatformGetSearchDomainList();
1347 if (!SearchDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
1348 for (sdPtr = SearchDomains; sdPtr; sdPtr = sdPtr->next)
1349 {
1350 err = AddDomainToBrowser(x, &sdPtr->name);
1351 if (err)
1352 {
1353 // only terminally bail if .local fails
1354 if (!SameDomainName(&localdomain, &sdPtr->name))
1355 LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c);
1356 else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
1357 }
1358 }
1359 }
1360
1361 // Succeeded: Wrap up and return
1362 EnableDeathNotificationForClient(client, x);
1363 mDNS_FreeDNameList(SearchDomains);
1364 return(mStatus_NoError);
1365
1366 badparam:
1367 err = mStatus_BadParamErr;
1368 fail:
1369 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err);
1370 if (SearchDomains) mDNS_FreeDNameList(SearchDomains);
1371 return(err);
1372 }
1373
1374 //*************************************************************************************************************
1375 // Resolve Service Info
1376
1377 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
1378 {
1379 kern_return_t status;
1380 DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
1381 NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
1382 if (query->info->InterfaceID == mDNSInterface_LocalOnly) ifx = mDNSNULL;
1383 struct sockaddr_storage interface;
1384 struct sockaddr_storage address;
1385 char cstring[1024];
1386 int i, pstrlen = query->info->TXTinfo[0];
1387 (void)m; // Unused
1388
1389 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
1390
1391 if (query->info->TXTlen > sizeof(cstring)) return;
1392
1393 bzero(&interface, sizeof(interface));
1394 bzero(&address, sizeof(address));
1395
1396 if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
1397 {
1398 struct sockaddr_in *sin = (struct sockaddr_in*)&interface;
1399 sin->sin_len = sizeof(*sin);
1400 sin->sin_family = AF_INET;
1401 sin->sin_port = 0;
1402 sin->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
1403 }
1404 else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
1405 {
1406 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
1407 sin6->sin6_len = sizeof(*sin6);
1408 sin6->sin6_family = AF_INET6;
1409 sin6->sin6_flowinfo = 0;
1410 sin6->sin6_port = 0;
1411 sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
1412 sin6->sin6_scope_id = ifx->scope_id;
1413 }
1414
1415 if (query->info->ip.type == mDNSAddrType_IPv4)
1416 {
1417 struct sockaddr_in *sin = (struct sockaddr_in*)&address;
1418 sin->sin_len = sizeof(*sin);
1419 sin->sin_family = AF_INET;
1420 sin->sin_port = query->info->port.NotAnInteger;
1421 sin->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
1422 }
1423 else
1424 {
1425 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
1426 sin6->sin6_len = sizeof(*sin6);
1427 sin6->sin6_family = AF_INET6;
1428 sin6->sin6_port = query->info->port.NotAnInteger;
1429 sin6->sin6_flowinfo = 0;
1430 sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
1431 sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
1432 }
1433
1434 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
1435 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
1436 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
1437 // ASCII-1 characters are used in the C-string as boundary markers,
1438 // to indicate the boundaries between the original constituent P-strings.
1439 for (i=1; i<query->info->TXTlen; i++)
1440 {
1441 if (--pstrlen >= 0)
1442 cstring[i-1] = query->info->TXTinfo[i];
1443 else
1444 {
1445 cstring[i-1] = 1;
1446 pstrlen = query->info->TXTinfo[i];
1447 }
1448 }
1449 cstring[i-1] = 0; // Put the terminating NULL on the end
1450
1451 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
1452 x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
1453 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
1454 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
1455 if (status == MACH_SEND_TIMED_OUT)
1456 AbortBlockedClient(x->ClientMachPort, "resolve", x);
1457 }
1458
1459 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
1460 DNSCString name, DNSCString regtype, DNSCString domain)
1461 {
1462 // Check client parameter
1463 (void)unusedserver; // Unused
1464 mStatus err = mStatus_NoError;
1465 const char *errormsg = "Unknown";
1466 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1467 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1468
1469 // Check other parameters
1470 domainlabel n;
1471 domainname t, d, srv;
1472 if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1473 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1474 if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
1475 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1476
1477 // Allocate memory, and handle failure
1478 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
1479 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1480
1481 // Set up object, and link into list
1482 x->ClientMachPort = client;
1483 x->i.InterfaceID = mDNSInterface_Any;
1484 x->i.name = srv;
1485 x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
1486 x->next = DNSServiceResolverList;
1487 DNSServiceResolverList = x;
1488
1489 // Do the operation
1490 LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c);
1491 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
1492 if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
1493
1494 // Succeeded: Wrap up and return
1495 EnableDeathNotificationForClient(client, x);
1496 return(mStatus_NoError);
1497
1498 badparam:
1499 err = mStatus_BadParamErr;
1500 fail:
1501 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err);
1502 return(err);
1503 }
1504
1505 //*************************************************************************************************************
1506 // Registration
1507
1508 mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
1509 {
1510 m->p->NotifyUser = NonZeroTime(m->timenow + delay);
1511 }
1512
1513 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
1514 {
1515 ServiceInstance *si = (ServiceInstance*)srs->ServiceContext;
1516
1517 if (result == mStatus_NoError)
1518 {
1519 kern_return_t status;
1520 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1521 status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1522 if (status == MACH_SEND_TIMED_OUT)
1523 AbortBlockedClient(si->ClientMachPort, "registration success", si);
1524 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1525 RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
1526 }
1527
1528 else if (result == mStatus_NameConflict)
1529 {
1530 LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
1531 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
1532 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
1533 if (si->autoname && CountPeerRegistrations(m, srs) == 0)
1534 {
1535 // On conflict for an autoname service, rename and reregister *all* autoname services
1536 IncrementLabelSuffix(&m->nicelabel, mDNStrue);
1537 m->MainCallback(m, mStatus_ConfigChanged);
1538 }
1539 else if (si->autoname)
1540 {
1541 mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
1542 return;
1543 }
1544 else
1545 {
1546 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
1547 // of their registration in the usual way (which we will catch via client death notification).
1548 // If the Mach queue is full, we forcibly abort the client immediately.
1549 kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
1550 if (status == MACH_SEND_TIMED_OUT)
1551 AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL);
1552 }
1553 }
1554
1555 else if (result == mStatus_MemFree)
1556 {
1557 if (si->autorename)
1558 {
1559 debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c);
1560 si->autorename = mDNSfalse;
1561 si->name = m->nicelabel;
1562 mDNS_RenameAndReregisterService(m, srs, &si->name);
1563 }
1564 else
1565 {
1566 // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
1567 DNSServiceRegistration *r;
1568 for (r = DNSServiceRegistrationList; r; r = r->next)
1569 {
1570 ServiceInstance *sp = r->regs, *prev = NULL;
1571 while (sp)
1572 {
1573 if (sp == si)
1574 {
1575 LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", srs->RR_SRV.resrec.name->c);
1576 if (prev) prev->next = sp->next;
1577 else r->regs = sp->next;
1578 break;
1579 }
1580 prev = sp;
1581 sp = sp->next;
1582 }
1583 }
1584 // END SANITY CHECK
1585 FreeServiceInstance(si);
1586 }
1587 }
1588
1589 else if (result != mStatus_NATTraversal)
1590 LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result);
1591 }
1592
1593 mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain)
1594 {
1595 mStatus err = 0;
1596 ServiceInstance *si = NULL;
1597 AuthRecord *SubTypes = NULL;
1598
1599 for (si = x->regs; si; si = si->next)
1600 {
1601 if (SameDomainName(&si->domain, domain))
1602 { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
1603 }
1604
1605 SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype);
1606 if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
1607
1608 si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
1609 if (!si) return mStatus_NoMemoryErr;
1610
1611 si->ClientMachPort = x->ClientMachPort;
1612 si->autorename = mDNSfalse;
1613 si->autoname = x->autoname;
1614 si->name = x->autoname ? mDNSStorage.nicelabel : x->name;
1615 si->domain = *domain;
1616
1617 err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL, x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si);
1618 if (!err)
1619 {
1620 si->next = x->regs;
1621 x->regs = si;
1622 }
1623 else
1624 {
1625 LogMsg("Error %d for registration of service in domain %##s", err, domain->c);
1626 freeL("ServiceInstance", si);
1627 }
1628 return err;
1629 }
1630
1631 mDNSexport void DefaultRegDomainChanged(const domainname *d, mDNSBool add)
1632 {
1633 DNSServiceRegistration *reg;
1634
1635 for (reg = DNSServiceRegistrationList; reg; reg = reg->next)
1636 {
1637 if (reg->DefaultDomain)
1638 {
1639 if (add)
1640 {
1641 AddServiceInstance(reg, d);
1642 }
1643 else
1644 {
1645 ServiceInstance *si = reg->regs, *prev = NULL;
1646 while (si)
1647 {
1648 if (SameDomainName(&si->domain, d))
1649 {
1650 if (prev) prev->next = si->next;
1651 else reg->regs = si->next;
1652 if (mDNS_DeregisterService(&mDNSStorage, &si->srs))
1653 FreeServiceInstance(si); // only free memory synchronously on error
1654 break;
1655 }
1656 prev = si;
1657 si = si->next;
1658 }
1659 if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed
1660 }
1661 }
1662 }
1663 }
1664
1665 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
1666 DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord)
1667 {
1668 (void)unusedserver; // Unused
1669 mStatus err = mStatus_NoError;
1670 const char *errormsg = "Unknown";
1671
1672 // older versions of this code passed the port via mach IPC as an int.
1673 // we continue to pass it as 4 bytes to maintain binary compatibility,
1674 // but now ensure that the network byte order is preserved by using a struct
1675 mDNSIPPort port;
1676 port.b[0] = IpPort.bytes[2];
1677 port.b[1] = IpPort.bytes[3];
1678
1679 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1680 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
1681
1682 // Check for sub-types after the service type
1683 size_t reglen = strlen(regtype) + 1;
1684 if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
1685 mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
1686 if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }
1687
1688 // Check other parameters
1689 domainlabel n;
1690 domainname t, d;
1691 domainname srv;
1692 if (!name[0]) n = mDNSStorage.nicelabel;
1693 else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
1694 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
1695 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
1696 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
1697
1698 unsigned char txtinfo[1024] = "";
1699 unsigned int data_len = 0;
1700 unsigned int size = sizeof(RDataBody);
1701 unsigned char *pstring = &txtinfo[data_len];
1702 char *ptr = txtRecord;
1703
1704 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
1705 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
1706 // Hence we have to convert the C-string to a P-string.
1707 // ASCII-1 characters are allowed in the C-string as boundary markers,
1708 // so that a single C-string can be used to represent one or more P-strings.
1709 while (*ptr)
1710 {
1711 if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
1712 if (*ptr == 1) // If this is our boundary marker, start a new P-string
1713 {
1714 pstring = &txtinfo[data_len];
1715 pstring[0] = 0;
1716 ptr++;
1717 }
1718 else
1719 {
1720 if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
1721 pstring[++pstring[0]] = *ptr++;
1722 }
1723 }
1724
1725 data_len++;
1726 if (size < data_len)
1727 size = data_len;
1728
1729 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1730 // a port number of zero. When two instances of the protected client are allowed to run on one
1731 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1732 if (port.NotAnInteger)
1733 {
1734 int count = CountExistingRegistrations(&srv, port);
1735 if (count)
1736 LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
1737 client, count+1, srv.c, mDNSVal16(port));
1738 }
1739
1740 // Allocate memory, and handle failure
1741 DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x));
1742 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1743 bzero(x, sizeof(*x));
1744
1745 // Set up object, and link into list
1746 x->ClientMachPort = client;
1747 x->DefaultDomain = !domain[0];
1748 x->autoname = (!name[0]);
1749 x->rdsize = size;
1750 x->NumSubTypes = NumSubTypes;
1751 memcpy(x->regtype, regtype, reglen);
1752 x->name = n;
1753 x->type = t;
1754 x->port = port;
1755 memcpy(x->txtinfo, txtinfo, 1024);
1756 x->txt_len = data_len;
1757 x->NextRef = 0;
1758 x->regs = NULL;
1759
1760 x->next = DNSServiceRegistrationList;
1761 DNSServiceRegistrationList = x;
1762
1763 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
1764 x->ClientMachPort, name, regtype, domain, mDNSVal16(port));
1765
1766 err = AddServiceInstance(x, &d);
1767 if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails
1768
1769 if (x->DefaultDomain)
1770 {
1771 DNameListElem *ptr, *regdomains = mDNSPlatformGetRegDomainList();
1772 for (ptr = regdomains; ptr; ptr = ptr->next)
1773 AddServiceInstance(x, &ptr->name);
1774 mDNS_FreeDNameList(regdomains);
1775 }
1776
1777 // Succeeded: Wrap up and return
1778 EnableDeathNotificationForClient(client, x);
1779 return(mStatus_NoError);
1780
1781 badtxt:
1782 LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
1783 badparam:
1784 err = mStatus_BadParamErr;
1785 fail:
1786 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1787 client, name, regtype, domain, mDNSVal16(port), errormsg, err);
1788 return(err);
1789 }
1790
1791 mDNSlocal domainlabel gNotificationPrefHostLabel; // The prefs as they were the last time we saw them
1792 mDNSlocal domainlabel gNotificationPrefNiceLabel;
1793 mDNSlocal domainlabel gNotificationUserHostLabel; // The prefs as they were the last time the user changed them
1794 mDNSlocal domainlabel gNotificationUserNiceLabel;
1795
1796 #ifndef NO_CFUSERNOTIFICATION
1797 mDNSlocal CFUserNotificationRef gNotification = NULL;
1798 mDNSlocal CFRunLoopSourceRef gNotificationRLS = NULL;
1799
1800 mDNSlocal void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
1801 {
1802 (void)responseFlags; // Unused
1803 if (userNotification != gNotification) LogMsg("NotificationCallBackDismissed: Wrong CFUserNotificationRef");
1804 if (gNotificationRLS)
1805 {
1806 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), gNotificationRLS, kCFRunLoopDefaultMode);
1807 CFRelease(gNotificationRLS);
1808 gNotificationRLS = NULL;
1809 CFRelease(gNotification);
1810 gNotification = NULL;
1811 }
1812 // By dismissing the alert, the user has conceptually acknowleged the rename.
1813 // (e.g. the machine's name is now officially "computer-2.local", not "computer.local".)
1814 // If we get *another* conflict, the new alert should refer to the 'old'.
1815 // name as now being "computer-2.local", not "computer.local"
1816 gNotificationUserHostLabel = gNotificationPrefHostLabel;
1817 gNotificationUserNiceLabel = gNotificationPrefNiceLabel;
1818 }
1819
1820 mDNSlocal void ShowNameConflictNotification(CFStringRef header, CFStringRef subtext)
1821 {
1822 CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1823 if (!dictionary) return;
1824 CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header);
1825 CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext);
1826
1827 CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true);
1828 if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); }
1829
1830 if (gNotification) // If notification already on-screen, update it in place
1831 CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary);
1832 else // else, we need to create it
1833 {
1834 SInt32 error;
1835 gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary);
1836 if (!gNotification) { LogMsg("ShowNameConflictNotification: CFUserNotificationRef"); return; }
1837 gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0);
1838 if (!gNotificationRLS) { LogMsg("ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; }
1839 CFRunLoopAddSource(CFRunLoopGetCurrent(), gNotificationRLS, kCFRunLoopDefaultMode);
1840 }
1841
1842 CFRelease(dictionary);
1843 }
1844 #endif /* NO_CFUSERNOTIFICATION */
1845
1846 // This updates either the text of the field currently labelled "Local Hostname",
1847 // or the text of the field currently labelled "Computer Name"
1848 // in the Sharing Prefs Control Panel
1849 mDNSlocal void RecordUpdatedName(const mDNS *const m, const domainlabel *const olddl, const domainlabel *const newdl,
1850 const char *const msg, const char *const suffix, const CFStringRef subtext)
1851 {
1852 char oldname[MAX_DOMAIN_LABEL+1];
1853 char newname[MAX_DOMAIN_LABEL+1];
1854 ConvertDomainLabelToCString_unescaped(olddl, oldname);
1855 ConvertDomainLabelToCString_unescaped(newdl, newname);
1856 const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8);
1857 const CFStringRef cfnewname = CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8);
1858 const CFStringRef f1 = CFStringCreateWithCString(NULL, " “%@%s” ", kCFStringEncodingUTF8);
1859 const CFStringRef f2 = CFStringCreateWithCString(NULL, " “%@%s” ", kCFStringEncodingUTF8);
1860 const SCPreferencesRef session = SCPreferencesCreate(NULL, CFSTR("mDNSResponder"), NULL);
1861 if (!cfoldname || !cfnewname || !f1 || !f2 || !session || !SCPreferencesLock(session, 0)) // If we can't get the lock don't wait
1862 LogMsg("RecordUpdatedName: ERROR: Couldn't create SCPreferences session");
1863 else
1864 {
1865 const CFStringRef s0 = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8);
1866 const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, f1, cfoldname, suffix);
1867 const CFStringRef s2 = CFStringCreateWithFormat(NULL, NULL, f2, cfnewname, suffix);
1868 // On Tiger and later, if we pass an array instead of a string, CFUserNotification will translate each
1869 // element of the array individually for us, and then concatenate the results to make the final message.
1870 // This lets us have the relevant bits localized, but not the literal names, which should not be translated.
1871 // On Panther this does not work, so we just build the string directly, and it will not be translated.
1872 const CFMutableStringRef alertHeader =
1873 (OSXVers < 8) ? CFStringCreateMutable(NULL, 0) : (CFMutableStringRef)CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1874 Boolean result;
1875 if (newdl == &gNotificationPrefHostLabel) result = SCPreferencesSetLocalHostName(session, cfnewname);
1876 else result = SCPreferencesSetComputerName(session, cfnewname, kCFStringEncodingUTF8);
1877 if (!result || !SCPreferencesCommitChanges(session) || !SCPreferencesApplyChanges(session) || !s0 || !s1 || !s2 || !alertHeader)
1878 LogMsg("RecordUpdatedName: ERROR: Couldn't update SCPreferences");
1879 else if (m->p->NotifyUser)
1880 {
1881 #ifndef NO_CFUSERNOTIFICATION
1882 uid_t uid;
1883 gid_t gid;
1884 CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid);
1885 if (userName)
1886 {
1887 CFRelease(userName);
1888 typedef void CFStringAppendFN(CFMutableStringRef theString, CFStringRef appendedString);
1889 CFStringAppendFN *const append = (OSXVers < 8) ? &CFStringAppend : (CFStringAppendFN*)&CFArrayAppendValue;
1890 append(alertHeader, s0);
1891 append(alertHeader, s1);
1892 append(alertHeader, CFSTR("is already in use on this network."));
1893 append(alertHeader, CFSTR(" "));
1894 append(alertHeader, CFSTR("The name has been changed to"));
1895 append(alertHeader, s2);
1896 append(alertHeader, CFSTR("automatically."));
1897 ShowNameConflictNotification(alertHeader, subtext);
1898 }
1899 #else
1900 (void)subtext;
1901 #endif /* NO_CFUSERNOTIFICATION */
1902 }
1903 if (s0) CFRelease(s0);
1904 if (s1) CFRelease(s1);
1905 if (s2) CFRelease(s2);
1906 if (alertHeader) CFRelease(alertHeader);
1907 SCPreferencesUnlock(session);
1908 }
1909 if (cfoldname) CFRelease(cfoldname);
1910 if (cfnewname) CFRelease(cfnewname);
1911 if (f1) CFRelease(f1);
1912 if (f2) CFRelease(f2);
1913 if (session) CFRelease(session);
1914 }
1915
1916 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
1917 {
1918 (void)m; // Unused
1919 if (result == mStatus_NoError)
1920 {
1921 // One second pause in case we get a Computer Name update too -- don't want to alert the user twice
1922 RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond);
1923 }
1924 else if (result == mStatus_ConfigChanged)
1925 {
1926 // If the user-specified hostlabel from System Configuration has changed since the last time
1927 // we saw it, and *we* didn't change it, then that implies that the user has changed it,
1928 // so we auto-dismiss the name conflict alert.
1929 if (!SameDomainLabel(m->p->userhostlabel.c, gNotificationPrefHostLabel.c) ||
1930 !SameDomainLabel(m->p->usernicelabel.c, gNotificationPrefNiceLabel.c))
1931 {
1932 gNotificationUserHostLabel = gNotificationPrefHostLabel = m->p->userhostlabel;
1933 gNotificationUserNiceLabel = gNotificationPrefNiceLabel = m->p->usernicelabel;
1934 #ifndef NO_CFUSERNOTIFICATION
1935 // If we're showing a name conflict notification, and the user has manually edited
1936 // the name to remedy the conflict, we should now remove the notification window.
1937 if (gNotificationRLS) CFUserNotificationCancel(gNotification);
1938 #endif /* NO_CFUSERNOTIFICATION */
1939 }
1940
1941 DNSServiceRegistration *r;
1942 for (r = DNSServiceRegistrationList; r; r=r->next)
1943 if (r->autoname)
1944 {
1945 ServiceInstance *si;
1946 for (si = r->regs; si; si = si->next)
1947 {
1948 if (!SameDomainLabel(si->name.c, m->nicelabel.c))
1949 {
1950 debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c);
1951 si->autorename = mDNStrue;
1952 if (mDNS_DeregisterService(m, &si->srs)) // If service deregistered already, we can re-register immediately
1953 RegCallback(m, &si->srs, mStatus_MemFree);
1954 }
1955 }
1956 }
1957 udsserver_handle_configchange();
1958 }
1959 else if (result == mStatus_GrowCache)
1960 {
1961 // Allocate another chunk of cache storage
1962 CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE);
1963 if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
1964 }
1965 }
1966
1967 //*************************************************************************************************************
1968 // Add / Update / Remove records from existing Registration
1969
1970 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1971 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
1972 {
1973 // Check client parameter
1974 uint32_t id;
1975 mStatus err = mStatus_NoError;
1976 const char *errormsg = "Unknown";
1977 DNSServiceRegistration *x = DNSServiceRegistrationList;
1978 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1979 ServiceInstance *si;
1980 size_t size;
1981 (void)unusedserver; // Unused
1982 while (x && x->ClientMachPort != client) x = x->next;
1983 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1984
1985 // Check other parameters
1986 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1987 if (data_len > sizeof(RDataBody)) size = data_len;
1988 else size = sizeof(RDataBody);
1989
1990 id = x->NextRef++;
1991 *reference = (natural_t)id;
1992 for (si = x->regs; si; si = si->next)
1993 {
1994 // Allocate memory, and handle failure
1995 ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
1996 if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1997
1998 // Fill in type, length, and data of new record
1999 extra->r.resrec.rrtype = type;
2000 extra->r.rdatastorage.MaxRDLength = size;
2001 extra->r.resrec.rdlength = data_len;
2002 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
2003
2004 // Do the operation
2005 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
2006 client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
2007 err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl);
2008
2009 if (err)
2010 {
2011 freeL("Extra Resource Record", extra);
2012 errormsg = "mDNS_AddRecordToService";
2013 goto fail;
2014 }
2015
2016 extra->ClientID = id;
2017 }
2018
2019 return mStatus_NoError;
2020
2021 fail:
2022 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, x->name.c, type, data_len, errormsg, err);
2023 return mStatus_UnknownErr;
2024 }
2025
2026 mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
2027 {
2028 (void)m; // Unused
2029 if (OldRData != &rr->rdatastorage)
2030 freeL("Old RData", OldRData);
2031 }
2032
2033 mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
2034 {
2035 // Check client parameter
2036 mStatus err = mStatus_NoError;
2037 const char *errormsg = "Unknown";
2038 domainname *name = (domainname *)"";
2039
2040 name = srs->RR_SRV.resrec.name;
2041
2042 unsigned int size = sizeof(RDataBody);
2043 if (size < data_len)
2044 size = data_len;
2045
2046 // Allocate memory, and handle failure
2047 RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
2048 if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
2049
2050 // Fill in new length, and data
2051 newrdata->MaxRDLength = size;
2052 memcpy(&newrdata->u, data, data_len);
2053
2054 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2055 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2056 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2057 if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; }
2058
2059 // Do the operation
2060 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
2061 client, srs->RR_SRV.resrec.name->c, data_len);
2062
2063 err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
2064 if (err)
2065 {
2066 errormsg = "mDNS_Update";
2067 freeL("RData", newrdata);
2068 return err;
2069 }
2070 return(mStatus_NoError);
2071
2072 fail:
2073 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%ld)", client, name->c, data_len, errormsg, err);
2074 return(err);
2075 }
2076
2077 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
2078 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
2079 {
2080 // Check client parameter
2081 mStatus err = mStatus_NoError;
2082 const char *errormsg = "Unknown";
2083 domainname *name = (domainname *)"";
2084 ServiceInstance *si;
2085
2086 (void)unusedserver; // unused
2087 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
2088 DNSServiceRegistration *x = DNSServiceRegistrationList;
2089 while (x && x->ClientMachPort != client) x = x->next;
2090 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
2091
2092 // Check other parameters
2093 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
2094
2095 for (si = x->regs; si; si = si->next)
2096 {
2097 AuthRecord *r = NULL;
2098
2099 // Find the record we're updating. NULL reference means update the primary TXT record
2100 if (!reference) r = &si->srs.RR_TXT;
2101 else
2102 {
2103 ExtraResourceRecord *ptr;
2104 for (ptr = si->srs.Extras; ptr; ptr = ptr->next)
2105 {
2106 if ((natural_t)ptr->ClientID == reference)
2107 { r = &ptr->r; break; }
2108 }
2109 if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
2110 }
2111 err = UpdateRecord(&si->srs, client, r, data, data_len, ttl);
2112 if (err) goto fail; //!!!KRS this will cause failures for non-local defaults!
2113 }
2114
2115 return mStatus_NoError;
2116
2117 fail:
2118 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err);
2119 return(err);
2120 }
2121
2122 mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
2123 {
2124 domainname *name = srs->RR_SRV.resrec.name;
2125 mStatus err = mStatus_NoError;
2126
2127 // Do the operation
2128 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c);
2129
2130 err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra);
2131 if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err);
2132
2133 return err;
2134 }
2135
2136 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
2137 natural_t reference)
2138 {
2139 // Check client parameter
2140 (void)unusedserver; // Unused
2141 mStatus err = mStatus_NoError;
2142 const char *errormsg = "Unknown";
2143 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
2144 DNSServiceRegistration *x = DNSServiceRegistrationList;
2145 ServiceInstance *si;
2146
2147 while (x && x->ClientMachPort != client) x = x->next;
2148 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
2149
2150 for (si = x->regs; si; si = si->next)
2151 {
2152 ExtraResourceRecord *e;
2153 for (e = si->srs.Extras; e; e = e->next)
2154 {
2155 if ((natural_t)e->ClientID == reference)
2156 {
2157 err = RemoveRecord(&si->srs, e, client);
2158 break;
2159 }
2160 }
2161 if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
2162 }
2163
2164 return mStatus_NoError;
2165
2166 fail:
2167 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err);
2168 return(err);
2169 }
2170
2171 //*************************************************************************************************************
2172 // Support Code
2173
2174 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
2175 {
2176 mig_reply_error_t *request = msg;
2177 mig_reply_error_t *reply;
2178 mach_msg_return_t mr;
2179 int options;
2180 (void)port; // Unused
2181 (void)size; // Unused
2182 (void)info; // Unused
2183
2184 /* allocate a reply buffer */
2185 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
2186
2187 /* call the MiG server routine */
2188 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
2189
2190 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
2191 {
2192 if (reply->RetCode == MIG_NO_REPLY)
2193 {
2194 /*
2195 * This return code is a little tricky -- it appears that the
2196 * demux routine found an error of some sort, but since that
2197 * error would not normally get returned either to the local
2198 * user or the remote one, we pretend it's ok.
2199 */
2200 CFAllocatorDeallocate(NULL, reply);
2201 return;
2202 }
2203
2204 /*
2205 * destroy any out-of-line data in the request buffer but don't destroy
2206 * the reply port right (since we need that to send an error message).
2207 */
2208 request->Head.msgh_remote_port = MACH_PORT_NULL;
2209 mach_msg_destroy(&request->Head);
2210 }
2211
2212 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
2213 {
2214 /* no reply port, so destroy the reply */
2215 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
2216 mach_msg_destroy(&reply->Head);
2217 CFAllocatorDeallocate(NULL, reply);
2218 return;
2219 }
2220
2221 /*
2222 * send reply.
2223 *
2224 * We don't want to block indefinitely because the client
2225 * isn't receiving messages from the reply port.
2226 * If we have a send-once right for the reply port, then
2227 * this isn't a concern because the send won't block.
2228 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
2229 * To avoid falling off the kernel's fast RPC path unnecessarily,
2230 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
2231 */
2232
2233 options = MACH_SEND_MSG;
2234 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
2235 options |= MACH_SEND_TIMEOUT;
2236
2237 mr = mach_msg(&reply->Head, /* msg */
2238 options, /* option */
2239 reply->Head.msgh_size, /* send_size */
2240 0, /* rcv_size */
2241 MACH_PORT_NULL, /* rcv_name */
2242 MACH_MSG_TIMEOUT_NONE, /* timeout */
2243 MACH_PORT_NULL); /* notify */
2244
2245 /* Has a message error occurred? */
2246 switch (mr)
2247 {
2248 case MACH_SEND_INVALID_DEST:
2249 case MACH_SEND_TIMED_OUT:
2250 /* the reply can't be delivered, so destroy it */
2251 mach_msg_destroy(&reply->Head);
2252 break;
2253
2254 default :
2255 /* Includes success case. */
2256 break;
2257 }
2258
2259 CFAllocatorDeallocate(NULL, reply);
2260 }
2261
2262 mDNSlocal kern_return_t registerBootstrapService()
2263 {
2264 kern_return_t status;
2265 mach_port_t service_send_port, service_rcv_port;
2266
2267 debugf("Registering Bootstrap Service");
2268
2269 /*
2270 * See if our service name is already registered and if we have privilege to check in.
2271 */
2272 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
2273 if (status == KERN_SUCCESS)
2274 {
2275 /*
2276 * If so, we must be a followup instance of an already defined server. In that case,
2277 * the bootstrap port we inherited from our parent is the server's privilege port, so set
2278 * that in case we have to unregister later (which requires the privilege port).
2279 */
2280 server_priv_port = bootstrap_port;
2281 restarting_via_mach_init = TRUE;
2282 }
2283 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
2284 {
2285 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
2286 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
2287 if (status != KERN_SUCCESS) return status;
2288
2289 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
2290 if (status != KERN_SUCCESS)
2291 {
2292 mach_port_deallocate(mach_task_self(), server_priv_port);
2293 return status;
2294 }
2295
2296 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
2297 if (status != KERN_SUCCESS)
2298 {
2299 mach_port_deallocate(mach_task_self(), server_priv_port);
2300 mach_port_deallocate(mach_task_self(), service_send_port);
2301 return status;
2302 }
2303 assert(service_send_port == service_rcv_port);
2304 }
2305
2306 /*
2307 * We have no intention of responding to requests on the service port. We are not otherwise
2308 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
2309 * So, we can dispose of all the rights we have for the service port. We don't destroy the
2310 * send right for the server's privileged bootstrap port - in case we have to unregister later.
2311 */
2312 mach_port_destroy(mach_task_self(), service_rcv_port);
2313 return status;
2314 }
2315
2316 mDNSlocal kern_return_t destroyBootstrapService()
2317 {
2318 debugf("Destroying Bootstrap Service");
2319 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
2320 }
2321
2322 mDNSlocal void ExitCallback(int signal)
2323 {
2324 LogMsgIdent(mDNSResponderVersionString, "stopping");
2325
2326 debugf("ExitCallback");
2327 if (!mDNS_DebugMode && !started_via_launchdaemon && signal != SIGHUP)
2328 destroyBootstrapService();
2329
2330 debugf("ExitCallback: Aborting MIG clients");
2331 while (DNSServiceDomainEnumerationList)
2332 AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
2333 while (DNSServiceBrowserList)
2334 AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
2335 while (DNSServiceResolverList)
2336 AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
2337 while (DNSServiceRegistrationList)
2338 AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
2339
2340 debugf("ExitCallback: mDNS_Close");
2341 mDNS_Close(&mDNSStorage);
2342 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
2343 exit(0);
2344 }
2345
2346 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
2347 mDNSlocal void HandleSIG(int signal)
2348 {
2349 debugf(" ");
2350 debugf("HandleSIG %d", signal);
2351 mach_msg_header_t header;
2352 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
2353 header.msgh_remote_port = signal_port;
2354 header.msgh_local_port = MACH_PORT_NULL;
2355 header.msgh_size = sizeof(header);
2356 header.msgh_id = signal;
2357 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
2358 {
2359 LogMsg("HandleSIG %d: mach_msg_send failed", signal);
2360 if (signal == SIGHUP || signal == SIGTERM || signal == SIGINT) exit(-1);
2361 }
2362 }
2363
2364 mDNSlocal void INFOCallback(void)
2365 {
2366 mDNSs32 utc = mDNSPlatformUTC();
2367 DNSServiceDomainEnumeration *e;
2368 DNSServiceBrowser *b;
2369 DNSServiceResolver *l;
2370 DNSServiceRegistration *r;
2371 NetworkInterfaceInfoOSX *i;
2372 DNSServer *s;
2373
2374 LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----");
2375
2376 udsserver_info(&mDNSStorage);
2377
2378 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
2379 LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
2380
2381 for (b = DNSServiceBrowserList; b; b=b->next)
2382 {
2383 DNSServiceBrowserQuestion *qptr;
2384 for (qptr = b->qlist; qptr; qptr = qptr->next)
2385 LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c);
2386 }
2387 for (l = DNSServiceResolverList; l; l=l->next)
2388 LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
2389
2390 for (r = DNSServiceRegistrationList; r; r=r->next)
2391 {
2392 ServiceInstance *si;
2393 for (si = r->regs; si; si = si->next)
2394 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));
2395 }
2396
2397 for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
2398 {
2399 if (!i->Exists)
2400 LogMsgNoIdent("Interface: %s %5s(%lu) %.6a DORMANT %d",
2401 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID, utc - i->LastSeen);
2402 else
2403 LogMsgNoIdent("Interface: %s %5s(%lu) %.6a %s %s %2d %s %2d InterfaceID %p %s %s %#a",
2404 i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID,
2405 i->ifinfo.InterfaceActive ? "Active" : " ",
2406 i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4,
2407 i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6,
2408 i->ifinfo.InterfaceID,
2409 i->ifinfo.Advertise ? "Adv" : " ",
2410 i->ifinfo.McastTxRx ? "TxRx" : " ",
2411 &i->ifinfo.ip);
2412 }
2413
2414 for (s = mDNSStorage.uDNS_info.Servers; s; s = s->next)
2415 LogMsgNoIdent("DNS Server %#a %##s", &s->addr, s->domain.c);
2416
2417 LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----");
2418 }
2419
2420 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
2421 {
2422 (void)port; // Unused
2423 (void)size; // Unused
2424 (void)info; // Unused
2425 mach_msg_header_t *m = (mach_msg_header_t *)msg;
2426 switch(m->msgh_id)
2427 {
2428 case SIGHUP:
2429 case SIGINT:
2430 case SIGTERM: ExitCallback(m->msgh_id); break;
2431 case SIGINFO: INFOCallback(); break;
2432 case SIGUSR1: LogMsg("SIGUSR1: Simulate Network Configuration Change Event");
2433 mDNSMacOSXNetworkChanged(&mDNSStorage); break;
2434 default: LogMsg("SignalCallback: Unknown signal %d", m->msgh_id); break;
2435 }
2436 }
2437
2438 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
2439 // On 10.3 and later, the MachServerName is com.apple.mDNSResponder
2440
2441 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
2442 {
2443 mStatus err;
2444 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
2445 CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
2446 CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL);
2447 mach_port_t m_port = CFMachPortGetPort(s_port);
2448 char *MachServerName = OSXVers < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
2449 kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
2450 CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
2451 CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
2452 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
2453
2454 if (status)
2455 {
2456 if (status == 1103)
2457 LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
2458 else
2459 LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
2460 return(status);
2461 }
2462
2463 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
2464 rrcachestorage, RR_CACHE_SIZE,
2465 mDNS_Init_AdvertiseLocalAddresses,
2466 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
2467
2468 if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); }
2469
2470 gNotificationUserHostLabel = gNotificationPrefHostLabel = PlatformStorage.userhostlabel;
2471 gNotificationUserNiceLabel = gNotificationPrefNiceLabel = PlatformStorage.usernicelabel;
2472
2473 client_death_port = CFMachPortGetPort(d_port);
2474 signal_port = CFMachPortGetPort(i_port);
2475
2476 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode);
2477 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
2478 CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode);
2479 CFRelease(d_rls);
2480 CFRelease(s_rls);
2481 CFRelease(i_rls);
2482 if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
2483 return(err);
2484 }
2485
2486 mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
2487 {
2488 mDNSs32 now = mDNS_TimeNow(m);
2489
2490 // 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
2491 // Detailed reason:
2492 // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
2493 // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
2494 // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
2495 // we then systematically lose our own looped-back packets.
2496 if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);
2497
2498 // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
2499 mDNSs32 nextevent = mDNS_Execute(m);
2500
2501 if (m->p->NetworkChanged)
2502 if (nextevent - m->p->NetworkChanged > 0)
2503 nextevent = m->p->NetworkChanged;
2504
2505 // 3. Deliver any waiting browse messages to clients
2506 DNSServiceBrowser *b = DNSServiceBrowserList;
2507
2508 while (b)
2509 {
2510 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
2511 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
2512 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
2513 DNSServiceBrowser *x = b;
2514 b = b->next;
2515 if (x->results) // Try to deliver the list of results
2516 {
2517 while (x->results)
2518 {
2519 DNSServiceBrowserResult *const r = x->results;
2520 domainlabel name;
2521 domainname type, domain;
2522 DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance()
2523 char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2524 char ctype[MAX_ESCAPED_DOMAIN_NAME];
2525 char cdom [MAX_ESCAPED_DOMAIN_NAME];
2526 ConvertDomainLabelToCString_unescaped(&name, cname);
2527 ConvertDomainNameToCString(&type, ctype);
2528 ConvertDomainNameToCString(&domain, cdom);
2529 DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
2530 kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
2531 // If we failed to send the mach message, try again in one second
2532 if (status == MACH_SEND_TIMED_OUT)
2533 {
2534 if (nextevent - now > mDNSPlatformOneSecond)
2535 nextevent = now + mDNSPlatformOneSecond;
2536 break;
2537 }
2538 else
2539 {
2540 x->lastsuccess = now;
2541 x->results = x->results->next;
2542 freeL("DNSServiceBrowserResult", r);
2543 }
2544 }
2545 // If this client hasn't read a single message in the last 60 seconds, abort it
2546 if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
2547 AbortBlockedClient(x->ClientMachPort, "browse", x);
2548 }
2549 }
2550
2551 DNSServiceResolver *l;
2552 for (l = DNSServiceResolverList; l; l=l->next)
2553 if (l->ReportTime && now - l->ReportTime >= 0)
2554 {
2555 l->ReportTime = 0;
2556 LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
2557 "This places considerable burden on the network.", l->i.name.c);
2558 }
2559
2560 if (m->p->NotifyUser)
2561 {
2562 if (m->p->NotifyUser - now < 0)
2563 {
2564 if (!SameDomainLabel(m->p->usernicelabel.c, m->nicelabel.c))
2565 {
2566 LogMsg("Updating Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c);
2567 gNotificationPrefNiceLabel = m->p->usernicelabel = m->nicelabel;
2568 RecordUpdatedName(m, &gNotificationUserNiceLabel, &gNotificationPrefNiceLabel, "The name of your computer", "",
2569 CFSTR("To change the name of your computer, open System Preferences and click Sharing. "
2570 "Then type the name in the Computer Name field."));
2571 // Clear m->p->NotifyUser here -- even if the hostlabel has changed too, we don't want to bug the user with *two* alerts
2572 m->p->NotifyUser = 0;
2573 }
2574 if (!SameDomainLabel(m->p->userhostlabel.c, m->hostlabel.c))
2575 {
2576 LogMsg("Updating Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
2577 gNotificationPrefHostLabel = m->p->userhostlabel = m->hostlabel;
2578 RecordUpdatedName(m, &gNotificationUserHostLabel, &gNotificationPrefHostLabel, "This computer’s local hostname", ".local",
2579 CFSTR("To change the local hostname, open System Preferences and click Sharing. "
2580 "Then click Edit and type the name in the Local Hostname field."));
2581 }
2582 m->p->NotifyUser = 0;
2583 }
2584 else
2585 if (nextevent - m->p->NotifyUser > 0)
2586 nextevent = m->p->NotifyUser;
2587 }
2588
2589 return(nextevent);
2590 }
2591
2592 mDNSlocal void ShowTaskSchedulingError(mDNS *const m)
2593 {
2594 mDNS_Lock(m);
2595
2596 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
2597
2598 // NOTE: To accurately diagnose *why* we're busy, the debugging code here to show needs to mirror the logic in GetNextScheduledEvent
2599
2600 if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
2601 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
2602 m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
2603 if (m->NewLocalOnlyQuestions)
2604 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
2605 m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
2606 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords))
2607 LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, m->NewLocalRecords));
2608 if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
2609 LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending);
2610 #ifndef UNICAST_DISABLED
2611 if (m->timenow - m->uDNS_info.nextevent >= 0)
2612 LogMsg("Task Scheduling Error: m->uDNS_info.nextevent %d", m->timenow - m->uDNS_info.nextevent);
2613 #endif
2614 if (m->timenow - m->NextCacheCheck >= 0)
2615 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck);
2616 if (m->timenow - m->NextScheduledQuery >= 0)
2617 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery);
2618 if (m->timenow - m->NextScheduledProbe >= 0)
2619 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe);
2620 if (m->timenow - m->NextScheduledResponse >= 0)
2621 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
2622
2623 mDNS_Unlock(&mDNSStorage);
2624 }
2625
2626 mDNSexport int main(int argc, char **argv)
2627 {
2628 int i;
2629 kern_return_t status;
2630
2631 for (i=1; i<argc; i++)
2632 {
2633 if (!strcmp(argv[i], "-d")) mDNS_DebugMode = mDNStrue;
2634 if (!strcmp(argv[i], "-launchdaemon")) started_via_launchdaemon = mDNStrue;
2635 }
2636
2637 signal(SIGHUP, HandleSIG); // (Debugging) Exit cleanly and let mach_init restart us (for debugging)
2638 signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
2639 signal(SIGPIPE, SIG_IGN ); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
2640 signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
2641 signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog
2642 signal(SIGUSR1, HandleSIG); // (Debugging) Simulate network change notification from System Configuration Framework
2643
2644 // Register the server with mach_init for automatic restart only during normal (non-debug) mode
2645 if (!mDNS_DebugMode && !started_via_launchdaemon)
2646 {
2647 registerBootstrapService();
2648 if (!restarting_via_mach_init) exit(0); // mach_init will restart us immediately as a daemon
2649 int fd = open(_PATH_DEVNULL, O_RDWR, 0);
2650 if (fd < 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno, strerror(errno));
2651 else
2652 {
2653 // Avoid unnecessarily duplicating a file descriptor to itself
2654 if (fd != STDIN_FILENO) if (dup2(fd, STDIN_FILENO) < 0) LogMsg("dup2(fd, STDIN_FILENO) failed errno %d (%s)", errno, strerror(errno));
2655 if (fd != STDOUT_FILENO) if (dup2(fd, STDOUT_FILENO) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno, strerror(errno));
2656 if (fd != STDERR_FILENO) if (dup2(fd, STDERR_FILENO) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno, strerror(errno));
2657 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) (void)close(fd);
2658 }
2659 }
2660
2661 // Make our PID file and Unix Domain Socket first, because launchd waits for those before it starts launching other daemons.
2662 // The sooner we do this, the faster the machine will boot.
2663 status = udsserver_init();
2664 if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
2665
2666 // First do the all the initialization we need root privilege for, before we change to user "nobody"
2667 LogMsgIdent(mDNSResponderVersionString, "starting");
2668 OSXVers = mDNSMacOSXSystemBuildNumber(NULL);
2669 status = mDNSDaemonInitialize();
2670
2671 #if CAN_UPDATE_DYNAMIC_STORE_WITHOUT_BEING_ROOT
2672 // Now that we're finished with anything privileged, switch over to running as "nobody"
2673 const struct passwd *pw = getpwnam("nobody");
2674 if (pw != NULL)
2675 setuid(pw->pw_uid);
2676 else
2677 setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database
2678 #endif
2679
2680 if (status == 0)
2681 {
2682 LogOperation("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last);
2683 int numevents = 0;
2684 int RunLoopStatus = kCFRunLoopRunTimedOut;
2685
2686 // This is the main work loop:
2687 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
2688 // (2) Then we make sure we've delivered all waiting browse messages to our clients
2689 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
2690 // (4) On wakeup we first process *all* events
2691 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
2692 while (RunLoopStatus == kCFRunLoopRunTimedOut)
2693 {
2694 // 1. Before going into a blocking wait call and letting our process to go sleep,
2695 // call mDNSDaemonIdle to allow any deferred work to be completed.
2696 mDNSs32 nextevent = mDNSDaemonIdle(&mDNSStorage);
2697 nextevent = udsserver_idle(nextevent);
2698
2699 // 2. Work out how long we expect to sleep before the next scheduled task
2700 mDNSs32 ticks = nextevent - mDNS_TimeNow(&mDNSStorage);
2701 static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins
2702 if (ticks > 1)
2703 RepeatedBusy = 0;
2704 else
2705 {
2706 ticks = 1;
2707 if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
2708 }
2709 CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond;
2710
2711 // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
2712 // (a) our next wakeup time, or (b) an event occurs.
2713 // The 'true' parameter makes it return after handling any event that occurs
2714 // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
2715 verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks);
2716 numevents = 0;
2717 RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true);
2718
2719 // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
2720 while (RunLoopStatus == kCFRunLoopRunHandledSource)
2721 {
2722 numevents++;
2723 RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
2724 }
2725 }
2726
2727 LogMsg("ERROR: CFRunLoopRun Exiting.");
2728 mDNS_Close(&mDNSStorage);
2729 }
2730
2731 LogMsgIdent(mDNSResponderVersionString, "exiting");
2732
2733 exit:
2734 if (!mDNS_DebugMode && !started_via_launchdaemon) destroyBootstrapService();
2735 return(status);
2736 }
2737
2738 // uds_daemon.c support routines /////////////////////////////////////////////
2739
2740 // We keep a list of client-supplied event sources in PosixEventSource records
2741 struct CFSocketEventSource
2742 {
2743 udsEventCallback Callback;
2744 void *Context;
2745 int fd;
2746 struct CFSocketEventSource *Next;
2747 CFSocketRef cfs;
2748 CFRunLoopSourceRef RLS;
2749 };
2750 typedef struct CFSocketEventSource CFSocketEventSource;
2751
2752 static GenLinkedList gEventSources; // linked list of CFSocketEventSource's
2753
2754 mDNSlocal void cf_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i)
2755 // Called by CFSocket when data appears on socket
2756 {
2757 (void)s; // Unused
2758 (void)t; // Unused
2759 (void)dr; // Unused
2760 (void)c; // Unused
2761 CFSocketEventSource *source = (CFSocketEventSource*) i;
2762 source->Callback(source->Context);
2763 }
2764
2765 mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
2766 // Arrange things so that callback is called with context when data appears on fd
2767 {
2768 CFSocketEventSource *newSource;
2769 CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL };
2770
2771 if (gEventSources.LinkOffset == 0)
2772 InitLinkedList(&gEventSources, offsetof(CFSocketEventSource, Next));
2773
2774 if (fd >= FD_SETSIZE || fd < 0)
2775 return mStatus_UnsupportedErr;
2776 if (callback == NULL)
2777 return mStatus_BadParamErr;
2778
2779 newSource = (CFSocketEventSource*) calloc(1, sizeof *newSource);
2780 if (NULL == newSource)
2781 return mStatus_NoMemoryErr;
2782
2783 newSource->Callback = callback;
2784 newSource->Context = context;
2785 newSource->fd = fd;
2786
2787 cfContext.info = newSource;
2788 if ( NULL != (newSource->cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack,
2789 cf_callback, &cfContext)) &&
2790 NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->cfs, 0)))
2791 {
2792 CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode);
2793 AddToTail(&gEventSources, newSource);
2794 }
2795 else
2796 {
2797 if (newSource->cfs)
2798 {
2799 CFSocketInvalidate(newSource->cfs); // Note: Also closes the underlying socket
2800 CFRelease(newSource->cfs);
2801 }
2802 return mStatus_NoMemoryErr;
2803 }
2804
2805 return mStatus_NoError;
2806 }
2807
2808 mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor
2809 // Reverse what was done in udsSupportAddFDToEventLoop().
2810 {
2811 CFSocketEventSource *iSource;
2812
2813 for (iSource=(CFSocketEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
2814 {
2815 if (fd == iSource->fd)
2816 {
2817 RemoveFromList(&gEventSources, iSource);
2818 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode);
2819 CFRunLoopSourceInvalidate(iSource->RLS);
2820 CFRelease(iSource->RLS);
2821 CFSocketInvalidate(iSource->cfs); // Note: Also closes the underlying socket
2822 CFRelease(iSource->cfs);
2823 free(iSource);
2824 return mStatus_NoError;
2825 }
2826 }
2827 return mStatus_NoSuchNameErr;
2828 }
2829
2830 // If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
2831 const char *__crashreporter_info__ = mDNSResponderVersionString;
2832 asm(".desc ___crashreporter_info__, 0x10");
2833
2834 // For convenience when using the "strings" command, this is the last thing in the file
2835 mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";