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