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