]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
9f221bca | 3 | * Copyright (c) 2002-2015 Apple Inc. All rights reserved. |
c9b9ae52 | 4 | * |
67c8f8a1 A |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at | |
83fb1e36 | 8 | * |
67c8f8a1 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
83fb1e36 | 10 | * |
67c8f8a1 A |
11 | * Unless required by applicable law or agreed to in writing, software |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
c9b9ae52 | 15 | * limitations under the License. |
c9b9ae52 | 16 | * |
263eeeab | 17 | */ |
c9b9ae52 | 18 | |
7f0064bd | 19 | #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above |
67c8f8a1 | 20 | #include "DNSCommon.h" |
83fb1e36 | 21 | #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform |
283ee3ff | 22 | #include "dns_sd.h" |
83fb1e36 A |
23 | #include "dnssec.h" |
24 | #include "nsec.h" | |
c9b9ae52 A |
25 | |
26 | #include <assert.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <errno.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
8e92c31c A |
32 | #include <syslog.h> |
33 | #include <stdarg.h> | |
c9b9ae52 A |
34 | #include <fcntl.h> |
35 | #include <sys/types.h> | |
8e92c31c | 36 | #include <sys/time.h> |
c9b9ae52 A |
37 | #include <sys/socket.h> |
38 | #include <sys/uio.h> | |
8e92c31c | 39 | #include <sys/select.h> |
c9b9ae52 | 40 | #include <netinet/in.h> |
7cb34e5c | 41 | #include <arpa/inet.h> |
8e92c31c A |
42 | #include <time.h> // platform support for UTC time |
43 | ||
44 | #if USES_NETLINK | |
45 | #include <asm/types.h> | |
46 | #include <linux/netlink.h> | |
47 | #include <linux/rtnetlink.h> | |
48 | #else // USES_NETLINK | |
49 | #include <net/route.h> | |
50 | #include <net/if.h> | |
51 | #endif // USES_NETLINK | |
c9b9ae52 A |
52 | |
53 | #include "mDNSUNP.h" | |
8e92c31c | 54 | #include "GenLinkedList.h" |
c9b9ae52 A |
55 | |
56 | // *************************************************************************** | |
57 | // Structures | |
58 | ||
83fb1e36 | 59 | // We keep a list of client-supplied event sources in PosixEventSource records |
8e92c31c | 60 | struct PosixEventSource |
83fb1e36 A |
61 | { |
62 | mDNSPosixEventCallback Callback; | |
63 | void *Context; | |
64 | int fd; | |
65 | struct PosixEventSource *Next; | |
66 | }; | |
67 | typedef struct PosixEventSource PosixEventSource; | |
c9b9ae52 | 68 | |
8e92c31c A |
69 | // Context record for interface change callback |
70 | struct IfChangeRec | |
83fb1e36 A |
71 | { |
72 | int NotifySD; | |
73 | mDNS *mDNS; | |
74 | }; | |
75 | typedef struct IfChangeRec IfChangeRec; | |
8e92c31c A |
76 | |
77 | // Note that static data is initialized to zero in (modern) C. | |
83fb1e36 A |
78 | static fd_set gEventFDs; |
79 | static int gMaxFD; // largest fd in gEventFDs | |
80 | static GenLinkedList gEventSources; // linked list of PosixEventSource's | |
81 | static sigset_t gEventSignalSet; // Signals which event loop listens for | |
82 | static sigset_t gEventSignals; // Signals which were received while inside loop | |
c9b9ae52 | 83 | |
95d7a4a3 A |
84 | static PosixNetworkInterface *gRecentInterfaces; |
85 | ||
c9b9ae52 A |
86 | // *************************************************************************** |
87 | // Globals (for debugging) | |
88 | ||
89 | static int num_registered_interfaces = 0; | |
90 | static int num_pkts_accepted = 0; | |
91 | static int num_pkts_rejected = 0; | |
92 | ||
93 | // *************************************************************************** | |
94 | // Functions | |
95 | ||
96 | int gMDNSPlatformPosixVerboseLevel = 0; | |
97 | ||
c9b9ae52 A |
98 | #define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) |
99 | ||
7f0064bd | 100 | mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort) |
83fb1e36 A |
101 | { |
102 | switch (sa->sa_family) | |
103 | { | |
104 | case AF_INET: | |
105 | { | |
106 | struct sockaddr_in *sin = (struct sockaddr_in*)sa; | |
107 | ipAddr->type = mDNSAddrType_IPv4; | |
108 | ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; | |
109 | if (ipPort) ipPort->NotAnInteger = sin->sin_port; | |
110 | break; | |
111 | } | |
c9b9ae52 | 112 | |
8e92c31c | 113 | #if HAVE_IPV6 |
83fb1e36 A |
114 | case AF_INET6: |
115 | { | |
116 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; | |
7f0064bd | 117 | #ifndef NOT_HAVE_SA_LEN |
83fb1e36 | 118 | assert(sin6->sin6_len == sizeof(*sin6)); |
7f0064bd | 119 | #endif |
83fb1e36 A |
120 | ipAddr->type = mDNSAddrType_IPv6; |
121 | ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; | |
122 | if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; | |
123 | break; | |
124 | } | |
c9b9ae52 A |
125 | #endif |
126 | ||
83fb1e36 A |
127 | default: |
128 | verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); | |
129 | ipAddr->type = mDNSAddrType_None; | |
130 | if (ipPort) ipPort->NotAnInteger = 0; | |
131 | break; | |
132 | } | |
133 | } | |
c9b9ae52 A |
134 | |
135 | #if COMPILER_LIKES_PRAGMA_MARK | |
136 | #pragma mark ***** Send and Receive | |
137 | #endif | |
138 | ||
139 | // mDNS core calls this routine when it needs to send a packet. | |
7f0064bd | 140 | mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, |
9f221bca | 141 | mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, |
83fb1e36 A |
142 | mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) |
143 | { | |
144 | int err = 0; | |
145 | struct sockaddr_storage to; | |
146 | PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); | |
147 | int sendingsocket = -1; | |
148 | ||
149 | (void)src; // Will need to use this parameter once we implement mDNSPlatformUDPSocket/mDNSPlatformUDPClose | |
150 | (void) useBackgroundTrafficClass; | |
151 | ||
152 | assert(m != NULL); | |
153 | assert(msg != NULL); | |
154 | assert(end != NULL); | |
155 | assert((((char *) end) - ((char *) msg)) > 0); | |
156 | ||
157 | if (dstPort.NotAnInteger == 0) | |
158 | { | |
159 | LogMsg("mDNSPlatformSendUDP: Invalid argument -dstPort is set to 0"); | |
160 | return PosixErrorToStatus(EINVAL); | |
161 | } | |
162 | if (dst->type == mDNSAddrType_IPv4) | |
163 | { | |
164 | struct sockaddr_in *sin = (struct sockaddr_in*)&to; | |
c9b9ae52 | 165 | #ifndef NOT_HAVE_SA_LEN |
83fb1e36 | 166 | sin->sin_len = sizeof(*sin); |
c9b9ae52 | 167 | #endif |
83fb1e36 A |
168 | sin->sin_family = AF_INET; |
169 | sin->sin_port = dstPort.NotAnInteger; | |
170 | sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; | |
171 | sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4; | |
172 | } | |
c9b9ae52 | 173 | |
8e92c31c | 174 | #if HAVE_IPV6 |
83fb1e36 A |
175 | else if (dst->type == mDNSAddrType_IPv6) |
176 | { | |
177 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; | |
178 | mDNSPlatformMemZero(sin6, sizeof(*sin6)); | |
7f0064bd | 179 | #ifndef NOT_HAVE_SA_LEN |
83fb1e36 | 180 | sin6->sin6_len = sizeof(*sin6); |
7f0064bd | 181 | #endif |
83fb1e36 A |
182 | sin6->sin6_family = AF_INET6; |
183 | sin6->sin6_port = dstPort.NotAnInteger; | |
184 | sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; | |
185 | sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6; | |
186 | } | |
c9b9ae52 A |
187 | #endif |
188 | ||
83fb1e36 A |
189 | if (sendingsocket >= 0) |
190 | err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); | |
c9b9ae52 | 191 | |
83fb1e36 A |
192 | if (err > 0) err = 0; |
193 | else if (err < 0) | |
194 | { | |
195 | static int MessageCount = 0; | |
67c8f8a1 | 196 | // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations |
83fb1e36 A |
197 | if (!mDNSAddressIsAllDNSLinkGroup(dst)) |
198 | if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); | |
199 | ||
200 | if (MessageCount < 1000) | |
201 | { | |
202 | MessageCount++; | |
203 | if (thisIntf) | |
204 | LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", | |
205 | errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); | |
206 | else | |
207 | LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); | |
208 | } | |
209 | } | |
210 | ||
211 | return PosixErrorToStatus(err); | |
212 | } | |
c9b9ae52 A |
213 | |
214 | // This routine is called when the main loop detects that data is available on a socket. | |
7f0064bd | 215 | mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) |
83fb1e36 A |
216 | { |
217 | mDNSAddr senderAddr, destAddr; | |
218 | mDNSIPPort senderPort; | |
219 | ssize_t packetLen; | |
220 | DNSMessage packet; | |
221 | struct my_in_pktinfo packetInfo; | |
222 | struct sockaddr_storage from; | |
223 | socklen_t fromLen; | |
224 | int flags; | |
225 | mDNSu8 ttl; | |
226 | mDNSBool reject; | |
227 | const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; | |
228 | ||
229 | assert(m != NULL); | |
230 | assert(skt >= 0); | |
231 | ||
232 | fromLen = sizeof(from); | |
233 | flags = 0; | |
234 | packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); | |
235 | ||
236 | if (packetLen >= 0) | |
237 | { | |
238 | SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); | |
239 | SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); | |
240 | ||
241 | // If we have broken IP_RECVDSTADDR functionality (so far | |
242 | // I've only seen this on OpenBSD) then apply a hack to | |
243 | // convince mDNS Core that this isn't a spoof packet. | |
244 | // Basically what we do is check to see whether the | |
245 | // packet arrived as a multicast and, if so, set its | |
246 | // destAddr to the mDNS address. | |
247 | // | |
248 | // I must admit that I could just be doing something | |
249 | // wrong on OpenBSD and hence triggering this problem | |
250 | // but I'm at a loss as to how. | |
251 | // | |
252 | // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have | |
253 | // no way to tell the destination address or interface this packet arrived on, | |
254 | // so all we can do is just assume it's a multicast | |
255 | ||
256 | #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) | |
257 | if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST)) | |
258 | { | |
259 | destAddr.type = senderAddr.type; | |
260 | if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4; | |
261 | else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6; | |
262 | } | |
263 | #endif | |
264 | ||
265 | // We only accept the packet if the interface on which it came | |
266 | // in matches the interface associated with this socket. | |
267 | // We do this match by name or by index, depending on which | |
268 | // information is available. recvfrom_flags sets the name | |
269 | // to "" if the name isn't available, or the index to -1 | |
270 | // if the index is available. This accomodates the various | |
271 | // different capabilities of our target platforms. | |
272 | ||
273 | reject = mDNSfalse; | |
274 | if (!intf) | |
275 | { | |
276 | // Ignore multicasts accidentally delivered to our unicast receiving socket | |
277 | if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1; | |
278 | } | |
279 | else | |
280 | { | |
281 | if (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); | |
282 | else if (packetInfo.ipi_ifindex != -1) reject = (packetInfo.ipi_ifindex != intf->index); | |
283 | ||
284 | if (reject) | |
285 | { | |
286 | verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d", | |
287 | &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, | |
288 | &intf->coreIntf.ip, intf->intfName, intf->index, skt); | |
289 | packetLen = -1; | |
290 | num_pkts_rejected++; | |
291 | if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) | |
292 | { | |
293 | fprintf(stderr, | |
294 | "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", | |
295 | num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); | |
296 | num_pkts_accepted = 0; | |
297 | num_pkts_rejected = 0; | |
298 | } | |
299 | } | |
300 | else | |
301 | { | |
302 | verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d", | |
303 | &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt); | |
304 | num_pkts_accepted++; | |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | if (packetLen >= 0) | |
310 | mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, | |
311 | &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); | |
312 | } | |
313 | ||
314 | mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass) | |
315 | { | |
316 | (void)m; // Unused | |
317 | (void)flags; // Unused | |
318 | (void)port; // Unused | |
319 | (void)useBackgroundTrafficClass; // Unused | |
320 | return NULL; | |
321 | } | |
67c8f8a1 A |
322 | |
323 | mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) | |
83fb1e36 A |
324 | { |
325 | (void)flags; // Unused | |
326 | (void)sd; // Unused | |
327 | return NULL; | |
328 | } | |
67c8f8a1 A |
329 | |
330 | mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) | |
83fb1e36 A |
331 | { |
332 | (void)sock; // Unused | |
333 | return -1; | |
334 | } | |
67c8f8a1 | 335 | |
263eeeab | 336 | mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, |
83fb1e36 A |
337 | TCPConnectionCallback callback, void *context) |
338 | { | |
339 | (void)sock; // Unused | |
340 | (void)dst; // Unused | |
341 | (void)dstport; // Unused | |
342 | (void)hostname; // Unused | |
343 | (void)InterfaceID; // Unused | |
344 | (void)callback; // Unused | |
345 | (void)context; // Unused | |
346 | return(mStatus_UnsupportedErr); | |
347 | } | |
8e92c31c | 348 | |
67c8f8a1 | 349 | mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) |
83fb1e36 A |
350 | { |
351 | (void)sock; // Unused | |
352 | } | |
8e92c31c | 353 | |
67c8f8a1 | 354 | mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool * closed) |
83fb1e36 A |
355 | { |
356 | (void)sock; // Unused | |
357 | (void)buf; // Unused | |
358 | (void)buflen; // Unused | |
359 | (void)closed; // Unused | |
360 | return 0; | |
361 | } | |
8e92c31c | 362 | |
67c8f8a1 | 363 | mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) |
83fb1e36 A |
364 | { |
365 | (void)sock; // Unused | |
366 | (void)msg; // Unused | |
367 | (void)len; // Unused | |
368 | return 0; | |
369 | } | |
67c8f8a1 A |
370 | |
371 | mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port) | |
83fb1e36 A |
372 | { |
373 | (void)m; // Unused | |
374 | (void)port; // Unused | |
375 | return NULL; | |
376 | } | |
67c8f8a1 A |
377 | |
378 | mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) | |
83fb1e36 A |
379 | { |
380 | (void)sock; // Unused | |
381 | } | |
382 | ||
32bb7e43 | 383 | mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) |
83fb1e36 A |
384 | { |
385 | (void)m; // Unused | |
386 | (void)InterfaceID; // Unused | |
387 | } | |
32bb7e43 A |
388 | |
389 | mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) | |
83fb1e36 A |
390 | { |
391 | (void)msg; // Unused | |
392 | (void)end; // Unused | |
393 | (void)InterfaceID; // Unused | |
394 | } | |
395 | ||
263eeeab | 396 | mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) |
83fb1e36 A |
397 | { |
398 | (void)m; // Unused | |
399 | (void)tpa; // Unused | |
400 | (void)tha; // Unused | |
401 | (void)InterfaceID; // Unused | |
402 | } | |
67c8f8a1 A |
403 | |
404 | mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) | |
83fb1e36 A |
405 | { |
406 | return(mStatus_UnsupportedErr); | |
407 | } | |
408 | ||
67c8f8a1 | 409 | mDNSexport void mDNSPlatformTLSTearDownCerts(void) |
83fb1e36 A |
410 | { |
411 | } | |
c9b9ae52 | 412 | |
294beb6e | 413 | mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) |
83fb1e36 A |
414 | { |
415 | (void) m; | |
416 | (void) allowSleep; | |
417 | (void) reason; | |
418 | } | |
294beb6e A |
419 | |
420 | #if COMPILER_LIKES_PRAGMA_MARK | |
421 | #pragma mark - | |
422 | #pragma mark - /etc/hosts support | |
423 | #endif | |
424 | ||
425 | mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) | |
83fb1e36 | 426 | { |
294beb6e | 427 | (void)m; // unused |
83fb1e36 A |
428 | (void)rr; |
429 | (void)result; | |
430 | } | |
294beb6e A |
431 | |
432 | ||
c9b9ae52 | 433 | #if COMPILER_LIKES_PRAGMA_MARK |
67c8f8a1 | 434 | #pragma mark ***** DDNS Config Platform Functions |
c9b9ae52 A |
435 | #endif |
436 | ||
51601d48 A |
437 | mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, |
438 | DNameListElem **BrowseDomains, mDNSBool ackConfig) | |
83fb1e36 A |
439 | { |
440 | (void) m; | |
441 | (void) setservers; | |
442 | (void) fqdn; | |
443 | (void) setsearch; | |
444 | (void) RegDomains; | |
445 | (void) BrowseDomains; | |
51601d48 A |
446 | (void) ackConfig; |
447 | ||
448 | return mDNStrue; | |
83fb1e36 | 449 | } |
8e92c31c | 450 | |
67c8f8a1 | 451 | mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router) |
83fb1e36 A |
452 | { |
453 | (void) m; | |
454 | (void) v4; | |
455 | (void) v6; | |
456 | (void) router; | |
67c8f8a1 | 457 | |
83fb1e36 A |
458 | return mStatus_UnsupportedErr; |
459 | } | |
c9b9ae52 | 460 | |
67c8f8a1 | 461 | mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) |
83fb1e36 A |
462 | { |
463 | (void) dname; | |
464 | (void) status; | |
465 | } | |
7f0064bd | 466 | |
8e92c31c A |
467 | #if COMPILER_LIKES_PRAGMA_MARK |
468 | #pragma mark ***** Init and Term | |
469 | #endif | |
470 | ||
c9b9ae52 A |
471 | // This gets the current hostname, truncating it at the first dot if necessary |
472 | mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) | |
83fb1e36 A |
473 | { |
474 | int len = 0; | |
475 | gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL); | |
476 | while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++; | |
477 | namelabel->c[0] = len; | |
478 | } | |
c9b9ae52 | 479 | |
8e92c31c A |
480 | // On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel |
481 | // Other platforms can either get the information from the appropriate place, | |
482 | // or they can alternatively just require all registering services to provide an explicit name | |
483 | mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) | |
83fb1e36 A |
484 | { |
485 | // On Unix we have no better name than the host name, so we just use that. | |
486 | GetUserSpecifiedRFC1034ComputerName(namelabel); | |
487 | } | |
8e92c31c | 488 | |
7cb34e5c | 489 | mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) |
83fb1e36 A |
490 | { |
491 | char line[256]; | |
492 | char nameserver[16]; | |
493 | char keyword[11]; | |
494 | int numOfServers = 0; | |
495 | FILE *fp = fopen(filePath, "r"); | |
496 | if (fp == NULL) return -1; | |
497 | while (fgets(line,sizeof(line),fp)) | |
498 | { | |
499 | struct in_addr ina; | |
500 | line[255]='\0'; // just to be safe | |
501 | if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces | |
502 | if (strncasecmp(keyword,"nameserver",10)) continue; | |
503 | if (inet_aton(nameserver, (struct in_addr *)&ina) != 0) | |
504 | { | |
505 | mDNSAddr DNSAddr; | |
506 | DNSAddr.type = mDNSAddrType_IPv4; | |
507 | DNSAddr.ip.v4.NotAnInteger = ina.s_addr; | |
51601d48 | 508 | mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse); |
83fb1e36 A |
509 | numOfServers++; |
510 | } | |
511 | } | |
9f221bca | 512 | fclose(fp); |
83fb1e36 A |
513 | return (numOfServers > 0) ? 0 : -1; |
514 | } | |
7cb34e5c | 515 | |
c9b9ae52 A |
516 | // Searches the interface list looking for the named interface. |
517 | // Returns a pointer to if it found, or NULL otherwise. | |
7f0064bd | 518 | mDNSlocal PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName) |
83fb1e36 A |
519 | { |
520 | PosixNetworkInterface *intf; | |
c9b9ae52 | 521 | |
83fb1e36 A |
522 | assert(m != NULL); |
523 | assert(intfName != NULL); | |
c9b9ae52 | 524 | |
83fb1e36 A |
525 | intf = (PosixNetworkInterface*)(m->HostInterfaces); |
526 | while ((intf != NULL) && (strcmp(intf->intfName, intfName) != 0)) | |
527 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
c9b9ae52 | 528 | |
83fb1e36 A |
529 | return intf; |
530 | } | |
c9b9ae52 | 531 | |
67c8f8a1 | 532 | mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index) |
83fb1e36 A |
533 | { |
534 | PosixNetworkInterface *intf; | |
8e92c31c | 535 | |
83fb1e36 | 536 | assert(m != NULL); |
8e92c31c | 537 | |
83fb1e36 A |
538 | if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); |
539 | if (index == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); | |
540 | if (index == kDNSServiceInterfaceIndexAny ) return(mDNSInterface_Any); | |
8e92c31c | 541 | |
83fb1e36 A |
542 | intf = (PosixNetworkInterface*)(m->HostInterfaces); |
543 | while ((intf != NULL) && (mDNSu32) intf->index != index) | |
544 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
545 | ||
546 | return (mDNSInterfaceID) intf; | |
547 | } | |
8e92c31c | 548 | |
294beb6e | 549 | mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) |
83fb1e36 A |
550 | { |
551 | PosixNetworkInterface *intf; | |
552 | (void) suppressNetworkChange; // Unused | |
8e92c31c | 553 | |
83fb1e36 | 554 | assert(m != NULL); |
8e92c31c | 555 | |
83fb1e36 A |
556 | if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); |
557 | if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); | |
558 | if (id == mDNSInterface_Any ) return(kDNSServiceInterfaceIndexAny); | |
8e92c31c | 559 | |
83fb1e36 A |
560 | intf = (PosixNetworkInterface*)(m->HostInterfaces); |
561 | while ((intf != NULL) && (mDNSInterfaceID) intf != id) | |
562 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
8e92c31c | 563 | |
95d7a4a3 A |
564 | if (intf) return intf->index; |
565 | ||
566 | // If we didn't find the interface, check the RecentInterfaces list as well | |
567 | intf = gRecentInterfaces; | |
568 | while ((intf != NULL) && (mDNSInterfaceID) intf != id) | |
569 | intf = (PosixNetworkInterface *)(intf->coreIntf.next); | |
570 | ||
83fb1e36 A |
571 | return intf ? intf->index : 0; |
572 | } | |
8e92c31c | 573 | |
c9b9ae52 A |
574 | // Frees the specified PosixNetworkInterface structure. The underlying |
575 | // interface must have already been deregistered with the mDNS core. | |
7f0064bd | 576 | mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf) |
83fb1e36 | 577 | { |
e0815622 | 578 | int rv; |
83fb1e36 A |
579 | assert(intf != NULL); |
580 | if (intf->intfName != NULL) free((void *)intf->intfName); | |
e0815622 A |
581 | if (intf->multicastSocket4 != -1) |
582 | { | |
583 | rv = close(intf->multicastSocket4); | |
584 | assert(rv == 0); | |
585 | } | |
8e92c31c | 586 | #if HAVE_IPV6 |
e0815622 A |
587 | if (intf->multicastSocket6 != -1) |
588 | { | |
589 | rv = close(intf->multicastSocket6); | |
590 | assert(rv == 0); | |
591 | } | |
8e92c31c | 592 | #endif |
95d7a4a3 A |
593 | |
594 | // Move interface to the RecentInterfaces list for a minute | |
595 | intf->LastSeen = mDNSPlatformUTC(); | |
596 | intf->coreIntf.next = &gRecentInterfaces->coreIntf; | |
597 | gRecentInterfaces = intf; | |
83fb1e36 | 598 | } |
c9b9ae52 A |
599 | |
600 | // Grab the first interface, deregister it, free it, and repeat until done. | |
7f0064bd | 601 | mDNSlocal void ClearInterfaceList(mDNS *const m) |
83fb1e36 A |
602 | { |
603 | assert(m != NULL); | |
604 | ||
605 | while (m->HostInterfaces) | |
606 | { | |
607 | PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); | |
608 | mDNS_DeregisterInterface(m, &intf->coreIntf, mDNSfalse); | |
609 | if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName); | |
610 | FreePosixNetworkInterface(intf); | |
611 | } | |
612 | num_registered_interfaces = 0; | |
613 | num_pkts_accepted = 0; | |
614 | num_pkts_rejected = 0; | |
615 | } | |
c9b9ae52 | 616 | |
8e92c31c A |
617 | // Sets up a send/receive socket. |
618 | // If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface | |
619 | // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries | |
7f0064bd | 620 | mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) |
83fb1e36 A |
621 | { |
622 | int err = 0; | |
623 | static const int kOn = 1; | |
624 | static const int kIntTwoFiveFive = 255; | |
625 | static const unsigned char kByteTwoFiveFive = 255; | |
626 | const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0); | |
627 | ||
628 | (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6 | |
629 | assert(intfAddr != NULL); | |
630 | assert(sktPtr != NULL); | |
631 | assert(*sktPtr == -1); | |
632 | ||
633 | // Open the socket... | |
634 | if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
8e92c31c | 635 | #if HAVE_IPV6 |
83fb1e36 | 636 | else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
c9b9ae52 | 637 | #endif |
83fb1e36 A |
638 | else return EINVAL; |
639 | ||
640 | if (*sktPtr < 0) { err = errno; perror((intfAddr->sa_family == AF_INET) ? "socket AF_INET" : "socket AF_INET6"); } | |
641 | ||
642 | // ... with a shared UDP port, if it's for multicast receiving | |
643 | if (err == 0 && port.NotAnInteger) | |
644 | { | |
9f221bca A |
645 | // <rdar://problem/20946253> |
646 | // We test for SO_REUSEADDR first, as suggested by Jonny Törnbom from Axis Communications | |
647 | // Linux kernel versions 3.9 introduces support for socket option | |
648 | // SO_REUSEPORT, however this is not implemented the same as on *BSD | |
649 | // systems. Linux version implements a "port hijacking" prevention | |
650 | // mechanism, limiting processes wanting to bind to an already existing | |
651 | // addr:port to have the same effective UID as the first who bound it. What | |
652 | // this meant for us was that the daemon ran as one user and when for | |
653 | // instance mDNSClientPosix was executed by another user, it wasn't allowed | |
654 | // to bind to the socket. Our suggestion was to switch the order in which | |
655 | // SO_REUSEPORT and SO_REUSEADDR was tested so that SO_REUSEADDR stays on | |
656 | // top and SO_REUSEPORT to be used only if SO_REUSEADDR doesn't exist. | |
657 | #if defined(SO_REUSEADDR) && !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) | |
83fb1e36 | 658 | err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); |
9f221bca A |
659 | #elif defined(SO_REUSEPORT) |
660 | err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); | |
83fb1e36 A |
661 | #else |
662 | #error This platform has no way to avoid address busy errors on multicast. | |
663 | #endif | |
664 | if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } | |
95d7a4a3 A |
665 | |
666 | // Enable inbound packets on IFEF_AWDL interface. | |
667 | // Only done for multicast sockets, since we don't expect unicast socket operations | |
668 | // on the IFEF_AWDL interface. Operation is a no-op for other interface types. | |
669 | #ifndef SO_RECV_ANYIF | |
670 | #define SO_RECV_ANYIF 0x1104 /* unrestricted inbound processing */ | |
671 | #endif | |
672 | if (setsockopt(*sktPtr, SOL_SOCKET, SO_RECV_ANYIF, &kOn, sizeof(kOn)) < 0) perror("setsockopt - SO_RECV_ANYIF"); | |
83fb1e36 A |
673 | } |
674 | ||
675 | // We want to receive destination addresses and interface identifiers. | |
676 | if (intfAddr->sa_family == AF_INET) | |
677 | { | |
678 | struct ip_mreq imr; | |
679 | struct sockaddr_in bindAddr; | |
680 | if (err == 0) | |
681 | { | |
682 | #if defined(IP_PKTINFO) // Linux | |
683 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); | |
684 | if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } | |
685 | #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris | |
686 | #if defined(IP_RECVDSTADDR) | |
687 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); | |
688 | if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } | |
689 | #endif | |
690 | #if defined(IP_RECVIF) | |
691 | if (err == 0) | |
692 | { | |
693 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); | |
694 | if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } | |
695 | } | |
696 | #endif | |
697 | #else | |
698 | #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts | |
699 | #endif | |
700 | } | |
701 | #if defined(IP_RECVTTL) // Linux | |
702 | if (err == 0) | |
703 | { | |
704 | setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); | |
705 | // We no longer depend on being able to get the received TTL, so don't worry if the option fails | |
706 | } | |
707 | #endif | |
708 | ||
709 | // Add multicast group membership on this interface | |
710 | if (err == 0 && JoinMulticastGroup) | |
711 | { | |
712 | imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; | |
713 | imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; | |
714 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); | |
715 | if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } | |
716 | } | |
717 | ||
718 | // Specify outgoing interface too | |
719 | if (err == 0 && JoinMulticastGroup) | |
720 | { | |
721 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); | |
722 | if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } | |
723 | } | |
724 | ||
725 | // Per the mDNS spec, send unicast packets with TTL 255 | |
726 | if (err == 0) | |
727 | { | |
728 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
729 | if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } | |
730 | } | |
731 | ||
732 | // and multicast packets with TTL 255 too | |
733 | // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. | |
734 | if (err == 0) | |
735 | { | |
736 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); | |
737 | if (err < 0 && errno == EINVAL) | |
738 | err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
739 | if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } | |
740 | } | |
741 | ||
742 | // And start listening for packets | |
743 | if (err == 0) | |
744 | { | |
745 | bindAddr.sin_family = AF_INET; | |
746 | bindAddr.sin_port = port.NotAnInteger; | |
747 | bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket | |
748 | err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); | |
749 | if (err < 0) { err = errno; perror("bind"); fflush(stderr); } | |
750 | } | |
751 | } // endif (intfAddr->sa_family == AF_INET) | |
c9b9ae52 | 752 | |
8e92c31c | 753 | #if HAVE_IPV6 |
83fb1e36 A |
754 | else if (intfAddr->sa_family == AF_INET6) |
755 | { | |
756 | struct ipv6_mreq imr6; | |
757 | struct sockaddr_in6 bindAddr6; | |
758 | #if defined(IPV6_PKTINFO) | |
759 | if (err == 0) | |
760 | { | |
761 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_PKTINFO, &kOn, sizeof(kOn)); | |
762 | if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } | |
763 | } | |
764 | #else | |
765 | #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts | |
766 | #endif | |
767 | #if defined(IPV6_HOPLIMIT) | |
768 | if (err == 0) | |
769 | { | |
770 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_HOPLIMIT, &kOn, sizeof(kOn)); | |
771 | if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } | |
772 | } | |
773 | #endif | |
774 | ||
775 | // Add multicast group membership on this interface | |
776 | if (err == 0 && JoinMulticastGroup) | |
777 | { | |
778 | imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; | |
779 | imr6.ipv6mr_interface = interfaceIndex; | |
780 | //LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); | |
781 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); | |
782 | if (err < 0) | |
783 | { | |
784 | err = errno; | |
785 | verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); | |
786 | perror("setsockopt - IPV6_JOIN_GROUP"); | |
787 | } | |
788 | } | |
789 | ||
790 | // Specify outgoing interface too | |
791 | if (err == 0 && JoinMulticastGroup) | |
792 | { | |
793 | u_int multicast_if = interfaceIndex; | |
794 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); | |
795 | if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } | |
796 | } | |
797 | ||
798 | // We want to receive only IPv6 packets on this socket. | |
799 | // Without this option, we may get IPv4 addresses as mapped addresses. | |
800 | if (err == 0) | |
801 | { | |
802 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); | |
803 | if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } | |
804 | } | |
805 | ||
806 | // Per the mDNS spec, send unicast packets with TTL 255 | |
807 | if (err == 0) | |
808 | { | |
809 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
810 | if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } | |
811 | } | |
812 | ||
813 | // and multicast packets with TTL 255 too | |
814 | // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. | |
815 | if (err == 0) | |
816 | { | |
817 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); | |
818 | if (err < 0 && errno == EINVAL) | |
819 | err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); | |
820 | if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } | |
821 | } | |
822 | ||
823 | // And start listening for packets | |
824 | if (err == 0) | |
825 | { | |
826 | mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); | |
7f0064bd | 827 | #ifndef NOT_HAVE_SA_LEN |
83fb1e36 | 828 | bindAddr6.sin6_len = sizeof(bindAddr6); |
7f0064bd | 829 | #endif |
83fb1e36 A |
830 | bindAddr6.sin6_family = AF_INET6; |
831 | bindAddr6.sin6_port = port.NotAnInteger; | |
832 | bindAddr6.sin6_flowinfo = 0; | |
833 | bindAddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket | |
834 | bindAddr6.sin6_scope_id = 0; | |
835 | err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); | |
836 | if (err < 0) { err = errno; perror("bind"); fflush(stderr); } | |
837 | } | |
838 | } // endif (intfAddr->sa_family == AF_INET6) | |
c9b9ae52 A |
839 | #endif |
840 | ||
83fb1e36 A |
841 | // Set the socket to non-blocking. |
842 | if (err == 0) | |
843 | { | |
844 | err = fcntl(*sktPtr, F_GETFL, 0); | |
845 | if (err < 0) err = errno; | |
846 | else | |
847 | { | |
848 | err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); | |
849 | if (err < 0) err = errno; | |
850 | } | |
851 | } | |
852 | ||
853 | // Clean up | |
e0815622 A |
854 | if (err != 0 && *sktPtr != -1) |
855 | { | |
856 | int rv; | |
857 | rv = close(*sktPtr); | |
858 | assert(rv == 0); | |
859 | *sktPtr = -1; | |
860 | } | |
83fb1e36 A |
861 | assert((err == 0) == (*sktPtr != -1)); |
862 | return err; | |
863 | } | |
c9b9ae52 A |
864 | |
865 | // Creates a PosixNetworkInterface for the interface whose IP address is | |
866 | // intfAddr and whose name is intfName and registers it with mDNS core. | |
7f0064bd | 867 | mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask, const char *intfName, int intfIndex) |
83fb1e36 A |
868 | { |
869 | int err = 0; | |
870 | PosixNetworkInterface *intf; | |
871 | PosixNetworkInterface *alias = NULL; | |
872 | ||
873 | assert(m != NULL); | |
874 | assert(intfAddr != NULL); | |
875 | assert(intfName != NULL); | |
876 | assert(intfMask != NULL); | |
877 | ||
878 | // Allocate the interface structure itself. | |
9f221bca | 879 | intf = (PosixNetworkInterface*)calloc(1, sizeof(*intf)); |
83fb1e36 A |
880 | if (intf == NULL) { assert(0); err = ENOMEM; } |
881 | ||
882 | // And make a copy of the intfName. | |
883 | if (err == 0) | |
884 | { | |
885 | intf->intfName = strdup(intfName); | |
886 | if (intf->intfName == NULL) { assert(0); err = ENOMEM; } | |
887 | } | |
888 | ||
889 | if (err == 0) | |
890 | { | |
891 | // Set up the fields required by the mDNS core. | |
892 | SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); | |
893 | SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL); | |
894 | ||
895 | //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask); | |
896 | strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname)); | |
897 | intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0; | |
898 | intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; | |
899 | intf->coreIntf.McastTxRx = mDNStrue; | |
900 | ||
901 | // Set up the extra fields in PosixNetworkInterface. | |
902 | assert(intf->intfName != NULL); // intf->intfName already set up above | |
903 | intf->index = intfIndex; | |
904 | intf->multicastSocket4 = -1; | |
8e92c31c | 905 | #if HAVE_IPV6 |
83fb1e36 | 906 | intf->multicastSocket6 = -1; |
8e92c31c | 907 | #endif |
83fb1e36 A |
908 | alias = SearchForInterfaceByName(m, intf->intfName); |
909 | if (alias == NULL) alias = intf; | |
910 | intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; | |
911 | ||
912 | if (alias != intf) | |
913 | debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip); | |
914 | } | |
915 | ||
916 | // Set up the multicast socket | |
917 | if (err == 0) | |
918 | { | |
919 | if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET) | |
920 | err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4); | |
8e92c31c | 921 | #if HAVE_IPV6 |
83fb1e36 A |
922 | else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6) |
923 | err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6); | |
c9b9ae52 | 924 | #endif |
83fb1e36 A |
925 | } |
926 | ||
f9f18293 A |
927 | // If interface is a direct link, address record will be marked as kDNSRecordTypeKnownUnique |
928 | // and skip the probe phase of the probe/announce packet sequence. | |
929 | intf->coreIntf.DirectLink = mDNSfalse; | |
b8d5688b | 930 | #ifdef DIRECTLINK_INTERFACE_NAME |
9f221bca A |
931 | if (strcmp(intfName, STRINGIFY(DIRECTLINK_INTERFACE_NAME)) == 0) |
932 | intf->coreIntf.DirectLink = mDNStrue; | |
b8d5688b | 933 | #endif |
9f221bca | 934 | intf->coreIntf.SupportsUnicastMDNSResponse = mDNStrue; |
f9f18293 | 935 | |
83fb1e36 A |
936 | // The interface is all ready to go, let's register it with the mDNS core. |
937 | if (err == 0) | |
938 | err = mDNS_RegisterInterface(m, &intf->coreIntf, mDNSfalse); | |
939 | ||
940 | // Clean up. | |
941 | if (err == 0) | |
942 | { | |
943 | num_registered_interfaces++; | |
944 | debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip); | |
945 | if (gMDNSPlatformPosixVerboseLevel > 0) | |
946 | fprintf(stderr, "Registered interface %s\n", intf->intfName); | |
947 | } | |
948 | else | |
949 | { | |
950 | // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL. | |
951 | debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err); | |
952 | if (intf) { FreePosixNetworkInterface(intf); intf = NULL; } | |
953 | } | |
954 | ||
955 | assert((err == 0) == (intf != NULL)); | |
956 | ||
957 | return err; | |
958 | } | |
c9b9ae52 | 959 | |
8e92c31c | 960 | // Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one. |
7f0064bd | 961 | mDNSlocal int SetupInterfaceList(mDNS *const m) |
83fb1e36 A |
962 | { |
963 | mDNSBool foundav4 = mDNSfalse; | |
964 | int err = 0; | |
965 | struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); | |
966 | struct ifi_info *firstLoopback = NULL; | |
c9b9ae52 | 967 | |
83fb1e36 A |
968 | assert(m != NULL); |
969 | debugf("SetupInterfaceList"); | |
c9b9ae52 | 970 | |
83fb1e36 | 971 | if (intfList == NULL) err = ENOENT; |
c9b9ae52 | 972 | |
8e92c31c | 973 | #if HAVE_IPV6 |
83fb1e36 A |
974 | if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ |
975 | { | |
976 | struct ifi_info **p = &intfList; | |
977 | while (*p) p = &(*p)->ifi_next; | |
978 | *p = get_ifi_info(AF_INET6, mDNStrue); | |
979 | } | |
c9b9ae52 A |
980 | #endif |
981 | ||
83fb1e36 A |
982 | if (err == 0) |
983 | { | |
984 | struct ifi_info *i = intfList; | |
985 | while (i) | |
986 | { | |
987 | if ( ((i->ifi_addr->sa_family == AF_INET) | |
8e92c31c | 988 | #if HAVE_IPV6 |
83fb1e36 | 989 | || (i->ifi_addr->sa_family == AF_INET6) |
c9b9ae52 | 990 | #endif |
83fb1e36 A |
991 | ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT)) |
992 | { | |
993 | if (i->ifi_flags & IFF_LOOPBACK) | |
994 | { | |
995 | if (firstLoopback == NULL) | |
996 | firstLoopback = i; | |
997 | } | |
998 | else | |
999 | { | |
1000 | if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0) | |
1001 | if (i->ifi_addr->sa_family == AF_INET) | |
1002 | foundav4 = mDNStrue; | |
1003 | } | |
1004 | } | |
1005 | i = i->ifi_next; | |
1006 | } | |
1007 | ||
1008 | // If we found no normal interfaces but we did find a loopback interface, register the | |
1009 | // loopback interface. This allows self-discovery if no interfaces are configured. | |
1010 | // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. | |
1011 | // In the interim, we skip loopback interface only if we found at least one v4 interface to use | |
1012 | // if ((m->HostInterfaces == NULL) && (firstLoopback != NULL)) | |
1013 | if (!foundav4 && firstLoopback) | |
1014 | (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index); | |
1015 | } | |
1016 | ||
1017 | // Clean up. | |
1018 | if (intfList != NULL) free_ifi_info(intfList); | |
95d7a4a3 A |
1019 | |
1020 | // Clean up any interfaces that have been hanging around on the RecentInterfaces list for more than a minute | |
1021 | PosixNetworkInterface **ri = &gRecentInterfaces; | |
1022 | const mDNSs32 utc = mDNSPlatformUTC(); | |
1023 | while (*ri) | |
1024 | { | |
1025 | PosixNetworkInterface *pi = *ri; | |
1026 | if (utc - pi->LastSeen < 60) ri = (PosixNetworkInterface **)&pi->coreIntf.next; | |
1027 | else { *ri = (PosixNetworkInterface *)pi->coreIntf.next; free(pi); } | |
1028 | } | |
1029 | ||
83fb1e36 A |
1030 | return err; |
1031 | } | |
c9b9ae52 | 1032 | |
8e92c31c A |
1033 | #if USES_NETLINK |
1034 | ||
1035 | // See <http://www.faqs.org/rfcs/rfc3549.html> for a description of NetLink | |
1036 | ||
1037 | // Open a socket that will receive interface change notifications | |
67c8f8a1 | 1038 | mDNSlocal mStatus OpenIfNotifySocket(int *pFD) |
83fb1e36 A |
1039 | { |
1040 | mStatus err = mStatus_NoError; | |
1041 | struct sockaddr_nl snl; | |
1042 | int sock; | |
1043 | int ret; | |
1044 | ||
1045 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
1046 | if (sock < 0) | |
1047 | return errno; | |
1048 | ||
1049 | // Configure read to be non-blocking because inbound msg size is not known in advance | |
1050 | (void) fcntl(sock, F_SETFL, O_NONBLOCK); | |
1051 | ||
1052 | /* Subscribe the socket to Link & IP addr notifications. */ | |
1053 | mDNSPlatformMemZero(&snl, sizeof snl); | |
1054 | snl.nl_family = AF_NETLINK; | |
1055 | snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; | |
1056 | ret = bind(sock, (struct sockaddr *) &snl, sizeof snl); | |
1057 | if (0 == ret) | |
1058 | *pFD = sock; | |
1059 | else | |
1060 | err = errno; | |
1061 | ||
1062 | return err; | |
1063 | } | |
8e92c31c A |
1064 | |
1065 | #if MDNS_DEBUGMSGS | |
83fb1e36 A |
1066 | mDNSlocal void PrintNetLinkMsg(const struct nlmsghdr *pNLMsg) |
1067 | { | |
1068 | const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; | |
1069 | const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; | |
1070 | ||
1071 | printf("nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, | |
1072 | pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[pNLMsg->nlmsg_type] : kNLRtMsgTypes[pNLMsg->nlmsg_type - RTM_BASE], | |
1073 | pNLMsg->nlmsg_flags); | |
1074 | ||
1075 | if (RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) | |
1076 | { | |
1077 | struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA(pNLMsg); | |
1078 | printf("ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, | |
1079 | pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); | |
1080 | ||
1081 | } | |
1082 | else if (RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) | |
1083 | { | |
1084 | struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA(pNLMsg); | |
1085 | printf("ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, | |
1086 | pIfAddr->ifa_index, pIfAddr->ifa_flags); | |
1087 | } | |
1088 | printf("\n"); | |
1089 | } | |
8e92c31c A |
1090 | #endif |
1091 | ||
83fb1e36 | 1092 | mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) |
8e92c31c A |
1093 | // Read through the messages on sd and if any indicate that any interface records should |
1094 | // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. | |
83fb1e36 A |
1095 | { |
1096 | ssize_t readCount; | |
1097 | char buff[4096]; | |
1098 | struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff; | |
1099 | mDNSu32 result = 0; | |
1100 | ||
1101 | // The structure here is more complex than it really ought to be because, | |
1102 | // unfortunately, there's no good way to size a buffer in advance large | |
1103 | // enough to hold all pending data and so avoid message fragmentation. | |
1104 | // (Note that FIONREAD is not supported on AF_NETLINK.) | |
1105 | ||
1106 | readCount = read(sd, buff, sizeof buff); | |
1107 | while (1) | |
1108 | { | |
1109 | // Make sure we've got an entire nlmsghdr in the buffer, and payload, too. | |
1110 | // If not, discard already-processed messages in buffer and read more data. | |
1111 | if (((char*) &pNLMsg[1] > (buff + readCount)) || // i.e. *pNLMsg extends off end of buffer | |
1112 | ((char*) pNLMsg + pNLMsg->nlmsg_len > (buff + readCount))) | |
1113 | { | |
1114 | if (buff < (char*) pNLMsg) // we have space to shuffle | |
1115 | { | |
1116 | // discard processed data | |
1117 | readCount -= ((char*) pNLMsg - buff); | |
1118 | memmove(buff, pNLMsg, readCount); | |
1119 | pNLMsg = (struct nlmsghdr*) buff; | |
1120 | ||
1121 | // read more data | |
1122 | readCount += read(sd, buff + readCount, sizeof buff - readCount); | |
1123 | continue; // spin around and revalidate with new readCount | |
1124 | } | |
1125 | else | |
1126 | break; // Otherwise message does not fit in buffer | |
1127 | } | |
8e92c31c A |
1128 | |
1129 | #if MDNS_DEBUGMSGS | |
83fb1e36 | 1130 | PrintNetLinkMsg(pNLMsg); |
8e92c31c A |
1131 | #endif |
1132 | ||
83fb1e36 A |
1133 | // Process the NetLink message |
1134 | if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) | |
1135 | result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index; | |
1136 | else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) | |
1137 | result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index; | |
1138 | ||
1139 | // Advance pNLMsg to the next message in the buffer | |
1140 | if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) | |
1141 | { | |
1142 | ssize_t len = readCount - ((char*)pNLMsg - buff); | |
1143 | pNLMsg = NLMSG_NEXT(pNLMsg, len); | |
1144 | } | |
1145 | else | |
1146 | break; // all done! | |
1147 | } | |
1148 | ||
1149 | return result; | |
1150 | } | |
8e92c31c A |
1151 | |
1152 | #else // USES_NETLINK | |
1153 | ||
1154 | // Open a socket that will receive interface change notifications | |
67c8f8a1 | 1155 | mDNSlocal mStatus OpenIfNotifySocket(int *pFD) |
83fb1e36 A |
1156 | { |
1157 | *pFD = socket(AF_ROUTE, SOCK_RAW, 0); | |
8e92c31c | 1158 | |
83fb1e36 A |
1159 | if (*pFD < 0) |
1160 | return mStatus_UnknownErr; | |
8e92c31c | 1161 | |
83fb1e36 A |
1162 | // Configure read to be non-blocking because inbound msg size is not known in advance |
1163 | (void) fcntl(*pFD, F_SETFL, O_NONBLOCK); | |
8e92c31c | 1164 | |
83fb1e36 A |
1165 | return mStatus_NoError; |
1166 | } | |
8e92c31c A |
1167 | |
1168 | #if MDNS_DEBUGMSGS | |
83fb1e36 A |
1169 | mDNSlocal void PrintRoutingSocketMsg(const struct ifa_msghdr *pRSMsg) |
1170 | { | |
1171 | const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", | |
1172 | "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", | |
1173 | "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; | |
8e92c31c | 1174 | |
83fb1e36 | 1175 | int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index; |
8e92c31c | 1176 | |
83fb1e36 A |
1177 | printf("ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[pRSMsg->ifam_type], index); |
1178 | } | |
8e92c31c A |
1179 | #endif |
1180 | ||
83fb1e36 | 1181 | mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) |
8e92c31c A |
1182 | // Read through the messages on sd and if any indicate that any interface records should |
1183 | // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. | |
83fb1e36 A |
1184 | { |
1185 | ssize_t readCount; | |
1186 | char buff[4096]; | |
1187 | struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff; | |
1188 | mDNSu32 result = 0; | |
8e92c31c | 1189 | |
83fb1e36 A |
1190 | readCount = read(sd, buff, sizeof buff); |
1191 | if (readCount < (ssize_t) sizeof(struct ifa_msghdr)) | |
1192 | return mStatus_UnsupportedErr; // cannot decipher message | |
8e92c31c A |
1193 | |
1194 | #if MDNS_DEBUGMSGS | |
83fb1e36 | 1195 | PrintRoutingSocketMsg(pRSMsg); |
8e92c31c A |
1196 | #endif |
1197 | ||
83fb1e36 A |
1198 | // Process the message |
1199 | if (pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR || | |
1200 | pRSMsg->ifam_type == RTM_IFINFO) | |
1201 | { | |
1202 | if (pRSMsg->ifam_type == RTM_IFINFO) | |
1203 | result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; | |
1204 | else | |
1205 | result |= 1 << pRSMsg->ifam_index; | |
1206 | } | |
8e92c31c | 1207 | |
83fb1e36 A |
1208 | return result; |
1209 | } | |
8e92c31c A |
1210 | |
1211 | #endif // USES_NETLINK | |
1212 | ||
1213 | // Called when data appears on interface change notification socket | |
67c8f8a1 | 1214 | mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context) |
83fb1e36 A |
1215 | { |
1216 | IfChangeRec *pChgRec = (IfChangeRec*) context; | |
1217 | fd_set readFDs; | |
1218 | mDNSu32 changedInterfaces = 0; | |
1219 | struct timeval zeroTimeout = { 0, 0 }; | |
1220 | ||
1221 | (void)fd; // Unused | |
1222 | (void)filter; // Unused | |
1223 | ||
1224 | FD_ZERO(&readFDs); | |
1225 | FD_SET(pChgRec->NotifySD, &readFDs); | |
1226 | ||
1227 | do | |
1228 | { | |
1229 | changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD); | |
1230 | } | |
1231 | while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); | |
1232 | ||
1233 | // Currently we rebuild the entire interface list whenever any interface change is | |
1234 | // detected. If this ever proves to be a performance issue in a multi-homed | |
1235 | // configuration, more care should be paid to changedInterfaces. | |
1236 | if (changedInterfaces) | |
1237 | mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS); | |
1238 | } | |
8e92c31c A |
1239 | |
1240 | // Register with either a Routing Socket or RtNetLink to listen for interface changes. | |
7f0064bd | 1241 | mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) |
83fb1e36 A |
1242 | { |
1243 | mStatus err; | |
1244 | IfChangeRec *pChgRec; | |
8e92c31c | 1245 | |
83fb1e36 A |
1246 | pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate(sizeof *pChgRec); |
1247 | if (pChgRec == NULL) | |
1248 | return mStatus_NoMemoryErr; | |
8e92c31c | 1249 | |
83fb1e36 A |
1250 | pChgRec->mDNS = m; |
1251 | err = OpenIfNotifySocket(&pChgRec->NotifySD); | |
1252 | if (err == 0) | |
1253 | err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); | |
8e92c31c | 1254 | |
83fb1e36 A |
1255 | return err; |
1256 | } | |
8e92c31c A |
1257 | |
1258 | // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. | |
1259 | // If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- | |
1260 | // we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. | |
7f0064bd | 1261 | mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) |
83fb1e36 A |
1262 | { |
1263 | int err; | |
1264 | int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
1265 | struct sockaddr_in s5353; | |
1266 | s5353.sin_family = AF_INET; | |
1267 | s5353.sin_port = MulticastDNSPort.NotAnInteger; | |
1268 | s5353.sin_addr.s_addr = 0; | |
1269 | err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); | |
1270 | close(s); | |
1271 | if (err) debugf("No unicast UDP responses"); | |
1272 | else debugf("Unicast UDP responses okay"); | |
1273 | return(err == 0); | |
1274 | } | |
8e92c31c | 1275 | |
c9b9ae52 A |
1276 | // mDNS core calls this routine to initialise the platform-specific data. |
1277 | mDNSexport mStatus mDNSPlatformInit(mDNS *const m) | |
83fb1e36 A |
1278 | { |
1279 | int err = 0; | |
1280 | struct sockaddr sa; | |
1281 | assert(m != NULL); | |
c9b9ae52 | 1282 | |
83fb1e36 | 1283 | if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; |
8e92c31c | 1284 | |
83fb1e36 | 1285 | // Tell mDNS core the names of this machine. |
c9b9ae52 | 1286 | |
83fb1e36 A |
1287 | // Set up the nice label |
1288 | m->nicelabel.c[0] = 0; | |
1289 | GetUserSpecifiedFriendlyComputerName(&m->nicelabel); | |
1290 | if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer"); | |
c9b9ae52 | 1291 | |
83fb1e36 A |
1292 | // Set up the RFC 1034-compliant label |
1293 | m->hostlabel.c[0] = 0; | |
1294 | GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); | |
1295 | if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); | |
c9b9ae52 | 1296 | |
83fb1e36 | 1297 | mDNS_SetFQDN(m); |
c9b9ae52 | 1298 | |
83fb1e36 A |
1299 | sa.sa_family = AF_INET; |
1300 | m->p->unicastSocket4 = -1; | |
1301 | if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); | |
8e92c31c | 1302 | #if HAVE_IPV6 |
83fb1e36 A |
1303 | sa.sa_family = AF_INET6; |
1304 | m->p->unicastSocket6 = -1; | |
1305 | if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); | |
8e92c31c A |
1306 | #endif |
1307 | ||
83fb1e36 A |
1308 | // Tell mDNS core about the network interfaces on this machine. |
1309 | if (err == mStatus_NoError) err = SetupInterfaceList(m); | |
1310 | ||
1311 | // Tell mDNS core about DNS Servers | |
1312 | mDNS_Lock(m); | |
1313 | if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE); | |
1314 | mDNS_Unlock(m); | |
1315 | ||
1316 | if (err == mStatus_NoError) | |
1317 | { | |
1318 | err = WatchForInterfaceChange(m); | |
1319 | // Failure to observe interface changes is non-fatal. | |
1320 | if (err != mStatus_NoError) | |
1321 | { | |
1322 | fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err); | |
1323 | err = mStatus_NoError; | |
1324 | } | |
1325 | } | |
1326 | ||
1327 | // We don't do asynchronous initialization on the Posix platform, so by the time | |
1328 | // we get here the setup will already have succeeded or failed. If it succeeded, | |
1329 | // we should just call mDNSCoreInitComplete() immediately. | |
1330 | if (err == mStatus_NoError) | |
1331 | mDNSCoreInitComplete(m, mStatus_NoError); | |
1332 | ||
1333 | return PosixErrorToStatus(err); | |
1334 | } | |
c9b9ae52 A |
1335 | |
1336 | // mDNS core calls this routine to clean up the platform-specific data. | |
1337 | // In our case all we need to do is to tear down every network interface. | |
1338 | mDNSexport void mDNSPlatformClose(mDNS *const m) | |
83fb1e36 | 1339 | { |
e0815622 | 1340 | int rv; |
83fb1e36 A |
1341 | assert(m != NULL); |
1342 | ClearInterfaceList(m); | |
e0815622 A |
1343 | if (m->p->unicastSocket4 != -1) |
1344 | { | |
1345 | rv = close(m->p->unicastSocket4); | |
1346 | assert(rv == 0); | |
1347 | } | |
8e92c31c | 1348 | #if HAVE_IPV6 |
e0815622 A |
1349 | if (m->p->unicastSocket6 != -1) |
1350 | { | |
1351 | rv = close(m->p->unicastSocket6); | |
1352 | assert(rv == 0); | |
1353 | } | |
8e92c31c | 1354 | #endif |
83fb1e36 | 1355 | } |
c9b9ae52 | 1356 | |
95d7a4a3 A |
1357 | // This is used internally by InterfaceChangeCallback. |
1358 | // It's also exported so that the Standalone Responder (mDNSResponderPosix) | |
1359 | // can call it in response to a SIGHUP (mainly for debugging purposes). | |
7cb34e5c | 1360 | mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) |
83fb1e36 A |
1361 | { |
1362 | int err; | |
95d7a4a3 A |
1363 | // This is a pretty heavyweight way to process interface changes -- |
1364 | // destroying the entire interface list and then making fresh one from scratch. | |
1365 | // We should make it like the OS X version, which leaves unchanged interfaces alone. | |
83fb1e36 A |
1366 | ClearInterfaceList(m); |
1367 | err = SetupInterfaceList(m); | |
1368 | return PosixErrorToStatus(err); | |
1369 | } | |
c9b9ae52 A |
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) | |
83fb1e36 A |
1381 | { |
1382 | (void) m; // Unused | |
1383 | } | |
c9b9ae52 A |
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) | |
83fb1e36 A |
1388 | { |
1389 | (void) m; // Unused | |
1390 | } | |
c9b9ae52 A |
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. | |
67c8f8a1 | 1398 | mDNSexport void mDNSPlatformStrCopy(void *dst, const void *src) |
83fb1e36 | 1399 | { |
9f221bca A |
1400 | strcpy((char *)dst, (const char *)src); |
1401 | } | |
1402 | ||
1403 | mDNSexport mDNSu32 mDNSPlatformStrLCopy(void *dst, const void *src, mDNSu32 len) | |
1404 | { | |
1405 | #if HAVE_STRLCPY | |
1406 | return ((mDNSu32)strlcpy((char *)dst, (const char *)src, len)); | |
1407 | #else | |
1408 | size_t srcLen; | |
1409 | ||
1410 | srcLen = strlen((const char *)src); | |
1411 | if (srcLen < len) | |
1412 | { | |
1413 | memcpy(dst, src, srcLen + 1); | |
1414 | } | |
1415 | else if (len > 0) | |
1416 | { | |
1417 | memcpy(dst, src, len - 1); | |
1418 | ((char *)dst)[len - 1] = '\0'; | |
1419 | } | |
1420 | ||
1421 | return ((mDNSu32)srcLen); | |
1422 | #endif | |
83fb1e36 | 1423 | } |
c9b9ae52 A |
1424 | |
1425 | // mDNS core calls this routine to get the length of a C string. | |
1426 | // On the Posix platform this maps directly to the ANSI C strlen. | |
1427 | mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) | |
83fb1e36 | 1428 | { |
9f221bca | 1429 | return strlen((const char*)src); |
83fb1e36 | 1430 | } |
c9b9ae52 A |
1431 | |
1432 | // mDNS core calls this routine to copy memory. | |
1433 | // On the Posix platform this maps directly to the ANSI C memcpy. | |
67c8f8a1 | 1434 | mDNSexport void mDNSPlatformMemCopy(void *dst, const void *src, mDNSu32 len) |
83fb1e36 A |
1435 | { |
1436 | memcpy(dst, src, len); | |
1437 | } | |
c9b9ae52 A |
1438 | |
1439 | // mDNS core calls this routine to test whether blocks of memory are byte-for-byte | |
1440 | // identical. On the Posix platform this is a simple wrapper around ANSI C memcmp. | |
67c8f8a1 | 1441 | mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) |
83fb1e36 A |
1442 | { |
1443 | return memcmp(dst, src, len) == 0; | |
1444 | } | |
1445 | ||
1446 | // If the caller wants to know the exact return of memcmp, then use this instead | |
1447 | // of mDNSPlatformMemSame | |
1448 | mDNSexport int mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len) | |
1449 | { | |
1450 | return (memcmp(dst, src, len)); | |
1451 | } | |
1452 | ||
1453 | mDNSexport void mDNSPlatformQsort(void *base, int nel, int width, int (*compar)(const void *, const void *)) | |
1454 | { | |
1455 | return (qsort(base, nel, width, compar)); | |
1456 | } | |
1457 | ||
1458 | // DNSSEC stub functions | |
1459 | mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) | |
1460 | { | |
1461 | (void)m; | |
1462 | (void)dv; | |
1463 | (void)q; | |
1464 | } | |
1465 | ||
1466 | mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) | |
1467 | { | |
1468 | (void)m; | |
1469 | (void)crlist; | |
1470 | (void)negcr; | |
1471 | (void)rcode; | |
1472 | return mDNSfalse; | |
1473 | } | |
c9b9ae52 | 1474 | |
51601d48 A |
1475 | mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) |
1476 | { | |
1477 | (void)m; | |
1478 | (void)action; | |
1479 | (void)type; | |
1480 | (void)value; | |
1481 | } | |
1482 | ||
1483 | // Proxy stub functions | |
1484 | mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) | |
1485 | { | |
1486 | (void) q; | |
1487 | (void) h; | |
1488 | (void) msg; | |
1489 | (void) ptr; | |
1490 | (void) limit; | |
1491 | ||
1492 | return ptr; | |
1493 | } | |
1494 | ||
1495 | mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf) | |
1496 | { | |
1497 | (void) m; | |
1498 | (void) IpIfArr; | |
1499 | (void) OpIf; | |
1500 | } | |
1501 | ||
1502 | mDNSexport void DNSProxyTerminate(mDNS *const m) | |
1503 | { | |
1504 | (void) m; | |
1505 | } | |
1506 | ||
c9b9ae52 A |
1507 | // mDNS core calls this routine to clear blocks of memory. |
1508 | // On the Posix platform this is a simple wrapper around ANSI C memset. | |
67c8f8a1 | 1509 | mDNSexport void mDNSPlatformMemZero(void *dst, mDNSu32 len) |
83fb1e36 A |
1510 | { |
1511 | memset(dst, 0, len); | |
1512 | } | |
c9b9ae52 A |
1513 | |
1514 | mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); } | |
1515 | mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); } | |
1516 | ||
7f0064bd | 1517 | mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) |
83fb1e36 A |
1518 | { |
1519 | struct timeval tv; | |
1520 | gettimeofday(&tv, NULL); | |
1521 | return(tv.tv_usec); | |
1522 | } | |
7f0064bd | 1523 | |
83fb1e36 | 1524 | mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; |
c9b9ae52 | 1525 | |
7f0064bd | 1526 | mDNSexport mStatus mDNSPlatformTimeInit(void) |
83fb1e36 A |
1527 | { |
1528 | // No special setup is required on Posix -- we just use gettimeofday(); | |
1529 | // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time | |
1530 | // We should find a better way to do this | |
1531 | return(mStatus_NoError); | |
1532 | } | |
c9b9ae52 | 1533 | |
7f0064bd | 1534 | mDNSexport mDNSs32 mDNSPlatformRawTime() |
83fb1e36 A |
1535 | { |
1536 | struct timeval tv; | |
1537 | gettimeofday(&tv, NULL); | |
1538 | // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) | |
1539 | // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) | |
1540 | // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result | |
1541 | // 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. | |
1542 | // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) | |
1543 | // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). | |
1544 | return((tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625)); | |
1545 | } | |
c9b9ae52 | 1546 | |
8e92c31c | 1547 | mDNSexport mDNSs32 mDNSPlatformUTC(void) |
83fb1e36 A |
1548 | { |
1549 | return time(NULL); | |
1550 | } | |
8e92c31c | 1551 | |
1f519c61 | 1552 | mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) |
83fb1e36 A |
1553 | { |
1554 | (void) m; | |
1555 | (void) InterfaceID; | |
1556 | (void) EthAddr; | |
1557 | (void) IPAddr; | |
1558 | (void) iteration; | |
1559 | } | |
1f519c61 | 1560 | |
9f221bca | 1561 | mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID) |
83fb1e36 A |
1562 | { |
1563 | (void) rr; | |
9f221bca | 1564 | (void) InterfaceID; |
83fb1e36 A |
1565 | |
1566 | return 1; | |
1567 | } | |
1568 | ||
1569 | mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf) | |
1570 | { | |
1571 | (void) q; | |
1572 | (void) intf; | |
1573 | ||
1574 | return 1; | |
1575 | } | |
1576 | ||
1577 | // Used for debugging purposes. For now, just set the buffer to zero | |
1578 | mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize) | |
1579 | { | |
1580 | (void) te; | |
1581 | if (bufsize) buf[0] = 0; | |
1582 | } | |
1583 | ||
1584 | mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) | |
1585 | { | |
1586 | (void) sadd; // Unused | |
1587 | (void) dadd; // Unused | |
1588 | (void) lport; // Unused | |
1589 | (void) rport; // Unused | |
1590 | (void) seq; // Unused | |
1591 | (void) ack; // Unused | |
1592 | (void) win; // Unused | |
1593 | } | |
1594 | ||
1595 | mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) | |
1596 | { | |
1597 | (void) m; // Unused | |
1598 | (void) laddr; // Unused | |
1599 | (void) raddr; // Unused | |
1600 | (void) lport; // Unused | |
1601 | (void) rport; // Unused | |
1602 | (void) mti; // Unused | |
1603 | ||
1604 | return mStatus_NoError; | |
1605 | } | |
294beb6e | 1606 | |
51601d48 A |
1607 | mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr) |
1608 | { | |
1609 | (void) raddr; // Unused | |
1610 | (void) m; // Unused | |
1611 | ||
1612 | return mStatus_NoError; | |
1613 | } | |
1614 | ||
1615 | mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) | |
1616 | { | |
1617 | (void) spsaddr; // Unused | |
1618 | (void) ifname; // Unused | |
1619 | ||
1620 | return mStatus_NoError; | |
1621 | } | |
1622 | ||
9f221bca | 1623 | mDNSexport mStatus mDNSPlatformClearSPSData(void) |
f9f18293 A |
1624 | { |
1625 | return mStatus_NoError; | |
1626 | } | |
1627 | ||
9f221bca A |
1628 | mDNSexport mStatus mDNSPlatformStoreOwnerOptRecord(char *ifname, DNSMessage *msg, int length) |
1629 | { | |
1630 | (void) ifname; // Unused | |
1631 | (void) msg; // Unused | |
1632 | (void) length; // Unused | |
1633 | return mStatus_UnsupportedErr; | |
1634 | } | |
1635 | ||
51601d48 A |
1636 | mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) |
1637 | { | |
1638 | (void) sock; // unused | |
9f221bca | 1639 | |
51601d48 A |
1640 | return (mDNSu16)-1; |
1641 | } | |
1642 | ||
1643 | mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) | |
1644 | { | |
1645 | (void) InterfaceID; // unused | |
51601d48 | 1646 | |
9f221bca | 1647 | return mDNSfalse; |
51601d48 A |
1648 | } |
1649 | ||
e0815622 | 1650 | mDNSexport void mDNSPlatformSetSocktOpt(void *sock, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q) |
51601d48 | 1651 | { |
9f221bca A |
1652 | (void) sock; |
1653 | (void) transType; | |
1654 | (void) addrType; | |
51601d48 A |
1655 | (void) q; |
1656 | } | |
1657 | ||
1658 | mDNSexport mDNSs32 mDNSPlatformGetPID() | |
1659 | { | |
1660 | return 0; | |
1661 | } | |
1662 | ||
8e92c31c | 1663 | mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) |
83fb1e36 A |
1664 | { |
1665 | if (*nfds < s + 1) *nfds = s + 1; | |
1666 | FD_SET(s, readfds); | |
1667 | } | |
8e92c31c A |
1668 | |
1669 | mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout) | |
83fb1e36 A |
1670 | { |
1671 | mDNSs32 ticks; | |
1672 | struct timeval interval; | |
c9b9ae52 | 1673 | |
83fb1e36 A |
1674 | // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do |
1675 | mDNSs32 nextevent = mDNS_Execute(m); | |
c9b9ae52 | 1676 | |
83fb1e36 A |
1677 | // 2. Build our list of active file descriptors |
1678 | PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); | |
1679 | if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); | |
8e92c31c | 1680 | #if HAVE_IPV6 |
83fb1e36 | 1681 | if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); |
8e92c31c | 1682 | #endif |
83fb1e36 A |
1683 | while (info) |
1684 | { | |
1685 | if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); | |
8e92c31c | 1686 | #if HAVE_IPV6 |
83fb1e36 | 1687 | if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); |
8e92c31c | 1688 | #endif |
83fb1e36 A |
1689 | info = (PosixNetworkInterface *)(info->coreIntf.next); |
1690 | } | |
c9b9ae52 | 1691 | |
83fb1e36 A |
1692 | // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) |
1693 | ticks = nextevent - mDNS_TimeNow(m); | |
1694 | if (ticks < 1) ticks = 1; | |
1695 | interval.tv_sec = ticks >> 10; // The high 22 bits are seconds | |
1696 | interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths | |
c9b9ae52 | 1697 | |
83fb1e36 A |
1698 | // 4. If client's proposed timeout is more than what we want, then reduce it |
1699 | if (timeout->tv_sec > interval.tv_sec || | |
1700 | (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec)) | |
1701 | *timeout = interval; | |
1702 | } | |
c9b9ae52 A |
1703 | |
1704 | mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) | |
83fb1e36 A |
1705 | { |
1706 | PosixNetworkInterface *info; | |
1707 | assert(m != NULL); | |
1708 | assert(readfds != NULL); | |
1709 | info = (PosixNetworkInterface *)(m->HostInterfaces); | |
1710 | ||
1711 | if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds)) | |
1712 | { | |
1713 | FD_CLR(m->p->unicastSocket4, readfds); | |
1714 | SocketDataReady(m, NULL, m->p->unicastSocket4); | |
1715 | } | |
8e92c31c | 1716 | #if HAVE_IPV6 |
83fb1e36 A |
1717 | if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds)) |
1718 | { | |
1719 | FD_CLR(m->p->unicastSocket6, readfds); | |
1720 | SocketDataReady(m, NULL, m->p->unicastSocket6); | |
1721 | } | |
8e92c31c A |
1722 | #endif |
1723 | ||
83fb1e36 A |
1724 | while (info) |
1725 | { | |
1726 | if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds)) | |
1727 | { | |
1728 | FD_CLR(info->multicastSocket4, readfds); | |
1729 | SocketDataReady(m, info, info->multicastSocket4); | |
1730 | } | |
8e92c31c | 1731 | #if HAVE_IPV6 |
83fb1e36 A |
1732 | if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds)) |
1733 | { | |
1734 | FD_CLR(info->multicastSocket6, readfds); | |
1735 | SocketDataReady(m, info, info->multicastSocket6); | |
1736 | } | |
8e92c31c | 1737 | #endif |
83fb1e36 A |
1738 | info = (PosixNetworkInterface *)(info->coreIntf.next); |
1739 | } | |
1740 | } | |
8e92c31c A |
1741 | |
1742 | // update gMaxFD | |
83fb1e36 A |
1743 | mDNSlocal void DetermineMaxEventFD(void) |
1744 | { | |
1745 | PosixEventSource *iSource; | |
1746 | ||
1747 | gMaxFD = 0; | |
1748 | for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) | |
1749 | if (gMaxFD < iSource->fd) | |
1750 | gMaxFD = iSource->fd; | |
1751 | } | |
8e92c31c A |
1752 | |
1753 | // Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. | |
67c8f8a1 | 1754 | mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context) |
83fb1e36 A |
1755 | { |
1756 | PosixEventSource *newSource; | |
8e92c31c | 1757 | |
83fb1e36 A |
1758 | if (gEventSources.LinkOffset == 0) |
1759 | InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next)); | |
8e92c31c | 1760 | |
83fb1e36 A |
1761 | if (fd >= (int) FD_SETSIZE || fd < 0) |
1762 | return mStatus_UnsupportedErr; | |
1763 | if (callback == NULL) | |
1764 | return mStatus_BadParamErr; | |
8e92c31c | 1765 | |
83fb1e36 A |
1766 | newSource = (PosixEventSource*) malloc(sizeof *newSource); |
1767 | if (NULL == newSource) | |
1768 | return mStatus_NoMemoryErr; | |
8e92c31c | 1769 | |
83fb1e36 A |
1770 | newSource->Callback = callback; |
1771 | newSource->Context = context; | |
1772 | newSource->fd = fd; | |
8e92c31c | 1773 | |
83fb1e36 A |
1774 | AddToTail(&gEventSources, newSource); |
1775 | FD_SET(fd, &gEventFDs); | |
8e92c31c | 1776 | |
83fb1e36 A |
1777 | DetermineMaxEventFD(); |
1778 | ||
1779 | return mStatus_NoError; | |
1780 | } | |
8e92c31c A |
1781 | |
1782 | // Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to. | |
67c8f8a1 | 1783 | mStatus mDNSPosixRemoveFDFromEventLoop(int fd) |
83fb1e36 A |
1784 | { |
1785 | PosixEventSource *iSource; | |
1786 | ||
1787 | for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) | |
1788 | { | |
1789 | if (fd == iSource->fd) | |
1790 | { | |
1791 | FD_CLR(fd, &gEventFDs); | |
1792 | RemoveFromList(&gEventSources, iSource); | |
1793 | free(iSource); | |
1794 | DetermineMaxEventFD(); | |
1795 | return mStatus_NoError; | |
1796 | } | |
1797 | } | |
1798 | return mStatus_NoSuchNameErr; | |
1799 | } | |
8e92c31c A |
1800 | |
1801 | // Simply note the received signal in gEventSignals. | |
83fb1e36 A |
1802 | mDNSlocal void NoteSignal(int signum) |
1803 | { | |
1804 | sigaddset(&gEventSignals, signum); | |
1805 | } | |
8e92c31c A |
1806 | |
1807 | // Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce(). | |
67c8f8a1 | 1808 | mStatus mDNSPosixListenForSignalInEventLoop(int signum) |
83fb1e36 A |
1809 | { |
1810 | struct sigaction action; | |
1811 | mStatus err; | |
1812 | ||
1813 | mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment | |
1814 | action.sa_handler = NoteSignal; | |
1815 | err = sigaction(signum, &action, (struct sigaction*) NULL); | |
8e92c31c | 1816 | |
83fb1e36 | 1817 | sigaddset(&gEventSignalSet, signum); |
8e92c31c | 1818 | |
83fb1e36 A |
1819 | return err; |
1820 | } | |
8e92c31c A |
1821 | |
1822 | // Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce(). | |
67c8f8a1 | 1823 | mStatus mDNSPosixIgnoreSignalInEventLoop(int signum) |
83fb1e36 A |
1824 | { |
1825 | struct sigaction action; | |
1826 | mStatus err; | |
8e92c31c | 1827 | |
83fb1e36 A |
1828 | mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment |
1829 | action.sa_handler = SIG_DFL; | |
1830 | err = sigaction(signum, &action, (struct sigaction*) NULL); | |
8e92c31c | 1831 | |
83fb1e36 A |
1832 | sigdelset(&gEventSignalSet, signum); |
1833 | ||
1834 | return err; | |
1835 | } | |
8e92c31c A |
1836 | |
1837 | // Do a single pass through the attendent event sources and dispatch any found to their callbacks. | |
1838 | // Return as soon as internal timeout expires, or a signal we're listening for is received. | |
83fb1e36 A |
1839 | mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout, |
1840 | sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) | |
1841 | { | |
1842 | fd_set listenFDs = gEventFDs; | |
1843 | int fdMax = 0, numReady; | |
1844 | struct timeval timeout = *pTimeout; | |
1845 | ||
1846 | // Include the sockets that are listening to the wire in our select() set | |
1847 | mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified | |
1848 | if (fdMax < gMaxFD) | |
1849 | fdMax = gMaxFD; | |
1850 | ||
1851 | numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); | |
1852 | ||
1853 | // If any data appeared, invoke its callback | |
1854 | if (numReady > 0) | |
1855 | { | |
1856 | PosixEventSource *iSource; | |
1857 | ||
1858 | (void) mDNSPosixProcessFDSet(m, &listenFDs); // call this first to process wire data for clients | |
1859 | ||
1860 | for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) | |
1861 | { | |
1862 | if (FD_ISSET(iSource->fd, &listenFDs)) | |
1863 | { | |
1864 | iSource->Callback(iSource->fd, 0, iSource->Context); | |
1865 | break; // in case callback removed elements from gEventSources | |
1866 | } | |
1867 | } | |
1868 | *pDataDispatched = mDNStrue; | |
1869 | } | |
1870 | else | |
1871 | *pDataDispatched = mDNSfalse; | |
1872 | ||
1873 | (void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); | |
1874 | *pSignalsReceived = gEventSignals; | |
1875 | sigemptyset(&gEventSignals); | |
1876 | (void) sigprocmask(SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); | |
1877 | ||
1878 | return mStatus_NoError; | |
1879 | } |