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