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