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