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