]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/mDNSUNP.c
8e60808c22e83a5134920bfdec4c17e2a2184fc9
[apple/mdnsresponder.git] / mDNSPosix / mDNSUNP.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24
25 Change History (most recent first):
26
27 $Log: mDNSUNP.c,v $
28 Revision 1.16 2004/03/20 05:37:09 cheshire
29 Fix contributed by Terry Lambert & Alfred Perlstein:
30 Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x
31
32 Revision 1.15 2004/02/14 01:09:45 rpantos
33 Just use HAVE_IPV6 rather than defined(HAVE_IPV6).
34
35 Revision 1.14 2003/12/11 18:53:40 cheshire
36 Fix compiler warning reported by Paul Guyot
37
38 Revision 1.13 2003/12/08 20:47:02 rpantos
39 Add support for mDNSResponder on Linux.
40
41 Revision 1.12 2003/09/02 20:47:13 cheshire
42 Fix signed/unsigned warning
43
44 Revision 1.11 2003/08/12 19:56:26 cheshire
45 Update to APSL 2.0
46
47 Revision 1.10 2003/08/06 18:20:51 cheshire
48 Makefile cleanup
49
50 Revision 1.9 2003/07/14 18:11:54 cheshire
51 Fix stricter compiler warnings
52
53 Revision 1.8 2003/07/02 21:19:59 cheshire
54 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
55
56 Revision 1.7 2003/03/20 21:10:31 cheshire
57 Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris
58
59 Revision 1.6 2003/03/13 03:46:21 cheshire
60 Fixes to make the code build on Linux
61
62 Revision 1.5 2003/02/07 03:02:02 cheshire
63 Submitted by: Mitsutaka Watanabe
64 The code saying "index += 1;" was effectively making up random interface index values.
65 The right way to find the correct interface index is if_nametoindex();
66
67 Revision 1.4 2002/12/23 22:13:31 jgraessl
68
69 Reviewed by: Stuart Cheshire
70 Initial IPv6 support for mDNSResponder.
71
72 Revision 1.3 2002/09/21 20:44:53 zarzycki
73 Added APSL info
74
75 Revision 1.2 2002/09/19 04:20:44 cheshire
76 Remove high-ascii characters that confuse some systems
77
78 Revision 1.1 2002/09/17 06:24:34 cheshire
79 First checkin
80
81 */
82
83 #include "mDNSUNP.h"
84
85 #include <errno.h>
86 #include <assert.h>
87 #include <string.h>
88 #include <stdlib.h>
89 #include <sys/uio.h>
90 #include <sys/ioctl.h>
91 #include <unistd.h>
92 #include <stdio.h>
93
94 /* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but
95 other platforms don't even have that include file. So,
96 if we haven't yet got a definition, let's try to find
97 <sys/sockio.h>.
98 */
99
100 #ifndef SIOCGIFCONF
101 #include <sys/sockio.h>
102 #endif
103
104 /* sockaddr_dl is only referenced if we're using IP_RECVIF,
105 so only include the header in that case.
106 */
107
108 #ifdef IP_RECVIF
109 #include <net/if_dl.h>
110 #endif
111
112
113 struct ifi_info *get_ifi_info(int family, int doaliases)
114 {
115 int junk;
116 struct ifi_info *ifi, *ifihead, **ifipnext;
117 int sockfd, len, lastlen, flags, myflags;
118 char *ptr, *buf, lastname[IFNAMSIZ], *cptr;
119 struct ifconf ifc;
120 struct ifreq *ifr, ifrcopy;
121 struct sockaddr_in *sinptr;
122
123 #if defined(AF_INET6) && HAVE_IPV6
124 struct sockaddr_in6 *sinptr6;
125 #endif
126
127 sockfd = -1;
128 buf = NULL;
129 ifihead = NULL;
130
131 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
132 if (sockfd < 0) {
133 goto gotError;
134 }
135
136 lastlen = 0;
137 len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
138 for ( ; ; ) {
139 buf = (char*)malloc(len);
140 if (buf == NULL) {
141 goto gotError;
142 }
143 ifc.ifc_len = len;
144 ifc.ifc_buf = buf;
145 if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
146 if (errno != EINVAL || lastlen != 0) {
147 goto gotError;
148 }
149 } else {
150 if (ifc.ifc_len == lastlen)
151 break; /* success, len has not changed */
152 lastlen = ifc.ifc_len;
153 }
154 len += 10 * sizeof(struct ifreq); /* increment */
155 free(buf);
156 }
157 ifihead = NULL;
158 ifipnext = &ifihead;
159 lastname[0] = 0;
160 /* end get_ifi_info1 */
161
162 /* include get_ifi_info2 */
163 for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
164 ifr = (struct ifreq *) ptr;
165
166 len = GET_SA_LEN(ifr->ifr_addr);
167 ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */
168
169 // fprintf(stderr, "intf %d name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family);
170
171 if (ifr->ifr_addr.sa_family != family)
172 continue; /* ignore if not desired address family */
173
174 myflags = 0;
175 if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
176 *cptr = 0; /* replace colon will null */
177 if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
178 if (doaliases == 0)
179 continue; /* already processed this interface */
180 myflags = IFI_ALIAS;
181 }
182 memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
183
184 ifrcopy = *ifr;
185 if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) {
186 goto gotError;
187 }
188
189 flags = ifrcopy.ifr_flags;
190 if ((flags & IFF_UP) == 0)
191 continue; /* ignore if interface not up */
192
193 ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
194 if (ifi == NULL) {
195 goto gotError;
196 }
197 *ifipnext = ifi; /* prev points to this new one */
198 ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
199
200 ifi->ifi_flags = flags; /* IFF_xxx values */
201 ifi->ifi_myflags = myflags; /* IFI_xxx values */
202 ifi->ifi_index = if_nametoindex(ifr->ifr_name);
203 memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
204 ifi->ifi_name[IFI_NAME-1] = '\0';
205 /* end get_ifi_info2 */
206 /* include get_ifi_info3 */
207 switch (ifr->ifr_addr.sa_family) {
208 case AF_INET:
209 sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
210 if (ifi->ifi_addr == NULL) {
211 ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
212 if (ifi->ifi_addr == NULL) {
213 goto gotError;
214 }
215 memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
216
217 #ifdef SIOCGIFBRDADDR
218 if (flags & IFF_BROADCAST) {
219 if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) {
220 goto gotError;
221 }
222 sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
223 ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
224 if (ifi->ifi_brdaddr == NULL) {
225 goto gotError;
226 }
227 memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
228 }
229 #endif
230
231 #ifdef SIOCGIFDSTADDR
232 if (flags & IFF_POINTOPOINT) {
233 if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) {
234 goto gotError;
235 }
236 sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
237 ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
238 if (ifi->ifi_dstaddr == NULL) {
239 goto gotError;
240 }
241 memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
242 }
243 #endif
244 }
245 break;
246
247 #if defined(AF_INET6) && HAVE_IPV6
248 case AF_INET6:
249 sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr;
250 if (ifi->ifi_addr == NULL) {
251 ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
252 if (ifi->ifi_addr == NULL) {
253 goto gotError;
254 }
255
256 /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */
257 /* We need to strip that out */
258 if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr))
259 sinptr6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
260 memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6));
261 }
262 break;
263 #endif
264
265 default:
266 break;
267 }
268 }
269 goto done;
270
271 gotError:
272 if (ifihead != NULL) {
273 free_ifi_info(ifihead);
274 ifihead = NULL;
275 }
276
277 done:
278 if (buf != NULL) {
279 free(buf);
280 }
281 if (sockfd != -1) {
282 junk = close(sockfd);
283 assert(junk == 0);
284 }
285 return(ifihead); /* pointer to first structure in linked list */
286 }
287 /* end get_ifi_info3 */
288
289 /* include free_ifi_info */
290 void
291 free_ifi_info(struct ifi_info *ifihead)
292 {
293 struct ifi_info *ifi, *ifinext;
294
295 for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
296 if (ifi->ifi_addr != NULL)
297 free(ifi->ifi_addr);
298 if (ifi->ifi_brdaddr != NULL)
299 free(ifi->ifi_brdaddr);
300 if (ifi->ifi_dstaddr != NULL)
301 free(ifi->ifi_dstaddr);
302 ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */
303 free(ifi); /* the ifi_info{} itself */
304 }
305 }
306 /* end free_ifi_info */
307
308 ssize_t
309 recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
310 struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl)
311 {
312 struct msghdr msg;
313 struct iovec iov[1];
314 ssize_t n;
315
316 #ifdef CMSG_FIRSTHDR
317 struct cmsghdr *cmptr;
318 union {
319 struct cmsghdr cm;
320 char control[1024];
321 } control_un;
322
323 *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be
324
325 msg.msg_control = control_un.control;
326 msg.msg_controllen = sizeof(control_un.control);
327 msg.msg_flags = 0;
328 #else
329 memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */
330 #endif /* CMSG_FIRSTHDR */
331
332 msg.msg_name = (char *) sa;
333 msg.msg_namelen = *salenptr;
334 iov[0].iov_base = (char *)ptr;
335 iov[0].iov_len = nbytes;
336 msg.msg_iov = iov;
337 msg.msg_iovlen = 1;
338
339 if ( (n = recvmsg(fd, &msg, *flagsp)) < 0)
340 return(n);
341
342 *salenptr = msg.msg_namelen; /* pass back results */
343 if (pktp) {
344 /* 0.0.0.0, i/f = -1 */
345 /* We set the interface to -1 so that the caller can
346 tell whether we returned a meaningful value or
347 just some default. Previously this code just
348 set the value to 0, but I'm concerned that 0
349 might be a valid interface value.
350 */
351 memset(pktp, 0, sizeof(struct my_in_pktinfo));
352 pktp->ipi_ifindex = -1;
353 }
354 /* end recvfrom_flags1 */
355
356 /* include recvfrom_flags2 */
357 #ifndef CMSG_FIRSTHDR
358 #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc.
359 *flagsp = 0; /* pass back results */
360 return(n);
361 #else
362
363 *flagsp = msg.msg_flags; /* pass back results */
364 if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) ||
365 (msg.msg_flags & MSG_CTRUNC) || pktp == NULL)
366 return(n);
367
368 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
369 cmptr = CMSG_NXTHDR(&msg, cmptr)) {
370
371 #ifdef IP_PKTINFO
372 #if in_pktinfo_definition_is_missing
373 struct in_pktinfo
374 {
375 int ipi_ifindex;
376 struct in_addr ipi_spec_dst;
377 struct in_addr ipi_addr;
378 };
379 #endif
380 if (cmptr->cmsg_level == IPPROTO_IP &&
381 cmptr->cmsg_type == IP_PKTINFO) {
382 struct in_pktinfo *tmp;
383 struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
384
385 tmp = (struct in_pktinfo *) CMSG_DATA(cmptr);
386 sin->sin_family = AF_INET;
387 sin->sin_addr = tmp->ipi_addr;
388 sin->sin_port = 0;
389 pktp->ipi_ifindex = tmp->ipi_ifindex;
390 continue;
391 }
392 #endif
393
394 #ifdef IP_RECVDSTADDR
395 if (cmptr->cmsg_level == IPPROTO_IP &&
396 cmptr->cmsg_type == IP_RECVDSTADDR) {
397 struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
398
399 sin->sin_family = AF_INET;
400 sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr);
401 sin->sin_port = 0;
402 continue;
403 }
404 #endif
405
406 #ifdef IP_RECVIF
407 if (cmptr->cmsg_level == IPPROTO_IP &&
408 cmptr->cmsg_type == IP_RECVIF) {
409 struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr);
410 int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1);
411 pktp->ipi_ifindex = sdl->sdl_index;
412 #ifndef HAVE_BROKEN_RECVIF_NAME
413 strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen);
414 #endif
415 assert(pktp->ipi_ifname[IFI_NAME - 1] == 0);
416 // null terminated because of memset above
417 continue;
418 }
419 #endif
420
421 #ifdef IP_RECVTTL
422 if (cmptr->cmsg_level == IPPROTO_IP &&
423 cmptr->cmsg_type == IP_RECVTTL) {
424 *ttl = *(u_char*)CMSG_DATA(cmptr);
425 continue;
426 }
427 else if (cmptr->cmsg_level == IPPROTO_IP &&
428 cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL
429 *ttl = *(int*)CMSG_DATA(cmptr);
430 continue;
431 }
432 #endif
433
434 #if defined(IPV6_PKTINFO) && HAVE_IPV6
435 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
436 cmptr->cmsg_type == IPV6_PKTINFO) {
437 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
438 struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
439
440 sin6->sin6_family = AF_INET6;
441 sin6->sin6_len = sizeof(*sin6);
442 sin6->sin6_addr = ip6_info->ipi6_addr;
443 sin6->sin6_flowinfo = 0;
444 sin6->sin6_scope_id = 0;
445 sin6->sin6_port = 0;
446 pktp->ipi_ifindex = ip6_info->ipi6_ifindex;
447 continue;
448 }
449 #endif
450
451 #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
452 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
453 cmptr->cmsg_type == IPV6_HOPLIMIT) {
454 *ttl = *(int*)CMSG_DATA(cmptr);
455 continue;
456 }
457 #endif
458 assert(0); // unknown ancillary data
459 }
460 return(n);
461 #endif /* CMSG_FIRSTHDR */
462 }