]>
Commit | Line | Data |
---|---|---|
c9b9ae52 A |
1 | /* |
2 | * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | ||
23 | Change History (most recent first): | |
24 | ||
25 | $Log: CFSocket.c,v $ | |
26 | Revision 1.115 2003/09/10 00:45:55 cheshire | |
27 | <rdar://problem/3412328> Don't log "sendto failed" errors during the first two minutes of startup | |
28 | ||
29 | Revision 1.114 2003/08/27 02:55:13 cheshire | |
30 | <rdar://problem/3387910>: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) | |
31 | ||
32 | Revision 1.113 2003/08/19 22:20:00 cheshire | |
33 | <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured | |
34 | More minor refinements | |
35 | ||
36 | Revision 1.112 2003/08/19 03:04:43 cheshire | |
37 | <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured | |
38 | ||
39 | Revision 1.111 2003/08/18 22:53:37 cheshire | |
40 | <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow() | |
41 | ||
42 | Revision 1.110 2003/08/16 03:39:00 cheshire | |
43 | <rdar://problem/3338440> InterfaceID -1 indicates "local only" | |
44 | ||
45 | Revision 1.109 2003/08/15 02:19:49 cheshire | |
46 | <rdar://problem/3375225> syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 | |
47 | Also limit number of messages to at most 100 | |
48 | ||
49 | Revision 1.108 2003/08/12 22:24:52 cheshire | |
50 | <rdar://problem/3375225> syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 | |
51 | This message indicates a kernel bug, but still we don't want to flood syslog. | |
52 | Do a sleep(1) after writing this log message, to limit the rate. | |
53 | ||
54 | Revision 1.107 2003/08/12 19:56:25 cheshire | |
55 | Update to APSL 2.0 | |
56 | ||
57 | Revision 1.106 2003/08/12 13:48:32 cheshire | |
58 | Add comment explaining clockdivisor calculation | |
59 | ||
60 | Revision 1.105 2003/08/12 13:44:14 cheshire | |
61 | <rdar://problem/3370229> mDNSResponder *VERY* unhappy if time goes backwards | |
62 | Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot) | |
63 | instead of gettimeofday() (which can jump back if the user manually changes their time/date) | |
64 | ||
65 | Revision 1.104 2003/08/12 13:12:07 cheshire | |
66 | Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static" | |
67 | ||
68 | Revision 1.103 2003/08/08 18:36:04 cheshire | |
69 | <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug | |
70 | ||
71 | Revision 1.102 2003/08/06 00:14:52 cheshire | |
72 | <rdar://problem/3330324> Need to check IP TTL on responses | |
73 | Also add corresponding checks in the IPv6 code path | |
74 | ||
75 | Revision 1.101 2003/08/05 22:20:16 cheshire | |
76 | <rdar://problem/3330324> Need to check IP TTL on responses | |
77 | ||
78 | Revision 1.100 2003/08/05 21:18:50 cheshire | |
79 | <rdar://problem/3363185> mDNSResponder should ignore 6to4 | |
80 | Only use interfaces that are marked as multicast-capable (IFF_MULTICAST) | |
81 | ||
82 | Revision 1.99 2003/08/05 20:13:52 cheshire | |
83 | <rdar://problem/3294080> mDNSResponder using IPv6 interfaces before they are ready | |
84 | Ignore interfaces with the IN6_IFF_NOTREADY flag set | |
85 | ||
86 | Revision 1.98 2003/07/20 03:38:51 ksekar | |
87 | Bug #: 3320722 | |
88 | Completed support for Unix-domain socket based API. | |
89 | ||
90 | Revision 1.97 2003/07/19 03:15:16 cheshire | |
91 | Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, | |
92 | and add the obvious trivial implementations to each platform support layer | |
93 | ||
94 | Revision 1.96 2003/07/18 00:30:00 cheshire | |
95 | <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead | |
96 | ||
97 | Revision 1.95 2003/07/12 03:15:20 cheshire | |
98 | <rdar://problem/3324848> After SCDynamicStore notification, mDNSResponder updates | |
99 | m->hostlabel even if user hasn't actually actually changed their dot-local hostname | |
100 | ||
101 | Revision 1.94 2003/07/03 00:51:54 cheshire | |
102 | <rdar://problem/3287213> When select() and recvmgs() disagree, get more info from kernel about the socket state | |
103 | ||
104 | Revision 1.93 2003/07/03 00:09:14 cheshire | |
105 | <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call | |
106 | Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name); | |
107 | ||
108 | Revision 1.92 2003/07/02 21:19:51 cheshire | |
109 | <rdar://problem/3313413> Update copyright notices, etc., in source code comments | |
110 | ||
111 | Revision 1.91 2003/06/24 01:53:51 cheshire | |
112 | Minor update to comments | |
113 | ||
114 | Revision 1.90 2003/06/24 01:51:47 cheshire | |
115 | <rdar://problem/3303118> Oops: Double-dispose of sockets | |
116 | Don't need to close sockets: CFSocketInvalidate() does that for us | |
117 | ||
118 | Revision 1.89 2003/06/21 18:12:47 cheshire | |
119 | <rdar://problem/3296061> Rendezvous cannot handle interfaces whose total name is >3 chars | |
120 | One-line change: should say "IF_NAMESIZE", not sizeof(ifname) | |
121 | ||
122 | Revision 1.88 2003/06/12 23:38:37 cheshire | |
123 | <rdar://problem/3291162> mDNSResponder doesn't detect some configuration changes | |
124 | Also check that scope_id matches before concluding that two interfaces are the same | |
125 | ||
126 | Revision 1.87 2003/06/10 01:14:11 cheshire | |
127 | <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call | |
128 | ||
129 | Revision 1.86 2003/05/28 02:41:52 cheshire | |
130 | <rdar://problem/3034346> Time to remove Mac OS 9 UDP Port 53 legacy support | |
131 | ||
132 | Revision 1.85 2003/05/28 02:39:47 cheshire | |
133 | Minor change to debugging messages | |
134 | ||
135 | Revision 1.84 2003/05/27 22:29:40 cheshire | |
136 | Remove out-dated comment | |
137 | ||
138 | Revision 1.83 2003/05/26 03:21:29 cheshire | |
139 | Tidy up address structure naming: | |
140 | mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) | |
141 | mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 | |
142 | mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 | |
143 | ||
144 | Revision 1.82 2003/05/26 03:01:27 cheshire | |
145 | <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead | |
146 | ||
147 | Revision 1.81 2003/05/24 02:06:42 cheshire | |
148 | <rdar://problem/3268480> IPv6 Multicast Loopback doesn't work | |
149 | Tried setting IPV6_MULTICAST_LOOP; it doesn't help. | |
150 | However, it is probably wise to have the code explicitly set this socket | |
151 | option anyway, in case the default changes in later versions of Unix. | |
152 | ||
153 | Revision 1.80 2003/05/24 02:02:24 cheshire | |
154 | <rdar://problem/3221880> if_indextoname consumes a lot of CPU | |
155 | Fix error in myIfIndexToName; was returning prematurely | |
156 | ||
157 | Revision 1.79 2003/05/23 23:07:44 cheshire | |
158 | <rdar://problem/3268199> Must not write to stderr when running as daemon | |
159 | ||
160 | Revision 1.78 2003/05/23 01:19:04 cheshire | |
161 | <rdar://problem/3267085> mDNSResponder needs to signal type of service to AirPort | |
162 | Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate | |
163 | ||
164 | Revision 1.77 2003/05/23 01:12:05 cheshire | |
165 | Minor code tidying | |
166 | ||
167 | Revision 1.76 2003/05/22 01:26:01 cheshire | |
168 | Tidy up log messages | |
169 | ||
170 | Revision 1.75 2003/05/22 00:07:09 cheshire | |
171 | <rdar://problem/3264366> myCFSocketCallBack recvfrom(5) error 1, errno 35 | |
172 | Extra logging to determine whether there is a bug in CFSocket | |
173 | ||
174 | Revision 1.74 2003/05/21 20:20:12 cheshire | |
175 | Fix warnings (mainly printf format string warnings, like using "%d" where | |
176 | it should say "%lu", etc.) and improve error logging (use strerror() | |
177 | to include textual error message as well as numeric error in log messages). | |
178 | ||
179 | Revision 1.73 2003/05/21 17:56:29 ksekar | |
180 | Bug #: <rdar://problem/3191277>: mDNSResponder doesn't watch for IPv6 address changes | |
181 | ||
182 | Revision 1.72 2003/05/14 18:48:41 cheshire | |
183 | <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations | |
184 | More minor refinements: | |
185 | CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory | |
186 | mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away | |
187 | ||
188 | Revision 1.71 2003/05/14 07:08:37 cheshire | |
189 | <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations | |
190 | Previously, when there was any network configuration change, mDNSResponder | |
191 | would tear down the entire list of active interfaces and start again. | |
192 | That was very disruptive, and caused the entire cache to be flushed, | |
193 | and caused lots of extra network traffic. Now it only removes interfaces | |
194 | that have really gone, and only adds new ones that weren't there before. | |
195 | ||
196 | Revision 1.70 2003/05/07 18:30:24 cheshire | |
197 | Fix signed/unsigned comparison warning | |
198 | ||
199 | Revision 1.69 2003/05/06 20:14:44 cheshire | |
200 | Change "tp" to "tv" | |
201 | ||
202 | Revision 1.68 2003/05/06 00:00:49 cheshire | |
203 | <rdar://problem/3248914> Rationalize naming of domainname manipulation functions | |
204 | ||
205 | Revision 1.67 2003/04/29 00:43:44 cheshire | |
206 | Fix compiler warnings | |
207 | ||
208 | Revision 1.66 2003/04/26 02:41:58 cheshire | |
209 | <rdar://problem/3241281> Change timenow from a local variable to a structure member | |
210 | ||
211 | Revision 1.65 2003/04/26 02:34:01 cheshire | |
212 | Add missing mDNSexport | |
213 | ||
214 | Revision 1.64 2003/04/15 16:48:06 jgraessl | |
215 | Bug #: 3228833 | |
216 | Modified code in CFSocket notifier function to read all packets on the socket | |
217 | instead of reading only one packet every time the notifier was called. | |
218 | ||
219 | Revision 1.63 2003/04/15 16:33:50 jgraessl | |
220 | Bug #: 3221880 | |
221 | Switched to our own copy of if_indextoname to improve performance. | |
222 | ||
223 | Revision 1.62 2003/03/28 01:55:44 cheshire | |
224 | Minor improvements to debugging messages | |
225 | ||
226 | Revision 1.61 2003/03/27 03:30:56 cheshire | |
227 | <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash | |
228 | Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback | |
229 | Fixes: | |
230 | 1. Make mDNS_DeregisterInterface() safe to call from a callback | |
231 | 2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead | |
232 | (it never really needed to deregister the interface at all) | |
233 | ||
234 | Revision 1.60 2003/03/15 04:40:38 cheshire | |
235 | Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" | |
236 | ||
237 | Revision 1.59 2003/03/11 01:23:26 cheshire | |
238 | Bug #: 3194246 mDNSResponder socket problems | |
239 | ||
240 | Revision 1.58 2003/03/06 01:43:04 cheshire | |
241 | Bug #: 3189097 Additional debugging code in mDNSResponder | |
242 | Improve "LIST_ALL_INTERFACES" output | |
243 | ||
244 | Revision 1.57 2003/03/05 22:36:27 cheshire | |
245 | Bug #: 3186338 Loopback doesn't work with mDNSResponder-27 | |
246 | Temporary workaround: Skip loopback interface *only* if we found at least one v4 interface to use | |
247 | ||
248 | Revision 1.56 2003/03/05 01:50:38 cheshire | |
249 | Bug #: 3189097 Additional debugging code in mDNSResponder | |
250 | ||
251 | Revision 1.55 2003/02/21 01:54:09 cheshire | |
252 | Bug #: 3099194 mDNSResponder needs performance improvements | |
253 | Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") | |
254 | ||
255 | Revision 1.54 2003/02/20 06:48:35 cheshire | |
256 | Bug #: 3169535 Xserve RAID needs to do interface-specific registrations | |
257 | Reviewed by: Josh Graessley, Bob Bradley | |
258 | ||
259 | Revision 1.53 2003/01/29 02:21:23 cheshire | |
260 | Return mStatus_Invalid if can't send packet because socket not available | |
261 | ||
262 | Revision 1.52 2003/01/28 19:39:43 jgraessl | |
263 | Enabling AAAA over IPv4 support. | |
264 | ||
265 | Revision 1.51 2003/01/28 05:11:23 cheshire | |
266 | Fixed backwards comparison in SearchForInterfaceByName | |
267 | ||
268 | Revision 1.50 2003/01/13 23:49:44 jgraessl | |
269 | Merged changes for the following fixes in to top of tree: | |
270 | 3086540 computer name changes not handled properly | |
271 | 3124348 service name changes are not properly handled | |
272 | 3124352 announcements sent in pairs, failing chattiness test | |
273 | ||
274 | Revision 1.49 2002/12/23 22:13:30 jgraessl | |
275 | Reviewed by: Stuart Cheshire | |
276 | Initial IPv6 support for mDNSResponder. | |
277 | ||
278 | Revision 1.48 2002/11/22 01:37:52 cheshire | |
279 | Bug #: 3108426 mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities | |
280 | ||
281 | Revision 1.47 2002/09/21 20:44:51 zarzycki | |
282 | Added APSL info | |
283 | ||
284 | Revision 1.46 2002/09/19 21:25:35 cheshire | |
285 | mDNS_snprintf() doesn't need to be in a separate file | |
286 | ||
287 | Revision 1.45 2002/09/17 01:45:13 cheshire | |
288 | Add LIST_ALL_INTERFACES symbol for debugging | |
289 | ||
290 | Revision 1.44 2002/09/17 01:36:23 cheshire | |
291 | Move Puma support to CFSocketPuma.c | |
292 | ||
293 | Revision 1.43 2002/09/17 01:05:28 cheshire | |
294 | Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global | |
295 | ||
296 | Revision 1.42 2002/09/16 23:13:50 cheshire | |
297 | Minor code tidying | |
298 | ||
299 | */ | |
300 | ||
301 | // *************************************************************************** | |
302 | // mDNS-CFSocket.c: | |
303 | // Supporting routines to run mDNS on a CFRunLoop platform | |
304 | // *************************************************************************** | |
305 | ||
306 | // Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53, | |
307 | // before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder | |
308 | // in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow | |
309 | // it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in | |
310 | // the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS | |
311 | // queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed. | |
312 | #define mDNS_AllowPort53 0 | |
313 | ||
314 | // For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces, | |
315 | // including ones that mDNSResponder chooses not to use. | |
316 | #define LIST_ALL_INTERFACES 0 | |
317 | ||
318 | // For enabling AAAA records over IPv4. Setting this to 0 sends only | |
319 | // A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both | |
320 | // AAAA and A records over both IPv4 and IPv6. | |
321 | #define AAAA_OVER_V4 1 | |
322 | ||
323 | #include "mDNSClientAPI.h" // Defines the interface provided to the client layer above | |
324 | #include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below | |
325 | #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform | |
326 | ||
327 | #include <stdio.h> | |
328 | #include <unistd.h> // For select() and close() | |
329 | #include <stdarg.h> // For va_list support | |
330 | #include <net/if.h> | |
331 | #include <net/if_dl.h> | |
332 | #include <sys/uio.h> | |
333 | #include <sys/param.h> | |
334 | #include <sys/socket.h> | |
335 | #include <sys/sysctl.h> | |
336 | #include <fcntl.h> | |
337 | #include <sys/ioctl.h> | |
338 | ||
339 | #include <netinet/in.h> // For IP_RECVTTL | |
340 | #ifndef IP_RECVTTL | |
341 | #define IP_RECVTTL 24 /* bool; receive reception TTL w/dgram */ | |
342 | #endif | |
343 | ||
344 | #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below | |
345 | #include <netinet/ip.h> // For IPTOS_LOWDELAY etc. | |
346 | #include <netinet6/in6_var.h> // For IN6_IFF_NOTREADY etc. | |
347 | ||
348 | // Code contributed by Dave Heller: | |
349 | // Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will | |
350 | // work on Mac OS X 10.1, which does not have the getifaddrs call. | |
351 | #define RUN_ON_PUMA_WITHOUT_IFADDRS 0 | |
352 | #if RUN_ON_PUMA_WITHOUT_IFADDRS | |
353 | #include "CFSocketPuma.c" | |
354 | #else | |
355 | #include <ifaddrs.h> | |
356 | #endif | |
357 | ||
358 | #include <IOKit/IOKitLib.h> | |
359 | #include <IOKit/IOMessage.h> | |
360 | #include <mach/mach_time.h> | |
361 | ||
362 | // *************************************************************************** | |
363 | // Globals | |
364 | ||
365 | static mDNSu32 clockdivisor = 0; | |
366 | ||
367 | // *************************************************************************** | |
368 | // Macros | |
369 | ||
370 | #define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) | |
371 | #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]) | |
372 | ||
373 | #define mDNSAddressIsAllDNSLinkGroup(X) ( \ | |
374 | ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup )) || \ | |
375 | ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) | |
376 | ||
377 | // *************************************************************************** | |
378 | // Functions | |
379 | ||
380 | // Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows | |
381 | // how to print special data types like IP addresses and length-prefixed domain names | |
382 | #if MDNS_DEBUGMSGS | |
383 | mDNSexport void debugf_(const char *format, ...) | |
384 | { | |
385 | unsigned char buffer[512]; | |
386 | va_list ptr; | |
387 | va_start(ptr,format); | |
388 | buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; | |
389 | va_end(ptr); | |
390 | fprintf(stderr,"%s\n", buffer); | |
391 | fflush(stderr); | |
392 | } | |
393 | #endif | |
394 | ||
395 | #if MDNS_DEBUGMSGS > 1 | |
396 | mDNSexport void verbosedebugf_(const char *format, ...) | |
397 | { | |
398 | unsigned char buffer[512]; | |
399 | va_list ptr; | |
400 | va_start(ptr,format); | |
401 | buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; | |
402 | va_end(ptr); | |
403 | fprintf(stderr,"%s\n", buffer); | |
404 | fflush(stderr); | |
405 | } | |
406 | #endif | |
407 | ||
408 | mDNSexport void LogMsg(const char *format, ...) | |
409 | { | |
410 | unsigned char buffer[512]; | |
411 | va_list ptr; | |
412 | va_start(ptr,format); | |
413 | buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; | |
414 | va_end(ptr); | |
415 | ||
416 | extern int debug_mode; | |
417 | if (debug_mode) // In debug_mode we write to stderr | |
418 | { | |
419 | fprintf(stderr,"%s\n", buffer); | |
420 | fflush(stderr); | |
421 | } | |
422 | else // else, in production mode, we write to syslog | |
423 | { | |
424 | openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON); | |
425 | syslog(LOG_ERR, "%s", buffer); | |
426 | closelog(); | |
427 | } | |
428 | } | |
429 | ||
430 | mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) | |
431 | { | |
432 | static struct ifaddrs *ifa = NULL; | |
433 | ||
434 | if (refresh && ifa) | |
435 | { | |
436 | freeifaddrs(ifa); | |
437 | ifa = NULL; | |
438 | } | |
439 | ||
440 | if (ifa == NULL) getifaddrs(&ifa); | |
441 | ||
442 | return ifa; | |
443 | } | |
444 | ||
445 | mDNSlocal int myIfIndexToName(u_short index, char* name) | |
446 | { | |
447 | struct ifaddrs *ifa; | |
448 | for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) | |
449 | if (ifa->ifa_addr->sa_family == AF_LINK) | |
450 | if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == index) | |
451 | { strncpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } | |
452 | return -1; | |
453 | } | |
454 | ||
455 | mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) | |
456 | { | |
457 | NetworkInterfaceInfoOSX *i; | |
458 | if (index == (uint32_t)~0) return((mDNSInterfaceID)~0); | |
459 | if (index) | |
460 | for (i = m->p->InterfaceList; i; i = i->next) | |
461 | if (i->scope_id == index) | |
462 | return(i->ifinfo.InterfaceID); | |
463 | return(mDNSNULL); | |
464 | } | |
465 | ||
466 | mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id) | |
467 | { | |
468 | NetworkInterfaceInfoOSX *i; | |
469 | if (id == (mDNSInterfaceID)~0) return((mDNSu32)~0); | |
470 | if (id) | |
471 | for (i = m->p->InterfaceList; i; i = i->next) | |
472 | if (i->ifinfo.InterfaceID == id) | |
473 | return i->scope_id; | |
474 | return 0; | |
475 | } | |
476 | ||
477 | mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, | |
478 | mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) | |
479 | { | |
480 | #pragma unused(m) | |
481 | NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; | |
482 | struct sockaddr_storage to; | |
483 | int s, err; | |
484 | ||
485 | if (!InterfaceID) { LogMsg("mDNSPlatformSendUDP ERROR! Cannot send from zero InterfaceID"); return mStatus_BadParamErr; } | |
486 | ||
487 | if (dst->type == mDNSAddrType_IPv4) | |
488 | { | |
489 | struct sockaddr_in* sin_to = (struct sockaddr_in*)&to; | |
490 | sin_to->sin_len = sizeof(*sin_to); | |
491 | sin_to->sin_family = AF_INET; | |
492 | sin_to->sin_port = dstPort.NotAnInteger; | |
493 | sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; | |
494 | } | |
495 | else if (dst->type == mDNSAddrType_IPv6) | |
496 | { | |
497 | struct sockaddr_in6* sin6_to = (struct sockaddr_in6*)&to; | |
498 | sin6_to->sin6_len = sizeof(*sin6_to); | |
499 | sin6_to->sin6_family = AF_INET6; | |
500 | sin6_to->sin6_port = dstPort.NotAnInteger; | |
501 | sin6_to->sin6_flowinfo = 0; | |
502 | sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; | |
503 | sin6_to->sin6_scope_id = info->scope_id; | |
504 | } | |
505 | else | |
506 | { | |
507 | LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n"); | |
508 | return mStatus_BadParamErr; | |
509 | } | |
510 | ||
511 | if (srcPort.NotAnInteger == MulticastDNSPort.NotAnInteger) | |
512 | { | |
513 | if (dst->type == mDNSAddrType_IPv4) s = info->sktv4; | |
514 | else if (dst->type == mDNSAddrType_IPv6) s = info->sktv6; | |
515 | else s = -1; | |
516 | } | |
517 | #if mDNS_AllowPort53 | |
518 | else if (srcPort.NotAnInteger == UnicastDNSPort.NotAnInteger && dst->type == mDNSAddrType_IPv4) | |
519 | s = info->skt53; | |
520 | #endif | |
521 | else { LogMsg("Source port %d not allowed", (mDNSu16)srcPort.b[0]<<8 | srcPort.b[1]); return(-1); } | |
522 | ||
523 | if (s >= 0) | |
524 | verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d", | |
525 | InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s); | |
526 | else | |
527 | verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %X %s/%d (socket of this type not available)", | |
528 | InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1]); | |
529 | ||
530 | // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet | |
531 | // If we don't have the corresponding type of socket available, then return mStatus_Invalid | |
532 | if (s < 0) return(mStatus_Invalid); | |
533 | ||
534 | err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); | |
535 | if (err < 0) | |
536 | { | |
537 | // Don't report EHOSTDOWN (i.e. ARP failure) to unicast destinations | |
538 | if (errno == EHOSTDOWN && !mDNSAddressIsAllDNSLinkGroup(dst)) return(err); | |
539 | // Don't report EHOSTUNREACH in the first two minutes after boot | |
540 | // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>) | |
541 | // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. | |
542 | if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 120)) return(err); | |
543 | LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s)", | |
544 | InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s, err, errno, strerror(errno)); | |
545 | return(err); | |
546 | } | |
547 | ||
548 | return(mStatus_NoError); | |
549 | } | |
550 | ||
551 | mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, | |
552 | struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) | |
553 | { | |
554 | static unsigned int numLogMessages = 0; | |
555 | struct iovec databuffers = { (char *)buffer, max }; | |
556 | struct msghdr msg; | |
557 | ssize_t n; | |
558 | struct cmsghdr *cmPtr; | |
559 | char ancillary[1024]; | |
560 | ||
561 | *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be | |
562 | ||
563 | // Set up the message | |
564 | msg.msg_name = (caddr_t)from; | |
565 | msg.msg_namelen = *fromlen; | |
566 | msg.msg_iov = &databuffers; | |
567 | msg.msg_iovlen = 1; | |
568 | msg.msg_control = (caddr_t)&ancillary; | |
569 | msg.msg_controllen = sizeof(ancillary); | |
570 | msg.msg_flags = 0; | |
571 | ||
572 | // Receive the data | |
573 | n = recvmsg(s, &msg, 0); | |
574 | if (n<0) | |
575 | { | |
576 | if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno); | |
577 | return(-1); | |
578 | } | |
579 | if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) | |
580 | { | |
581 | if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", | |
582 | s, msg.msg_controllen, sizeof(struct cmsghdr)); | |
583 | return(-1); | |
584 | } | |
585 | if (msg.msg_flags & MSG_CTRUNC) | |
586 | { | |
587 | if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); | |
588 | return(-1); | |
589 | } | |
590 | ||
591 | *fromlen = msg.msg_namelen; | |
592 | ||
593 | // Parse each option out of the ancillary data. | |
594 | for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) | |
595 | { | |
596 | // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); | |
597 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) | |
598 | { | |
599 | dstaddr->type = mDNSAddrType_IPv4; | |
600 | dstaddr->ip.v4.NotAnInteger = *(u_int32_t*)CMSG_DATA(cmPtr); | |
601 | } | |
602 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) | |
603 | { | |
604 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); | |
605 | if (sdl->sdl_nlen < IF_NAMESIZE) | |
606 | { | |
607 | mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); | |
608 | ifname[sdl->sdl_nlen] = 0; | |
609 | // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); | |
610 | } | |
611 | } | |
612 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) | |
613 | { | |
614 | *ttl = *(u_char*)CMSG_DATA(cmPtr); | |
615 | } | |
616 | if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) | |
617 | { | |
618 | struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); | |
619 | dstaddr->type = mDNSAddrType_IPv6; | |
620 | dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; | |
621 | myIfIndexToName(ip6_info->ipi6_ifindex, ifname); | |
622 | } | |
623 | if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) | |
624 | { | |
625 | *ttl = *(int*)CMSG_DATA(cmPtr); | |
626 | } | |
627 | } | |
628 | ||
629 | return(n); | |
630 | } | |
631 | ||
632 | mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) | |
633 | { | |
634 | mDNSAddr senderAddr, destAddr; | |
635 | mDNSIPPort senderPort, destPort = MulticastDNSPort; | |
636 | NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)context; | |
637 | mDNS *const m = info->m; | |
638 | DNSMessage packet; | |
639 | struct sockaddr_storage from; | |
640 | size_t fromlen = sizeof(from); | |
641 | char packetifname[IF_NAMESIZE] = ""; | |
642 | int err, s1 = -1, skt = CFSocketGetNative(cfs); | |
643 | int count = 0; | |
644 | ||
645 | (void)address; // Parameter not used | |
646 | (void)data; // Parameter not used | |
647 | ||
648 | if (CallBackType != kCFSocketReadCallBack) LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType); | |
649 | ||
650 | #if mDNS_AllowPort53 | |
651 | if (cfs == info->cfs53) { s1 = info->skt53; destPort = UnicastDNSPort; } | |
652 | else | |
653 | #endif | |
654 | if (cfs == info->cfsv4) s1 = info->sktv4; | |
655 | else if (cfs == info->cfsv6) s1 = info->sktv6; | |
656 | ||
657 | if (s1 < 0 || s1 != skt) | |
658 | { | |
659 | LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs); | |
660 | #if mDNS_AllowPort53 | |
661 | LogMsg("myCFSocketCallBack: cfs53 %p, skt53 %d", info->cfs53, info->skt53); | |
662 | #endif | |
663 | LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", info->cfsv4, info->sktv4); | |
664 | LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", info->cfsv6, info->sktv6); | |
665 | } | |
666 | ||
667 | mDNSu8 ttl; | |
668 | while ((err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) | |
669 | { | |
670 | count++; | |
671 | if (from.ss_family == AF_INET) | |
672 | { | |
673 | struct sockaddr_in *sin = (struct sockaddr_in*)&from; | |
674 | senderAddr.type = mDNSAddrType_IPv4; | |
675 | senderAddr.ip.v4.NotAnInteger = sin->sin_addr.s_addr; | |
676 | senderPort.NotAnInteger = sin->sin_port; | |
677 | } | |
678 | else if (from.ss_family == AF_INET6) | |
679 | { | |
680 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; | |
681 | senderAddr.type = mDNSAddrType_IPv6; | |
682 | senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; | |
683 | senderPort.NotAnInteger = sin6->sin6_port; | |
684 | } | |
685 | else | |
686 | { | |
687 | LogMsg("myCFSocketCallBack from is unknown address family %d", from.ss_family); | |
688 | return; | |
689 | } | |
690 | ||
691 | // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the | |
692 | // sockets API means that even though this socket has only officially joined the multicast group | |
693 | // on one specific interface, the kernel will still deliver multicast packets to it no matter which | |
694 | // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. | |
695 | // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface | |
696 | // on which the packet arrived, and ignore the packet if it really arrived on some other interface. | |
697 | if (strcmp(info->ifa_name, packetifname)) | |
698 | { | |
699 | verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)", | |
700 | &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name, packetifname); | |
701 | return; | |
702 | } | |
703 | else | |
704 | verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s", | |
705 | &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name); | |
706 | ||
707 | if (err < (int)sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } | |
708 | ||
709 | mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, &senderAddr, senderPort, &destAddr, destPort, info->ifinfo.InterfaceID, ttl); | |
710 | } | |
711 | ||
712 | if (err < 0 && (errno != EWOULDBLOCK || count == 0)) | |
713 | { | |
714 | // Something is busted here. | |
715 | // CFSocket says there is a packet, but myrecvfrom says there is not. | |
716 | // Try calling select() to get another opinion. | |
717 | // Find out about other socket parameter that can help understand why select() says the socket is ready for read | |
718 | // All of this is racy, as data may have arrived after the call to select() | |
719 | int save_errno = errno; | |
720 | int so_error = -1; | |
721 | int so_nread = -1; | |
722 | int fionread = -1; | |
723 | int solen = sizeof(int); | |
724 | fd_set readfds; | |
725 | FD_ZERO(&readfds); | |
726 | FD_SET(s1, &readfds); | |
727 | struct timeval timeout; | |
728 | timeout.tv_sec = 0; | |
729 | timeout.tv_usec = 0; | |
730 | int selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); | |
731 | if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) | |
732 | LogMsg("myCFSocketCallBack getsockopt(SO_ERROR) error %d", errno); | |
733 | if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) | |
734 | LogMsg("myCFSocketCallBack getsockopt(SO_NREAD) error %d", errno); | |
735 | if (ioctl(s1, FIONREAD, &fionread) == -1) | |
736 | LogMsg("myCFSocketCallBack ioctl(FIONREAD) error %d", errno); | |
737 | static unsigned int numLogMessages = 0; | |
738 | if (numLogMessages++ < 100) | |
739 | LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", | |
740 | s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); | |
741 | sleep(1); // After logging this error, rate limit so we don't flood syslog | |
742 | } | |
743 | } | |
744 | ||
745 | // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel | |
746 | mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) | |
747 | { | |
748 | CFStringEncoding encoding = kCFStringEncodingUTF8; | |
749 | CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); | |
750 | if (cfs) | |
751 | { | |
752 | CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); | |
753 | CFRelease(cfs); | |
754 | } | |
755 | } | |
756 | ||
757 | // This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel | |
758 | mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) | |
759 | { | |
760 | CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); | |
761 | if (cfs) | |
762 | { | |
763 | CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); | |
764 | CFRelease(cfs); | |
765 | } | |
766 | } | |
767 | ||
768 | mDNSlocal mStatus SetupSocket(NetworkInterfaceInfoOSX *i, mDNSIPPort port, int *s, CFSocketRef *c) | |
769 | { | |
770 | const int on = 1; | |
771 | const int twofivefive = 255; | |
772 | ||
773 | if (*s >= 0) { LogMsg("SetupSocket ERROR: socket %d is already set", *s); return(-1); } | |
774 | if (*c) { LogMsg("SetupSocket ERROR: CFSocketRef %p is already set", *c); return(-1); } | |
775 | ||
776 | // Open the socket... | |
777 | int skt = socket(i->sa_family, SOCK_DGRAM, IPPROTO_UDP); | |
778 | if (skt < 0) { LogMsg("socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } | |
779 | ||
780 | // ... with a shared UDP port | |
781 | mStatus err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); | |
782 | if (err < 0) { LogMsg("setsockopt - SO_REUSEPORT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
783 | ||
784 | if (i->sa_family == AF_INET) | |
785 | { | |
786 | // We want to receive destination addresses | |
787 | err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); | |
788 | if (err < 0) { LogMsg("setsockopt - IP_RECVDSTADDR error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
789 | ||
790 | // We want to receive interface identifiers | |
791 | err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); | |
792 | if (err < 0) { LogMsg("setsockopt - IP_RECVIF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
793 | ||
794 | // We want to receive packet TTL value so we can check it | |
795 | err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); | |
796 | // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it | |
797 | ||
798 | // Add multicast group membership on this interface | |
799 | struct in_addr addr = { i->ifinfo.ip.ip.v4.NotAnInteger }; | |
800 | struct ip_mreq imr; | |
801 | imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; | |
802 | imr.imr_interface = addr; | |
803 | err = setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); | |
804 | if (err < 0) { LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
805 | ||
806 | // Specify outgoing interface too | |
807 | err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); | |
808 | if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
809 | ||
810 | // Send unicast packets with TTL 255 | |
811 | err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); | |
812 | if (err < 0) { LogMsg("setsockopt - IP_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
813 | ||
814 | // And multicast packets with TTL 255 too | |
815 | err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); | |
816 | if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
817 | ||
818 | // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate | |
819 | const int ip_tosbits = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; | |
820 | err = setsockopt(skt, IPPROTO_IP, IP_TOS, &ip_tosbits, sizeof(ip_tosbits)); | |
821 | if (err < 0) { LogMsg("setsockopt - IP_TOS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
822 | ||
823 | // And start listening for packets | |
824 | struct sockaddr_in listening_sockaddr; | |
825 | listening_sockaddr.sin_family = AF_INET; | |
826 | listening_sockaddr.sin_port = port.NotAnInteger; | |
827 | listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket | |
828 | err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); | |
829 | if (err) | |
830 | { | |
831 | // If we fail to bind to port 53 (because we're not root), that's okay, just tidy up and silently continue | |
832 | if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) { close(skt); err = 0; } | |
833 | else LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); | |
834 | return(err); | |
835 | } | |
836 | } | |
837 | else if (i->sa_family == AF_INET6) | |
838 | { | |
839 | // We want to receive destination addresses and receive interface identifiers | |
840 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); | |
841 | if (err < 0) { LogMsg("setsockopt - IPV6_PKTINFO error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
842 | ||
843 | // We want to receive packet hop count value so we can check it | |
844 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); | |
845 | if (err < 0) { LogMsg("setsockopt - IPV6_HOPLIMIT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
846 | ||
847 | // We want to receive only IPv6 packets, without this option, we may | |
848 | // get IPv4 addresses as mapped addresses. | |
849 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); | |
850 | if (err < 0) { LogMsg("setsockopt - IPV6_V6ONLY error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
851 | ||
852 | // Add multicast group membership on this interface | |
853 | int interface_id = if_nametoindex(i->ifa_name); | |
854 | struct ipv6_mreq i6mr; | |
855 | i6mr.ipv6mr_interface = interface_id; | |
856 | i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroupv6; | |
857 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); | |
858 | if (err < 0) { LogMsg("setsockopt - IPV6_JOIN_GROUP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
859 | ||
860 | // Specify outgoing interface too | |
861 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id)); | |
862 | if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
863 | ||
864 | // Send unicast packets with TTL 255 | |
865 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); | |
866 | if (err < 0) { LogMsg("setsockopt - IPV6_UNICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
867 | ||
868 | // And multicast packets with TTL 255 too | |
869 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); | |
870 | if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
871 | ||
872 | // Note: IPV6_TCLASS appears not to be implemented on OS X right now (or indeed on ANY version of Unix?) | |
873 | #ifdef IPV6_TCLASS | |
874 | // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate | |
875 | int tclass = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; // This may not be right (since tclass is not implemented on OS X, I can't test it) | |
876 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); | |
877 | if (err < 0) { LogMsg("setsockopt - IPV6_TCLASS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
878 | #endif | |
879 | ||
880 | // Want to receive our own packets | |
881 | err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); | |
882 | if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_LOOP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
883 | ||
884 | // And start listening for packets | |
885 | struct sockaddr_in6 listening_sockaddr6; | |
886 | bzero(&listening_sockaddr6, sizeof(listening_sockaddr6)); | |
887 | listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); | |
888 | listening_sockaddr6.sin6_family = AF_INET6; | |
889 | listening_sockaddr6.sin6_port = port.NotAnInteger; | |
890 | listening_sockaddr6.sin6_flowinfo = 0; | |
891 | // listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket | |
892 | listening_sockaddr6.sin6_scope_id = 0; | |
893 | err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); | |
894 | if (err) { LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } | |
895 | } | |
896 | ||
897 | fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking | |
898 | *s = skt; | |
899 | CFSocketContext myCFSocketContext = { 0, i->ifinfo.InterfaceID, NULL, NULL, NULL }; | |
900 | *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); | |
901 | CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); | |
902 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
903 | CFRelease(rls); | |
904 | ||
905 | return(err); | |
906 | } | |
907 | ||
908 | mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) | |
909 | { | |
910 | if (sa->sa_family == AF_INET) | |
911 | { | |
912 | struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; | |
913 | ip->type = mDNSAddrType_IPv4; | |
914 | ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; | |
915 | return(0); | |
916 | } | |
917 | else if (sa->sa_family == AF_INET6) | |
918 | { | |
919 | struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; | |
920 | ip->type = mDNSAddrType_IPv6; | |
921 | if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; | |
922 | ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; | |
923 | return(0); | |
924 | } | |
925 | else | |
926 | { | |
927 | LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); | |
928 | return(-1); | |
929 | } | |
930 | } | |
931 | ||
932 | mDNSlocal mStatus AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) | |
933 | { | |
934 | mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); | |
935 | mDNSAddr ip; | |
936 | SetupAddr(&ip, ifa->ifa_addr); | |
937 | NetworkInterfaceInfoOSX **p; | |
938 | for (p = &m->p->InterfaceList; *p; p = &(*p)->next) | |
939 | if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip)) | |
940 | { | |
941 | debugf("AddInterfaceToList: Found existing interface %u with address %#a", scope_id, &ip); | |
942 | (*p)->CurrentlyActive = mDNStrue; | |
943 | return(0); | |
944 | } | |
945 | ||
946 | debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip); | |
947 | NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); | |
948 | if (!i) return(-1); | |
949 | i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1); | |
950 | if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(-1); } | |
951 | strcpy(i->ifa_name, ifa->ifa_name); | |
952 | ||
953 | i->ifinfo.InterfaceID = mDNSNULL; | |
954 | i->ifinfo.ip = ip; | |
955 | i->ifinfo.Advertise = m->AdvertiseLocalAddresses; | |
956 | i->ifinfo.TxAndRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList | |
957 | ||
958 | i->next = mDNSNULL; | |
959 | i->m = m; | |
960 | i->scope_id = scope_id; | |
961 | i->CurrentlyActive = mDNStrue; | |
962 | i->sa_family = ifa->ifa_addr->sa_family; | |
963 | #if mDNS_AllowPort53 | |
964 | i->skt53 = -1; | |
965 | i->cfs53 = NULL; | |
966 | #endif | |
967 | i->sktv4 = -1; | |
968 | i->cfsv4 = NULL; | |
969 | i->sktv6 = -1; | |
970 | i->cfsv6 = NULL; | |
971 | ||
972 | if (!i->ifa_name) return(-1); | |
973 | *p = i; | |
974 | return(0); | |
975 | } | |
976 | ||
977 | mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) | |
978 | { | |
979 | NetworkInterfaceInfoOSX *i; | |
980 | for (i = m->p->InterfaceList; i; i = i->next) | |
981 | if (i->CurrentlyActive && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) | |
982 | if (!(i->ifinfo.ip.ip.v4.b[0] == 169 && i->ifinfo.ip.ip.v4.b[1] == 254)) | |
983 | return(i); | |
984 | return(mDNSNULL); | |
985 | } | |
986 | ||
987 | mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) | |
988 | { | |
989 | mDNSBool foundav4 = mDNSfalse; | |
990 | struct ifaddrs *ifa = myGetIfAddrs(1); | |
991 | struct ifaddrs *theLoopback = NULL; | |
992 | int err = (ifa != NULL) ? 0 : (errno != 0 ? errno : -1); | |
993 | int InfoSocket = err ? -1 : socket(AF_INET6, SOCK_DGRAM, 0); | |
994 | if (err) return(err); | |
995 | ||
996 | // Set up the nice label | |
997 | m->nicelabel.c[0] = 0; | |
998 | GetUserSpecifiedFriendlyComputerName(&m->nicelabel); | |
999 | if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); | |
1000 | ||
1001 | // Set up the RFC 1034-compliant label | |
1002 | domainlabel hostlabel; | |
1003 | hostlabel.c[0] = 0; | |
1004 | GetUserSpecifiedRFC1034ComputerName(&hostlabel); | |
1005 | if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, "Macintosh"); | |
1006 | // If the user has changed their dot-local host name since the last time we checked, then update our local copy. | |
1007 | // If the user has not changed their dot-local host name, then leave ours alone (m->hostlabel may have gone through | |
1008 | // repeated conflict resolution to get to its current value, and if we reset it, we'll have to go through all that again.) | |
1009 | if (SameDomainLabel(m->p->userhostlabel.c, hostlabel.c)) | |
1010 | debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); | |
1011 | else | |
1012 | { | |
1013 | debugf("Updating m->hostlabel to %#s", hostlabel.c); | |
1014 | m->p->userhostlabel = m->hostlabel = hostlabel; | |
1015 | mDNS_GenerateFQDN(m); | |
1016 | } | |
1017 | ||
1018 | while (ifa) | |
1019 | { | |
1020 | #if LIST_ALL_INTERFACES | |
1021 | if (ifa->ifa_addr->sa_family == AF_APPLETALK) | |
1022 | debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_APPLETALK", | |
1023 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
1024 | else if (ifa->ifa_addr->sa_family == AF_LINK) | |
1025 | debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_LINK", | |
1026 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
1027 | else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) | |
1028 | debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", | |
1029 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
1030 | if (!(ifa->ifa_flags & IFF_UP)) | |
1031 | debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_UP", | |
1032 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
1033 | if (ifa->ifa_flags & IFF_POINTOPOINT) | |
1034 | debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", | |
1035 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
1036 | if (ifa->ifa_flags & IFF_LOOPBACK) | |
1037 | debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", | |
1038 | ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
1039 | #endif | |
1040 | if ((ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) && | |
1041 | (ifa->ifa_flags & IFF_MULTICAST) && | |
1042 | (ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_POINTOPOINT)) | |
1043 | { | |
1044 | int ifru_flags6 = 0; | |
1045 | if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) | |
1046 | { | |
1047 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; | |
1048 | struct in6_ifreq ifr6; | |
1049 | bzero((char *)&ifr6, sizeof(ifr6)); | |
1050 | strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); | |
1051 | ifr6.ifr_addr = *sin6; | |
1052 | if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) | |
1053 | ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; | |
1054 | verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); | |
1055 | } | |
1056 | if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) | |
1057 | { | |
1058 | if (ifa->ifa_flags & IFF_LOOPBACK) | |
1059 | theLoopback = ifa; | |
1060 | else | |
1061 | { | |
1062 | AddInterfaceToList(m, ifa); | |
1063 | if (ifa->ifa_addr->sa_family == AF_INET) | |
1064 | foundav4 = mDNStrue; | |
1065 | } | |
1066 | } | |
1067 | } | |
1068 | ifa = ifa->ifa_next; | |
1069 | } | |
1070 | ||
1071 | // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. | |
1072 | // In the interim, we skip loopback interface only if we found at least one v4 interface to use | |
1073 | if (!foundav4 && theLoopback) | |
1074 | AddInterfaceToList(m, theLoopback); | |
1075 | ||
1076 | // Now the list is complete, set the TxAndRx setting for each interface. | |
1077 | // We always send and receive using IPv4. | |
1078 | // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address. | |
1079 | // Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, | |
1080 | // which means there's a good chance that most or all the other devices on that network should also have v4. | |
1081 | // 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. | |
1082 | // At this time, reducing the packet rate is more important than v6-only devices on a large configured network, | |
1083 | // so we are willing to make that sacrifice. | |
1084 | NetworkInterfaceInfoOSX *i; | |
1085 | for (i = m->p->InterfaceList; i; i = i->next) | |
1086 | if (i->CurrentlyActive) | |
1087 | { | |
1088 | mDNSBool txrx = ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); | |
1089 | if (i->ifinfo.TxAndRx != txrx) | |
1090 | { | |
1091 | i->ifinfo.TxAndRx = txrx; | |
1092 | i->CurrentlyActive = 2; // State change; need to deregister and reregister this interface | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | if (InfoSocket >= 0) close(InfoSocket); | |
1097 | return(err); | |
1098 | } | |
1099 | ||
1100 | mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, char *ifname, int type) | |
1101 | { | |
1102 | NetworkInterfaceInfoOSX *i; | |
1103 | for (i = m->p->InterfaceList; i; i = i->next) | |
1104 | if (!strcmp(i->ifa_name, ifname) && | |
1105 | ((AAAA_OVER_V4 ) || | |
1106 | (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || | |
1107 | (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6) )) return(i); | |
1108 | return(NULL); | |
1109 | } | |
1110 | ||
1111 | mDNSlocal void SetupActiveInterfaces(mDNS *const m) | |
1112 | { | |
1113 | NetworkInterfaceInfoOSX *i; | |
1114 | for (i = m->p->InterfaceList; i; i = i->next) | |
1115 | { | |
1116 | mStatus err = 0; | |
1117 | NetworkInterfaceInfo *n = &i->ifinfo; | |
1118 | NetworkInterfaceInfoOSX *alias = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); | |
1119 | if (!alias) alias = i; | |
1120 | ||
1121 | if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)alias) | |
1122 | { | |
1123 | LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != alias %p", n->InterfaceID, alias); | |
1124 | n->InterfaceID = mDNSNULL; | |
1125 | } | |
1126 | ||
1127 | if (!n->InterfaceID) | |
1128 | { | |
1129 | n->InterfaceID = (mDNSInterfaceID)alias; | |
1130 | mDNS_RegisterInterface(m, n); | |
1131 | debugf("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s", | |
1132 | i->ifa_name, i->scope_id, alias, &n->ip, n->InterfaceActive ? " (Primary)" : ""); | |
1133 | } | |
1134 | ||
1135 | if (!n->TxAndRx) | |
1136 | debugf("SetupActiveInterfaces: No TX/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, alias, &n->ip); | |
1137 | else | |
1138 | { | |
1139 | if (i->sa_family == AF_INET && alias->sktv4 == -1) | |
1140 | { | |
1141 | #if mDNS_AllowPort53 | |
1142 | err = SetupSocket(i, UnicastDNSPort, &alias->skt53, &alias->cfs53); | |
1143 | #endif | |
1144 | if (!err) err = SetupSocket(i, MulticastDNSPort, &alias->sktv4, &alias->cfsv4); | |
1145 | if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a", alias->sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); | |
1146 | else LogMsg("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a FAILED", alias->sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); | |
1147 | } | |
1148 | ||
1149 | if (i->sa_family == AF_INET6 && alias->sktv6 == -1) | |
1150 | { | |
1151 | err = SetupSocket(i, MulticastDNSPort, &alias->sktv6, &alias->cfsv6); | |
1152 | if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a", alias->sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); | |
1153 | else LogMsg("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a FAILED", alias->sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); | |
1154 | } | |
1155 | } | |
1156 | } | |
1157 | } | |
1158 | ||
1159 | mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) | |
1160 | { | |
1161 | NetworkInterfaceInfoOSX *i; | |
1162 | for (i = m->p->InterfaceList; i; i = i->next) | |
1163 | i->CurrentlyActive = mDNSfalse; | |
1164 | } | |
1165 | ||
1166 | mDNSlocal void ClearInactiveInterfaces(mDNS *const m) | |
1167 | { | |
1168 | // First pass: | |
1169 | // If an interface is going away, then deregister this from the mDNSCore. | |
1170 | // We also have to deregister it if the alias interface that it's using for its InterfaceID is going away. | |
1171 | // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory | |
1172 | // it refers to has gone away we'll crash. | |
1173 | // Don't actually close the sockets or free the memory yet: When the last representative of an interface goes away | |
1174 | // mDNSCore may want to send goodbye packets on that interface. (Not yet implemented, but a good idea anyway.) | |
1175 | NetworkInterfaceInfoOSX *i; | |
1176 | for (i = m->p->InterfaceList; i; i = i->next) | |
1177 | { | |
1178 | // 1. If this interface is no longer active, or it's InterfaceID is changing, deregister it | |
1179 | NetworkInterfaceInfoOSX *alias = (NetworkInterfaceInfoOSX *)(i->ifinfo.InterfaceID); | |
1180 | if (i->ifinfo.InterfaceID && (!i->CurrentlyActive || (alias && !alias->CurrentlyActive) || i->CurrentlyActive == 2)) | |
1181 | { | |
1182 | debugf("ClearInactiveInterfaces: Deregistering %#a", &i->ifinfo.ip); | |
1183 | mDNS_DeregisterInterface(m, &i->ifinfo); | |
1184 | i->ifinfo.InterfaceID = mDNSNULL; | |
1185 | } | |
1186 | } | |
1187 | ||
1188 | // Second pass: | |
1189 | // Now that everything that's going to deregister has done so, we can close sockets and free the memory | |
1190 | NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; | |
1191 | while (*p) | |
1192 | { | |
1193 | i = *p; | |
1194 | // 2. Close all our CFSockets. We'll recreate them later as necessary. | |
1195 | // (We may have previously had both v4 and v6, and we may not need both any more.) | |
1196 | // Note: MUST NOT close the underlying native BSD sockets. | |
1197 | // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, | |
1198 | // because it first has to unhook the sockets from its select() call, before it can safely close them. | |
1199 | #if mDNS_AllowPort53 | |
1200 | if (i->cfs53) { CFSocketInvalidate(i->cfs53); CFRelease(i->cfs53); } | |
1201 | i->skt53 = -1; | |
1202 | i->cfs53 = NULL; | |
1203 | #endif | |
1204 | if (i->cfsv4) { CFSocketInvalidate(i->cfsv4); CFRelease(i->cfsv4); } | |
1205 | if (i->cfsv6) { CFSocketInvalidate(i->cfsv6); CFRelease(i->cfsv6); } | |
1206 | i->sktv4 = i->sktv6 = -1; | |
1207 | i->cfsv4 = i->cfsv6 = NULL; | |
1208 | ||
1209 | // 3. If no longer active, delete interface from list and free memory | |
1210 | if (!i->CurrentlyActive) | |
1211 | { | |
1212 | debugf("ClearInactiveInterfaces: Deleting %#a", &i->ifinfo.ip); | |
1213 | *p = i->next; | |
1214 | if (i->ifa_name) freeL("NetworkInterfaceInfoOSX name", i->ifa_name); | |
1215 | freeL("NetworkInterfaceInfoOSX", i); | |
1216 | } | |
1217 | else | |
1218 | p = &i->next; | |
1219 | } | |
1220 | } | |
1221 | ||
1222 | mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) | |
1223 | { | |
1224 | (void)store; // Parameter not used | |
1225 | (void)changedKeys; // Parameter not used | |
1226 | debugf("*** Network Configuration Change ***"); | |
1227 | ||
1228 | mDNS *const m = (mDNS *const)context; | |
1229 | MarkAllInterfacesInactive(m); | |
1230 | UpdateInterfaceList(m); | |
1231 | ClearInactiveInterfaces(m); | |
1232 | SetupActiveInterfaces(m); | |
1233 | ||
1234 | if (m->MainCallback) | |
1235 | m->MainCallback(m, mStatus_ConfigChanged); | |
1236 | } | |
1237 | ||
1238 | mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) | |
1239 | { | |
1240 | mStatus err = -1; | |
1241 | SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; | |
1242 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context); | |
1243 | CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); | |
1244 | CFStringRef key2 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); | |
1245 | CFStringRef key3 = SCDynamicStoreKeyCreateComputerName(NULL); | |
1246 | CFStringRef key4 = SCDynamicStoreKeyCreateHostNames(NULL); | |
1247 | CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); | |
1248 | CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); | |
1249 | ||
1250 | CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
1251 | CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
1252 | ||
1253 | if (!store) { LogMsg("SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; } | |
1254 | if (!key1 || !key2 || !key3 || !key4 || !keys || !pattern1 || !pattern2 || !patterns) goto error; | |
1255 | ||
1256 | CFArrayAppendValue(keys, key1); | |
1257 | CFArrayAppendValue(keys, key2); | |
1258 | CFArrayAppendValue(keys, key3); | |
1259 | CFArrayAppendValue(keys, key4); | |
1260 | CFArrayAppendValue(patterns, pattern1); | |
1261 | CFArrayAppendValue(patterns, pattern2); | |
1262 | if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) | |
1263 | { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; } | |
1264 | ||
1265 | m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); | |
1266 | if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; } | |
1267 | ||
1268 | CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); | |
1269 | m->p->Store = store; | |
1270 | err = 0; | |
1271 | goto exit; | |
1272 | ||
1273 | error: | |
1274 | if (store) CFRelease(store); | |
1275 | ||
1276 | exit: | |
1277 | if (key1) CFRelease(key1); | |
1278 | if (key2) CFRelease(key2); | |
1279 | if (key3) CFRelease(key3); | |
1280 | if (key4) CFRelease(key4); | |
1281 | if (pattern1) CFRelease(pattern1); | |
1282 | if (pattern2) CFRelease(pattern2); | |
1283 | if (keys) CFRelease(keys); | |
1284 | if (patterns) CFRelease(patterns); | |
1285 | ||
1286 | return(err); | |
1287 | } | |
1288 | ||
1289 | mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) | |
1290 | { | |
1291 | mDNS *const m = (mDNS *const)refcon; | |
1292 | (void)service; // Parameter not used | |
1293 | switch(messageType) | |
1294 | { | |
1295 | case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 | |
1296 | case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreMachineSleep(m, true); break; // E0000250 | |
1297 | case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 | |
1298 | case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 | |
1299 | case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreMachineSleep(m, true); break; // E0000280 | |
1300 | case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 | |
1301 | case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreMachineSleep(m, false); break; // E0000300 | |
1302 | default: debugf("PowerChanged unknown message %X", messageType); break; | |
1303 | } | |
1304 | IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); | |
1305 | } | |
1306 | ||
1307 | mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) | |
1308 | { | |
1309 | IONotificationPortRef thePortRef; | |
1310 | m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier); | |
1311 | if (m->p->PowerConnection) | |
1312 | { | |
1313 | m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef); | |
1314 | CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); | |
1315 | return(mStatus_NoError); | |
1316 | } | |
1317 | return(-1); | |
1318 | } | |
1319 | ||
1320 | CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); | |
1321 | CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; | |
1322 | CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; | |
1323 | CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; | |
1324 | ||
1325 | mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) | |
1326 | { | |
1327 | int major = 0, minor = 0; | |
1328 | char letter = 0, prodname[256]="Mac OS X", prodvers[256]="", buildver[256]="?"; | |
1329 | CFDictionaryRef vers = _CFCopySystemVersionDictionary(); | |
1330 | if (vers) | |
1331 | { | |
1332 | CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); | |
1333 | CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); | |
1334 | CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); | |
1335 | if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); | |
1336 | if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); | |
1337 | if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); | |
1338 | sscanf(buildver, "%d%c%d", &major, &letter, &minor); | |
1339 | CFRelease(vers); | |
1340 | } | |
1341 | if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, mDNSResponderVersionString); | |
1342 | return(major); | |
1343 | } | |
1344 | ||
1345 | mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) | |
1346 | { | |
1347 | mStatus err; | |
1348 | ||
1349 | m->hostlabel.c[0] = 0; | |
1350 | ||
1351 | char *HINFO_HWstring = "Macintosh"; | |
1352 | char HINFO_HWstring_buffer[256]; | |
1353 | int get_model[2] = { CTL_HW, HW_MODEL }; | |
1354 | size_t len_model = sizeof(HINFO_HWstring_buffer); | |
1355 | if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) | |
1356 | HINFO_HWstring = HINFO_HWstring_buffer; | |
1357 | ||
1358 | char HINFO_SWstring[256] = ""; | |
1359 | if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs = mDNS_KnownBug_PhantomInterfaces; | |
1360 | ||
1361 | mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); | |
1362 | mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); | |
1363 | if (hlen + slen < 254) | |
1364 | { | |
1365 | m->HIHardware.c[0] = hlen; | |
1366 | m->HISoftware.c[0] = slen; | |
1367 | mDNSPlatformMemCopy(HINFO_HWstring, &m->HIHardware.c[1], hlen); | |
1368 | mDNSPlatformMemCopy(HINFO_SWstring, &m->HISoftware.c[1], slen); | |
1369 | } | |
1370 | ||
1371 | m->p->InterfaceList = mDNSNULL; | |
1372 | m->p->userhostlabel.c[0] = 0; | |
1373 | UpdateInterfaceList(m); | |
1374 | SetupActiveInterfaces(m); | |
1375 | ||
1376 | err = WatchForNetworkChanges(m); | |
1377 | if (err) return(err); | |
1378 | ||
1379 | err = WatchForPowerChanges(m); | |
1380 | return(err); | |
1381 | } | |
1382 | ||
1383 | mDNSexport mStatus mDNSPlatformInit(mDNS *const m) | |
1384 | { | |
1385 | mStatus result = mDNSPlatformInit_setup(m); | |
1386 | // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already | |
1387 | // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately | |
1388 | if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); | |
1389 | return(result); | |
1390 | } | |
1391 | ||
1392 | mDNSexport void mDNSPlatformClose(mDNS *const m) | |
1393 | { | |
1394 | if (m->p->PowerConnection) | |
1395 | { | |
1396 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); | |
1397 | CFRunLoopSourceInvalidate(m->p->PowerRLS); | |
1398 | CFRelease(m->p->PowerRLS); | |
1399 | IODeregisterForSystemPower(&m->p->PowerNotifier); | |
1400 | m->p->PowerConnection = NULL; | |
1401 | m->p->PowerNotifier = NULL; | |
1402 | m->p->PowerRLS = NULL; | |
1403 | } | |
1404 | ||
1405 | if (m->p->Store) | |
1406 | { | |
1407 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); | |
1408 | CFRunLoopSourceInvalidate(m->p->StoreRLS); | |
1409 | CFRelease(m->p->StoreRLS); | |
1410 | CFRelease(m->p->Store); | |
1411 | m->p->Store = NULL; | |
1412 | m->p->StoreRLS = NULL; | |
1413 | } | |
1414 | ||
1415 | MarkAllInterfacesInactive(m); | |
1416 | ClearInactiveInterfaces(m); | |
1417 | } | |
1418 | ||
1419 | mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; | |
1420 | ||
1421 | mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) | |
1422 | { | |
1423 | // Notes: Typical values for mach_timebase_info: | |
1424 | // tbi.numer = 1000 million | |
1425 | // tbi.denom = 33 million | |
1426 | // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; | |
1427 | // numer / denom = nanoseconds per hardware clock tick (e.g. 30); | |
1428 | // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) | |
1429 | // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) | |
1430 | // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds | |
1431 | // | |
1432 | // Arithmetic notes: | |
1433 | // tbi.denom is at least 1, and not more than 2^32-1. | |
1434 | // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. | |
1435 | // tbi.denom is at least 1, and not more than 2^32-1. | |
1436 | // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. | |
1437 | // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, | |
1438 | // which is unlikely on any current or future Macintosh. | |
1439 | // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. | |
1440 | // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. | |
1441 | struct mach_timebase_info tbi; | |
1442 | kern_return_t result = mach_timebase_info(&tbi); | |
1443 | if (result != KERN_SUCCESS) return(result); | |
1444 | clockdivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; | |
1445 | *timenow = mDNSPlatformTimeNow(); | |
1446 | return(mStatus_NoError); | |
1447 | } | |
1448 | ||
1449 | mDNSexport mDNSs32 mDNSPlatformTimeNow(void) | |
1450 | { | |
1451 | if (clockdivisor == 0) { LogMsg("mDNSPlatformTimeNow called before mDNSPlatformTimeInit"); return(0); } | |
1452 | return((mDNSs32)(mach_absolute_time() / clockdivisor)); | |
1453 | } | |
1454 | ||
1455 | // Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves | |
1456 | mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } | |
1457 | mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } | |
1458 | mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); } | |
1459 | mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); } | |
1460 | mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) { memcpy(dst, src, len); } | |
1461 | mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } | |
1462 | mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { bzero(dst, len); } | |
1463 | mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } | |
1464 | mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } |