]>
Commit | Line | Data |
---|---|---|
6528fe3e A |
1 | /* |
2 | * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in | |
7 | * and are subject to the Apple Public Source License Version 1.1 | |
8 | * (the "License"). You may not use this file except in compliance | |
9 | * with the License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | ||
23 | // *************************************************************************** | |
24 | // mDNS-CFSocket.c: | |
25 | // Supporting routines to run mDNS on a CFRunLoop platform | |
26 | // *************************************************************************** | |
27 | ||
28 | // Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53, | |
29 | // before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder | |
30 | // in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow | |
31 | // it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in | |
32 | // the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS | |
33 | // queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed. | |
34 | #define mDNS_AllowPort53 1 | |
35 | ||
36 | // Normally mDNSResponder is advertising local services on all active interfaces. | |
37 | // However, should you wish to build a query-only mDNS client, setting mDNS_AdvertiseLocalAddresses | |
38 | // to zero will cause CFSocket.c to not set the Advertise flag in its mDNS_RegisterInterface calls. | |
39 | int mDNS_AdvertiseLocalAddresses = 1; | |
40 | ||
377735b0 A |
41 | void (*NotifyClientNetworkChanged)(void); |
42 | ||
6528fe3e A |
43 | #include "mDNSClientAPI.h" // Defines the interface provided to the client layer above |
44 | #include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below | |
45 | #include "mDNSPlatformEnvironment.h" // Defines the specific types needed to run mDNS on this platform | |
46 | #include "mDNSvsprintf.h" // Used to implement debugf_(); | |
47 | ||
48 | #include <stdio.h> | |
49 | #include <stdarg.h> // For va_list support | |
50 | #include <net/if.h> | |
51 | #include <net/if_dl.h> | |
52 | #include <sys/uio.h> | |
53 | #include <sys/param.h> | |
54 | #include <sys/socket.h> | |
55 | ||
56 | // Code contributed by Dave Heller: | |
57 | // Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will | |
58 | // work on Mac OS X 10.1, which does not have the getifaddrs call. | |
59 | #define RUN_ON_PUMA_WITHOUT_IFADDRS 0 | |
60 | ||
61 | #if RUN_ON_PUMA_WITHOUT_IFADDRS | |
62 | ||
63 | #include <sys/ioctl.h> | |
64 | #include <sys/sockio.h> | |
65 | #define ifaddrs ifa_info | |
66 | #ifndef ifa_broadaddr | |
67 | #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ | |
68 | #endif | |
69 | #include <sys/cdefs.h> | |
70 | ||
71 | #else | |
72 | ||
73 | #include <ifaddrs.h> | |
74 | ||
75 | #endif | |
76 | ||
77 | #include <IOKit/IOKitLib.h> | |
78 | #include <IOKit/IOMessage.h> | |
79 | ||
80 | // *************************************************************************** | |
81 | // Structures | |
82 | ||
83 | typedef struct NetworkInterfaceInfo2_struct NetworkInterfaceInfo2; | |
84 | struct NetworkInterfaceInfo2_struct | |
85 | { | |
86 | NetworkInterfaceInfo ifinfo; | |
87 | mDNS *m; | |
88 | char *ifa_name; | |
89 | NetworkInterfaceInfo2 *alias; | |
90 | int socket; | |
91 | CFSocketRef cfsocket; | |
92 | #if mDNS_AllowPort53 | |
93 | int socket53; | |
94 | CFSocketRef cfsocket53; | |
95 | #endif | |
96 | }; | |
97 | ||
98 | // *************************************************************************** | |
99 | // Functions | |
100 | ||
101 | mDNSexport void debugf_(const char *format, ...) | |
102 | { | |
103 | unsigned char buffer[512]; | |
104 | va_list ptr; | |
105 | va_start(ptr,format); | |
106 | buffer[mDNS_vsprintf((char *)buffer, format, ptr)] = 0; | |
107 | va_end(ptr); | |
108 | fprintf(stderr, "%s\n", buffer); | |
109 | fflush(stderr); | |
110 | } | |
111 | ||
112 | mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, | |
113 | mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport) | |
114 | { | |
115 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)(m->HostInterfaces); | |
116 | struct sockaddr_in to; | |
117 | to.sin_family = AF_INET; | |
118 | to.sin_port = dstport.NotAnInteger; | |
119 | to.sin_addr.s_addr = dst. NotAnInteger; | |
120 | ||
121 | if (src.NotAnInteger == 0) debugf("mDNSPlatformSendUDP ERROR! Cannot send from zero source address"); | |
122 | ||
123 | while (info) | |
124 | { | |
125 | if (info->ifinfo.ip.NotAnInteger == src.NotAnInteger) | |
126 | { | |
127 | int s, err; | |
128 | if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) s = info->socket; | |
129 | #if mDNS_AllowPort53 | |
130 | else if (srcport.NotAnInteger == UnicastDNSPort.NotAnInteger ) s = info->socket53; | |
131 | #endif | |
132 | else { debugf("Source port %d not allowed", (mDNSu16)srcport.b[0]<<8 | srcport.b[1]); return(-1); } | |
133 | err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, sizeof(to)); | |
134 | if (err < 0) { perror("mDNSPlatformSendUDP sendto"); return(err); } | |
135 | } | |
136 | info = (NetworkInterfaceInfo2 *)(info->ifinfo.next); | |
137 | } | |
138 | ||
139 | return(mStatus_NoError); | |
140 | } | |
141 | ||
142 | static ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, | |
143 | struct sockaddr *const from, size_t *const fromlen, struct in_addr *dstaddr, char ifname[128]) | |
144 | { | |
145 | struct iovec databuffers = { (char *)buffer, max }; | |
146 | struct msghdr msg; | |
147 | ssize_t n; | |
148 | struct cmsghdr *cmPtr; | |
149 | char ancillary[1024]; | |
150 | ||
151 | // Set up the message | |
152 | msg.msg_name = (caddr_t)from; | |
153 | msg.msg_namelen = *fromlen; | |
154 | msg.msg_iov = &databuffers; | |
155 | msg.msg_iovlen = 1; | |
156 | msg.msg_control = (caddr_t)&ancillary; | |
157 | msg.msg_controllen = sizeof(ancillary); | |
158 | msg.msg_flags = 0; | |
159 | ||
160 | // Receive the data | |
161 | n = recvmsg(s, &msg, 0); | |
162 | if (n<0 || msg.msg_controllen < sizeof(struct cmsghdr) || (msg.msg_flags & MSG_CTRUNC)) | |
163 | { perror("recvmsg"); return(n); } | |
164 | ||
165 | *fromlen = msg.msg_namelen; | |
166 | ||
167 | // Parse each option out of the ancillary data. | |
168 | for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) | |
169 | { | |
170 | // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); | |
171 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) | |
172 | *dstaddr = *(struct in_addr *)CMSG_DATA(cmPtr); | |
173 | if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) | |
174 | { | |
175 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); | |
176 | if (sdl->sdl_nlen < sizeof(ifname)) | |
177 | { | |
178 | mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); | |
179 | ifname[sdl->sdl_nlen] = 0; | |
180 | // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | return(n); | |
186 | } | |
187 | ||
188 | mDNSlocal void myCFSocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *context) | |
189 | { | |
190 | mDNSIPAddr senderaddr, destaddr; | |
191 | mDNSIPPort senderport; | |
192 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)context; | |
193 | mDNS *const m = info->m; | |
194 | DNSMessage packet; | |
195 | struct in_addr to; | |
196 | struct sockaddr_in from; | |
197 | size_t fromlen = sizeof(from); | |
198 | char packetifname[128] = ""; | |
199 | int err; | |
200 | ||
201 | (void)address; // Parameter not used | |
202 | (void)data; // Parameter not used | |
203 | ||
204 | if (type != kCFSocketReadCallBack) debugf("myCFSocketCallBack: Why is type not kCFSocketReadCallBack?"); | |
205 | #if mDNS_AllowPort53 | |
206 | if (s == info->cfsocket53) | |
207 | err = myrecvfrom(info->socket53, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &to, packetifname); | |
208 | else | |
209 | #endif | |
210 | err = myrecvfrom(info->socket, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &to, packetifname); | |
211 | ||
212 | if (err < 0) { debugf("myCFSocketCallBack recvfrom error %d", err); return; } | |
213 | ||
214 | senderaddr.NotAnInteger = from.sin_addr.s_addr; | |
215 | senderport.NotAnInteger = from.sin_port; | |
216 | destaddr.NotAnInteger = to.s_addr; | |
217 | ||
218 | // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the | |
219 | // sockets API means that even though this socket has only officially joined the multicast group | |
220 | // on one specific interface, the kernel will still deliver multicast packets to it no matter which | |
221 | // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. | |
222 | // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface | |
223 | // on which the packet arrived, and ignore the packet if it really arrived on some other interface. | |
224 | if (strcmp(info->ifa_name, packetifname)) | |
225 | { | |
226 | verbosedebugf("myCFSocketCallBack got a packet from %.4a to %.4a on interface %.4a/%s (Ignored -- really arrived on interface %s)", | |
227 | &senderaddr, &destaddr, &info->ifinfo.ip, info->ifa_name, packetifname); | |
228 | return; | |
229 | } | |
230 | else | |
231 | verbosedebugf("myCFSocketCallBack got a packet from %.4a to %.4a on interface %.4a/%s", | |
232 | &senderaddr, &destaddr, &info->ifinfo.ip, info->ifa_name); | |
233 | ||
234 | if (err < sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } | |
235 | ||
236 | #if mDNS_AllowPort53 | |
237 | if (s == info->cfsocket53) | |
238 | mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, senderaddr, senderport, destaddr, UnicastDNSPort, info->ifinfo.ip); | |
239 | else | |
240 | #endif | |
241 | mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, senderaddr, senderport, destaddr, MulticastDNSPort, info->ifinfo.ip); | |
242 | } | |
243 | ||
244 | mDNSlocal void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info) | |
245 | { | |
246 | (void)timer; // Parameter not used | |
247 | mDNSCoreTask((mDNS *const)info); | |
248 | } | |
249 | ||
250 | // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel | |
251 | mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) | |
252 | { | |
253 | CFStringEncoding encoding = kCFStringEncodingUTF8; | |
254 | CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); | |
255 | if (cfs) | |
256 | { | |
257 | CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); | |
258 | CFRelease(cfs); | |
259 | } | |
260 | } | |
261 | ||
262 | // This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel | |
263 | mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) | |
264 | { | |
265 | CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); | |
266 | if (cfs) | |
267 | { | |
268 | CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); | |
269 | CFRelease(cfs); | |
270 | } | |
271 | } | |
272 | ||
273 | mDNSlocal mStatus SetupSocket(struct sockaddr_in *ifa_addr, mDNSIPPort port, int *s, CFSocketRef *c, CFSocketContext *context) | |
274 | { | |
275 | mStatus err; | |
276 | const int on = 1; | |
277 | const int twofivefive = 255; | |
278 | struct ip_mreq imr; | |
279 | struct sockaddr_in listening_sockaddr; | |
280 | CFRunLoopSourceRef rls; | |
281 | ||
282 | // Open the socket... | |
283 | *s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
284 | *c = NULL; | |
285 | if (*s < 0) { perror("socket"); return(*s); } | |
286 | ||
287 | // ... with a shared UDP port | |
288 | err = setsockopt(*s, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); | |
289 | if (err < 0) { perror("setsockopt - SO_REUSEPORT"); return(err); } | |
290 | ||
291 | // We want to receive destination addresses | |
292 | err = setsockopt(*s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); | |
293 | if (err < 0) { perror("setsockopt - IP_RECVDSTADDR"); return(err); } | |
294 | ||
295 | // We want to receive interface identifiers | |
296 | err = setsockopt(*s, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); | |
297 | if (err < 0) { perror("setsockopt - IP_RECVIF"); return(err); } | |
298 | ||
299 | // Add multicast group membership on this interface | |
300 | imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; | |
301 | imr.imr_interface = ifa_addr->sin_addr; | |
302 | err = setsockopt(*s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)); | |
303 | if (err < 0) { perror("setsockopt - IP_ADD_MEMBERSHIP"); return(err); } | |
304 | ||
305 | // Specify outgoing interface too | |
306 | err = setsockopt(*s, IPPROTO_IP, IP_MULTICAST_IF, &ifa_addr->sin_addr, sizeof(ifa_addr->sin_addr)); | |
307 | if (err < 0) { perror("setsockopt - IP_MULTICAST_IF"); return(err); } | |
308 | ||
309 | // Send unicast packets with TTL 255 | |
310 | err = setsockopt(*s, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); | |
311 | if (err < 0) { perror("setsockopt - IP_TTL"); return(err); } | |
312 | ||
313 | // And multicast packets with TTL 255 too | |
314 | err = setsockopt(*s, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); | |
315 | if (err < 0) { perror("setsockopt - IP_MULTICAST_TTL"); return(err); } | |
316 | ||
317 | // And start listening for packets | |
318 | listening_sockaddr.sin_family = AF_INET; | |
319 | listening_sockaddr.sin_port = port.NotAnInteger; | |
320 | listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket | |
321 | err = bind(*s, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); | |
322 | if (err) | |
323 | { | |
324 | if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) err = 0; | |
325 | else perror("bind"); | |
326 | return(err); | |
327 | } | |
328 | ||
329 | *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, context); | |
330 | rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); | |
331 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
332 | CFRelease(rls); | |
333 | ||
334 | return(err); | |
335 | } | |
336 | ||
337 | #if 0 | |
338 | mDNSlocal NetworkInterfaceInfo2 *SearchForInterfaceByAddr(mDNS *const m, mDNSIPAddr ip) | |
339 | { | |
340 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces); | |
341 | while (info) | |
342 | { | |
343 | if (info->ifinfo.ip.NotAnInteger == ip.NotAnInteger) return(info); | |
344 | info = (NetworkInterfaceInfo2 *)(info->ifinfo.next); | |
345 | } | |
346 | return(NULL); | |
347 | } | |
348 | #endif | |
349 | ||
350 | mDNSlocal NetworkInterfaceInfo2 *SearchForInterfaceByName(mDNS *const m, char *ifname) | |
351 | { | |
352 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces); | |
353 | while (info) | |
354 | { | |
355 | if (!strcmp(info->ifa_name, ifname)) return(info); | |
356 | info = (NetworkInterfaceInfo2 *)(info->ifinfo.next); | |
357 | } | |
358 | return(NULL); | |
359 | } | |
360 | ||
361 | #if RUN_ON_PUMA_WITHOUT_IFADDRS | |
362 | ||
363 | /* Our own header for the programs that need interface configuration info. | |
364 | Include this file, instead of "unp.h". */ | |
365 | ||
366 | #define IFA_NAME 16 /* same as IFNAMSIZ in <net/if.h> */ | |
367 | #define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ | |
368 | ||
369 | struct ifa_info { | |
370 | char ifa_name[IFA_NAME]; /* interface name, null terminated */ | |
371 | u_char ifa_haddr[IFA_HADDR]; /* hardware address */ | |
372 | u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ | |
373 | short ifa_flags; /* IFF_xxx constants from <net/if.h> */ | |
374 | short ifa_myflags; /* our own IFI_xxx flags */ | |
375 | struct sockaddr *ifa_addr; /* primary address */ | |
376 | struct sockaddr *ifa_brdaddr;/* broadcast address */ | |
377 | struct sockaddr *ifa_dstaddr;/* destination address */ | |
378 | struct ifa_info *ifa_next; /* next of these structures */ | |
379 | }; | |
380 | ||
381 | #define IFI_ALIAS 1 /* ifa_addr is an alias */ | |
382 | ||
383 | /* function prototypes */ | |
384 | struct ifa_info *get_ifa_info(int, int); | |
385 | struct ifa_info *Get_ifa_info(int, int); | |
386 | void free_ifa_info(struct ifa_info *); | |
387 | ||
388 | #define HAVE_SOCKADDR_SA_LEN 1 | |
389 | ||
390 | struct ifa_info * | |
391 | get_ifa_info(int family, int doaliases) | |
392 | { | |
393 | struct ifa_info *ifi, *ifihead, **ifipnext; | |
394 | int sockfd, len, lastlen, flags, myflags; | |
395 | char *ptr, *buf, lastname[IFNAMSIZ], *cptr; | |
396 | struct ifconf ifc; | |
397 | struct ifreq *ifr, ifrcopy; | |
398 | struct sockaddr_in *sinptr; | |
399 | ||
400 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); | |
401 | ||
402 | lastlen = 0; | |
403 | len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ | |
404 | for ( ; ; ) { | |
405 | buf = (char *) malloc(len); | |
406 | ifc.ifc_len = len; | |
407 | ifc.ifc_buf = buf; | |
408 | if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { | |
409 | if (errno != EINVAL || lastlen != 0) | |
410 | debugf("ioctl error"); | |
411 | } else { | |
412 | if (ifc.ifc_len == lastlen) | |
413 | break; /* success, len has not changed */ | |
414 | lastlen = ifc.ifc_len; | |
415 | } | |
416 | len += 10 * sizeof(struct ifreq); /* increment */ | |
417 | free(buf); | |
418 | } | |
419 | ifihead = NULL; | |
420 | ifipnext = &ifihead; | |
421 | lastname[0] = 0; | |
422 | /* end get_ifa_info1 */ | |
423 | ||
424 | /* include get_ifa_info2 */ | |
425 | for (ptr = buf; ptr < buf + ifc.ifc_len; ) { | |
426 | ifr = (struct ifreq *) ptr; | |
427 | ||
428 | #ifdef HAVE_SOCKADDR_SA_LEN | |
429 | len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); | |
430 | #else | |
431 | switch (ifr->ifr_addr.sa_family) { | |
432 | #ifdef IPV6 | |
433 | case AF_INET6: | |
434 | len = sizeof(struct sockaddr_in6); | |
435 | break; | |
436 | #endif | |
437 | case AF_INET: | |
438 | default: | |
439 | len = sizeof(struct sockaddr); | |
440 | break; | |
441 | } | |
442 | #endif /* HAVE_SOCKADDR_SA_LEN */ | |
443 | ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ | |
444 | ||
445 | if (ifr->ifr_addr.sa_family != family) | |
446 | continue; /* ignore if not desired address family */ | |
447 | ||
448 | myflags = 0; | |
449 | if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) | |
450 | *cptr = 0; /* replace colon will null */ | |
451 | if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { | |
452 | if (doaliases == 0) | |
453 | continue; /* already processed this interface */ | |
454 | myflags = IFI_ALIAS; | |
455 | } | |
456 | memcpy(lastname, ifr->ifr_name, IFNAMSIZ); | |
457 | ||
458 | ifrcopy = *ifr; | |
459 | ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); | |
460 | flags = ifrcopy.ifr_flags; | |
461 | if ((flags & IFF_UP) == 0) | |
462 | continue; /* ignore if interface not up */ | |
463 | ||
464 | ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); | |
465 | *ifipnext = ifi; /* prev points to this new one */ | |
466 | ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ | |
467 | ||
468 | ifi->ifa_flags = flags; /* IFF_xxx values */ | |
469 | ifi->ifa_myflags = myflags; /* IFI_xxx values */ | |
470 | memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); | |
471 | ifi->ifa_name[IFA_NAME-1] = '\0'; | |
472 | /* end get_ifa_info2 */ | |
473 | /* include get_ifa_info3 */ | |
474 | switch (ifr->ifr_addr.sa_family) { | |
475 | case AF_INET: | |
476 | sinptr = (struct sockaddr_in *) &ifr->ifr_addr; | |
477 | if (ifi->ifa_addr == NULL) { | |
478 | ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); | |
479 | memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); | |
480 | ||
481 | #ifdef SIOCGIFBRDADDR | |
482 | if (flags & IFF_BROADCAST) { | |
483 | ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); | |
484 | sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; | |
485 | ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); | |
486 | memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); | |
487 | } | |
488 | #endif | |
489 | ||
490 | #ifdef SIOCGIFDSTADDR | |
491 | if (flags & IFF_POINTOPOINT) { | |
492 | ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); | |
493 | sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; | |
494 | ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); | |
495 | memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); | |
496 | } | |
497 | #endif | |
498 | } | |
499 | break; | |
500 | ||
501 | default: | |
502 | break; | |
503 | } | |
504 | } | |
505 | free(buf); | |
506 | return(ifihead); /* pointer to first structure in linked list */ | |
507 | } | |
508 | /* end get_ifa_info3 */ | |
509 | ||
510 | /* include free_ifa_info */ | |
511 | mDNSlocal void freeifaddrs(struct ifa_info *ifihead) | |
512 | { | |
513 | struct ifa_info *ifi, *ifinext; | |
514 | ||
515 | for (ifi = ifihead; ifi != NULL; ifi = ifinext) { | |
516 | if (ifi->ifa_addr != NULL) | |
517 | free(ifi->ifa_addr); | |
518 | if (ifi->ifa_brdaddr != NULL) | |
519 | free(ifi->ifa_brdaddr); | |
520 | if (ifi->ifa_dstaddr != NULL) | |
521 | free(ifi->ifa_dstaddr); | |
522 | ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ | |
523 | free(ifi); /* the ifa_info{} itself */ | |
524 | } | |
525 | } | |
526 | /* end free_ifa_info */ | |
527 | ||
528 | struct ifa_info * | |
529 | Get_ifa_info(int family, int doaliases) | |
530 | { | |
531 | struct ifa_info *ifi; | |
532 | ||
533 | if ( (ifi = get_ifa_info(family, doaliases)) == NULL) | |
534 | debugf("get_ifa_info error"); | |
535 | return(ifi); | |
536 | } | |
537 | ||
538 | mDNSlocal int getifaddrs(struct ifa_info **ifalist) | |
539 | { | |
540 | *ifalist = get_ifa_info(PF_INET, false); | |
541 | if( ifalist == nil ) | |
542 | return -1; | |
543 | else | |
544 | return(0); | |
545 | } | |
546 | ||
547 | #endif | |
548 | ||
549 | mDNSlocal mStatus SetupInterface(mDNS *const m, NetworkInterfaceInfo2 *info, struct ifaddrs *ifa) | |
550 | { | |
551 | mStatus err = 0; | |
552 | struct sockaddr_in *ifa_addr = (struct sockaddr_in *)ifa->ifa_addr; | |
553 | CFSocketContext myCFSocketContext = { 0, info, NULL, NULL, NULL }; | |
554 | ||
555 | info->ifinfo.ip.NotAnInteger = ifa_addr->sin_addr.s_addr; | |
556 | info->ifinfo.Advertise = mDNS_AdvertiseLocalAddresses; | |
557 | info->m = m; | |
558 | info->ifa_name = (char *)mallocL("NetworkInterfaceInfo2 name", strlen(ifa->ifa_name) + 1); | |
559 | if (!info->ifa_name) return(-1); | |
560 | strcpy(info->ifa_name, ifa->ifa_name); | |
561 | info->alias = SearchForInterfaceByName(m, ifa->ifa_name); | |
562 | info->socket = 0; | |
563 | info->cfsocket = 0; | |
564 | #if mDNS_AllowPort53 | |
565 | info->socket53 = 0; | |
566 | info->cfsocket53 = 0; | |
567 | #endif | |
568 | ||
569 | mDNS_RegisterInterface(m, &info->ifinfo); | |
570 | ||
571 | if (info->alias) | |
572 | debugf("SetupInterface: %s Flags %04X %.4a is an alias of %.4a", | |
573 | ifa->ifa_name, ifa->ifa_flags, &info->ifinfo.ip, &info->alias->ifinfo.ip); | |
574 | ||
575 | #if mDNS_AllowPort53 | |
576 | err = SetupSocket(ifa_addr, UnicastDNSPort, &info->socket53, &info->cfsocket53, &myCFSocketContext); | |
577 | #endif | |
578 | if (!err) | |
579 | err = SetupSocket(ifa_addr, MulticastDNSPort, &info->socket, &info->cfsocket, &myCFSocketContext); | |
580 | ||
581 | debugf("SetupInterface: %s Flags %04X %.4a Registered", | |
582 | ifa->ifa_name, ifa->ifa_flags, &info->ifinfo.ip); | |
583 | ||
584 | return(err); | |
585 | } | |
586 | ||
587 | mDNSlocal void ClearInterfaceList(mDNS *const m) | |
588 | { | |
589 | while (m->HostInterfaces) | |
590 | { | |
591 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces); | |
592 | mDNS_DeregisterInterface(m, &info->ifinfo); | |
593 | if (info->ifa_name ) freeL("NetworkInterfaceInfo2 name", info->ifa_name); | |
594 | if (info->socket > 0) shutdown(info->socket, 2); | |
595 | if (info->cfsocket) { CFSocketInvalidate(info->cfsocket); CFRelease(info->cfsocket); } | |
596 | #if mDNS_AllowPort53 | |
597 | if (info->socket53 > 0) shutdown(info->socket53, 2); | |
598 | if (info->cfsocket53) { CFSocketInvalidate(info->cfsocket53); CFRelease(info->cfsocket53); } | |
599 | #endif | |
600 | freeL("NetworkInterfaceInfo2", info); | |
601 | } | |
602 | } | |
603 | ||
604 | mDNSlocal mStatus SetupInterfaceList(mDNS *const m) | |
605 | { | |
606 | struct ifaddrs *ifalist; | |
607 | int err = getifaddrs(&ifalist); | |
608 | struct ifaddrs *ifa = ifalist; | |
609 | struct ifaddrs *theLoopback = NULL; | |
610 | if (err) return(err); | |
611 | ||
612 | // Set up the nice label | |
613 | m->nicelabel.c[0] = 0; | |
614 | GetUserSpecifiedFriendlyComputerName(&m->nicelabel); | |
615 | if (m->nicelabel.c[0] == 0) ConvertCStringToDomainLabel("Macintosh", &m->nicelabel); | |
616 | ||
617 | // Set up the RFC 1034-compliant label | |
618 | m->hostlabel.c[0] = 0; | |
619 | GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); | |
620 | if (m->hostlabel.c[0] == 0) ConvertCStringToDomainLabel("Macintosh", &m->hostlabel); | |
621 | ||
622 | mDNS_GenerateFQDN(m); | |
623 | ||
624 | while (ifa) | |
625 | { | |
626 | #if 0 | |
627 | if (ifa->ifa_addr->sa_family != AF_INET) | |
628 | debugf("SetupInterface: %s Flags %04X Family %d not AF_INET", | |
629 | ifa->ifa_name, ifa->ifa_flags, ifa->ifa_addr->sa_family); | |
630 | if (!(ifa->ifa_flags & IFF_UP)) | |
631 | debugf("SetupInterface: %s Flags %04X Interface not IFF_UP", ifa->ifa_name, ifa->ifa_flags); | |
632 | if (ifa->ifa_flags & IFF_LOOPBACK) | |
633 | debugf("SetupInterface: %s Flags %04X Interface IFF_LOOPBACK", ifa->ifa_name, ifa->ifa_flags); | |
634 | if (ifa->ifa_flags & IFF_POINTOPOINT) | |
635 | debugf("SetupInterface: %s Flags %04X Interface IFF_POINTOPOINT", ifa->ifa_name, ifa->ifa_flags); | |
636 | #endif | |
637 | if (ifa->ifa_addr->sa_family == AF_INET && (ifa->ifa_flags & IFF_UP) && | |
638 | !(ifa->ifa_flags & IFF_POINTOPOINT)) | |
639 | { | |
640 | if (ifa->ifa_flags & IFF_LOOPBACK) | |
641 | theLoopback = ifa; | |
642 | else | |
643 | { | |
644 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)mallocL("NetworkInterfaceInfo2", sizeof(*info)); | |
645 | if (!info) debugf("SetupInterfaceList: Out of Memory!"); | |
646 | else SetupInterface(m, info, ifa); | |
647 | } | |
648 | } | |
649 | ifa = ifa->ifa_next; | |
650 | } | |
651 | ||
652 | if (!m->HostInterfaces && theLoopback) | |
653 | { | |
654 | NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)mallocL("NetworkInterfaceInfo2", sizeof(*info)); | |
655 | if (!info) debugf("SetupInterfaceList: (theLoopback) Out of Memory!"); | |
656 | else SetupInterface(m, info, theLoopback); | |
657 | } | |
658 | ||
659 | freeifaddrs(ifalist); | |
660 | return(err); | |
661 | } | |
662 | ||
663 | mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) | |
664 | { | |
665 | mDNS *const m = (mDNS *const)context; | |
666 | debugf("*** Network Configuration Change ***"); | |
667 | (void)store; // Parameter not used | |
668 | (void)changedKeys; // Parameter not used | |
377735b0 | 669 | |
6528fe3e A |
670 | ClearInterfaceList(m); |
671 | SetupInterfaceList(m); | |
377735b0 | 672 | if (NotifyClientNetworkChanged) NotifyClientNetworkChanged(); |
6528fe3e A |
673 | mDNSCoreSleep(m, false); |
674 | } | |
675 | ||
676 | mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) | |
677 | { | |
678 | mStatus err = -1; | |
679 | SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; | |
680 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context); | |
681 | CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); | |
682 | CFStringRef key2 = SCDynamicStoreKeyCreateComputerName(NULL); | |
683 | CFStringRef key3 = SCDynamicStoreKeyCreateHostNames(NULL); | |
377735b0 | 684 | CFStringRef pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); |
6528fe3e A |
685 | CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
686 | CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
687 | ||
688 | if (!store) { fprintf(stderr, "SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; } | |
689 | if (!key1 || !key2 || !key3 || !keys || !pattern || !patterns) goto error; | |
690 | ||
691 | CFArrayAppendValue(keys, key1); | |
692 | CFArrayAppendValue(keys, key2); | |
693 | CFArrayAppendValue(keys, key3); | |
694 | CFArrayAppendValue(patterns, pattern); | |
695 | if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) | |
696 | { fprintf(stderr, "SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; } | |
697 | ||
698 | m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); | |
699 | if (!m->p->StoreRLS) { fprintf(stderr, "SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; } | |
700 | ||
701 | CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); | |
702 | m->p->Store = store; | |
703 | err = 0; | |
704 | goto exit; | |
705 | ||
706 | error: | |
707 | if (store) CFRelease(store); | |
708 | ||
709 | exit: | |
710 | if (key1) CFRelease(key1); | |
711 | if (key2) CFRelease(key2); | |
712 | if (key3) CFRelease(key3); | |
713 | if (pattern) CFRelease(pattern); | |
714 | if (keys) CFRelease(keys); | |
715 | if (patterns) CFRelease(patterns); | |
716 | ||
717 | return(err); | |
718 | } | |
719 | ||
720 | mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) | |
721 | { | |
722 | mDNS *const m = (mDNS *const)refcon; | |
723 | (void)service; // Parameter not used | |
724 | switch(messageType) | |
725 | { | |
726 | case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 | |
727 | case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreSleep(m, true); break; // E0000250 | |
728 | case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 | |
729 | case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 | |
730 | case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreSleep(m, true); break; // E0000280 | |
731 | case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 | |
732 | case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreSleep(m, false); break; // E0000300 | |
733 | default: debugf("PowerChanged unknown message %X", messageType); break; | |
734 | } | |
735 | IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); | |
736 | } | |
737 | ||
738 | mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) | |
739 | { | |
740 | IONotificationPortRef thePortRef; | |
741 | m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier); | |
742 | if (m->p->PowerConnection) | |
743 | { | |
744 | m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef); | |
745 | CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); | |
746 | return(mStatus_NoError); | |
747 | } | |
748 | return(-1); | |
749 | } | |
750 | ||
751 | mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) | |
752 | { | |
753 | mStatus err; | |
754 | ||
755 | CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, m, NULL, NULL, NULL }; | |
756 | ||
757 | // Note: Every CFRunLoopTimer has to be created with an initial fire time, and a repeat interval, or it becomes | |
758 | // a one-shot timer and you can't use CFRunLoopTimerSetNextFireDate(timer, when) to schedule subsequent firings. | |
759 | // Here we create it with an initial fire time ten seconds from now, and a repeat interval of ten seconds, | |
760 | // knowing that we'll reschedule it using CFRunLoopTimerSetNextFireDate(timer, when) long before that happens. | |
761 | m->p->CFTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 10.0, 10.0, 0, 1, | |
762 | myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext); | |
763 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), m->p->CFTimer, kCFRunLoopDefaultMode); | |
764 | ||
765 | SetupInterfaceList(m); | |
766 | ||
767 | err = WatchForNetworkChanges(m); | |
768 | if (err) return(err); | |
769 | ||
770 | err = WatchForPowerChanges(m); | |
771 | return(err); | |
772 | } | |
773 | ||
774 | mDNSexport mStatus mDNSPlatformInit(mDNS *const m) | |
775 | { | |
776 | mStatus result = mDNSPlatformInit_setup(m); | |
777 | // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already | |
778 | // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately | |
779 | if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); | |
780 | return(result); | |
781 | } | |
782 | ||
783 | mDNSexport void mDNSPlatformClose(mDNS *const m) | |
784 | { | |
785 | if (m->p->PowerConnection) | |
786 | { | |
787 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); | |
788 | CFRunLoopSourceInvalidate(m->p->PowerRLS); | |
789 | CFRelease(m->p->PowerRLS); | |
790 | IODeregisterForSystemPower(&m->p->PowerNotifier); | |
791 | m->p->PowerConnection = NULL; | |
792 | m->p->PowerNotifier = NULL; | |
793 | m->p->PowerRLS = NULL; | |
794 | } | |
795 | ||
796 | if (m->p->Store) | |
797 | { | |
798 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); | |
799 | CFRunLoopSourceInvalidate(m->p->StoreRLS); | |
800 | CFRelease(m->p->StoreRLS); | |
801 | CFRelease(m->p->Store); | |
802 | m->p->Store = NULL; | |
803 | m->p->StoreRLS = NULL; | |
804 | } | |
805 | ||
806 | ClearInterfaceList(m); | |
807 | ||
808 | if (m->p->CFTimer) | |
809 | { | |
810 | CFRunLoopTimerInvalidate(m->p->CFTimer); | |
811 | CFRelease(m->p->CFTimer); | |
812 | m->p->CFTimer = NULL; | |
813 | } | |
814 | } | |
815 | ||
6528fe3e A |
816 | mDNSexport void mDNSPlatformScheduleTask(const mDNS *const m, SInt32 NextTaskTime) |
817 | { | |
818 | if (m->p->CFTimer) | |
819 | { | |
820 | CFAbsoluteTime ticks = (CFAbsoluteTime)(NextTaskTime - mDNSPlatformTimeNow()); | |
821 | CFAbsoluteTime interval = ticks / (CFAbsoluteTime)mDNSPlatformOneSecond; | |
822 | CFRunLoopTimerSetNextFireDate(m->p->CFTimer, CFAbsoluteTimeGetCurrent() + interval); | |
823 | } | |
824 | } | |
825 | ||
826 | // Locking is a no-op here, because we're CFRunLoop-based, so we can never interrupt ourselves | |
827 | mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } | |
828 | mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } | |
829 | mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); } | |
830 | mDNSexport UInt32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); } | |
831 | mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 len) { memcpy(dst, src, len); } | |
832 | mDNSexport Boolean mDNSPlatformMemSame(const void *src, const void *dst, UInt32 len) { return(memcmp(dst, src, len) == 0); } | |
833 | mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { bzero(dst, len); } | |
834 | ||
835 | mDNSexport SInt32 mDNSPlatformTimeNow() | |
836 | { | |
837 | struct timeval tp; | |
838 | gettimeofday(&tp, NULL); | |
839 | // tp.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) | |
840 | // tp.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) | |
841 | // We use the lower 22 bits of tp.tv_sec for the top 22 bits of our result | |
842 | // and we multiply tp.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. | |
843 | // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) | |
844 | // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). | |
845 | return( (tp.tv_sec << 10) | (tp.tv_usec * 16 / 15625) ); | |
846 | } | |
847 | ||
848 | mDNSexport SInt32 mDNSPlatformOneSecond = 1024; |