]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/CFSocket.c
388cdf27f5c3410f65a149549531e86fb2396a2c
[apple/mdnsresponder.git] / mDNSMacOSX / CFSocket.c
1 /*
2 * Copright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24
25 Change History (most recent first):
26
27 $Log: CFSocket.c,v $
28 Revision 1.157 2004/06/08 18:54:48 ksekar
29 <rdar://problem/3681378>: mDNSResponder leaks after exploring in Printer Setup Utility
30
31 Revision 1.156 2004/06/05 00:04:26 cheshire
32 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
33
34 Revision 1.155 2004/06/04 08:58:30 ksekar
35 <rdar://problem/3668624>: Keychain integration for secure dynamic update
36
37 Revision 1.154 2004/05/31 22:22:28 ksekar
38 <rdar://problem/3668639>: wide-area domains should be returned in
39 reg. domain enumeration
40
41 Revision 1.153 2004/05/26 17:06:33 cheshire
42 <rdar://problem/3668515>: Don't rely on CFSocketInvalidate() to remove RunLoopSource
43
44 Revision 1.152 2004/05/18 23:51:26 cheshire
45 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
46
47 Revision 1.151 2004/05/17 21:46:34 cheshire
48 <rdar://problem/3616426>: When interface is turned off, browse "remove" events are delivered with interface index zero
49 Take care to correctly update InterfaceIDs when a dormant interface comes back to life
50
51 Revision 1.150 2004/05/13 04:54:20 ksekar
52 Unified list copy/free code. Added symetric list for
53
54 Revision 1.149 2004/05/13 03:55:14 ksekar
55 Fixed list traversal bug in FoundDefSearchDomain.
56
57 Revision 1.148 2004/05/12 22:03:08 ksekar
58 Made GetSearchDomainList a true platform-layer call (declaration moved
59 from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local"
60 only on non-OSX platforms. Changed call to return a copy of the list
61 to avoid shared memory issues. Added a routine to free the list.
62
63 Revision 1.147 2004/05/12 02:03:25 ksekar
64 Non-local domains will only be browsed by default, and show up in
65 _browse domain enumeration, if they contain an _browse._dns-sd ptr record.
66
67 Revision 1.146 2004/04/27 02:49:15 cheshire
68 <rdar://problem/3634655>: mDNSResponder leaks sockets on bind() error
69
70 Revision 1.145 2004/04/21 03:08:03 cheshire
71 Rename 'alias' to more descriptive name 'primary'
72
73 Revision 1.144 2004/04/21 03:04:35 cheshire
74 Minor cleanup for clarity
75
76 Revision 1.143 2004/04/21 03:03:30 cheshire
77 Preparation work: AddInterfaceToList() should return pointer to structure it creates
78
79 Revision 1.142 2004/04/21 02:49:11 cheshire
80 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
81
82 Revision 1.141 2004/04/21 02:20:47 cheshire
83 Rename interface field 'CurrentlyActive' to more descriptive 'Exists'
84
85 Revision 1.140 2004/04/14 23:09:29 ksekar
86 Support for TSIG signed dynamic updates.
87
88 Revision 1.139 2004/04/09 17:40:26 cheshire
89 Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
90
91 Revision 1.138 2004/04/09 16:37:16 cheshire
92 Suggestion from Bob Bradley:
93 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
94
95 Revision 1.137 2004/04/08 00:59:55 cheshire
96 <rdar://problem/3609972> When interface turned off, browse "remove" events delivered with interface index zero
97 Unify use of the InterfaceID field, and make code that walks the list respect the 'Exists' flag
98
99 Revision 1.136 2004/04/07 01:08:57 cheshire
100 <rdar://problem/3609972> When interface turned off, browse "remove" events delivered with interface index zero
101
102 Revision 1.135 2004/03/19 01:01:03 ksekar
103 Fixed config file parsing to chop newline
104
105 Revision 1.134 2004/03/13 01:57:34 ksekar
106 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
107
108 Revision 1.133 2004/02/02 22:46:56 cheshire
109 Move "CFRelease(dict);" inside the "if (dict)" check
110
111 Revision 1.132 2004/01/28 02:30:08 ksekar
112 Added default Search Domains to unicast browsing, controlled via
113 Networking sharing prefs pane. Stopped sending unicast messages on
114 every interface. Fixed unicast resolving via mach-port API.
115
116 Revision 1.131 2004/01/27 22:57:48 cheshire
117 <rdar://problem/3534352>: Need separate socket for issuing unicast queries
118
119 Revision 1.130 2004/01/27 22:28:40 cheshire
120 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
121 Additional lingering port 53 code deleted
122
123 Revision 1.129 2004/01/27 20:15:23 cheshire
124 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
125
126 Revision 1.128 2004/01/24 23:58:17 cheshire
127 Change to use mDNSVal16() instead of shifting and ORing
128
129 Revision 1.127 2004/01/24 04:59:16 cheshire
130 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
131
132 Revision 1.126 2004/01/23 23:23:15 ksekar
133 Added TCP support for truncated unicast messages.
134
135 Revision 1.125 2004/01/22 03:43:09 cheshire
136 Export constants like mDNSInterface_LocalOnly so that the client layers can use them
137
138 Revision 1.124 2004/01/21 21:53:19 cheshire
139 <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
140
141 Revision 1.123 2004/01/20 03:18:25 cheshire
142 Removed "LogMsg("Hey There!");" that evidently got checked in my mistake
143
144 Revision 1.122 2003/12/17 20:43:59 cheshire
145 <rdar://problem/3496728>: Syslog messages saying "sendto failed"
146
147 Revision 1.121 2003/12/13 03:05:28 ksekar
148 <rdar://problem/3192548>: DynDNS: Unicast query of service records
149
150 Revision 1.120 2003/12/08 21:00:46 rpantos
151 Changes to support mDNSResponder on Linux.
152
153 Revision 1.119 2003/12/03 02:35:15 cheshire
154 Also report value of m->timenow when logging sendto() failure
155
156 Revision 1.118 2003/11/14 20:59:09 cheshire
157 Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
158 Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file.
159
160 Revision 1.117 2003/11/08 22:18:29 cheshire
161 <rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message
162
163 Revision 1.116 2003/09/23 16:39:49 cheshire
164 When LogAllOperations is set, also report registration and deregistration of interfaces
165
166 Revision 1.115 2003/09/10 00:45:55 cheshire
167 <rdar://problem/3412328> Don't log "sendto failed" errors during the first two minutes of startup
168
169 Revision 1.114 2003/08/27 02:55:13 cheshire
170 <rdar://problem/3387910>: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down)
171
172 Revision 1.113 2003/08/19 22:20:00 cheshire
173 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
174 More minor refinements
175
176 Revision 1.112 2003/08/19 03:04:43 cheshire
177 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
178
179 Revision 1.111 2003/08/18 22:53:37 cheshire
180 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
181
182 Revision 1.110 2003/08/16 03:39:00 cheshire
183 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
184
185 Revision 1.109 2003/08/15 02:19:49 cheshire
186 <rdar://problem/3375225> syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35
187 Also limit number of messages to at most 100
188
189 Revision 1.108 2003/08/12 22:24:52 cheshire
190 <rdar://problem/3375225> syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35
191 This message indicates a kernel bug, but still we don't want to flood syslog.
192 Do a sleep(1) after writing this log message, to limit the rate.
193
194 Revision 1.107 2003/08/12 19:56:25 cheshire
195 Update to APSL 2.0
196
197 Revision 1.106 2003/08/12 13:48:32 cheshire
198 Add comment explaining clockdivisor calculation
199
200 Revision 1.105 2003/08/12 13:44:14 cheshire
201 <rdar://problem/3370229> mDNSResponder *VERY* unhappy if time goes backwards
202 Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot)
203 instead of gettimeofday() (which can jump back if the user manually changes their time/date)
204
205 Revision 1.104 2003/08/12 13:12:07 cheshire
206 Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static"
207
208 Revision 1.103 2003/08/08 18:36:04 cheshire
209 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
210
211 Revision 1.102 2003/08/06 00:14:52 cheshire
212 <rdar://problem/3330324> Need to check IP TTL on responses
213 Also add corresponding checks in the IPv6 code path
214
215 Revision 1.101 2003/08/05 22:20:16 cheshire
216 <rdar://problem/3330324> Need to check IP TTL on responses
217
218 Revision 1.100 2003/08/05 21:18:50 cheshire
219 <rdar://problem/3363185> mDNSResponder should ignore 6to4
220 Only use interfaces that are marked as multicast-capable (IFF_MULTICAST)
221
222 Revision 1.99 2003/08/05 20:13:52 cheshire
223 <rdar://problem/3294080> mDNSResponder using IPv6 interfaces before they are ready
224 Ignore interfaces with the IN6_IFF_NOTREADY flag set
225
226 Revision 1.98 2003/07/20 03:38:51 ksekar
227 <rdar://problem/3320722>
228 Completed support for Unix-domain socket based API.
229
230 Revision 1.97 2003/07/19 03:15:16 cheshire
231 Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h,
232 and add the obvious trivial implementations to each platform support layer
233
234 Revision 1.96 2003/07/18 00:30:00 cheshire
235 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
236
237 Revision 1.95 2003/07/12 03:15:20 cheshire
238 <rdar://problem/3324848> After SCDynamicStore notification, mDNSResponder updates
239 m->hostlabel even if user hasn't actually actually changed their dot-local hostname
240
241 Revision 1.94 2003/07/03 00:51:54 cheshire
242 <rdar://problem/3287213> When select() and recvmgs() disagree, get more info from kernel about the socket state
243
244 Revision 1.93 2003/07/03 00:09:14 cheshire
245 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
246 Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name);
247
248 Revision 1.92 2003/07/02 21:19:51 cheshire
249 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
250
251 Revision 1.91 2003/06/24 01:53:51 cheshire
252 Minor update to comments
253
254 Revision 1.90 2003/06/24 01:51:47 cheshire
255 <rdar://problem/3303118> Oops: Double-dispose of sockets
256 Don't need to close sockets: CFSocketInvalidate() does that for us
257
258 Revision 1.89 2003/06/21 18:12:47 cheshire
259 <rdar://problem/3296061> Rendezvous cannot handle interfaces whose total name is >3 chars
260 One-line change: should say "IF_NAMESIZE", not sizeof(ifname)
261
262 Revision 1.88 2003/06/12 23:38:37 cheshire
263 <rdar://problem/3291162> mDNSResponder doesn't detect some configuration changes
264 Also check that scope_id matches before concluding that two interfaces are the same
265
266 Revision 1.87 2003/06/10 01:14:11 cheshire
267 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
268
269 Revision 1.86 2003/05/28 02:41:52 cheshire
270 <rdar://problem/3034346> Time to remove Mac OS 9 UDP Port 53 legacy support
271
272 Revision 1.85 2003/05/28 02:39:47 cheshire
273 Minor change to debugging messages
274
275 Revision 1.84 2003/05/27 22:29:40 cheshire
276 Remove out-dated comment
277
278 Revision 1.83 2003/05/26 03:21:29 cheshire
279 Tidy up address structure naming:
280 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
281 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
282 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
283
284 Revision 1.82 2003/05/26 03:01:27 cheshire
285 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
286
287 Revision 1.81 2003/05/24 02:06:42 cheshire
288 <rdar://problem/3268480> IPv6 Multicast Loopback doesn't work
289 Tried setting IPV6_MULTICAST_LOOP; it doesn't help.
290 However, it is probably wise to have the code explicitly set this socket
291 option anyway, in case the default changes in later versions of Unix.
292
293 Revision 1.80 2003/05/24 02:02:24 cheshire
294 <rdar://problem/3221880> if_indextoname consumes a lot of CPU
295 Fix error in myIfIndexToName; was returning prematurely
296
297 Revision 1.79 2003/05/23 23:07:44 cheshire
298 <rdar://problem/3268199> Must not write to stderr when running as daemon
299
300 Revision 1.78 2003/05/23 01:19:04 cheshire
301 <rdar://problem/3267085> mDNSResponder needs to signal type of service to AirPort
302 Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate
303
304 Revision 1.77 2003/05/23 01:12:05 cheshire
305 Minor code tidying
306
307 Revision 1.76 2003/05/22 01:26:01 cheshire
308 Tidy up log messages
309
310 Revision 1.75 2003/05/22 00:07:09 cheshire
311 <rdar://problem/3264366> myCFSocketCallBack recvfrom(5) error 1, errno 35
312 Extra logging to determine whether there is a bug in CFSocket
313
314 Revision 1.74 2003/05/21 20:20:12 cheshire
315 Fix warnings (mainly printf format string warnings, like using "%d" where
316 it should say "%lu", etc.) and improve error logging (use strerror()
317 to include textual error message as well as numeric error in log messages).
318
319 Revision 1.73 2003/05/21 17:56:29 ksekar
320 <rdar://problem/3191277>: mDNSResponder doesn't watch for IPv6 address changes
321
322 Revision 1.72 2003/05/14 18:48:41 cheshire
323 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
324 More minor refinements:
325 CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
326 mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
327
328 Revision 1.71 2003/05/14 07:08:37 cheshire
329 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
330 Previously, when there was any network configuration change, mDNSResponder
331 would tear down the entire list of active interfaces and start again.
332 That was very disruptive, and caused the entire cache to be flushed,
333 and caused lots of extra network traffic. Now it only removes interfaces
334 that have really gone, and only adds new ones that weren't there before.
335
336 Revision 1.70 2003/05/07 18:30:24 cheshire
337 Fix signed/unsigned comparison warning
338
339 Revision 1.69 2003/05/06 20:14:44 cheshire
340 Change "tp" to "tv"
341
342 Revision 1.68 2003/05/06 00:00:49 cheshire
343 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
344
345 Revision 1.67 2003/04/29 00:43:44 cheshire
346 Fix compiler warnings
347
348 Revision 1.66 2003/04/26 02:41:58 cheshire
349 <rdar://problem/3241281> Change timenow from a local variable to a structure member
350
351 Revision 1.65 2003/04/26 02:34:01 cheshire
352 Add missing mDNSexport
353
354 Revision 1.64 2003/04/15 16:48:06 jgraessl
355 <rdar://problem/3228833>
356 Modified code in CFSocket notifier function to read all packets on the socket
357 instead of reading only one packet every time the notifier was called.
358
359 Revision 1.63 2003/04/15 16:33:50 jgraessl
360 <rdar://problem/3221880>
361 Switched to our own copy of if_indextoname to improve performance.
362
363 Revision 1.62 2003/03/28 01:55:44 cheshire
364 Minor improvements to debugging messages
365
366 Revision 1.61 2003/03/27 03:30:56 cheshire
367 <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
368 Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
369 Fixes:
370 1. Make mDNS_DeregisterInterface() safe to call from a callback
371 2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead
372 (it never really needed to deregister the interface at all)
373
374 Revision 1.60 2003/03/15 04:40:38 cheshire
375 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
376
377 Revision 1.59 2003/03/11 01:23:26 cheshire
378 <rdar://problem/3194246> mDNSResponder socket problems
379
380 Revision 1.58 2003/03/06 01:43:04 cheshire
381 <rdar://problem/3189097> Additional debugging code in mDNSResponder
382 Improve "LIST_ALL_INTERFACES" output
383
384 Revision 1.57 2003/03/05 22:36:27 cheshire
385 <rdar://problem/3186338> Loopback doesn't work with mDNSResponder-27
386 Temporary workaround: Skip loopback interface *only* if we found at least one v4 interface to use
387
388 Revision 1.56 2003/03/05 01:50:38 cheshire
389 <rdar://problem/3189097> Additional debugging code in mDNSResponder
390
391 Revision 1.55 2003/02/21 01:54:09 cheshire
392 <rdar://problem/3099194> mDNSResponder needs performance improvements
393 Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
394
395 Revision 1.54 2003/02/20 06:48:35 cheshire
396 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
397 Reviewed by: Josh Graessley, Bob Bradley
398
399 Revision 1.53 2003/01/29 02:21:23 cheshire
400 Return mStatus_Invalid if can't send packet because socket not available
401
402 Revision 1.52 2003/01/28 19:39:43 jgraessl
403 Enabling AAAA over IPv4 support.
404
405 Revision 1.51 2003/01/28 05:11:23 cheshire
406 Fixed backwards comparison in SearchForInterfaceByName
407
408 Revision 1.50 2003/01/13 23:49:44 jgraessl
409 Merged changes for the following fixes in to top of tree:
410 <rdar://problem/3086540> computer name changes not handled properly
411 <rdar://problem/3124348> service name changes are not properly handled
412 <rdar://problem/3124352> announcements sent in pairs, failing chattiness test
413
414 Revision 1.49 2002/12/23 22:13:30 jgraessl
415 Reviewed by: Stuart Cheshire
416 Initial IPv6 support for mDNSResponder.
417
418 Revision 1.48 2002/11/22 01:37:52 cheshire
419 <rdar://problem/3108426> mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities
420
421 Revision 1.47 2002/09/21 20:44:51 zarzycki
422 Added APSL info
423
424 Revision 1.46 2002/09/19 21:25:35 cheshire
425 mDNS_snprintf() doesn't need to be in a separate file
426
427 Revision 1.45 2002/09/17 01:45:13 cheshire
428 Add LIST_ALL_INTERFACES symbol for debugging
429
430 Revision 1.44 2002/09/17 01:36:23 cheshire
431 Move Puma support to CFSocketPuma.c
432
433 Revision 1.43 2002/09/17 01:05:28 cheshire
434 Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global
435
436 Revision 1.42 2002/09/16 23:13:50 cheshire
437 Minor code tidying
438
439 */
440
441 // ***************************************************************************
442 // mDNS-CFSocket.c:
443 // Supporting routines to run mDNS on a CFRunLoop platform
444 // ***************************************************************************
445
446 // For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces,
447 // including ones that mDNSResponder chooses not to use.
448 #define LIST_ALL_INTERFACES 0
449
450 // For enabling AAAA records over IPv4. Setting this to 0 sends only
451 // A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both
452 // AAAA and A records over both IPv4 and IPv6.
453 #define AAAA_OVER_V4 1
454
455 #include "mDNSClientAPI.h" // Defines the interface provided to the client layer above
456 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
457
458 #include <stdio.h>
459 #include <unistd.h> // For select() and close()
460 #include <stdarg.h> // For va_list support
461 #include <net/if.h>
462 #include <net/if_dl.h>
463 #include <sys/uio.h>
464 #include <sys/param.h>
465 #include <sys/socket.h>
466 #include <sys/sysctl.h>
467 #include <fcntl.h>
468 #include <sys/ioctl.h>
469 #include <time.h> // platform support for UTC time
470 #include <arpa/inet.h> // for inet_aton
471
472 #include <netinet/in.h> // For IP_RECVTTL
473 #ifndef IP_RECVTTL
474 #define IP_RECVTTL 24 // bool; receive reception TTL w/dgram
475 #endif
476
477 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
478 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
479 #include <netinet6/in6_var.h> // For IN6_IFF_NOTREADY etc.
480
481 #include <Security/Security.h>
482
483 // Code contributed by Dave Heller:
484 // Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will
485 // work on Mac OS X 10.1, which does not have the getifaddrs call.
486 #define RUN_ON_PUMA_WITHOUT_IFADDRS 0
487 #if RUN_ON_PUMA_WITHOUT_IFADDRS
488 #include "CFSocketPuma.c"
489 #else
490 #include <ifaddrs.h>
491 #endif
492
493 #include <IOKit/IOKitLib.h>
494 #include <IOKit/IOMessage.h>
495 #include <mach/mach_time.h>
496
497 typedef struct AuthRecordListElem
498 {
499 struct AuthRecordListElem *next;
500 AuthRecord ar;
501 } AuthRecordListElem;
502
503 typedef struct SearchListElem
504 {
505 struct SearchListElem *next;
506 domainname domain;
507 int flag;
508 DNSQuestion browseQ;
509 DNSQuestion registerQ;
510 AuthRecordListElem *AuthRecs;
511 } SearchListElem;
512
513
514 // ***************************************************************************
515 // Globals
516
517 static mDNSu32 clockdivisor = 0;
518 static mDNSBool DNSConfigInitialized = mDNSfalse;
519 #define MAX_SEARCH_DOMAINS 32
520
521 // for domain enumeration and default browsing
522 static SearchListElem *SearchList = NULL; // where we search for _browse domains
523 static DNSQuestion DefBrowseDomainQ; // our local enumeration query for _browse domains
524 static DNameListElem *DefBrowseList = NULL; // cache of answers to above query (where we search for empty string browses)
525
526 #define CONFIG_FILE "/etc/mDNSResponder.conf"
527 #define LH_KEYCHAIN_DESC "Lighthouse Shared Secret"
528 #define LH_KEYCHAIN_SERVICE "Lighthouse"
529 #define SYS_KEYCHAIN_PATH "/Library/Keychains/System.keychain"
530 #define LH_SUFFIX "members.mac.com."
531
532 // ***************************************************************************
533 // Macros
534
535 #define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger)
536 #define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3])
537
538 #define mDNSAddressIsAllDNSLinkGroup(X) ( \
539 ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup )) || \
540 ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) )
541
542 // ***************************************************************************
543 // Functions
544
545 mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh)
546 {
547 static struct ifaddrs *ifa = NULL;
548
549 if (refresh && ifa)
550 {
551 freeifaddrs(ifa);
552 ifa = NULL;
553 }
554
555 if (ifa == NULL) getifaddrs(&ifa);
556
557 return ifa;
558 }
559
560 mDNSlocal int myIfIndexToName(u_short index, char* name)
561 {
562 struct ifaddrs *ifa;
563 for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next)
564 if (ifa->ifa_addr->sa_family == AF_LINK)
565 if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == index)
566 { strncpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; }
567 return -1;
568 }
569
570 mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index)
571 {
572 NetworkInterfaceInfoOSX *i;
573 if (index == (uint32_t)~0) return(mDNSInterface_LocalOnly);
574 if (index)
575 for (i = m->p->InterfaceList; i; i = i->next)
576 // Don't get tricked by inactive interfaces with no InterfaceID set
577 if (i->ifinfo.InterfaceID && i->scope_id == index) return(i->ifinfo.InterfaceID);
578 return(mDNSNULL);
579 }
580
581 mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id)
582 {
583 NetworkInterfaceInfoOSX *i;
584 if (id == mDNSInterface_LocalOnly) return((mDNSu32)~0);
585 if (id)
586 for (i = m->p->InterfaceList; i; i = i->next)
587 // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces, which have no InterfaceID set
588 if ((mDNSInterfaceID)i == id) return(i->scope_id);
589 return 0;
590 }
591
592 // NOTE: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket"
593 // NOTE: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface"
594 mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
595 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort)
596 {
597 #pragma unused(m)
598
599 // Note: For this platform we've adopted the convention that InterfaceIDs are secretly pointers
600 // to the NetworkInterfaceInfoOSX structure that holds the active sockets. The mDNSCore code
601 // doesn't know that and doesn't need to know that -- it just treats InterfaceIDs as opaque identifiers.
602 NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID;
603 char *ifa_name = info ? info->ifa_name : "unicast";
604 struct sockaddr_storage to;
605 int s = -1, err;
606
607 if (dst->type == mDNSAddrType_IPv4)
608 {
609 struct sockaddr_in *sin_to = (struct sockaddr_in*)&to;
610 sin_to->sin_len = sizeof(*sin_to);
611 sin_to->sin_family = AF_INET;
612 sin_to->sin_port = dstPort.NotAnInteger;
613 sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
614 s = info ? info->ss.sktv4 : m->p->unicastsockets.sktv4;
615 }
616 else if (dst->type == mDNSAddrType_IPv6)
617 {
618 struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to;
619 sin6_to->sin6_len = sizeof(*sin6_to);
620 sin6_to->sin6_family = AF_INET6;
621 sin6_to->sin6_port = dstPort.NotAnInteger;
622 sin6_to->sin6_flowinfo = 0;
623 sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6;
624 sin6_to->sin6_scope_id = info ? info->scope_id : 0;
625 s = info ? info->ss.sktv6 : m->p->unicastsockets.sktv6;
626 }
627 else
628 {
629 LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n");
630 return mStatus_BadParamErr;
631 }
632
633 if (s >= 0)
634 verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d",
635 InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s);
636 else
637 verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %X %s/%d (socket of this type not available)",
638 InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort));
639
640 // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet
641 // If we don't have the corresponding type of socket available, then return mStatus_Invalid
642 if (s < 0) return(mStatus_Invalid);
643
644 err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len);
645 if (err < 0)
646 {
647 // Don't report EHOSTDOWN (i.e. ARP failure) to unicast destinations
648 if (errno == EHOSTDOWN && !mDNSAddressIsAllDNSLinkGroup(dst)) return(err);
649 // Don't report EHOSTUNREACH in the first three minutes after boot
650 // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>)
651 // but this means that sometimes it starts before configd has finished setting up the multicast routing entries.
652 if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(err);
653 LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s) %lu",
654 InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
655 return(err);
656 }
657
658 return(mStatus_NoError);
659 }
660
661 mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
662 struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl)
663 {
664 static unsigned int numLogMessages = 0;
665 struct iovec databuffers = { (char *)buffer, max };
666 struct msghdr msg;
667 ssize_t n;
668 struct cmsghdr *cmPtr;
669 char ancillary[1024];
670
671 *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be
672
673 // Set up the message
674 msg.msg_name = (caddr_t)from;
675 msg.msg_namelen = *fromlen;
676 msg.msg_iov = &databuffers;
677 msg.msg_iovlen = 1;
678 msg.msg_control = (caddr_t)&ancillary;
679 msg.msg_controllen = sizeof(ancillary);
680 msg.msg_flags = 0;
681
682 // Receive the data
683 n = recvmsg(s, &msg, 0);
684 if (n<0)
685 {
686 if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno);
687 return(-1);
688 }
689 if (msg.msg_controllen < (int)sizeof(struct cmsghdr))
690 {
691 if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu",
692 s, msg.msg_controllen, sizeof(struct cmsghdr));
693 return(-1);
694 }
695 if (msg.msg_flags & MSG_CTRUNC)
696 {
697 if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s);
698 return(-1);
699 }
700
701 *fromlen = msg.msg_namelen;
702
703 // Parse each option out of the ancillary data.
704 for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr))
705 {
706 // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type);
707 if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR)
708 {
709 dstaddr->type = mDNSAddrType_IPv4;
710 dstaddr->ip.v4.NotAnInteger = *(u_int32_t*)CMSG_DATA(cmPtr);
711 }
712 if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF)
713 {
714 struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr);
715 if (sdl->sdl_nlen < IF_NAMESIZE)
716 {
717 mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen);
718 ifname[sdl->sdl_nlen] = 0;
719 // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen);
720 }
721 }
722 if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL)
723 {
724 *ttl = *(u_char*)CMSG_DATA(cmPtr);
725 }
726 if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO)
727 {
728 struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr);
729 dstaddr->type = mDNSAddrType_IPv6;
730 dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr;
731 myIfIndexToName(ip6_info->ipi6_ifindex, ifname);
732 }
733 if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT)
734 {
735 *ttl = *(int*)CMSG_DATA(cmPtr);
736 }
737 }
738
739 return(n);
740 }
741
742 // On entry, context points to our CFSocketSet
743 // If ss->info is NULL, we received this packet on our anonymous unicast socket
744 // If ss->info is non-NULL, we received this packet on port 5353 on the indicated interface
745 mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context)
746 {
747 mDNSAddr senderAddr, destAddr;
748 mDNSIPPort senderPort, destPort = MulticastDNSPort;
749 const CFSocketSet *ss = (const CFSocketSet *)context;
750 mDNS *const m = ss->m;
751 const mDNSInterfaceID InterfaceID = ss->info ? ss->info->ifinfo.InterfaceID : mDNSNULL;
752 DNSMessage packet;
753 struct sockaddr_storage from;
754 size_t fromlen = sizeof(from);
755 char packetifname[IF_NAMESIZE] = "";
756 int err, s1 = -1, skt = CFSocketGetNative(cfs);
757 int count = 0;
758
759 (void)address; // Parameter not used
760 (void)data; // Parameter not used
761
762 if (CallBackType != kCFSocketReadCallBack)
763 LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType);
764
765 if (cfs == ss->cfsv4) s1 = ss->sktv4;
766 else if (cfs == ss->cfsv6) s1 = ss->sktv6;
767
768 if (s1 < 0 || s1 != skt)
769 {
770 LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs);
771 LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", ss->cfsv4, ss->sktv4);
772 LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", ss->cfsv6, ss->sktv6);
773 }
774
775 mDNSu8 ttl;
776 while ((err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0)
777 {
778 count++;
779 if (from.ss_family == AF_INET)
780 {
781 struct sockaddr_in *sin = (struct sockaddr_in*)&from;
782 senderAddr.type = mDNSAddrType_IPv4;
783 senderAddr.ip.v4.NotAnInteger = sin->sin_addr.s_addr;
784 senderPort.NotAnInteger = sin->sin_port;
785 }
786 else if (from.ss_family == AF_INET6)
787 {
788 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from;
789 senderAddr.type = mDNSAddrType_IPv6;
790 senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
791 senderPort.NotAnInteger = sin6->sin6_port;
792 }
793 else
794 {
795 LogMsg("myCFSocketCallBack from is unknown address family %d", from.ss_family);
796 return;
797 }
798
799 // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the
800 // sockets API means that even though this socket has only officially joined the multicast group
801 // on one specific interface, the kernel will still deliver multicast packets to it no matter which
802 // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug.
803 // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface
804 // on which the packet arrived, and ignore the packet if it really arrived on some other interface.
805 if (!ss->info)
806 verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on unicast socket", &senderAddr, &destAddr);
807 else if (!strcmp(ss->info->ifa_name, packetifname))
808 verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s",
809 &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name);
810 else
811 {
812 verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)",
813 &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name, packetifname);
814 return;
815 }
816
817 if (err < (int)sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; }
818
819 mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, &senderAddr, senderPort, &destAddr, destPort, InterfaceID, ttl);
820 }
821
822 if (err < 0 && (errno != EWOULDBLOCK || count == 0))
823 {
824 // Something is busted here.
825 // CFSocket says there is a packet, but myrecvfrom says there is not.
826 // Try calling select() to get another opinion.
827 // Find out about other socket parameter that can help understand why select() says the socket is ready for read
828 // All of this is racy, as data may have arrived after the call to select()
829 int save_errno = errno;
830 int so_error = -1;
831 int so_nread = -1;
832 int fionread = -1;
833 int solen = sizeof(int);
834 fd_set readfds;
835 FD_ZERO(&readfds);
836 FD_SET(s1, &readfds);
837 struct timeval timeout;
838 timeout.tv_sec = 0;
839 timeout.tv_usec = 0;
840 int selectresult = select(s1+1, &readfds, NULL, NULL, &timeout);
841 if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1)
842 LogMsg("myCFSocketCallBack getsockopt(SO_ERROR) error %d", errno);
843 if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1)
844 LogMsg("myCFSocketCallBack getsockopt(SO_NREAD) error %d", errno);
845 if (ioctl(s1, FIONREAD, &fionread) == -1)
846 LogMsg("myCFSocketCallBack ioctl(FIONREAD) error %d", errno);
847 static unsigned int numLogMessages = 0;
848 if (numLogMessages++ < 100)
849 LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d",
850 s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count);
851 sleep(1); // After logging this error, rate limit so we don't flood syslog
852 }
853 }
854
855 // TCP socket support for unicast DNS and Dynamic DNS Update
856
857 typedef struct
858 {
859 TCPConnectionCallback callback;
860 void *context;
861 int connected;
862 } tcpInfo_t;
863
864 mDNSlocal void tcpCFSocketCallback(CFSocketRef cfs, CFSocketCallBackType CallbackType, CFDataRef address,
865 const void *data, void *context)
866 {
867 #pragma unused(CallbackType, address, data)
868 mDNSBool connect = mDNSfalse;
869
870 tcpInfo_t *info = context;
871 if (!info->connected)
872 {
873 connect = mDNStrue;
874 info->connected = mDNStrue; // prevent connected flag from being set in future callbacks
875 }
876 info->callback(CFSocketGetNative(cfs), info->context, connect);
877 // NOTE: the callback may call CloseConnection here, which frees the context structure!
878 }
879
880 mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID,
881 TCPConnectionCallback callback, void *context, int *descriptor)
882 {
883 int sd, on = 1; // "on" for setsockopt
884 struct sockaddr_in saddr;
885 CFSocketContext cfContext = { 0, NULL, 0, 0, 0 };
886 tcpInfo_t *info;
887 CFSocketRef sr;
888 CFRunLoopSourceRef rls;
889 CFOptionFlags srFlags;
890
891 (void)InterfaceID; //!!!KRS use this if non-zero!!!
892
893 *descriptor = 0;
894 if (dst->type != mDNSAddrType_IPv4)
895 {
896 LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: opperation not supported");
897 return mStatus_UnknownErr;
898 }
899
900 sd = socket(AF_INET, SOCK_STREAM, 0);
901 if (sd < 0)
902 {
903 LogMsg("ERROR: socket; %s", strerror(errno));
904 return mStatus_UnknownErr;
905 }
906 // set non-blocking
907 if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
908 {
909 LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno));
910 return mStatus_UnknownErr;
911 }
912
913 // receive interface identifiers
914 if (setsockopt(sd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0)
915 {
916 LogMsg("setsockopt IP_RECVIF - %s", strerror(errno));
917 return mStatus_UnknownErr;
918 }
919 // set up CF wrapper, add to Run Loop
920 info = mallocL("mDNSPlatformTCPConnect", sizeof(tcpInfo_t));
921 info->callback = callback;
922 info->context = context;
923 cfContext.info = info;
924 sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack | kCFSocketConnectCallBack,
925 tcpCFSocketCallback, &cfContext);
926 if (!sr)
927 {
928 LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketRefCreateWithNative failed");
929 freeL("mDNSPlatformTCPConnect", info);
930 return mStatus_UnknownErr;
931 }
932
933 // prevent closing of native socket
934 srFlags = CFSocketGetSocketFlags(sr);
935 CFSocketSetSocketFlags(sr, srFlags & (~kCFSocketCloseOnInvalidate));
936
937 rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0);
938 if (!rls)
939 {
940 LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketCreateRunLoopSource failed");
941 freeL("mDNSPlatformTCPConnect", info);
942 return mStatus_UnknownErr;
943 }
944
945 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
946 CFRelease(rls);
947
948 // initiate connection wth peer
949 bzero(&saddr, sizeof(saddr));
950 saddr.sin_family = AF_INET;
951 saddr.sin_port = dstport.NotAnInteger;
952 memcpy(&saddr.sin_addr, &dst->ip.v4.NotAnInteger, sizeof(saddr.sin_addr));
953 if (connect(sd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
954 {
955 if (errno == EINPROGRESS)
956 {
957 info->connected = 0;
958 *descriptor= sd;
959 return mStatus_ConnectionPending;
960 }
961 LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: %s", strerror(errno));
962 freeL("mDNSPlatformTCPConnect", info);
963 CFSocketInvalidate(sr);
964 return mStatus_ConnectionFailed;
965 }
966 info->connected = 1;
967 *descriptor = sd;
968 return mStatus_ConnectionEstablished;
969 }
970
971 mDNSexport void mDNSPlatformTCPCloseConnection(int sd)
972 {
973 CFSocketContext cfContext;
974 tcpInfo_t *info;
975 CFSocketRef sr;
976
977 // get the CFSocket for the descriptor, if it exists
978 sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, NULL, NULL, NULL);
979 if (!sr)
980 {
981 LogMsg("ERROR: mDNSPlatformTCPCloseConnection - attempt to close a socket that was not properly created");
982 return;
983 }
984 CFSocketGetContext(sr, &cfContext);
985 if (!cfContext.info)
986 {
987 LogMsg("ERROR: mDNSPlatformTCPCloseConnection - could not retreive tcpInfo from socket context");
988 CFRelease(sr);
989 return;
990 }
991 CFRelease(sr); // this only releases the copy we allocated with CreateWithNative above
992
993 info = cfContext.info;
994 CFSocketInvalidate(sr);
995 CFRelease(sr);
996 close(sd);
997 freeL("mDNSPlatformTCPCloseConnection", info);
998 }
999
1000 mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen)
1001 {
1002 int nread = recv(sd, buf, buflen, 0);
1003 if (nread < 0)
1004 {
1005 if (errno == EAGAIN) return 0; // no data available (call would block)
1006 LogMsg("ERROR: mDNSPlatformReadTCP - recv: %s", strerror(errno));
1007 return -1;
1008 }
1009 return nread;
1010 }
1011
1012 mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len)
1013 {
1014 int nsent = send(sd, msg, len, 0);
1015
1016 if (nsent < 0)
1017 {
1018 if (errno == EAGAIN) return 0; // blocked
1019 LogMsg("ERROR: mDNSPlatformWriteTCP - sendL %s", strerror(errno));
1020 return -1;
1021 }
1022 return nsent;
1023 }
1024
1025 // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel
1026 mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel)
1027 {
1028 CFStringEncoding encoding = kCFStringEncodingUTF8;
1029 CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding);
1030 if (cfs)
1031 {
1032 CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
1033 CFRelease(cfs);
1034 }
1035 }
1036
1037 // This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel
1038 mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel)
1039 {
1040 CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL);
1041 if (cfs)
1042 {
1043 CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
1044 CFRelease(cfs);
1045 }
1046 }
1047
1048 // If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface
1049 // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
1050 mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr *ifaddr, u_short sa_family)
1051 {
1052 int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
1053 CFSocketRef *c = (sa_family == AF_INET) ? &cp->cfsv4 : &cp->cfsv6;
1054 CFRunLoopSourceRef *r = (sa_family == AF_INET) ? &cp->rlsv4 : &cp->rlsv6;
1055 const int on = 1;
1056 const int twofivefive = 255;
1057 mStatus err = mStatus_NoError;
1058 char *errstr = mDNSNULL;
1059
1060 if (*s >= 0) { LogMsg("SetupSocket ERROR: socket %d is already set", *s); return(-1); }
1061 if (*c) { LogMsg("SetupSocket ERROR: CFSocketRef %p is already set", *c); return(-1); }
1062
1063 // Open the socket...
1064 int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP);
1065 if (skt < 0) { LogMsg("socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); }
1066
1067 // ... with a shared UDP port, if it's for multicast receiving
1068 if (port.NotAnInteger) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
1069 if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; }
1070
1071 if (sa_family == AF_INET)
1072 {
1073 // We want to receive destination addresses
1074 err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
1075 if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; }
1076
1077 // We want to receive interface identifiers
1078 err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
1079 if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; }
1080
1081 // We want to receive packet TTL value so we can check it
1082 err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on));
1083 // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it
1084
1085 // Add multicast group membership on this interface, if it's for multicast receiving
1086 if (port.NotAnInteger)
1087 {
1088 struct in_addr addr = { ifaddr->ip.v4.NotAnInteger };
1089 struct ip_mreq imr;
1090 imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger;
1091 imr.imr_interface = addr;
1092 err = setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr));
1093 if (err < 0) { errstr = "setsockopt - IP_ADD_MEMBERSHIP"; goto fail; }
1094
1095 // Specify outgoing interface too
1096 err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr));
1097 if (err < 0) { errstr = "setsockopt - IP_MULTICAST_IF"; goto fail; }
1098 }
1099
1100 // Send unicast packets with TTL 255
1101 err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive));
1102 if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; }
1103
1104 // And multicast packets with TTL 255 too
1105 err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive));
1106 if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; }
1107
1108 // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate
1109 const int ip_tosbits = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
1110 err = setsockopt(skt, IPPROTO_IP, IP_TOS, &ip_tosbits, sizeof(ip_tosbits));
1111 if (err < 0) { errstr = "setsockopt - IP_TOS"; goto fail; }
1112
1113 // And start listening for packets
1114 struct sockaddr_in listening_sockaddr;
1115 listening_sockaddr.sin_family = AF_INET;
1116 listening_sockaddr.sin_port = port.NotAnInteger;
1117 listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket
1118 err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr));
1119 if (err) { errstr = "bind"; goto fail; }
1120 }
1121 else if (sa_family == AF_INET6)
1122 {
1123 // We want to receive destination addresses and receive interface identifiers
1124 err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
1125 if (err < 0) { errstr = "setsockopt - IPV6_PKTINFO"; goto fail; }
1126
1127 // We want to receive packet hop count value so we can check it
1128 err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
1129 if (err < 0) { errstr = "setsockopt - IPV6_HOPLIMIT"; goto fail; }
1130
1131 // We want to receive only IPv6 packets, without this option, we may
1132 // get IPv4 addresses as mapped addresses.
1133 err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
1134 if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; }
1135
1136 if (port.NotAnInteger)
1137 {
1138 // Add multicast group membership on this interface, if it's for multicast receiving
1139 int interface_id = if_nametoindex(cp->info->ifa_name);
1140 struct ipv6_mreq i6mr;
1141 i6mr.ipv6mr_interface = interface_id;
1142 i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroupv6;
1143 err = setsockopt(skt, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr));
1144 if (err < 0) { errstr = "setsockopt - IPV6_JOIN_GROUP"; goto fail; }
1145
1146 // Specify outgoing interface too
1147 err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id));
1148 if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_IF"; goto fail; }
1149 }
1150
1151 // Send unicast packets with TTL 255
1152 err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive));
1153 if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; }
1154
1155 // And multicast packets with TTL 255 too
1156 err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive));
1157 if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; }
1158
1159 // Note: IPV6_TCLASS appears not to be implemented on OS X right now (or indeed on ANY version of Unix?)
1160 #ifdef IPV6_TCLASS
1161 // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate
1162 int tclass = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; // This may not be right (since tclass is not implemented on OS X, I can't test it)
1163 err = setsockopt(skt, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass));
1164 if (err < 0) { errstr = "setsockopt - IPV6_TCLASS"; goto fail; }
1165 #endif
1166
1167 // Want to receive our own packets
1168 err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on));
1169 if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; }
1170
1171 // And start listening for packets
1172 struct sockaddr_in6 listening_sockaddr6;
1173 bzero(&listening_sockaddr6, sizeof(listening_sockaddr6));
1174 listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6);
1175 listening_sockaddr6.sin6_family = AF_INET6;
1176 listening_sockaddr6.sin6_port = port.NotAnInteger;
1177 listening_sockaddr6.sin6_flowinfo = 0;
1178 // listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket
1179 listening_sockaddr6.sin6_scope_id = 0;
1180 err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6));
1181 if (err) { errstr = "bind"; goto fail; }
1182 }
1183
1184 fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
1185 *s = skt;
1186 CFSocketContext myCFSocketContext = { 0, cp, NULL, NULL, NULL };
1187 *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext);
1188 *r = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0);
1189 CFRunLoopAddSource(CFRunLoopGetCurrent(), *r, kCFRunLoopDefaultMode);
1190
1191 return(err);
1192
1193 fail:
1194 LogMsg("%s error %ld errno %d (%s)", errstr, err, errno, strerror(errno));
1195 close(skt);
1196 return(err);
1197 }
1198
1199 mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa)
1200 {
1201 if (sa->sa_family == AF_INET)
1202 {
1203 struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa;
1204 ip->type = mDNSAddrType_IPv4;
1205 ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr;
1206 return(0);
1207 }
1208 else if (sa->sa_family == AF_INET6)
1209 {
1210 struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa;
1211 ip->type = mDNSAddrType_IPv6;
1212 if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
1213 ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr;
1214 return(0);
1215 }
1216 else
1217 {
1218 LogMsg("SetupAddr invalid sa_family %d", sa->sa_family);
1219 return(-1);
1220 }
1221 }
1222
1223 mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa)
1224 {
1225 mDNSu32 scope_id = if_nametoindex(ifa->ifa_name);
1226 mDNSAddr ip;
1227 SetupAddr(&ip, ifa->ifa_addr);
1228 NetworkInterfaceInfoOSX **p;
1229 for (p = &m->p->InterfaceList; *p; p = &(*p)->next)
1230 if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip))
1231 {
1232 debugf("AddInterfaceToList: Found existing interface %u with address %#a", scope_id, &ip);
1233 (*p)->Exists = mDNStrue;
1234 return(*p);
1235 }
1236
1237 debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip);
1238 NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i));
1239 if (!i) return(mDNSNULL);
1240 i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1);
1241 if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(mDNSNULL); }
1242 strcpy(i->ifa_name, ifa->ifa_name);
1243
1244 bzero(&i->ifinfo.uDNS_info, sizeof(uDNS_NetworkInterfaceInfo));
1245 i->ifinfo.InterfaceID = mDNSNULL;
1246 i->ifinfo.ip = ip;
1247 i->ifinfo.Advertise = m->AdvertiseLocalAddresses;
1248 i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList
1249
1250 i->next = mDNSNULL;
1251 i->Exists = mDNStrue;
1252 i->scope_id = scope_id;
1253 i->sa_family = ifa->ifa_addr->sa_family;
1254 i->Multicast = (ifa->ifa_flags & IFF_MULTICAST) && !(ifa->ifa_flags & IFF_POINTOPOINT);
1255
1256 i->ss.m = m;
1257 i->ss.info = i;
1258 i->ss.sktv4 = i->ss.sktv6 = -1;
1259 i->ss.cfsv4 = i->ss.cfsv6 = NULL;
1260 i->ss.rlsv4 = i->ss.rlsv6 = NULL;
1261
1262 *p = i;
1263 return(i);
1264 }
1265
1266 mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id)
1267 {
1268 NetworkInterfaceInfoOSX *i;
1269 for (i = m->p->InterfaceList; i; i = i->next)
1270 if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4)
1271 if (!(i->ifinfo.ip.ip.v4.b[0] == 169 && i->ifinfo.ip.ip.v4.b[1] == 254))
1272 return(i);
1273 return(mDNSNULL);
1274 }
1275
1276 mDNSlocal mStatus UpdateInterfaceList(mDNS *const m)
1277 {
1278 mDNSBool foundav4 = mDNSfalse;
1279 struct ifaddrs *ifa = myGetIfAddrs(1);
1280 struct ifaddrs *theLoopback = NULL;
1281 int err = (ifa != NULL) ? 0 : (errno != 0 ? errno : -1);
1282 int InfoSocket = err ? -1 : socket(AF_INET6, SOCK_DGRAM, 0);
1283 if (err) return(err);
1284
1285 // Set up the nice label
1286 m->nicelabel.c[0] = 0;
1287 GetUserSpecifiedFriendlyComputerName(&m->nicelabel);
1288 if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh");
1289
1290 // Set up the RFC 1034-compliant label
1291 domainlabel hostlabel;
1292 hostlabel.c[0] = 0;
1293 GetUserSpecifiedRFC1034ComputerName(&hostlabel);
1294 if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, "Macintosh");
1295 // If the user has changed their dot-local host name since the last time we checked, then update our local copy.
1296 // If the user has not changed their dot-local host name, then leave ours alone (m->hostlabel may have gone through
1297 // repeated conflict resolution to get to its current value, and if we reset it, we'll have to go through all that again.)
1298 if (SameDomainLabel(m->p->userhostlabel.c, hostlabel.c))
1299 debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c);
1300 else
1301 {
1302 debugf("Updating m->hostlabel to %#s", hostlabel.c);
1303 m->p->userhostlabel = m->hostlabel = hostlabel;
1304 mDNS_GenerateFQDN(m);
1305 if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m);
1306 }
1307
1308 while (ifa)
1309 {
1310 #if LIST_ALL_INTERFACES
1311 if (ifa->ifa_addr->sa_family == AF_APPLETALK)
1312 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_APPLETALK",
1313 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1314 else if (ifa->ifa_addr->sa_family == AF_LINK)
1315 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_LINK",
1316 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1317 else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)
1318 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)",
1319 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1320 if (!(ifa->ifa_flags & IFF_UP))
1321 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_UP",
1322 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1323 if (!(ifa->ifa_flags & IFF_MULTICAST))
1324 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST",
1325 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1326 if (ifa->ifa_flags & IFF_POINTOPOINT)
1327 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT",
1328 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1329 if (ifa->ifa_flags & IFF_LOOPBACK)
1330 debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK",
1331 ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family);
1332 #endif
1333 if (ifa->ifa_flags & IFF_UP)
1334 if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6)
1335 {
1336 int ifru_flags6 = 0;
1337 if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
1338 {
1339 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
1340 struct in6_ifreq ifr6;
1341 bzero((char *)&ifr6, sizeof(ifr6));
1342 strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
1343 ifr6.ifr_addr = *sin6;
1344 if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1)
1345 ifru_flags6 = ifr6.ifr_ifru.ifru_flags6;
1346 verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6);
1347 }
1348 if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
1349 {
1350 if (ifa->ifa_flags & IFF_LOOPBACK)
1351 theLoopback = ifa;
1352 else
1353 {
1354 AddInterfaceToList(m, ifa);
1355 if (ifa->ifa_addr->sa_family == AF_INET)
1356 foundav4 = mDNStrue;
1357 }
1358 }
1359 }
1360 ifa = ifa->ifa_next;
1361 }
1362
1363 // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work.
1364 // In the interim, we skip loopback interface only if we found at least one v4 interface to use
1365 if (!foundav4 && theLoopback)
1366 AddInterfaceToList(m, theLoopback);
1367
1368 // Now the list is complete, set the McastTxRx setting for each interface.
1369 // We always send and receive using IPv4.
1370 // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address.
1371 // Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network,
1372 // which means there's a good chance that most or all the other devices on that network should also have v4.
1373 // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half.
1374 // At this time, reducing the packet rate is more important than v6-only devices on a large configured network,
1375 // so we are willing to make that sacrifice.
1376 NetworkInterfaceInfoOSX *i;
1377 for (i = m->p->InterfaceList; i; i = i->next)
1378 if (i->Exists)
1379 {
1380 mDNSBool txrx = i->Multicast && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id));
1381 if (i->ifinfo.McastTxRx != txrx)
1382 {
1383 i->ifinfo.McastTxRx = txrx;
1384 i->Exists = 2; // State change; need to deregister and reregister this interface
1385 }
1386 }
1387
1388 if (InfoSocket >= 0) close(InfoSocket);
1389 return(err);
1390 }
1391
1392 mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, char *ifname, int type)
1393 {
1394 NetworkInterfaceInfoOSX *i;
1395 for (i = m->p->InterfaceList; i; i = i->next)
1396 if (i->Exists && !strcmp(i->ifa_name, ifname) &&
1397 ((AAAA_OVER_V4 ) ||
1398 (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) ||
1399 (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6) )) return(i);
1400 return(NULL);
1401 }
1402
1403 mDNSlocal void SetupActiveInterfaces(mDNS *const m)
1404 {
1405 NetworkInterfaceInfoOSX *i;
1406 for (i = m->p->InterfaceList; i; i = i->next)
1407 if (i->Exists)
1408 {
1409 NetworkInterfaceInfo *n = &i->ifinfo;
1410 NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family);
1411 if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifa_name);
1412
1413 if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)primary) // Sanity check
1414 {
1415 LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != primary %p", n->InterfaceID, primary);
1416 n->InterfaceID = mDNSNULL;
1417 }
1418
1419 if (!n->InterfaceID)
1420 {
1421 // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface,
1422 // so we need to make sure we call mDNS_DeregisterInterface() before disposing it.
1423 // If n->InterfaceID is NOT set, then we haven't registered it and we should not try to deregister it
1424 n->InterfaceID = (mDNSInterfaceID)primary;
1425 mDNS_RegisterInterface(m, n);
1426 LogOperation("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s",
1427 i->ifa_name, i->scope_id, primary, &n->ip, n->InterfaceActive ? " (Primary)" : "");
1428 }
1429
1430 if (!n->McastTxRx)
1431 debugf("SetupActiveInterfaces: No Tx/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, primary, &n->ip);
1432 else
1433 {
1434 if (i->sa_family == AF_INET && primary->ss.sktv4 == -1)
1435 {
1436 mStatus err = SetupSocket(&primary->ss, MulticastDNSPort, &i->ifinfo.ip, AF_INET);
1437 if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a", primary->ss.sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
1438 else LogMsg("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a FAILED", primary->ss.sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
1439 }
1440
1441 if (i->sa_family == AF_INET6 && primary->ss.sktv6 == -1)
1442 {
1443 mStatus err = SetupSocket(&primary->ss, MulticastDNSPort, &i->ifinfo.ip, AF_INET6);
1444 if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a", primary->ss.sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
1445 else LogMsg("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a FAILED", primary->ss.sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
1446 }
1447 }
1448 }
1449 }
1450
1451 mDNSlocal void MarkAllInterfacesInactive(mDNS *const m)
1452 {
1453 NetworkInterfaceInfoOSX *i;
1454 for (i = m->p->InterfaceList; i; i = i->next)
1455 i->Exists = mDNSfalse;
1456 }
1457
1458 mDNSlocal void CloseSocketSet(CFSocketSet *ss)
1459 {
1460 // Note: MUST NOT close the underlying native BSD sockets.
1461 // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately,
1462 // because it first has to unhook the sockets from its select() call, before it can safely close them.
1463 if (ss->cfsv4) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), ss->rlsv4, kCFRunLoopDefaultMode); CFRelease(ss->rlsv4); CFSocketInvalidate(ss->cfsv4); CFRelease(ss->cfsv4); }
1464 if (ss->cfsv6) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), ss->rlsv6, kCFRunLoopDefaultMode); CFRelease(ss->rlsv6); CFSocketInvalidate(ss->cfsv6); CFRelease(ss->cfsv6); }
1465 ss->sktv4 = ss->sktv6 = -1;
1466 ss->cfsv4 = ss->cfsv6 = NULL;
1467 ss->rlsv4 = ss->rlsv6 = NULL;
1468 }
1469
1470 mDNSlocal void ClearInactiveInterfaces(mDNS *const m)
1471 {
1472 // First pass:
1473 // If an interface is going away, then deregister this from the mDNSCore.
1474 // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away.
1475 // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory
1476 // it refers to has gone away we'll crash.
1477 // Don't actually close the sockets or free the memory yet: When the last representative of an interface goes away
1478 // mDNSCore may want to send goodbye packets on that interface. (Not yet implemented, but a good idea anyway.)
1479 NetworkInterfaceInfoOSX *i;
1480 for (i = m->p->InterfaceList; i; i = i->next)
1481 {
1482 // 1. If this interface is no longer active, or its InterfaceID is changing, deregister it
1483 NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family);
1484 if (i->ifinfo.InterfaceID)
1485 if (i->Exists == 0 || i->Exists == 2 || i->ifinfo.InterfaceID != (mDNSInterfaceID)primary)
1486 {
1487 LogOperation("ClearInactiveInterfaces: Deregistering %s(%lu) InterfaceID %p %#a%s",
1488 i->ifa_name, i->scope_id, i->ifinfo.InterfaceID, &i->ifinfo.ip, i->ifinfo.InterfaceActive ? " (Primary)" : "");
1489 mDNS_DeregisterInterface(m, &i->ifinfo);
1490 i->ifinfo.InterfaceID = mDNSNULL;
1491 // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface,
1492 // so we need to make sure we call mDNS_DeregisterInterface() before disposing it.
1493 // If n->InterfaceID is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it.
1494 }
1495 }
1496
1497 // Second pass:
1498 // Now that everything that's going to deregister has done so, we can close sockets and free the memory
1499 NetworkInterfaceInfoOSX **p = &m->p->InterfaceList;
1500 while (*p)
1501 {
1502 i = *p;
1503 // 2. Close all our CFSockets. We'll recreate them later as necessary.
1504 // (We may have previously had both v4 and v6, and we may not need both any more.)
1505 CloseSocketSet(&i->ss);
1506 // 3. If no longer active, delete interface from list and free memory
1507 if (!i->Exists && NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0)
1508 {
1509 debugf("ClearInactiveInterfaces: Deleting %#a", &i->ifinfo.ip);
1510 *p = i->next;
1511 if (i->ifa_name) freeL("NetworkInterfaceInfoOSX name", i->ifa_name);
1512 freeL("NetworkInterfaceInfoOSX", i);
1513 }
1514 else
1515 p = &i->next;
1516 }
1517 }
1518
1519
1520 mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict)
1521 {
1522 int i, count;
1523 CFArrayRef values;
1524 char buf[256];
1525 mDNSv4Addr saddr;
1526 CFStringRef s;
1527
1528
1529 mDNS_DeregisterDNSList(m); // deregister orig list
1530 values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses);
1531 if (!values) return mStatus_NoError;
1532
1533 count = CFArrayGetCount(values);
1534 for (i = 0; i < count; i++)
1535 {
1536 s = CFArrayGetValueAtIndex(values, i);
1537 if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; }
1538 if (!CFStringGetCString(s, buf, 256, kCFStringEncodingASCII))
1539 {
1540 LogMsg("ERROR: RegisterNameServers - CFStringGetCString");
1541 continue;
1542 }
1543 if (!inet_aton(buf, (struct in_addr *)saddr.b))
1544 {
1545 LogMsg("ERROR: RegisterNameServers - invalid address string %s", buf);
1546 continue;
1547 }
1548 mDNS_RegisterDNS(m, &saddr);
1549 }
1550 return mStatus_NoError;
1551 }
1552
1553 mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
1554 {
1555 (void)m; // unused
1556 AuthRecordListElem *elem = rr->RecordContext;
1557 if (result == mStatus_MemFree) freeL("FreeARElemCallback", elem);
1558 }
1559
1560 mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
1561 {
1562 SearchListElem *slElem = question->QuestionContext;
1563 AuthRecordListElem *arElem, *ptr, *prev;
1564 AuthRecord *dereg;
1565 char *name;
1566 mStatus err;
1567
1568 if (AddRecord)
1569 {
1570 arElem = mallocL("FoundDomain - arElem", sizeof(AuthRecordListElem));
1571 if (!arElem) { LogMsg("ERROR: malloc"); return; }
1572 mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem);
1573 if (question == &slElem->browseQ) name = "_browse._dns-sd._udp.local.";
1574 else name = "_register._dns-sd._udp.local.";
1575 MakeDomainNameFromDNSNameString(&arElem->ar.resrec.name, name);
1576 strcpy(arElem->ar.resrec.rdata->u.name.c, answer->rdata->u.name.c);
1577 err = mDNS_Register(m, &arElem->ar);
1578 if (err)
1579 {
1580 LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err);
1581 freeL("FoundDomain - arElem", arElem);
1582 return;
1583 }
1584 arElem->next = slElem->AuthRecs;
1585 slElem->AuthRecs = arElem;
1586 }
1587 else
1588 {
1589 ptr = slElem->AuthRecs;
1590 prev = NULL;
1591 while (ptr)
1592 {
1593 if (SameDomainName(&ptr->ar.resrec.name, &answer->name) && SameDomainName(&ptr->ar.resrec.rdata->u.name, &answer->rdata->u.name))
1594 {
1595 debugf("Deregistering PTR %s -> %s", ptr->ar.resrec.name.c, ptr->ar.resrec.rdata->u.name.c);
1596 dereg = &ptr->ar;
1597 if (prev) prev->next = ptr->next;
1598 else slElem->AuthRecs = ptr->next;
1599 ptr = ptr->next;
1600 err = mDNS_Deregister(m, dereg);
1601 if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err);
1602 }
1603 else
1604 {
1605 prev = ptr;
1606 ptr = ptr->next;
1607 }
1608 }
1609 }
1610 }
1611
1612 mDNSlocal mStatus RegisterSearchDomains(mDNS *const m, CFDictionaryRef dict)
1613 {
1614 int i, count;
1615 CFArrayRef values;
1616 domainname domain;
1617 char buf[MAX_ESCAPED_DOMAIN_NAME];
1618 CFStringRef s;
1619 SearchListElem *new, *ptr, *prev, *freeSLPtr;
1620 AuthRecordListElem *arList;
1621 mStatus err;
1622
1623 // step 1: mark each elem for removal (-1)
1624 for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = -1;
1625
1626 values = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains);
1627 if (values)
1628 {
1629 count = CFArrayGetCount(values);
1630 for (i = 0; i < count; i++)
1631 {
1632 s = CFArrayGetValueAtIndex(values, i);
1633 if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; }
1634 if (!CFStringGetCString(s, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingASCII))
1635 {
1636 LogMsg("ERROR: RegisterNameServers - CFStringGetCString");
1637 continue;
1638 }
1639 if (!MakeDomainNameFromDNSNameString(&domain, buf))
1640 {
1641 LogMsg("ERROR: RegisterNameServers - invalid search domain %s", buf);
1642 continue;
1643 }
1644 // if domain is in list, mark as pre-existent (0)
1645 for (ptr = SearchList; ptr; ptr = ptr->next)
1646 if (SameDomainName(&ptr->domain, &domain)) { ptr->flag = 0; break; }
1647
1648 // if domain not in list, add to list, mark as add (1)
1649 if (!ptr)
1650 {
1651 new = mallocL("RegisterSearchDomains - SearchListElem", sizeof(SearchListElem));
1652 if (!new) { LogMsg("ERROR: RegisterSearchDomains - malloc"); return mStatus_UnknownErr; }
1653 bzero(new, sizeof(SearchListElem));
1654 strcpy(new->domain.c, domain.c);
1655 new->flag = 1; // add
1656 new->next = SearchList;
1657 SearchList = new;
1658 }
1659 }
1660 }
1661 // delete elems marked for removal, do queries for elems marked add
1662 prev = NULL;
1663 ptr = SearchList;
1664 while (ptr)
1665 {
1666 if (ptr->flag == -1) // remove
1667 {
1668 mDNS_StopQuery(m, &ptr->browseQ);
1669 mDNS_StopQuery(m, &ptr->registerQ);
1670 // deregister records generated from answers to the query
1671 arList = ptr->AuthRecs;
1672 ptr->AuthRecs = NULL;
1673 while (arList)
1674 {
1675 AuthRecord *dereg = &arList->ar;
1676 arList = arList->next;
1677 debugf("Deregistering PTR %s -> %s", dereg->resrec.name.c, dereg->resrec.rdata->u.name.c);
1678 err = mDNS_Deregister(m, dereg);
1679 if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err);
1680 }
1681
1682 // remove elem from list, delete
1683 if (prev) prev->next = ptr->next;
1684 else SearchList = ptr->next;
1685 freeSLPtr = ptr;
1686 ptr = ptr->next;
1687 freeL("RegisterNameServers - freeSLPtr", freeSLPtr);
1688 continue;
1689 }
1690
1691 if (ptr->flag == 1) // add
1692 {
1693 err = mDNS_GetDomains(m, &ptr->browseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
1694 if (err) LogMsg("ERROR: RegisterNameServers - mDNS_DomainTypeBrowse, %d", err);
1695
1696 err = mDNS_GetDomains(m, &ptr->registerQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
1697 if (err) LogMsg("ERROR: RegisterNameServers - mDNS_DomainTypeRegistration, %d", err);
1698 ptr->flag = 0;
1699 }
1700
1701 if (ptr->flag) { LogMsg("RegisterNameServers - unknown flag %d. Skipping.", ptr->flag); }
1702
1703 prev = ptr;
1704 ptr = ptr->next;
1705 }
1706
1707 return mStatus_NoError;
1708 }
1709
1710 // key must be kSCPropNetDNSServerAddresses or kSCPropNetDNSSearchDomains
1711 mDNSlocal mStatus RegisterDNSConfig(mDNS *const m, CFDictionaryRef dict, const CFStringRef key)
1712 {
1713 if (key == kSCPropNetDNSSearchDomains) return RegisterSearchDomains(m, dict);
1714 if (key == kSCPropNetDNSServerAddresses) return RegisterNameServers(m, dict);
1715 LogMsg("ERROR: RegisterDNSConfig - bad key"); return mStatus_UnknownErr;
1716 }
1717
1718
1719 mDNSlocal void DNSConfigChanged(SCDynamicStoreRef session, CFArrayRef changes, void *context)
1720 {
1721 mDNS *m = context;
1722 CFDictionaryRef dict;
1723 CFStringRef key;
1724
1725 if (DNSConfigInitialized && (!changes || CFArrayGetCount(changes) == 0)) return;
1726
1727 //!!!KRS fixme - we need a list of registerd servers. this wholesale
1728 // dereg doesn't work if there's an error and we bail out before registering the new list
1729
1730 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
1731 if (!key) { LogMsg("ERROR: DNSConfigChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return; }
1732 dict = SCDynamicStoreCopyValue(session, key);
1733 CFRelease(key);
1734 if (dict)
1735 {
1736 RegisterDNSConfig(m, dict, kSCPropNetDNSServerAddresses);
1737 RegisterDNSConfig(m, dict, kSCPropNetDNSSearchDomains);
1738 CFRelease(dict);
1739 }
1740 if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m);
1741 // no-op if label & domain are unchanged
1742 }
1743
1744 mDNSlocal mStatus WatchForDNSChanges(mDNS *const m)
1745 {
1746 CFStringRef key;
1747 CFMutableArrayRef keyList;
1748 CFRunLoopSourceRef rls;
1749 SCDynamicStoreRef session;
1750 SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL };
1751
1752 session = SCDynamicStoreCreate(NULL, CFSTR("trackDNS"), DNSConfigChanged, &context);
1753 if (!session) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreCreate"); return mStatus_UnknownErr; }
1754
1755 keyList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1756 if (!keyList) { LogMsg("ERROR: WatchForDNSChanges - CFArrayCreateMutable"); return mStatus_UnknownErr; }
1757
1758 // create a pattern that matches the global DNS dictionary key
1759 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
1760 if (!key) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return mStatus_UnknownErr; }
1761
1762 CFArrayAppendValue(keyList, key);
1763 CFRelease(key);
1764
1765 // set the keys for our DynamicStore session
1766 SCDynamicStoreSetNotificationKeys(session, keyList, NULL);
1767 CFRelease(keyList);
1768
1769 // create a CFRunLoopSource for our DynamicStore session
1770 rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0);
1771 if (!rls) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreCreateRunLoopSource"); return mStatus_UnknownErr; }
1772
1773 // add the run loop source to our current run loop
1774 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1775 CFRelease(rls);
1776
1777 // get initial configuration
1778 DNSConfigChanged(session, NULL, m);
1779 return mStatus_NoError;
1780 }
1781
1782 mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
1783 {
1784 (void)store; // Parameter not used
1785 (void)changedKeys; // Parameter not used
1786 debugf("*** Network Configuration Change ***");
1787
1788 mDNS *const m = (mDNS *const)context;
1789 MarkAllInterfacesInactive(m);
1790 UpdateInterfaceList(m);
1791 ClearInactiveInterfaces(m);
1792 SetupActiveInterfaces(m);
1793
1794 if (m->MainCallback)
1795 m->MainCallback(m, mStatus_ConfigChanged);
1796 }
1797
1798 mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
1799 {
1800 mStatus err = -1;
1801 SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL };
1802 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context);
1803 CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
1804 CFStringRef key2 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6);
1805 CFStringRef key3 = SCDynamicStoreKeyCreateComputerName(NULL);
1806 CFStringRef key4 = SCDynamicStoreKeyCreateHostNames(NULL);
1807 CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
1808 CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
1809
1810 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1811 CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1812
1813 if (!store) { LogMsg("SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; }
1814 if (!key1 || !key2 || !key3 || !key4 || !keys || !pattern1 || !pattern2 || !patterns) goto error;
1815
1816 CFArrayAppendValue(keys, key1);
1817 CFArrayAppendValue(keys, key2);
1818 CFArrayAppendValue(keys, key3);
1819 CFArrayAppendValue(keys, key4);
1820 CFArrayAppendValue(patterns, pattern1);
1821 CFArrayAppendValue(patterns, pattern2);
1822 if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns))
1823 { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; }
1824
1825 m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
1826 if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; }
1827
1828 CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
1829 m->p->Store = store;
1830 err = 0;
1831 goto exit;
1832
1833 error:
1834 if (store) CFRelease(store);
1835
1836 exit:
1837 if (key1) CFRelease(key1);
1838 if (key2) CFRelease(key2);
1839 if (key3) CFRelease(key3);
1840 if (key4) CFRelease(key4);
1841 if (pattern1) CFRelease(pattern1);
1842 if (pattern2) CFRelease(pattern2);
1843 if (keys) CFRelease(keys);
1844 if (patterns) CFRelease(patterns);
1845
1846 return(err);
1847 }
1848
1849 mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
1850 {
1851 mDNS *const m = (mDNS *const)refcon;
1852 (void)service; // Parameter not used
1853 switch(messageType)
1854 {
1855 case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240
1856 case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreMachineSleep(m, true); break; // E0000250
1857 case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260
1858 case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270
1859 case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreMachineSleep(m, true); break; // E0000280
1860 case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290
1861 case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreMachineSleep(m, false); break; // E0000300
1862 default: debugf("PowerChanged unknown message %X", messageType); break;
1863 }
1864 IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument);
1865 }
1866
1867 mDNSlocal mStatus WatchForPowerChanges(mDNS *const m)
1868 {
1869 IONotificationPortRef thePortRef;
1870 m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier);
1871 if (m->p->PowerConnection)
1872 {
1873 m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef);
1874 CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode);
1875 return(mStatus_NoError);
1876 }
1877 return(-1);
1878 }
1879
1880 mDNSexport mDNSBool haveSecInfo = mDNSfalse; // this must go away once we have full keychain integration
1881 mDNSlocal void GetAuthInfoFromKeychainItem(mDNS *m, SecKeychainItemRef item)
1882 {
1883 OSStatus err;
1884 mDNSu32 infoTag = kSecAccountItemAttr;
1885 mDNSu32 infoFmt = 0; // string
1886 SecKeychainAttributeInfo info;
1887 SecKeychainAttributeList *authAttrList = NULL;
1888 void *data;
1889 mDNSu32 dataLen;
1890
1891 mStatus regErr;
1892 char accountName[MAX_ESCAPED_DOMAIN_NAME];
1893 domainname zone;
1894 AuthRecord *rrReg, *rrBrowse;
1895
1896 info.count = 1;
1897 info.tag = &infoTag;
1898 info.format = &infoFmt;
1899
1900 err = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &authAttrList, &dataLen, &data);
1901 if (err) { LogMsg("SecKeychainItemCopyAttributesAndData returned error %d", err); return; }
1902
1903 // copy account name
1904 if (!authAttrList->count || authAttrList->attr->tag != kSecAccountItemAttr)
1905 { LogMsg("Received bad authAttrList"); return; }
1906
1907 if (authAttrList->attr->length + strlen(LH_SUFFIX) > MAX_ESCAPED_DOMAIN_NAME)
1908 { LogMsg("Account name too long (%d bytes)", authAttrList->attr->length); return; }
1909 memcpy(accountName, authAttrList->attr->data, authAttrList->attr->length);
1910 accountName[authAttrList->attr->length] = '\0';
1911
1912 zone.c[0] = '\0';
1913 if (!AppendLiteralLabelString(&zone, accountName) ||
1914 !AppendDNSNameString(&zone, LH_SUFFIX))
1915 { LogMsg("InitAuthInfo - bad account name"); return; }
1916
1917 mDNS_UpdateDomainRequiresAuthentication(m, &zone, &zone, data, dataLen, mDNStrue);
1918 if(m->uDNS_info.NameRegDomain) { debugf("Overwriting config file options with KeyChain values"); }
1919
1920 if (!ConvertDomainNameToCString(&zone, m->uDNS_info.NameRegDomain) ||
1921 !ConvertDomainNameToCString(&zone, m->uDNS_info.ServiceRegDomain))
1922 { LogMsg("Couldn't set keychain username in uDNS global info"); }
1923
1924 mDNS_GenerateGlobalFQDN(m);
1925 // normally we'd query the zone for _register/_browse domains, but to reduce server load we manually generate the records
1926
1927 haveSecInfo = mDNStrue;
1928 //!!!KRS need to do better bookkeeping once we support multiple users
1929 rrReg = mallocL("AuthRecord", sizeof(AuthRecord));
1930 rrBrowse = mallocL("AuthRecord", sizeof(AuthRecord));
1931 if (!rrReg || !rrBrowse) { LogMsg("ERROR: Malloc"); return; }
1932
1933 // set up _browse
1934 mDNS_SetupResourceRecord(rrBrowse, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
1935 MakeDomainNameFromDNSNameString(&rrBrowse->resrec.name, "_browse._dns-sd._udp.local.");
1936 strcpy(rrBrowse->resrec.rdata->u.name.c, zone.c);
1937
1938 // set up _register
1939 mDNS_SetupResourceRecord(rrReg, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
1940 MakeDomainNameFromDNSNameString(&rrReg->resrec.name, "_register._dns-sd._udp.local.");
1941 strcpy(rrReg->resrec.rdata->u.name.c, zone.c);
1942
1943 regErr = mDNS_Register(m, rrReg);
1944 if (regErr) LogMsg("Registration of local-only reg domain %s failed", zone.c);
1945
1946 regErr = mDNS_Register(m, rrBrowse);
1947 if (regErr) LogMsg("Registration of local-only browse domain %s failed", zone.c);
1948 SecKeychainItemFreeContent(authAttrList, data);
1949 }
1950
1951 mDNSlocal void InitAuthInfo(mDNS *m);
1952
1953 mDNSlocal OSStatus KeychainCallback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context)
1954 {
1955 (void)event;
1956 (void)info;
1957 // unused
1958
1959 debugf("SecKeychainAddCallback received event %d", event);
1960 InitAuthInfo((mDNS *)context); // keychain events happen rarely - just rebuild the list
1961 return 0;
1962 }
1963
1964 mDNSexport void InitAuthInfo(mDNS *m)
1965 {
1966 OSStatus err;
1967
1968 SecKeychainSearchRef searchRef = NULL;
1969 SecKeychainRef sysKeychain = NULL;
1970 SecKeychainAttribute searchAttrs[] = { { kSecDescriptionItemAttr, strlen(LH_KEYCHAIN_DESC), LH_KEYCHAIN_DESC },
1971 { kSecServiceItemAttr, strlen(LH_KEYCHAIN_SERVICE), LH_KEYCHAIN_SERVICE } };
1972 SecKeychainAttributeList searchList = { sizeof(searchAttrs) / sizeof(*searchAttrs), searchAttrs };
1973 SecKeychainItemRef item;
1974
1975 // clear any previous entries
1976 mDNS_ClearAuthenticationList(m);
1977
1978 err = SecKeychainOpen(SYS_KEYCHAIN_PATH, &sysKeychain);
1979 if (err) { LogMsg("ERROR: InitAuthInfo - couldn't open system keychain - %d", err); goto release_refs; }
1980 err = SecKeychainSetDomainDefault(kSecPreferencesDomainSystem, sysKeychain);
1981 if (err) { LogMsg("ERROR: InitAuthInfo - couldn't set domain default for system keychain - %d", err); goto release_refs; }
1982
1983 err = SecKeychainSearchCreateFromAttributes(sysKeychain, kSecGenericPasswordItemClass, &searchList, &searchRef);
1984 if (err) { LogMsg("ERROR: InitAuthInfo - SecKeychainSearchCreateFromAttributes %d", err); goto release_refs; }
1985
1986 while (!SecKeychainSearchCopyNext(searchRef, &item))
1987 {
1988 GetAuthInfoFromKeychainItem(m, item);
1989 CFRelease(item);
1990 }
1991 err = SecKeychainAddCallback(KeychainCallback, kSecAddEventMask | kSecDeleteEventMask | kSecUpdateEventMask | kSecPasswordChangedEventMask, m);
1992 if (err && err != errSecDuplicateCallback) { LogMsg("SecKeychainAddCallback returned error %d", err); }
1993
1994 release_refs:
1995
1996 if (searchRef) CFRelease(searchRef);
1997 if (sysKeychain) CFRelease(sysKeychain);
1998 }
1999
2000 CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
2001 CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey;
2002 CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey;
2003 CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
2004
2005 mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
2006 {
2007 int major = 0, minor = 0;
2008 char letter = 0, prodname[256]="Mac OS X", prodvers[256]="", buildver[256]="?";
2009 CFDictionaryRef vers = _CFCopySystemVersionDictionary();
2010 if (vers)
2011 {
2012 CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey);
2013 CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey);
2014 CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
2015 if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8);
2016 if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8);
2017 if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8);
2018 sscanf(buildver, "%d%c%d", &major, &letter, &minor);
2019 CFRelease(vers);
2020 }
2021 if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, mDNSResponderVersionString);
2022 return(major);
2023 }
2024
2025 // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
2026 // If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses --
2027 // we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses.
2028 mDNSlocal mDNSBool mDNSPlatformInit_ReceiveUnicast(void)
2029 {
2030 int err;
2031 int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
2032 struct sockaddr_in s5353;
2033 s5353.sin_family = AF_INET;
2034 s5353.sin_port = MulticastDNSPort.NotAnInteger;
2035 s5353.sin_addr.s_addr = 0;
2036 err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353));
2037 close(s);
2038 if (err) debugf("No unicast UDP responses");
2039 else debugf("Unicast UDP responses okay");
2040 return(err == 0);
2041 }
2042
2043
2044 //!!!KRS this should be less order-dependent as we support more configuration options
2045 mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
2046 {
2047 char buf[1024];
2048 int len;
2049
2050 if (!fgets(buf, 1024, f)) { LogMsg("Option %s not set", option); return mDNSfalse; }
2051 len = strlen(option);
2052 if (!strncmp(buf, option, len))
2053 {
2054 strcpy(dst, buf + len + 1);
2055 len = strlen(dst);
2056 if ( len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline
2057 return mDNStrue;
2058 }
2059 LogMsg("Malformatted config file - %s not set", option);
2060 return mDNSfalse;
2061 }
2062
2063
2064
2065 mDNSlocal void ReadRegDomainFromConfig(mDNS *const m)
2066 {
2067 FILE *f;
2068 uDNS_GlobalInfo *u = &m->uDNS_info;;
2069 char key[MAX_ESCAPED_DOMAIN_NAME];
2070 domainname key_d, name_d, service_d;
2071 char secret[1024];
2072 int slen;
2073 mStatus err;
2074
2075 // read registration domain (for dynamic updates) from config file
2076 // !!!KRS these must go away once we can learn the reg domain from the network or prefs
2077 if (m->uDNS_info.NameRegDomain[0] || m->uDNS_info.ServiceRegDomain[0])
2078 { debugf("Options from config already set via keychain. Ignoring config file."); return; }
2079
2080 f = fopen(CONFIG_FILE, "r");
2081 if (!f)
2082 {
2083 if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened.");
2084 return;
2085 }
2086
2087 if (!GetConfigOption(u->NameRegDomain, "name-reg", f)) goto end;
2088 if (!GetConfigOption(u->ServiceRegDomain, "service-reg", f)) goto end;
2089 if (!GetConfigOption(key, "key-name", f)) goto end;
2090 if (!GetConfigOption(secret, "secret-64", f)) { LogMsg("ERROR: config file contains key without secret"); goto end; }
2091
2092 // we don't actually need this in domain-name format - just convert it to error check
2093 if (!MakeDomainNameFromDNSNameString(&service_d, u->ServiceRegDomain))
2094 { LogMsg("ERROR: config file contains bad service reg domain %s", u->ServiceRegDomain); u->ServiceRegDomain[0] = '\0'; }
2095
2096 if (!MakeDomainNameFromDNSNameString(&name_d, u->NameRegDomain))
2097 { LogMsg("ERROR: config file contains bad name reg domain %s", u->NameRegDomain); u->NameRegDomain[0] = '\0'; }
2098
2099 if (!MakeDomainNameFromDNSNameString(&key_d, key))
2100 { LogMsg("ERROR: config file contains bad key %s", key); key[0] = '\0'; }
2101
2102 if (key[0])
2103 {
2104 slen = strlen(secret);
2105 if (u->ServiceRegDomain[0])
2106 {
2107 err = mDNS_UpdateDomainRequiresAuthentication(m, &service_d, &key_d, secret, slen, mDNStrue);
2108 if (err) LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication returned %d for domain ", err, u->ServiceRegDomain);
2109 }
2110 if (u->NameRegDomain[0])
2111 {
2112 err = mDNS_UpdateDomainRequiresAuthentication(m, &name_d, &key_d, secret, slen, mDNStrue);
2113 if (err) LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication returned %d for domain ", err, u->NameRegDomain);
2114 }
2115 }
2116
2117 end:
2118 fclose(f);
2119 }
2120
2121 mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void)
2122 {
2123 return mDNS_CopyDNameList(DefBrowseList);
2124 }
2125
2126 mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void)
2127 {
2128 static DNameListElem tmp;
2129 static mDNSBool init = mDNSfalse;
2130
2131 if (!init)
2132 {
2133 MakeDomainNameFromDNSNameString(&tmp.name, "local.");
2134 tmp.next = NULL;
2135 init = mDNStrue;
2136 }
2137 return mDNS_CopyDNameList(&tmp);
2138 }
2139
2140
2141 mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
2142 {
2143 DNameListElem *ptr, *prev, *new;
2144 (void)m; // unused;
2145 (void)question; // unused
2146
2147 if (AddRecord)
2148 {
2149 new = mallocL("FoundDefBrowseDomain", sizeof(DNameListElem));
2150 if (!new) { LogMsg("ERROR: malloc"); return; }
2151 strcpy(new->name.c, answer->rdata->u.name.c);
2152 new->next = DefBrowseList;
2153 DefBrowseList = new;
2154 return;
2155 }
2156 else
2157 {
2158 ptr = DefBrowseList;
2159 prev = NULL;
2160 while (ptr)
2161 {
2162 if (SameDomainName(&ptr->name, &answer->rdata->u.name))
2163 {
2164 if (prev) prev->next = ptr->next;
2165 else DefBrowseList = ptr->next;
2166 freeL("FoundDefBrowseDomain", ptr);
2167 return;
2168 }
2169 prev = ptr;
2170 ptr = ptr->next;
2171 }
2172 LogMsg("FoundDefBrowseDomain: Got remove event for domain %s not in list", answer->rdata->u.name.c);
2173 }
2174 }
2175
2176 // Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
2177 // 1) query for _browse._dns-sd._udp.local on LocalOnly interface
2178 // (.local manually generated via explicit callback)
2179 // 2) for each search domain (from prefs pane), query for _browse._dns-sd._udp.<searchdomain>.
2180 // 3) for each result from (2), register LocalOnly PTR record_browse._dns-sd._udp.local. -> <result>
2181 // 4) result above should generate a callback from question in (1). result added to global list
2182 // 5) global list delivered to client via GetSearchDomainList()
2183 // 6) client calls to enumerate domains now go over LocalOnly interface
2184 // (!!!KRS may add outgoing interface in addition)
2185
2186 mDNSlocal mStatus InitDNSConfig(mDNS *const m)
2187 {
2188 mStatus err;
2189 AuthRecord local;
2190 DNSConfigInitialized = mDNStrue;
2191
2192 // start query for domains to be used in default (empty string domain) browses
2193 err = mDNS_GetDomains(m, &DefBrowseDomainQ, mDNS_DomainTypeBrowse, NULL, mDNSInterface_LocalOnly, FoundDefBrowseDomain, NULL);
2194
2195 // provide .local automatically
2196 mDNS_SetupResourceRecord(&local, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
2197 MakeDomainNameFromDNSNameString(&local.resrec.name, "_browse._dns-sd._udp.local.");
2198 MakeDomainNameFromDNSNameString(&local.resrec.rdata->u.name, "local.");
2199 // other fields ignored
2200 FoundDefBrowseDomain(m, &DefBrowseDomainQ, &local.resrec, 1);
2201
2202 return mStatus_NoError;
2203 }
2204
2205 mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
2206 {
2207 mStatus err;
2208
2209 m->hostlabel.c[0] = 0;
2210
2211 char *HINFO_HWstring = "Macintosh";
2212 char HINFO_HWstring_buffer[256];
2213 int get_model[2] = { CTL_HW, HW_MODEL };
2214 size_t len_model = sizeof(HINFO_HWstring_buffer);
2215 if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0)
2216 HINFO_HWstring = HINFO_HWstring_buffer;
2217
2218 char HINFO_SWstring[256] = "";
2219 if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces;
2220 if (mDNSPlatformInit_ReceiveUnicast()) m->CanReceiveUnicast = mDNStrue;
2221
2222 mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
2223 mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring);
2224 if (hlen + slen < 254)
2225 {
2226 m->HIHardware.c[0] = hlen;
2227 m->HISoftware.c[0] = slen;
2228 mDNSPlatformMemCopy(HINFO_HWstring, &m->HIHardware.c[1], hlen);
2229 mDNSPlatformMemCopy(HINFO_SWstring, &m->HISoftware.c[1], slen);
2230 }
2231
2232 m->p->unicastsockets.m = m;
2233 m->p->unicastsockets.info = NULL;
2234 m->p->unicastsockets.sktv4 = m->p->unicastsockets.sktv6 = -1;
2235 m->p->unicastsockets.cfsv4 = m->p->unicastsockets.cfsv6 = NULL;
2236 m->p->unicastsockets.rlsv4 = m->p->unicastsockets.rlsv6 = NULL;
2237
2238 err = SetupSocket(&m->p->unicastsockets, zeroIPPort, &zeroAddr, AF_INET);
2239 err = SetupSocket(&m->p->unicastsockets, zeroIPPort, &zeroAddr, AF_INET6);
2240
2241 m->p->InterfaceList = mDNSNULL;
2242 m->p->userhostlabel.c[0] = 0;
2243 UpdateInterfaceList(m);
2244 SetupActiveInterfaces(m);
2245
2246 err = WatchForNetworkChanges(m);
2247 if (err) return(err);
2248
2249 err = WatchForPowerChanges(m);
2250 if (err) return err;
2251
2252 err = WatchForDNSChanges(m);
2253
2254 InitDNSConfig(m);
2255
2256 m->uDNS_info.ServiceRegDomain[0] = '\0';
2257 m->uDNS_info.NameRegDomain[0] = '\0';
2258 InitAuthInfo(m);
2259 ReadRegDomainFromConfig(m);
2260
2261 return(err);
2262 }
2263
2264 mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
2265 {
2266 mStatus result = mDNSPlatformInit_setup(m);
2267
2268 // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already
2269 // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately
2270 if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError);
2271 return(result);
2272 }
2273
2274 mDNSexport void mDNSPlatformClose(mDNS *const m)
2275 {
2276 if (m->p->PowerConnection)
2277 {
2278 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode);
2279 CFRunLoopSourceInvalidate(m->p->PowerRLS);
2280 CFRelease(m->p->PowerRLS);
2281 IODeregisterForSystemPower(&m->p->PowerNotifier);
2282 m->p->PowerConnection = NULL;
2283 m->p->PowerNotifier = NULL;
2284 m->p->PowerRLS = NULL;
2285 }
2286
2287 if (m->p->Store)
2288 {
2289 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
2290 CFRunLoopSourceInvalidate(m->p->StoreRLS);
2291 CFRelease(m->p->StoreRLS);
2292 CFRelease(m->p->Store);
2293 m->p->Store = NULL;
2294 m->p->StoreRLS = NULL;
2295 }
2296
2297 MarkAllInterfacesInactive(m);
2298 ClearInactiveInterfaces(m);
2299 CloseSocketSet(&m->p->unicastsockets);
2300 }
2301
2302 mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000;
2303
2304 mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow)
2305 {
2306 // Notes: Typical values for mach_timebase_info:
2307 // tbi.numer = 1000 million
2308 // tbi.denom = 33 million
2309 // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds;
2310 // numer / denom = nanoseconds per hardware clock tick (e.g. 30);
2311 // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033)
2312 // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333)
2313 // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds
2314 //
2315 // Arithmetic notes:
2316 // tbi.denom is at least 1, and not more than 2^32-1.
2317 // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t.
2318 // tbi.denom is at least 1, and not more than 2^32-1.
2319 // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9.
2320 // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz,
2321 // which is unlikely on any current or future Macintosh.
2322 // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz.
2323 // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code.
2324 struct mach_timebase_info tbi;
2325 kern_return_t result = mach_timebase_info(&tbi);
2326 if (result != KERN_SUCCESS) return(result);
2327 clockdivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer;
2328 *timenow = mDNSPlatformTimeNow();
2329 return(mStatus_NoError);
2330 }
2331
2332 mDNSexport mDNSs32 mDNSPlatformTimeNow(void)
2333 {
2334 if (clockdivisor == 0) { LogMsg("mDNSPlatformTimeNow called before mDNSPlatformTimeInit"); return(0); }
2335 return((mDNSs32)(mach_absolute_time() / clockdivisor));
2336 }
2337
2338 mDNSexport mDNSs32 mDNSPlatformUTC(void)
2339 {
2340 return time(NULL);
2341 }
2342
2343 // Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves
2344 mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; }
2345 mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; }
2346 mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); }
2347 mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); }
2348 mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) { memcpy(dst, src, len); }
2349 mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) { return(memcmp(dst, src, len) == 0); }
2350 mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { bzero(dst, len); }
2351 mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); }
2352 mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); }