]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. | |
c9b9ae52 | 4 | * |
c9d2d929 | 5 | * @APPLE_LICENSE_HEADER_START@ |
c9b9ae52 | 6 | * |
c9d2d929 A |
7 | * This file contains Original Code and/or Modifications of Original Code |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
c9b9ae52 | 13 | * |
c9d2d929 A |
14 | * The Original Code and all software distributed under the License are |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
c9b9ae52 | 20 | * limitations under the License. |
c9d2d929 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
c9b9ae52 A |
23 | * |
24 | * Formatting notes: | |
25 | * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion | |
26 | * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, | |
27 | * but for the sake of brevity here I will say just this: Curly braces are not syntactially | |
28 | * part of an "if" statement; they are the beginning and ending markers of a compound statement; | |
29 | * therefore common sense dictates that if they are part of a compound statement then they | |
30 | * should be indented to the same level as everything else in that compound statement. | |
31 | * Indenting curly braces at the same level as the "if" implies that curly braces are | |
32 | * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" | |
33 | * thinking that variables x and y are both of type "char*" -- and anyone who doesn't | |
34 | * understand why variable y is not of type "char*" just proves the point that poor code | |
35 | * layout leads people to unfortunate misunderstandings about how the C language really works.) | |
36 | ||
37 | Change History (most recent first): | |
38 | ||
39 | $Log: mDNSPosix.c,v $ | |
4aea607d A |
40 | Revision 1.73 2005/10/11 21:31:46 cheshire |
41 | <rdar://problem/4296177> Don't depend on IP_RECVTTL succeeding (not available on all platforms) | |
42 | ||
43 | Revision 1.72 2005/09/08 20:45:26 cheshire | |
44 | Default dot-local host name should be "Computer" not "Macintosh", | |
45 | since the machine this is running on is most likely NOT a Mac. | |
46 | ||
7cb34e5c A |
47 | Revision 1.71 2005/02/26 01:29:12 cheshire |
48 | Ignore multicasts accidentally delivered to our unicast receiving socket | |
49 | ||
50 | Revision 1.70 2005/02/04 00:39:59 cheshire | |
51 | Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it | |
52 | ||
283ee3ff A |
53 | Revision 1.69 2004/12/18 02:03:28 cheshire |
54 | Need to #include "dns_sd.h" | |
55 | ||
56 | Revision 1.68 2004/12/18 00:51:52 cheshire | |
57 | Use symbolic constant kDNSServiceInterfaceIndexLocalOnly instead of (mDNSu32) ~0 | |
58 | ||
59 | Revision 1.67 2004/12/17 23:37:48 cheshire | |
60 | <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association | |
61 | (and other repetitive configuration changes) | |
62 | ||
7f0064bd A |
63 | Revision 1.66 2004/12/01 04:27:28 cheshire |
64 | <rdar://problem/3872803> Darwin patches for Solaris and Suse | |
65 | Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc. | |
66 | ||
67 | Revision 1.65 2004/11/30 22:37:01 cheshire | |
68 | Update copyright dates and add "Mode: C; tab-width: 4" headers | |
69 | ||
70 | Revision 1.64 2004/11/23 03:39:47 cheshire | |
71 | Let interface name/index mapping capability live directly in JNISupport.c, | |
72 | instead of having to call through to the daemon via IPC to get this information. | |
73 | ||
74 | Revision 1.63 2004/11/12 03:16:43 rpantos | |
75 | rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName | |
76 | ||
77 | Revision 1.62 2004/10/28 03:24:42 cheshire | |
78 | Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 | |
79 | ||
80 | Revision 1.61 2004/10/16 00:17:01 cheshire | |
81 | <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check | |
82 | ||
83 | Revision 1.60 2004/09/26 23:20:36 ksekar | |
84 | <rdar://problem/3813108> Allow default registrations in multiple wide-area domains | |
85 | ||
86 | Revision 1.59 2004/09/21 21:02:55 cheshire | |
87 | Set up ifname before calling mDNS_RegisterInterface() | |
88 | ||
89 | Revision 1.58 2004/09/17 01:08:54 cheshire | |
90 | Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h | |
91 | The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces | |
92 | declared in that file are ONLY appropriate to single-address-space embedded applications. | |
93 | For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. | |
94 | ||
95 | Revision 1.57 2004/09/17 00:19:11 cheshire | |
96 | For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 | |
97 | ||
98 | Revision 1.56 2004/09/17 00:15:56 cheshire | |
99 | Rename mDNSPlatformInit_ReceiveUnicast to mDNSPlatformInit_CanReceiveUnicast | |
100 | ||
101 | Revision 1.55 2004/09/16 00:24:49 cheshire | |
102 | <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() | |
103 | ||
104 | Revision 1.54 2004/09/15 23:55:00 ksekar | |
105 | <rdar://problem/3800597> mDNSPosix should #include stdint.h | |
106 | ||
107 | Revision 1.53 2004/09/14 23:42:36 cheshire | |
108 | <rdar://problem/3801296> Need to seed random number generator from platform-layer data | |
109 | ||
110 | Revision 1.52 2004/08/25 16:42:13 ksekar | |
111 | Fix Posix build - change mDNS_SetFQDNs to mDNS_SetFQDN, remove unicast | |
112 | hostname parameter. | |
113 | ||
114 | Revision 1.51 2004/08/14 03:22:42 cheshire | |
115 | <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue | |
116 | Add GetUserSpecifiedDDNSName() routine | |
117 | Convert ServiceRegDomain to domainname instead of C string | |
118 | Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs | |
119 | ||
120 | Revision 1.50 2004/08/11 01:20:20 cheshire | |
121 | Declare private local functions using "mDNSlocal" | |
122 | ||
123 | Revision 1.49 2004/07/26 22:49:31 ksekar | |
cc340f17 | 124 | <rdar://problem/3651409>: Feature #9516: Need support for NAT-PMP in client |
7f0064bd A |
125 | |
126 | Revision 1.48 2004/07/20 01:47:36 rpantos | |
127 | NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr. | |
128 | ||
129 | Revision 1.47 2004/06/25 00:26:27 rpantos | |
130 | Changes to fix the Posix build on Solaris. | |
131 | ||
8e92c31c A |
132 | Revision 1.46 2004/05/13 04:54:20 ksekar |
133 | Unified list copy/free code. Added symetric list for | |
134 | ||
135 | Revision 1.45 2004/05/12 22:03:09 ksekar | |
136 | Made GetSearchDomainList a true platform-layer call (declaration moved | |
7f0064bd | 137 | from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local" |
8e92c31c A |
138 | only on non-OSX platforms. Changed call to return a copy of the list |
139 | to avoid shared memory issues. Added a routine to free the list. | |
140 | ||
141 | Revision 1.44 2004/04/21 02:49:11 cheshire | |
142 | To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' | |
143 | ||
144 | Revision 1.43 2004/04/14 23:09:29 ksekar | |
145 | Support for TSIG signed dynamic updates. | |
146 | ||
147 | Revision 1.42 2004/04/09 17:43:04 cheshire | |
148 | Make sure to set the McastTxRx field so that duplicate suppression works correctly | |
149 | ||
150 | Revision 1.41 2004/02/06 01:19:51 cheshire | |
151 | Conditionally exclude IPv6 code unless HAVE_IPV6 is set | |
152 | ||
153 | Revision 1.40 2004/02/05 01:00:01 rpantos | |
154 | Fix some issues that turned up when building for FreeBSD. | |
155 | ||
156 | Revision 1.39 2004/01/28 21:12:15 cheshire | |
157 | Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) | |
158 | ||
159 | Revision 1.38 2004/01/27 20:15:23 cheshire | |
160 | <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 | |
161 | ||
162 | Revision 1.37 2004/01/24 05:12:03 cheshire | |
163 | <rdar://problem/3534352>: Need separate socket for issuing unicast queries | |
164 | ||
165 | Revision 1.36 2004/01/24 04:59:16 cheshire | |
166 | Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again | |
167 | ||
168 | Revision 1.35 2004/01/23 21:37:08 cheshire | |
169 | For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6 | |
170 | ||
171 | Revision 1.34 2004/01/22 03:43:09 cheshire | |
172 | Export constants like mDNSInterface_LocalOnly so that the client layers can use them | |
173 | ||
174 | Revision 1.33 2004/01/21 21:54:20 cheshire | |
175 | <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port | |
176 | ||
177 | Revision 1.32 2004/01/20 01:49:28 rpantos | |
178 | Tweak error handling of last checkin a bit. | |
179 | ||
180 | Revision 1.31 2004/01/20 01:39:27 rpantos | |
181 | Respond to If changes by rebuilding interface list. | |
182 | ||
183 | Revision 1.30 2003/12/11 19:40:36 cheshire | |
184 | Fix 'destAddr.type == senderAddr.type;' that should have said 'destAddr.type = senderAddr.type;' | |
185 | ||
186 | Revision 1.29 2003/12/11 18:53:22 cheshire | |
187 | Fix compiler warning reported by Paul Guyot | |
188 | ||
189 | Revision 1.28 2003/12/11 03:03:51 rpantos | |
190 | Clean up mDNSPosix so that it builds on OS X again. | |
191 | ||
192 | Revision 1.27 2003/12/08 20:47:02 rpantos | |
193 | Add support for mDNSResponder on Linux. | |
194 | ||
195 | Revision 1.26 2003/11/14 20:59:09 cheshire | |
196 | Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. | |
7f0064bd | 197 | Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. |
73792575 | 198 | |
716635cc A |
199 | Revision 1.25 2003/10/30 19:25:49 cheshire |
200 | Fix signed/unsigned warning on certain compilers | |
201 | ||
c9b9ae52 | 202 | Revision 1.24 2003/08/18 23:12:23 cheshire |
7f0064bd | 203 | <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime() |
c9b9ae52 A |
204 | |
205 | Revision 1.23 2003/08/12 19:56:26 cheshire | |
206 | Update to APSL 2.0 | |
207 | ||
208 | Revision 1.22 2003/08/06 18:46:15 cheshire | |
209 | LogMsg() errors are serious -- always report them to stderr, regardless of debugging level | |
210 | ||
211 | Revision 1.21 2003/08/06 18:20:51 cheshire | |
212 | Makefile cleanup | |
213 | ||
214 | Revision 1.20 2003/08/05 23:56:26 cheshire | |
215 | Update code to compile with the new mDNSCoreReceive() function that requires a TTL | |
216 | (Right now mDNSPosix.c just reports 255 -- we should fix this) | |
217 | ||
218 | Revision 1.19 2003/07/19 03:15:16 cheshire | |
219 | Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, | |
220 | and add the obvious trivial implementations to each platform support layer | |
221 | ||
222 | Revision 1.18 2003/07/14 18:11:54 cheshire | |
223 | Fix stricter compiler warnings | |
224 | ||
225 | Revision 1.17 2003/07/13 01:08:38 cheshire | |
226 | There's not much point running mDNS over a point-to-point link; exclude those | |
227 | ||
228 | Revision 1.16 2003/07/02 21:19:59 cheshire | |
229 | <rdar://problem/3313413> Update copyright notices, etc., in source code comments | |
230 | ||
231 | Revision 1.15 2003/06/18 05:48:41 cheshire | |
232 | Fix warnings | |
233 | ||
234 | Revision 1.14 2003/05/26 03:21:30 cheshire | |
235 | Tidy up address structure naming: | |
236 | mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) | |
237 | mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 | |
238 | mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 | |
239 | ||
240 | Revision 1.13 2003/05/26 03:01:28 cheshire | |
241 | <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead | |
242 | ||
243 | Revision 1.12 2003/05/21 03:49:18 cheshire | |
244 | Fix warning | |
245 | ||
246 | Revision 1.11 2003/05/06 00:00:50 cheshire | |
247 | <rdar://problem/3248914> Rationalize naming of domainname manipulation functions | |
248 | ||
249 | Revision 1.10 2003/04/25 01:45:57 cheshire | |
250 | <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name | |
251 | ||
252 | Revision 1.9 2003/03/20 21:10:31 cheshire | |
253 | Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris | |
254 | ||
255 | Revision 1.8 2003/03/15 04:40:38 cheshire | |
256 | Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" | |
257 | ||
258 | Revision 1.7 2003/03/13 03:46:21 cheshire | |
259 | Fixes to make the code build on Linux | |
260 | ||
261 | Revision 1.6 2003/03/08 00:35:56 cheshire | |
262 | Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") | |
263 | ||
264 | Revision 1.5 2002/12/23 22:13:31 jgraessl | |
265 | Reviewed by: Stuart Cheshire | |
266 | Initial IPv6 support for mDNSResponder. | |
267 | ||
268 | Revision 1.4 2002/09/27 01:47:45 cheshire | |
269 | Workaround for Linux 2.0 systems that don't have IP_PKTINFO | |
270 | ||
271 | Revision 1.3 2002/09/21 20:44:53 zarzycki | |
272 | Added APSL info | |
273 | ||
274 | Revision 1.2 2002/09/19 21:25:36 cheshire | |
275 | mDNS_snprintf() doesn't need to be in a separate file | |
276 | ||
277 | Revision 1.1 2002/09/17 06:24:34 cheshire | |
278 | First checkin | |
279 | */ | |
280 | ||
7f0064bd | 281 | #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above |
c9b9ae52 | 282 | #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform |
283ee3ff | 283 | #include "dns_sd.h" |
c9b9ae52 A |
284 | |
285 | #include <assert.h> | |
286 | #include <stdio.h> | |
287 | #include <stdlib.h> | |
288 | #include <errno.h> | |
289 | #include <string.h> | |
290 | #include <unistd.h> | |
8e92c31c A |
291 | #include <syslog.h> |
292 | #include <stdarg.h> | |
c9b9ae52 A |
293 | #include <fcntl.h> |
294 | #include <sys/types.h> | |
8e92c31c | 295 | #include <sys/time.h> |
c9b9ae52 A |
296 | #include <sys/socket.h> |
297 | #include <sys/uio.h> | |
8e92c31c | 298 | #include <sys/select.h> |
c9b9ae52 | 299 | #include <netinet/in.h> |
7cb34e5c | 300 | #include <arpa/inet.h> |
8e92c31c A |
301 | #include <time.h> // platform support for UTC time |
302 | ||
303 | #if USES_NETLINK | |
304 | #include <asm/types.h> | |
305 | #include <linux/netlink.h> | |
306 | #include <linux/rtnetlink.h> | |
307 | #else // USES_NETLINK | |
308 | #include <net/route.h> | |
309 | #include <net/if.h> | |
310 | #endif // USES_NETLINK | |
c9b9ae52 A |
311 | |
312 | #include "mDNSUNP.h" | |
8e92c31c | 313 | #include "GenLinkedList.h" |
c9b9ae52 A |
314 | |
315 | // *************************************************************************** | |
316 | // Structures | |
317 | ||
8e92c31c A |
318 | // We keep a list of client-supplied event sources in PosixEventSource records |
319 | struct PosixEventSource | |
320 | { | |
321 | mDNSPosixEventCallback Callback; | |
322 | void *Context; | |
323 | int fd; | |
324 | struct PosixEventSource *Next; | |
325 | }; | |
326 | typedef struct PosixEventSource PosixEventSource; | |
c9b9ae52 | 327 | |
8e92c31c A |
328 | // Context record for interface change callback |
329 | struct IfChangeRec | |
c9b9ae52 | 330 | { |
8e92c31c A |
331 | int NotifySD; |
332 | mDNS* mDNS; | |
c9b9ae52 | 333 | }; |
8e92c31c A |
334 | typedef struct IfChangeRec IfChangeRec; |
335 | ||
336 | // Note that static data is initialized to zero in (modern) C. | |
337 | static fd_set gEventFDs; | |
338 | static int gMaxFD; // largest fd in gEventFDs | |
339 | static GenLinkedList gEventSources; // linked list of PosixEventSource's | |
340 | static sigset_t gEventSignalSet; // Signals which event loop listens for | |
341 | static sigset_t gEventSignals; // Signals which were received while inside loop | |
c9b9ae52 A |
342 | |
343 | // *************************************************************************** | |
344 | // Globals (for debugging) | |
345 | ||
346 | static int num_registered_interfaces = 0; | |
347 | static int num_pkts_accepted = 0; | |
348 | static int num_pkts_rejected = 0; | |
349 | ||
350 | // *************************************************************************** | |
351 | // Functions | |
352 | ||
353 | int gMDNSPlatformPosixVerboseLevel = 0; | |
354 | ||
c9b9ae52 A |
355 | #define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) |
356 | ||
7f0064bd | 357 | mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort) |
c9b9ae52 A |
358 | { |
359 | switch (sa->sa_family) | |
360 | { | |
361 | case AF_INET: | |
362 | { | |
363 | struct sockaddr_in* sin = (struct sockaddr_in*)sa; | |
364 | ipAddr->type = mDNSAddrType_IPv4; | |
365 | ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; | |
366 | if (ipPort) ipPort->NotAnInteger = sin->sin_port; | |
367 | break; | |
368 | } | |
369 | ||
8e92c31c | 370 | #if HAVE_IPV6 |
c9b9ae52 A |
371 | case AF_INET6: |
372 | { | |
373 | struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; | |
7f0064bd | 374 | #ifndef NOT_HAVE_SA_LEN |
c9b9ae52 | 375 | assert(sin6->sin6_len == sizeof(*sin6)); |
7f0064bd | 376 | #endif |
c9b9ae52 A |
377 | ipAddr->type = mDNSAddrType_IPv6; |
378 | ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; | |
379 | if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; | |
380 | break; | |
381 | } | |
382 | #endif | |
383 | ||
384 | default: | |
385 | verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); | |
386 | ipAddr->type = mDNSAddrType_None; | |
387 | if (ipPort) ipPort->NotAnInteger = 0; | |
388 | break; | |
389 | } | |
390 | } | |
391 | ||
392 | #if COMPILER_LIKES_PRAGMA_MARK | |
393 | #pragma mark ***** Send and Receive | |
394 | #endif | |
395 | ||
396 | // mDNS core calls this routine when it needs to send a packet. | |
7f0064bd | 397 | mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, |
8e92c31c | 398 | mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) |
c9b9ae52 | 399 | { |
8e92c31c | 400 | int err = 0; |
c9b9ae52 | 401 | struct sockaddr_storage to; |
8e92c31c A |
402 | PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); |
403 | int sendingsocket = -1; | |
c9b9ae52 A |
404 | |
405 | assert(m != NULL); | |
406 | assert(msg != NULL); | |
407 | assert(end != NULL); | |
408 | assert( (((char *) end) - ((char *) msg)) > 0 ); | |
8e92c31c | 409 | assert(dstPort.NotAnInteger != 0); |
c9b9ae52 A |
410 | |
411 | if (dst->type == mDNSAddrType_IPv4) | |
412 | { | |
413 | struct sockaddr_in *sin = (struct sockaddr_in*)&to; | |
414 | #ifndef NOT_HAVE_SA_LEN | |
415 | sin->sin_len = sizeof(*sin); | |
416 | #endif | |
417 | sin->sin_family = AF_INET; | |
418 | sin->sin_port = dstPort.NotAnInteger; | |
419 | sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; | |
8e92c31c | 420 | sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4; |
c9b9ae52 A |
421 | } |
422 | ||
8e92c31c | 423 | #if HAVE_IPV6 |
c9b9ae52 A |
424 | else if (dst->type == mDNSAddrType_IPv6) |
425 | { | |
426 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; | |
427 | mDNSPlatformMemZero(sin6, sizeof(*sin6)); | |
7f0064bd | 428 | #ifndef NOT_HAVE_SA_LEN |
c9b9ae52 | 429 | sin6->sin6_len = sizeof(*sin6); |
7f0064bd | 430 | #endif |
c9b9ae52 A |
431 | sin6->sin6_family = AF_INET6; |
432 | sin6->sin6_port = dstPort.NotAnInteger; | |
433 | sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; | |
8e92c31c | 434 | sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6; |
c9b9ae52 A |
435 | } |
436 | #endif | |
437 | ||
8e92c31c A |
438 | if (sendingsocket >= 0) |
439 | err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); | |
c9b9ae52 | 440 | |
8e92c31c | 441 | if (err > 0) err = 0; |
c9b9ae52 | 442 | else if (err < 0) |
8e92c31c | 443 | { |
c9d2d929 A |
444 | if (thisIntf) |
445 | verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", | |
446 | errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); | |
447 | else | |
448 | verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); | |
8e92c31c | 449 | } |
c9b9ae52 A |
450 | |
451 | return PosixErrorToStatus(err); | |
452 | } | |
453 | ||
454 | // This routine is called when the main loop detects that data is available on a socket. | |
7f0064bd | 455 | mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) |
c9b9ae52 A |
456 | { |
457 | mDNSAddr senderAddr, destAddr; | |
458 | mDNSIPPort senderPort; | |
459 | ssize_t packetLen; | |
460 | DNSMessage packet; | |
461 | struct my_in_pktinfo packetInfo; | |
462 | struct sockaddr_storage from; | |
463 | socklen_t fromLen; | |
464 | int flags; | |
8e92c31c | 465 | mDNSu8 ttl; |
c9b9ae52 | 466 | mDNSBool reject; |
8e92c31c | 467 | const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; |
c9b9ae52 A |
468 | |
469 | assert(m != NULL); | |
c9b9ae52 A |
470 | assert(skt >= 0); |
471 | ||
472 | fromLen = sizeof(from); | |
473 | flags = 0; | |
8e92c31c | 474 | packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); |
c9b9ae52 A |
475 | |
476 | if (packetLen >= 0) | |
477 | { | |
478 | SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); | |
479 | SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); | |
480 | ||
481 | // If we have broken IP_RECVDSTADDR functionality (so far | |
482 | // I've only seen this on OpenBSD) then apply a hack to | |
483 | // convince mDNS Core that this isn't a spoof packet. | |
484 | // Basically what we do is check to see whether the | |
485 | // packet arrived as a multicast and, if so, set its | |
486 | // destAddr to the mDNS address. | |
487 | // | |
488 | // I must admit that I could just be doing something | |
489 | // wrong on OpenBSD and hence triggering this problem | |
490 | // but I'm at a loss as to how. | |
491 | // | |
492 | // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have | |
493 | // no way to tell the destination address or interface this packet arrived on, | |
494 | // so all we can do is just assume it's a multicast | |
495 | ||
496 | #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) | |
497 | if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) ) | |
498 | { | |
8e92c31c | 499 | destAddr.type = senderAddr.type; |
7f0064bd | 500 | if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroupv4; |
c9b9ae52 A |
501 | else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6; |
502 | } | |
503 | #endif | |
504 | ||
505 | // We only accept the packet if the interface on which it came | |
506 | // in matches the interface associated with this socket. | |
507 | // We do this match by name or by index, depending on which | |
508 | // information is available. recvfrom_flags sets the name | |
509 | // to "" if the name isn't available, or the index to -1 | |
510 | // if the index is available. This accomodates the various | |
511 | // different capabilities of our target platforms. | |
512 | ||
513 | reject = mDNSfalse; | |
7cb34e5c A |
514 | if (!intf) |
515 | { | |
516 | // Ignore multicasts accidentally delivered to our unicast receiving socket | |
517 | if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1; | |
518 | } | |
519 | else | |
c9b9ae52 | 520 | { |
8e92c31c A |
521 | if ( packetInfo.ipi_ifname[0] != 0 ) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); |
522 | else if ( packetInfo.ipi_ifindex != -1 ) reject = (packetInfo.ipi_ifindex != intf->index); | |
523 | ||
524 | if (reject) | |
525 | { | |
7cb34e5c | 526 | verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d", |
8e92c31c | 527 | &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, |
7cb34e5c | 528 | &intf->coreIntf.ip, intf->intfName, intf->index, skt); |
8e92c31c A |
529 | packetLen = -1; |
530 | num_pkts_rejected++; | |
531 | if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) | |
532 | { | |
533 | fprintf(stderr, | |
534 | "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", | |
535 | num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); | |
536 | num_pkts_accepted = 0; | |
537 | num_pkts_rejected = 0; | |
538 | } | |
539 | } | |
540 | else | |
c9b9ae52 | 541 | { |
7cb34e5c A |
542 | verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d", |
543 | &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt); | |
8e92c31c | 544 | num_pkts_accepted++; |
c9b9ae52 A |
545 | } |
546 | } | |
c9b9ae52 A |
547 | } |
548 | ||
c9b9ae52 A |
549 | if (packetLen >= 0) |
550 | mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, | |
7f0064bd | 551 | &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); |
8e92c31c A |
552 | } |
553 | ||
554 | mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, | |
555 | TCPConnectionCallback callback, void *context, int *descriptor) | |
556 | { | |
557 | (void)dst; // Unused | |
558 | (void)dstport; // Unused | |
559 | (void)InterfaceID; // Unused | |
560 | (void)callback; // Unused | |
561 | (void)context; // Unused | |
562 | (void)descriptor; // Unused | |
563 | return(mStatus_UnsupportedErr); | |
564 | } | |
565 | ||
566 | mDNSexport void mDNSPlatformTCPCloseConnection(int sd) | |
567 | { | |
568 | (void)sd; // Unused | |
569 | } | |
570 | ||
571 | mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) | |
572 | { | |
573 | (void)sd; // Unused | |
574 | (void)buf; // Unused | |
575 | (void)buflen; // Unused | |
576 | return(0); | |
577 | } | |
578 | ||
579 | mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) | |
580 | { | |
581 | (void)sd; // Unused | |
582 | (void)msg; // Unused | |
583 | (void)len; // Unused | |
584 | return(0); | |
c9b9ae52 A |
585 | } |
586 | ||
587 | #if COMPILER_LIKES_PRAGMA_MARK | |
8e92c31c | 588 | #pragma mark ***** Get/Free Search Domain List |
c9b9ae52 A |
589 | #endif |
590 | ||
8e92c31c | 591 | mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) |
c9b9ae52 | 592 | { |
8e92c31c A |
593 | static DNameListElem tmp; |
594 | static mDNSBool init = mDNSfalse; | |
595 | ||
596 | if (!init) | |
597 | { | |
598 | MakeDomainNameFromDNSNameString(&tmp.name, "local."); | |
599 | tmp.next = NULL; | |
600 | init = mDNStrue; | |
601 | } | |
602 | return mDNS_CopyDNameList(&tmp); | |
c9b9ae52 A |
603 | } |
604 | ||
7f0064bd A |
605 | mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) |
606 | { | |
607 | return NULL; | |
608 | } | |
609 | ||
8e92c31c A |
610 | #if COMPILER_LIKES_PRAGMA_MARK |
611 | #pragma mark ***** Init and Term | |
612 | #endif | |
613 | ||
c9b9ae52 A |
614 | // This gets the current hostname, truncating it at the first dot if necessary |
615 | mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) | |
616 | { | |
617 | int len = 0; | |
716635cc | 618 | gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL); |
c9b9ae52 A |
619 | while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++; |
620 | namelabel->c[0] = len; | |
621 | } | |
622 | ||
8e92c31c A |
623 | // On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel |
624 | // Other platforms can either get the information from the appropriate place, | |
625 | // or they can alternatively just require all registering services to provide an explicit name | |
626 | mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) | |
627 | { | |
628 | // On Unix we have no better name than the host name, so we just use that. | |
629 | GetUserSpecifiedRFC1034ComputerName( namelabel); | |
630 | } | |
631 | ||
7cb34e5c A |
632 | mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) |
633 | { | |
634 | char line[256]; | |
635 | char nameserver[16]; | |
636 | char keyword[10]; | |
637 | int numOfServers = 0; | |
638 | FILE *fp = fopen(filePath, "r"); | |
639 | if (fp == NULL) return -1; | |
640 | while (fgets(line,sizeof(line),fp)) | |
641 | { | |
642 | struct in_addr ina; | |
643 | line[255]='\0'; // just to be safe | |
644 | if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces | |
645 | if (strncmp(keyword,"nameserver",10)) continue; | |
646 | if (inet_aton(nameserver, (struct in_addr *)&ina) != 0) | |
647 | { | |
648 | mDNSAddr DNSAddr; | |
649 | DNSAddr.type = mDNSAddrType_IPv4; | |
650 | DNSAddr.ip.v4.NotAnInteger = ina.s_addr; | |
651 | mDNS_AddDNSServer(m, &DNSAddr, NULL); | |
652 | numOfServers++; | |
653 | } | |
654 | } | |
655 | return (numOfServers > 0) ? 0 : -1; | |
656 | } | |
657 | ||
c9b9ae52 A |
658 | // Searches the interface list looking for the named interface. |
659 | // Returns a pointer to if it found, or NULL otherwise. | |
7f0064bd | 660 | mDNSlocal PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName) |
c9b9ae52 A |
661 | { |
662 | PosixNetworkInterface *intf; | |
663 | ||
664 | assert(m != NULL); | |
665 | assert(intfName != NULL); | |
666 | ||
667 | intf = (PosixNetworkInterface*)(m->HostInterfaces); | |
668 | while ( (intf != NULL) && (strcmp(intf->intfName, intfName) != 0) ) | |
669 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
670 | ||
671 | return intf; | |
672 | } | |
673 | ||
c9d2d929 | 674 | mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) |
8e92c31c A |
675 | { |
676 | PosixNetworkInterface *intf; | |
677 | ||
678 | assert(m != NULL); | |
679 | ||
283ee3ff | 680 | if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); |
8e92c31c A |
681 | |
682 | intf = (PosixNetworkInterface*)(m->HostInterfaces); | |
683 | while ( (intf != NULL) && (mDNSu32) intf->index != index) | |
684 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
685 | ||
686 | return (mDNSInterfaceID) intf; | |
687 | } | |
688 | ||
c9d2d929 | 689 | mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id) |
8e92c31c A |
690 | { |
691 | PosixNetworkInterface *intf; | |
692 | ||
693 | assert(m != NULL); | |
694 | ||
283ee3ff | 695 | if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); |
8e92c31c A |
696 | |
697 | intf = (PosixNetworkInterface*)(m->HostInterfaces); | |
698 | while ( (intf != NULL) && (mDNSInterfaceID) intf != id) | |
699 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
700 | ||
701 | return intf ? intf->index : 0; | |
702 | } | |
703 | ||
c9b9ae52 A |
704 | // Frees the specified PosixNetworkInterface structure. The underlying |
705 | // interface must have already been deregistered with the mDNS core. | |
7f0064bd | 706 | mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf) |
c9b9ae52 A |
707 | { |
708 | assert(intf != NULL); | |
709 | if (intf->intfName != NULL) free((void *)intf->intfName); | |
8e92c31c A |
710 | if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); |
711 | #if HAVE_IPV6 | |
712 | if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); | |
713 | #endif | |
c9b9ae52 A |
714 | free(intf); |
715 | } | |
716 | ||
717 | // Grab the first interface, deregister it, free it, and repeat until done. | |
7f0064bd | 718 | mDNSlocal void ClearInterfaceList(mDNS *const m) |
c9b9ae52 A |
719 | { |
720 | assert(m != NULL); | |
721 | ||
722 | while (m->HostInterfaces) | |
723 | { | |
724 | PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); | |
c9d2d929 | 725 | mDNS_DeregisterInterface(m, &intf->coreIntf); |
c9b9ae52 A |
726 | if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName); |
727 | FreePosixNetworkInterface(intf); | |
728 | } | |
729 | num_registered_interfaces = 0; | |
730 | num_pkts_accepted = 0; | |
731 | num_pkts_rejected = 0; | |
732 | } | |
733 | ||
8e92c31c A |
734 | // Sets up a send/receive socket. |
735 | // If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface | |
736 | // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries | |
7f0064bd | 737 | mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) |
c9b9ae52 A |
738 | { |
739 | int err = 0; | |
740 | static const int kOn = 1; | |
741 | static const int kIntTwoFiveFive = 255; | |
742 | static const unsigned char kByteTwoFiveFive = 255; | |
4aea607d | 743 | const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0); |
c9b9ae52 | 744 | |
8e92c31c | 745 | (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6 |
c9b9ae52 A |
746 | assert(intfAddr != NULL); |
747 | assert(sktPtr != NULL); | |
748 | assert(*sktPtr == -1); | |
749 | ||
750 | // Open the socket... | |
7cb34e5c | 751 | if (intfAddr->sa_family == AF_INET ) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
8e92c31c | 752 | #if HAVE_IPV6 |
c9b9ae52 A |
753 | else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
754 | #endif | |
755 | else return EINVAL; | |
756 | ||
757 | if (*sktPtr < 0) { err = errno; perror("socket"); } | |
758 | ||
8e92c31c A |
759 | // ... with a shared UDP port, if it's for multicast receiving |
760 | if (err == 0 && port.NotAnInteger) | |
c9b9ae52 A |
761 | { |
762 | #if defined(SO_REUSEPORT) | |
763 | err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); | |
764 | #elif defined(SO_REUSEADDR) | |
765 | err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); | |
766 | #else | |
767 | #error This platform has no way to avoid address busy errors on multicast. | |
768 | #endif | |
769 | if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } | |
770 | } | |
771 | ||
772 | // We want to receive destination addresses and interface identifiers. | |
773 | if (intfAddr->sa_family == AF_INET) | |
774 | { | |
775 | struct ip_mreq imr; | |
776 | struct sockaddr_in bindAddr; | |
777 | if (err == 0) | |
778 | { | |
779 | #if defined(IP_PKTINFO) // Linux | |
780 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); | |
781 | if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } | |
782 | #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris | |
783 | #if defined(IP_RECVDSTADDR) | |
784 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); | |
785 | if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } | |
786 | #endif | |
787 | #if defined(IP_RECVIF) | |
788 | if (err == 0) | |
789 | { | |
790 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); | |
791 | if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } | |
792 | } | |
793 | #endif | |
794 | #else | |
795 | #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts | |
796 | #endif | |
797 | } | |
8e92c31c A |
798 | #if defined(IP_RECVTTL) // Linux |
799 | if (err == 0) | |
800 | { | |
4aea607d A |
801 | setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); |
802 | // We no longer depend on being able to get the received TTL, so don't worry if the option fails | |
8e92c31c A |
803 | } |
804 | #endif | |
c9b9ae52 A |
805 | |
806 | // Add multicast group membership on this interface | |
4aea607d | 807 | if (err == 0 && JoinMulticastGroup) |
c9b9ae52 | 808 | { |
7f0064bd | 809 | imr.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; |
c9b9ae52 A |
810 | imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; |
811 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); | |
812 | if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } | |
813 | } | |
814 | ||
815 | // Specify outgoing interface too | |
4aea607d | 816 | if (err == 0 && JoinMulticastGroup) |
c9b9ae52 A |
817 | { |
818 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); | |
819 | if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } | |
820 | } | |
821 | ||
822 | // Per the mDNS spec, send unicast packets with TTL 255 | |
823 | if (err == 0) | |
824 | { | |
825 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
826 | if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } | |
827 | } | |
828 | ||
829 | // and multicast packets with TTL 255 too | |
830 | // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. | |
831 | if (err == 0) | |
832 | { | |
833 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); | |
834 | if (err < 0 && errno == EINVAL) | |
835 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
836 | if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } | |
837 | } | |
838 | ||
839 | // And start listening for packets | |
840 | if (err == 0) | |
841 | { | |
842 | bindAddr.sin_family = AF_INET; | |
843 | bindAddr.sin_port = port.NotAnInteger; | |
844 | bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket | |
845 | err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); | |
846 | if (err < 0) { err = errno; perror("bind"); fflush(stderr); } | |
847 | } | |
848 | } // endif (intfAddr->sa_family == AF_INET) | |
849 | ||
8e92c31c | 850 | #if HAVE_IPV6 |
c9b9ae52 A |
851 | else if (intfAddr->sa_family == AF_INET6) |
852 | { | |
853 | struct ipv6_mreq imr6; | |
854 | struct sockaddr_in6 bindAddr6; | |
8e92c31c | 855 | #if defined(IPV6_PKTINFO) |
c9b9ae52 A |
856 | if (err == 0) |
857 | { | |
c9b9ae52 A |
858 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_PKTINFO, &kOn, sizeof(kOn)); |
859 | if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } | |
c9b9ae52 | 860 | } |
8e92c31c A |
861 | #else |
862 | #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts | |
863 | #endif | |
864 | #if defined(IPV6_HOPLIMIT) | |
865 | if (err == 0) | |
866 | { | |
867 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_HOPLIMIT, &kOn, sizeof(kOn)); | |
868 | if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } | |
869 | } | |
870 | #endif | |
c9b9ae52 A |
871 | |
872 | // Add multicast group membership on this interface | |
4aea607d | 873 | if (err == 0 && JoinMulticastGroup) |
c9b9ae52 A |
874 | { |
875 | imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; | |
876 | imr6.ipv6mr_interface = interfaceIndex; | |
877 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); | |
878 | if (err < 0) | |
879 | { | |
880 | err = errno; | |
881 | verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); | |
882 | perror("setsockopt - IPV6_JOIN_GROUP"); | |
883 | } | |
884 | } | |
885 | ||
886 | // Specify outgoing interface too | |
4aea607d | 887 | if (err == 0 && JoinMulticastGroup) |
c9b9ae52 A |
888 | { |
889 | u_int multicast_if = interfaceIndex; | |
890 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); | |
891 | if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } | |
892 | } | |
893 | ||
894 | // We want to receive only IPv6 packets on this socket. | |
895 | // Without this option, we may get IPv4 addresses as mapped addresses. | |
896 | if (err == 0) | |
897 | { | |
898 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); | |
899 | if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } | |
900 | } | |
901 | ||
902 | // Per the mDNS spec, send unicast packets with TTL 255 | |
903 | if (err == 0) | |
904 | { | |
905 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
906 | if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } | |
907 | } | |
908 | ||
909 | // and multicast packets with TTL 255 too | |
910 | // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. | |
911 | if (err == 0) | |
912 | { | |
913 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); | |
914 | if (err < 0 && errno == EINVAL) | |
915 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
916 | if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } | |
917 | } | |
918 | ||
919 | // And start listening for packets | |
920 | if (err == 0) | |
921 | { | |
922 | mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); | |
7f0064bd | 923 | #ifndef NOT_HAVE_SA_LEN |
c9b9ae52 | 924 | bindAddr6.sin6_len = sizeof(bindAddr6); |
7f0064bd | 925 | #endif |
c9b9ae52 A |
926 | bindAddr6.sin6_family = AF_INET6; |
927 | bindAddr6.sin6_port = port.NotAnInteger; | |
928 | bindAddr6.sin6_flowinfo = 0; | |
c9d2d929 | 929 | // bindAddr6.sin6_addr.s_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket |
c9b9ae52 A |
930 | bindAddr6.sin6_scope_id = 0; |
931 | err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); | |
932 | if (err < 0) { err = errno; perror("bind"); fflush(stderr); } | |
933 | } | |
934 | } // endif (intfAddr->sa_family == AF_INET6) | |
935 | #endif | |
936 | ||
937 | // Set the socket to non-blocking. | |
938 | if (err == 0) | |
939 | { | |
940 | err = fcntl(*sktPtr, F_GETFL, 0); | |
941 | if (err < 0) err = errno; | |
942 | else | |
943 | { | |
944 | err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); | |
945 | if (err < 0) err = errno; | |
946 | } | |
947 | } | |
948 | ||
949 | // Clean up | |
950 | if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } | |
951 | assert( (err == 0) == (*sktPtr != -1) ); | |
952 | return err; | |
953 | } | |
954 | ||
955 | // Creates a PosixNetworkInterface for the interface whose IP address is | |
956 | // intfAddr and whose name is intfName and registers it with mDNS core. | |
7f0064bd | 957 | mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask, const char *intfName, int intfIndex) |
c9b9ae52 A |
958 | { |
959 | int err = 0; | |
960 | PosixNetworkInterface *intf; | |
961 | PosixNetworkInterface *alias = NULL; | |
962 | ||
963 | assert(m != NULL); | |
964 | assert(intfAddr != NULL); | |
965 | assert(intfName != NULL); | |
7f0064bd | 966 | assert(intfMask != NULL); |
c9b9ae52 A |
967 | |
968 | // Allocate the interface structure itself. | |
8e92c31c | 969 | intf = (PosixNetworkInterface*)malloc(sizeof(*intf)); |
c9b9ae52 A |
970 | if (intf == NULL) { assert(0); err = ENOMEM; } |
971 | ||
972 | // And make a copy of the intfName. | |
973 | if (err == 0) | |
974 | { | |
975 | intf->intfName = strdup(intfName); | |
976 | if (intf->intfName == NULL) { assert(0); err = ENOMEM; } | |
977 | } | |
978 | ||
979 | if (err == 0) | |
980 | { | |
981 | // Set up the fields required by the mDNS core. | |
982 | SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); | |
7f0064bd A |
983 | SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL); |
984 | //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask); | |
985 | strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname)); | |
986 | intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0; | |
c9b9ae52 | 987 | intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; |
8e92c31c | 988 | intf->coreIntf.McastTxRx = mDNStrue; |
c9b9ae52 A |
989 | |
990 | // Set up the extra fields in PosixNetworkInterface. | |
991 | assert(intf->intfName != NULL); // intf->intfName already set up above | |
7f0064bd | 992 | intf->index = intfIndex; |
8e92c31c A |
993 | intf->multicastSocket4 = -1; |
994 | #if HAVE_IPV6 | |
995 | intf->multicastSocket6 = -1; | |
996 | #endif | |
c9b9ae52 A |
997 | alias = SearchForInterfaceByName(m, intf->intfName); |
998 | if (alias == NULL) alias = intf; | |
999 | intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; | |
1000 | ||
1001 | if (alias != intf) | |
1002 | debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip); | |
1003 | } | |
1004 | ||
1005 | // Set up the multicast socket | |
1006 | if (err == 0) | |
1007 | { | |
8e92c31c A |
1008 | if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET) |
1009 | err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4); | |
1010 | #if HAVE_IPV6 | |
1011 | else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6) | |
1012 | err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6); | |
c9b9ae52 A |
1013 | #endif |
1014 | } | |
1015 | ||
1016 | // The interface is all ready to go, let's register it with the mDNS core. | |
1017 | if (err == 0) | |
c9d2d929 | 1018 | err = mDNS_RegisterInterface(m, &intf->coreIntf, 0); |
c9b9ae52 A |
1019 | |
1020 | // Clean up. | |
1021 | if (err == 0) | |
1022 | { | |
1023 | num_registered_interfaces++; | |
1024 | debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip); | |
1025 | if (gMDNSPlatformPosixVerboseLevel > 0) | |
1026 | fprintf(stderr, "Registered interface %s\n", intf->intfName); | |
1027 | } | |
1028 | else | |
1029 | { | |
1030 | // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL. | |
1031 | debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err); | |
1032 | if (intf) { FreePosixNetworkInterface(intf); intf = NULL; } | |
1033 | } | |
1034 | ||
1035 | assert( (err == 0) == (intf != NULL) ); | |
1036 | ||
1037 | return err; | |
1038 | } | |
1039 | ||
8e92c31c | 1040 | // Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one. |
7f0064bd | 1041 | mDNSlocal int SetupInterfaceList(mDNS *const m) |
c9b9ae52 A |
1042 | { |
1043 | mDNSBool foundav4 = mDNSfalse; | |
1044 | int err = 0; | |
1045 | struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); | |
1046 | struct ifi_info *firstLoopback = NULL; | |
1047 | ||
1048 | assert(m != NULL); | |
1049 | debugf("SetupInterfaceList"); | |
1050 | ||
1051 | if (intfList == NULL) err = ENOENT; | |
1052 | ||
8e92c31c | 1053 | #if HAVE_IPV6 |
c9b9ae52 A |
1054 | if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ |
1055 | { | |
1056 | struct ifi_info **p = &intfList; | |
1057 | while (*p) p = &(*p)->ifi_next; | |
1058 | *p = get_ifi_info(AF_INET6, mDNStrue); | |
1059 | } | |
1060 | #endif | |
1061 | ||
1062 | if (err == 0) | |
1063 | { | |
1064 | struct ifi_info *i = intfList; | |
1065 | while (i) | |
1066 | { | |
1067 | if ( ((i->ifi_addr->sa_family == AF_INET) | |
8e92c31c | 1068 | #if HAVE_IPV6 |
c9b9ae52 A |
1069 | || (i->ifi_addr->sa_family == AF_INET6) |
1070 | #endif | |
1071 | ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT) ) | |
1072 | { | |
1073 | if (i->ifi_flags & IFF_LOOPBACK) | |
1074 | { | |
1075 | if (firstLoopback == NULL) | |
1076 | firstLoopback = i; | |
1077 | } | |
1078 | else | |
1079 | { | |
7f0064bd | 1080 | if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0) |
c9b9ae52 A |
1081 | if (i->ifi_addr->sa_family == AF_INET) |
1082 | foundav4 = mDNStrue; | |
1083 | } | |
1084 | } | |
1085 | i = i->ifi_next; | |
1086 | } | |
1087 | ||
1088 | // If we found no normal interfaces but we did find a loopback interface, register the | |
1089 | // loopback interface. This allows self-discovery if no interfaces are configured. | |
1090 | // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. | |
1091 | // In the interim, we skip loopback interface only if we found at least one v4 interface to use | |
1092 | // if ( (m->HostInterfaces == NULL) && (firstLoopback != NULL) ) | |
1093 | if ( !foundav4 && firstLoopback ) | |
7f0064bd | 1094 | (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index); |
c9b9ae52 A |
1095 | } |
1096 | ||
1097 | // Clean up. | |
1098 | if (intfList != NULL) free_ifi_info(intfList); | |
1099 | return err; | |
1100 | } | |
1101 | ||
8e92c31c A |
1102 | #if USES_NETLINK |
1103 | ||
1104 | // See <http://www.faqs.org/rfcs/rfc3549.html> for a description of NetLink | |
1105 | ||
1106 | // Open a socket that will receive interface change notifications | |
7f0064bd | 1107 | mDNSlocal mStatus OpenIfNotifySocket( int *pFD) |
8e92c31c A |
1108 | { |
1109 | mStatus err = mStatus_NoError; | |
1110 | struct sockaddr_nl snl; | |
1111 | int sock; | |
1112 | int ret; | |
1113 | ||
1114 | sock = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
1115 | if (sock < 0) | |
1116 | return errno; | |
1117 | ||
1118 | // Configure read to be non-blocking because inbound msg size is not known in advance | |
1119 | (void) fcntl( sock, F_SETFL, O_NONBLOCK); | |
1120 | ||
1121 | /* Subscribe the socket to Link & IP addr notifications. */ | |
1122 | bzero( &snl, sizeof snl); | |
1123 | snl.nl_family = AF_NETLINK; | |
1124 | snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; | |
1125 | ret = bind( sock, (struct sockaddr *) &snl, sizeof snl); | |
1126 | if ( 0 == ret) | |
1127 | *pFD = sock; | |
1128 | else | |
1129 | err = errno; | |
1130 | ||
1131 | return err; | |
1132 | } | |
1133 | ||
1134 | #if MDNS_DEBUGMSGS | |
7f0064bd | 1135 | mDNSlocal void PrintNetLinkMsg( const struct nlmsghdr *pNLMsg) |
8e92c31c A |
1136 | { |
1137 | const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; | |
1138 | const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; | |
1139 | ||
1140 | printf( "nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, | |
1141 | pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[ pNLMsg->nlmsg_type] : kNLRtMsgTypes[ pNLMsg->nlmsg_type - RTM_BASE], | |
1142 | pNLMsg->nlmsg_flags); | |
1143 | ||
1144 | if ( RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) | |
1145 | { | |
1146 | struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA( pNLMsg); | |
1147 | printf( "ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, | |
1148 | pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); | |
1149 | ||
1150 | } | |
1151 | else if ( RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) | |
1152 | { | |
1153 | struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA( pNLMsg); | |
1154 | printf( "ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, | |
1155 | pIfAddr->ifa_index, pIfAddr->ifa_flags); | |
1156 | } | |
1157 | printf( "\n"); | |
1158 | } | |
1159 | #endif | |
1160 | ||
7f0064bd | 1161 | mDNSlocal mDNSu32 ProcessRoutingNotification( int sd) |
8e92c31c A |
1162 | // Read through the messages on sd and if any indicate that any interface records should |
1163 | // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. | |
1164 | { | |
1165 | ssize_t readCount; | |
1166 | char buff[ 4096]; | |
1167 | struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff; | |
7f0064bd | 1168 | mDNSu32 result = 0; |
8e92c31c A |
1169 | |
1170 | // The structure here is more complex than it really ought to be because, | |
1171 | // unfortunately, there's no good way to size a buffer in advance large | |
1172 | // enough to hold all pending data and so avoid message fragmentation. | |
1173 | // (Note that FIONREAD is not supported on AF_NETLINK.) | |
1174 | ||
1175 | readCount = read( sd, buff, sizeof buff); | |
1176 | while ( 1) | |
1177 | { | |
1178 | // Make sure we've got an entire nlmsghdr in the buffer, and payload, too. | |
1179 | // If not, discard already-processed messages in buffer and read more data. | |
1180 | if ( ( (char*) &pNLMsg[1] > ( buff + readCount)) || // i.e. *pNLMsg extends off end of buffer | |
1181 | ( (char*) pNLMsg + pNLMsg->nlmsg_len > ( buff + readCount))) | |
1182 | { | |
1183 | if ( buff < (char*) pNLMsg) // we have space to shuffle | |
1184 | { | |
1185 | // discard processed data | |
1186 | readCount -= ( (char*) pNLMsg - buff); | |
1187 | memmove( buff, pNLMsg, readCount); | |
1188 | pNLMsg = (struct nlmsghdr*) buff; | |
1189 | ||
1190 | // read more data | |
1191 | readCount += read( sd, buff + readCount, sizeof buff - readCount); | |
1192 | continue; // spin around and revalidate with new readCount | |
1193 | } | |
1194 | else | |
1195 | break; // Otherwise message does not fit in buffer | |
1196 | } | |
1197 | ||
1198 | #if MDNS_DEBUGMSGS | |
1199 | PrintNetLinkMsg( pNLMsg); | |
1200 | #endif | |
1201 | ||
1202 | // Process the NetLink message | |
1203 | if ( pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) | |
1204 | result |= 1 << ((struct ifinfomsg*) NLMSG_DATA( pNLMsg))->ifi_index; | |
1205 | else if ( pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) | |
1206 | result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA( pNLMsg))->ifa_index; | |
1207 | ||
1208 | // Advance pNLMsg to the next message in the buffer | |
1209 | if ( ( pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) | |
1210 | { | |
1211 | ssize_t len = readCount - ( (char*)pNLMsg - buff); | |
1212 | pNLMsg = NLMSG_NEXT( pNLMsg, len); | |
1213 | } | |
1214 | else | |
1215 | break; // all done! | |
1216 | } | |
1217 | ||
1218 | return result; | |
1219 | } | |
1220 | ||
1221 | #else // USES_NETLINK | |
1222 | ||
1223 | // Open a socket that will receive interface change notifications | |
7f0064bd | 1224 | mDNSlocal mStatus OpenIfNotifySocket( int *pFD) |
8e92c31c A |
1225 | { |
1226 | *pFD = socket( AF_ROUTE, SOCK_RAW, 0); | |
1227 | ||
1228 | if ( *pFD < 0) | |
1229 | return mStatus_UnknownErr; | |
1230 | ||
1231 | // Configure read to be non-blocking because inbound msg size is not known in advance | |
1232 | (void) fcntl( *pFD, F_SETFL, O_NONBLOCK); | |
1233 | ||
1234 | return mStatus_NoError; | |
1235 | } | |
1236 | ||
1237 | #if MDNS_DEBUGMSGS | |
7f0064bd | 1238 | mDNSlocal void PrintRoutingSocketMsg( const struct ifa_msghdr *pRSMsg) |
8e92c31c A |
1239 | { |
1240 | const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", | |
1241 | "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", | |
1242 | "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; | |
1243 | ||
1244 | int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index; | |
1245 | ||
1246 | printf( "ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[ pRSMsg->ifam_type], index); | |
1247 | } | |
1248 | #endif | |
1249 | ||
7f0064bd | 1250 | mDNSlocal mDNSu32 ProcessRoutingNotification( int sd) |
8e92c31c A |
1251 | // Read through the messages on sd and if any indicate that any interface records should |
1252 | // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. | |
1253 | { | |
1254 | ssize_t readCount; | |
1255 | char buff[ 4096]; | |
1256 | struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff; | |
7f0064bd | 1257 | mDNSu32 result = 0; |
8e92c31c A |
1258 | |
1259 | readCount = read( sd, buff, sizeof buff); | |
1260 | if ( readCount < (ssize_t) sizeof( struct ifa_msghdr)) | |
1261 | return mStatus_UnsupportedErr; // cannot decipher message | |
1262 | ||
1263 | #if MDNS_DEBUGMSGS | |
1264 | PrintRoutingSocketMsg( pRSMsg); | |
1265 | #endif | |
1266 | ||
1267 | // Process the message | |
1268 | if ( pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR || | |
1269 | pRSMsg->ifam_type == RTM_IFINFO) | |
1270 | { | |
1271 | if ( pRSMsg->ifam_type == RTM_IFINFO) | |
1272 | result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; | |
1273 | else | |
1274 | result |= 1 << pRSMsg->ifam_index; | |
1275 | } | |
1276 | ||
1277 | return result; | |
1278 | } | |
1279 | ||
1280 | #endif // USES_NETLINK | |
1281 | ||
1282 | // Called when data appears on interface change notification socket | |
7f0064bd | 1283 | mDNSlocal void InterfaceChangeCallback( void *context) |
8e92c31c A |
1284 | { |
1285 | IfChangeRec *pChgRec = (IfChangeRec*) context; | |
1286 | fd_set readFDs; | |
7f0064bd | 1287 | mDNSu32 changedInterfaces = 0; |
8e92c31c A |
1288 | struct timeval zeroTimeout = { 0, 0 }; |
1289 | ||
1290 | FD_ZERO( &readFDs); | |
1291 | FD_SET( pChgRec->NotifySD, &readFDs); | |
1292 | ||
1293 | do | |
1294 | { | |
1295 | changedInterfaces |= ProcessRoutingNotification( pChgRec->NotifySD); | |
1296 | } | |
1297 | while ( 0 < select( pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); | |
1298 | ||
1299 | // Currently we rebuild the entire interface list whenever any interface change is | |
1300 | // detected. If this ever proves to be a performance issue in a multi-homed | |
1301 | // configuration, more care should be paid to changedInterfaces. | |
1302 | if ( changedInterfaces) | |
1303 | mDNSPlatformPosixRefreshInterfaceList( pChgRec->mDNS); | |
1304 | } | |
1305 | ||
1306 | // Register with either a Routing Socket or RtNetLink to listen for interface changes. | |
7f0064bd | 1307 | mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) |
8e92c31c A |
1308 | { |
1309 | mStatus err; | |
1310 | IfChangeRec *pChgRec; | |
1311 | ||
1312 | pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate( sizeof *pChgRec); | |
1313 | if ( pChgRec == NULL) | |
1314 | return mStatus_NoMemoryErr; | |
1315 | ||
1316 | pChgRec->mDNS = m; | |
1317 | err = OpenIfNotifySocket( &pChgRec->NotifySD); | |
1318 | if ( err == 0) | |
1319 | err = mDNSPosixAddFDToEventLoop( pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); | |
1320 | ||
1321 | return err; | |
1322 | } | |
1323 | ||
1324 | // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. | |
1325 | // If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- | |
1326 | // we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. | |
7f0064bd | 1327 | mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) |
8e92c31c A |
1328 | { |
1329 | int err; | |
1330 | int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
1331 | struct sockaddr_in s5353; | |
1332 | s5353.sin_family = AF_INET; | |
1333 | s5353.sin_port = MulticastDNSPort.NotAnInteger; | |
1334 | s5353.sin_addr.s_addr = 0; | |
1335 | err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); | |
1336 | close(s); | |
1337 | if (err) debugf("No unicast UDP responses"); | |
1338 | else debugf("Unicast UDP responses okay"); | |
1339 | return(err == 0); | |
1340 | } | |
1341 | ||
c9b9ae52 A |
1342 | // mDNS core calls this routine to initialise the platform-specific data. |
1343 | mDNSexport mStatus mDNSPlatformInit(mDNS *const m) | |
1344 | { | |
8e92c31c A |
1345 | int err = 0; |
1346 | struct sockaddr sa; | |
c9b9ae52 A |
1347 | assert(m != NULL); |
1348 | ||
7f0064bd | 1349 | if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; |
8e92c31c | 1350 | |
c9b9ae52 A |
1351 | // Tell mDNS core the names of this machine. |
1352 | ||
1353 | // Set up the nice label | |
1354 | m->nicelabel.c[0] = 0; | |
1355 | GetUserSpecifiedFriendlyComputerName(&m->nicelabel); | |
4aea607d | 1356 | if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer"); |
c9b9ae52 A |
1357 | |
1358 | // Set up the RFC 1034-compliant label | |
1359 | m->hostlabel.c[0] = 0; | |
1360 | GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); | |
4aea607d | 1361 | if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); |
c9b9ae52 | 1362 | |
7f0064bd | 1363 | mDNS_SetFQDN(m); |
c9b9ae52 | 1364 | |
8e92c31c A |
1365 | sa.sa_family = AF_INET; |
1366 | m->p->unicastSocket4 = -1; | |
1367 | if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); | |
1368 | #if HAVE_IPV6 | |
1369 | sa.sa_family = AF_INET6; | |
1370 | m->p->unicastSocket6 = -1; | |
1371 | if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); | |
1372 | #endif | |
1373 | ||
c9b9ae52 | 1374 | // Tell mDNS core about the network interfaces on this machine. |
8e92c31c A |
1375 | if (err == mStatus_NoError) err = SetupInterfaceList(m); |
1376 | ||
7cb34e5c A |
1377 | // Tell mDNS core about DNS Servers |
1378 | if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE); | |
1379 | ||
8e92c31c A |
1380 | if (err == mStatus_NoError) |
1381 | { | |
1382 | err = WatchForInterfaceChange(m); | |
1383 | // Failure to observe interface changes is non-fatal. | |
1384 | if ( err != mStatus_NoError) | |
1385 | { | |
1386 | fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err); | |
1387 | err = mStatus_NoError; | |
1388 | } | |
1389 | } | |
c9b9ae52 A |
1390 | |
1391 | // We don't do asynchronous initialization on the Posix platform, so by the time | |
1392 | // we get here the setup will already have succeeded or failed. If it succeeded, | |
1393 | // we should just call mDNSCoreInitComplete() immediately. | |
8e92c31c | 1394 | if (err == mStatus_NoError) |
c9b9ae52 A |
1395 | mDNSCoreInitComplete(m, mStatus_NoError); |
1396 | ||
1397 | return PosixErrorToStatus(err); | |
1398 | } | |
1399 | ||
1400 | // mDNS core calls this routine to clean up the platform-specific data. | |
1401 | // In our case all we need to do is to tear down every network interface. | |
1402 | mDNSexport void mDNSPlatformClose(mDNS *const m) | |
1403 | { | |
1404 | assert(m != NULL); | |
1405 | ClearInterfaceList(m); | |
8e92c31c A |
1406 | if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); |
1407 | #if HAVE_IPV6 | |
1408 | if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); | |
1409 | #endif | |
c9b9ae52 A |
1410 | } |
1411 | ||
7cb34e5c | 1412 | mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) |
c9b9ae52 A |
1413 | { |
1414 | int err; | |
1415 | ClearInterfaceList(m); | |
1416 | err = SetupInterfaceList(m); | |
1417 | return PosixErrorToStatus(err); | |
1418 | } | |
1419 | ||
1420 | #if COMPILER_LIKES_PRAGMA_MARK | |
1421 | #pragma mark ***** Locking | |
1422 | #endif | |
1423 | ||
1424 | // On the Posix platform, locking is a no-op because we only ever enter | |
1425 | // mDNS core on the main thread. | |
1426 | ||
1427 | // mDNS core calls this routine when it wants to prevent | |
1428 | // the platform from reentering mDNS core code. | |
1429 | mDNSexport void mDNSPlatformLock (const mDNS *const m) | |
1430 | { | |
1431 | (void) m; // Unused | |
1432 | } | |
1433 | ||
1434 | // mDNS core calls this routine when it release the lock taken by | |
1435 | // mDNSPlatformLock and allow the platform to reenter mDNS core code. | |
1436 | mDNSexport void mDNSPlatformUnlock (const mDNS *const m) | |
1437 | { | |
1438 | (void) m; // Unused | |
1439 | } | |
1440 | ||
1441 | #if COMPILER_LIKES_PRAGMA_MARK | |
1442 | #pragma mark ***** Strings | |
1443 | #endif | |
1444 | ||
1445 | // mDNS core calls this routine to copy C strings. | |
1446 | // On the Posix platform this maps directly to the ANSI C strcpy. | |
1447 | mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) | |
1448 | { | |
1449 | strcpy((char *)dst, (char *)src); | |
1450 | } | |
1451 | ||
1452 | // mDNS core calls this routine to get the length of a C string. | |
1453 | // On the Posix platform this maps directly to the ANSI C strlen. | |
1454 | mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) | |
1455 | { | |
1456 | return strlen((char*)src); | |
1457 | } | |
1458 | ||
1459 | // mDNS core calls this routine to copy memory. | |
1460 | // On the Posix platform this maps directly to the ANSI C memcpy. | |
1461 | mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) | |
1462 | { | |
1463 | memcpy(dst, src, len); | |
1464 | } | |
1465 | ||
1466 | // mDNS core calls this routine to test whether blocks of memory are byte-for-byte | |
1467 | // identical. On the Posix platform this is a simple wrapper around ANSI C memcmp. | |
1468 | mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) | |
1469 | { | |
1470 | return memcmp(dst, src, len) == 0; | |
1471 | } | |
1472 | ||
1473 | // mDNS core calls this routine to clear blocks of memory. | |
1474 | // On the Posix platform this is a simple wrapper around ANSI C memset. | |
1475 | mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) | |
1476 | { | |
1477 | memset(dst, 0, len); | |
1478 | } | |
1479 | ||
1480 | mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); } | |
1481 | mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); } | |
1482 | ||
7f0064bd A |
1483 | mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) |
1484 | { | |
1485 | struct timeval tv; | |
1486 | gettimeofday(&tv, NULL); | |
1487 | return(tv.tv_usec); | |
1488 | } | |
1489 | ||
c9b9ae52 A |
1490 | mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; |
1491 | ||
7f0064bd | 1492 | mDNSexport mStatus mDNSPlatformTimeInit(void) |
c9b9ae52 A |
1493 | { |
1494 | // No special setup is required on Posix -- we just use gettimeofday(); | |
1495 | // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time | |
1496 | // We should find a better way to do this | |
c9b9ae52 A |
1497 | return(mStatus_NoError); |
1498 | } | |
1499 | ||
7f0064bd | 1500 | mDNSexport mDNSs32 mDNSPlatformRawTime() |
c9b9ae52 A |
1501 | { |
1502 | struct timeval tv; | |
1503 | gettimeofday(&tv, NULL); | |
1504 | // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) | |
1505 | // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) | |
1506 | // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result | |
1507 | // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. | |
1508 | // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) | |
1509 | // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). | |
1510 | return( (tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625) ); | |
1511 | } | |
1512 | ||
8e92c31c A |
1513 | mDNSexport mDNSs32 mDNSPlatformUTC(void) |
1514 | { | |
1515 | return time(NULL); | |
1516 | } | |
1517 | ||
1518 | mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) | |
1519 | { | |
1520 | if (*nfds < s + 1) *nfds = s + 1; | |
1521 | FD_SET(s, readfds); | |
1522 | } | |
1523 | ||
1524 | mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout) | |
c9b9ae52 A |
1525 | { |
1526 | mDNSs32 ticks; | |
1527 | struct timeval interval; | |
1528 | ||
1529 | // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do | |
1530 | mDNSs32 nextevent = mDNS_Execute(m); | |
1531 | ||
1532 | // 2. Build our list of active file descriptors | |
1533 | PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); | |
8e92c31c A |
1534 | if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); |
1535 | #if HAVE_IPV6 | |
1536 | if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); | |
1537 | #endif | |
c9b9ae52 A |
1538 | while (info) |
1539 | { | |
8e92c31c A |
1540 | if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); |
1541 | #if HAVE_IPV6 | |
1542 | if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); | |
1543 | #endif | |
c9b9ae52 A |
1544 | info = (PosixNetworkInterface *)(info->coreIntf.next); |
1545 | } | |
1546 | ||
1547 | // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) | |
7f0064bd | 1548 | ticks = nextevent - mDNS_TimeNow(m); |
c9b9ae52 A |
1549 | if (ticks < 1) ticks = 1; |
1550 | interval.tv_sec = ticks >> 10; // The high 22 bits are seconds | |
1551 | interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths | |
1552 | ||
1553 | // 4. If client's proposed timeout is more than what we want, then reduce it | |
1554 | if (timeout->tv_sec > interval.tv_sec || | |
1555 | (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec)) | |
1556 | *timeout = interval; | |
1557 | } | |
1558 | ||
1559 | mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) | |
1560 | { | |
1561 | PosixNetworkInterface *info; | |
1562 | assert(m != NULL); | |
1563 | assert(readfds != NULL); | |
1564 | info = (PosixNetworkInterface *)(m->HostInterfaces); | |
8e92c31c A |
1565 | |
1566 | if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds)) | |
1567 | { | |
1568 | FD_CLR(m->p->unicastSocket4, readfds); | |
1569 | SocketDataReady(m, NULL, m->p->unicastSocket4); | |
1570 | } | |
1571 | #if HAVE_IPV6 | |
1572 | if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds)) | |
1573 | { | |
1574 | FD_CLR(m->p->unicastSocket6, readfds); | |
1575 | SocketDataReady(m, NULL, m->p->unicastSocket6); | |
1576 | } | |
1577 | #endif | |
1578 | ||
c9b9ae52 A |
1579 | while (info) |
1580 | { | |
8e92c31c | 1581 | if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds)) |
c9b9ae52 | 1582 | { |
8e92c31c A |
1583 | FD_CLR(info->multicastSocket4, readfds); |
1584 | SocketDataReady(m, info, info->multicastSocket4); | |
c9b9ae52 | 1585 | } |
8e92c31c A |
1586 | #if HAVE_IPV6 |
1587 | if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds)) | |
c9b9ae52 | 1588 | { |
8e92c31c A |
1589 | FD_CLR(info->multicastSocket6, readfds); |
1590 | SocketDataReady(m, info, info->multicastSocket6); | |
c9b9ae52 | 1591 | } |
8e92c31c | 1592 | #endif |
c9b9ae52 A |
1593 | info = (PosixNetworkInterface *)(info->coreIntf.next); |
1594 | } | |
1595 | } | |
8e92c31c A |
1596 | |
1597 | // update gMaxFD | |
7f0064bd | 1598 | mDNSlocal void DetermineMaxEventFD( void ) |
8e92c31c A |
1599 | { |
1600 | PosixEventSource *iSource; | |
1601 | ||
1602 | gMaxFD = 0; | |
1603 | for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) | |
1604 | if ( gMaxFD < iSource->fd) | |
1605 | gMaxFD = iSource->fd; | |
1606 | } | |
1607 | ||
1608 | // Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. | |
1609 | mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context) | |
1610 | { | |
1611 | PosixEventSource *newSource; | |
1612 | ||
1613 | if ( gEventSources.LinkOffset == 0) | |
1614 | InitLinkedList( &gEventSources, offsetof( PosixEventSource, Next)); | |
1615 | ||
1616 | if ( fd >= (int) FD_SETSIZE || fd < 0) | |
1617 | return mStatus_UnsupportedErr; | |
1618 | if ( callback == NULL) | |
1619 | return mStatus_BadParamErr; | |
1620 | ||
1621 | newSource = (PosixEventSource*) malloc( sizeof *newSource); | |
1622 | if ( NULL == newSource) | |
1623 | return mStatus_NoMemoryErr; | |
1624 | ||
1625 | newSource->Callback = callback; | |
1626 | newSource->Context = context; | |
1627 | newSource->fd = fd; | |
1628 | ||
1629 | AddToTail( &gEventSources, newSource); | |
1630 | FD_SET( fd, &gEventFDs); | |
1631 | ||
1632 | DetermineMaxEventFD(); | |
1633 | ||
1634 | return mStatus_NoError; | |
1635 | } | |
1636 | ||
1637 | // Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to. | |
1638 | mStatus mDNSPosixRemoveFDFromEventLoop( int fd) | |
1639 | { | |
1640 | PosixEventSource *iSource; | |
1641 | ||
1642 | for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) | |
1643 | { | |
1644 | if ( fd == iSource->fd) | |
1645 | { | |
1646 | FD_CLR( fd, &gEventFDs); | |
1647 | RemoveFromList( &gEventSources, iSource); | |
1648 | free( iSource); | |
1649 | DetermineMaxEventFD(); | |
1650 | return mStatus_NoError; | |
1651 | } | |
1652 | } | |
1653 | return mStatus_NoSuchNameErr; | |
1654 | } | |
1655 | ||
1656 | // Simply note the received signal in gEventSignals. | |
7f0064bd | 1657 | mDNSlocal void NoteSignal( int signum) |
8e92c31c A |
1658 | { |
1659 | sigaddset( &gEventSignals, signum); | |
1660 | } | |
1661 | ||
1662 | // Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce(). | |
1663 | mStatus mDNSPosixListenForSignalInEventLoop( int signum) | |
1664 | { | |
1665 | struct sigaction action; | |
1666 | mStatus err; | |
1667 | ||
1668 | bzero( &action, sizeof action); // more portable than member-wise assignment | |
1669 | action.sa_handler = NoteSignal; | |
1670 | err = sigaction( signum, &action, (struct sigaction*) NULL); | |
1671 | ||
1672 | sigaddset( &gEventSignalSet, signum); | |
1673 | ||
1674 | return err; | |
1675 | } | |
1676 | ||
1677 | // Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce(). | |
1678 | mStatus mDNSPosixIgnoreSignalInEventLoop( int signum) | |
1679 | { | |
1680 | struct sigaction action; | |
1681 | mStatus err; | |
1682 | ||
1683 | bzero( &action, sizeof action); // more portable than member-wise assignment | |
1684 | action.sa_handler = SIG_DFL; | |
1685 | err = sigaction( signum, &action, (struct sigaction*) NULL); | |
1686 | ||
1687 | sigdelset( &gEventSignalSet, signum); | |
1688 | ||
1689 | return err; | |
1690 | } | |
1691 | ||
1692 | // Do a single pass through the attendent event sources and dispatch any found to their callbacks. | |
1693 | // Return as soon as internal timeout expires, or a signal we're listening for is received. | |
1694 | mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, | |
1695 | sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) | |
1696 | { | |
1697 | fd_set listenFDs = gEventFDs; | |
1698 | int fdMax = 0, numReady; | |
1699 | struct timeval timeout = *pTimeout; | |
1700 | ||
1701 | // Include the sockets that are listening to the wire in our select() set | |
1702 | mDNSPosixGetFDSet( m, &fdMax, &listenFDs, &timeout); // timeout may get modified | |
1703 | if ( fdMax < gMaxFD) | |
1704 | fdMax = gMaxFD; | |
1705 | ||
1706 | numReady = select( fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); | |
1707 | ||
1708 | // If any data appeared, invoke its callback | |
1709 | if ( numReady > 0) | |
1710 | { | |
1711 | PosixEventSource *iSource; | |
1712 | ||
1713 | (void) mDNSPosixProcessFDSet( m, &listenFDs); // call this first to process wire data for clients | |
1714 | ||
1715 | for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) | |
1716 | { | |
1717 | if ( FD_ISSET( iSource->fd, &listenFDs)) | |
1718 | { | |
1719 | iSource->Callback( iSource->Context); | |
1720 | break; // in case callback removed elements from gEventSources | |
1721 | } | |
1722 | } | |
1723 | *pDataDispatched = mDNStrue; | |
1724 | } | |
1725 | else | |
1726 | *pDataDispatched = mDNSfalse; | |
1727 | ||
1728 | (void) sigprocmask( SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); | |
1729 | *pSignalsReceived = gEventSignals; | |
1730 | sigemptyset( &gEventSignals); | |
1731 | (void) sigprocmask( SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); | |
1732 | ||
1733 | return mStatus_NoError; | |
1734 | } |