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