| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| /* -*- Mode: C; tab-width: 4 -*- |
| * |
| * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| |
| Change History (most recent first): |
| |
| $Log: mDNSUNP.c,v $ |
| Revision 1.34 2006/08/14 23:24:47 cheshire |
| Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 |
| |
| Revision 1.33 2006/03/13 23:14:21 cheshire |
| <rdar://problem/4427969> Compile problems on FreeBSD |
| Use <netinet/in_var.h> instead of <netinet6/in6_var.h> |
| |
| Revision 1.32 2005/12/21 02:56:43 cheshire |
| <rdar://problem/4243433> get_ifi_info() should fake ifi_index when SIOCGIFINDEX undefined |
| |
| Revision 1.31 2005/12/21 02:46:05 cheshire |
| <rdar://problem/4243514> mDNSUNP.c needs to include <sys/param.h> on 4.4BSD Lite |
| |
| Revision 1.30 2005/11/29 20:03:02 mkrochma |
| Wrapped sin_len with #ifndef NOT_HAVE_SA_LEN |
| |
| Revision 1.29 2005/11/12 02:23:10 cheshire |
| <rdar://problem/4317680> mDNSUNP.c needs to deal with lame results from SIOCGIFNETMASK, SIOCGIFBRDADDR and SIOCGIFDSTADDR |
| |
| Revision 1.28 2005/10/31 22:09:45 cheshire |
| Buffer "char addr6[33]" was seven bytes too small |
| |
| Revision 1.27 2005/06/29 15:54:21 cheshire |
| <rdar://problem/4113742> mDNSResponder-107.1 does not work on FreeBSD |
| Refine last checkin so that it (hopefully) doesn't break get_ifi_info() for every other OS |
| |
| Revision 1.26 2005/04/08 21:43:59 ksekar |
| <rdar://problem/4083426> mDNSPosix (v98) retrieve interface list bug on AMD64 architecture |
| Submitted by Andrew de Quincey |
| |
| Revision 1.25 2005/04/08 21:37:57 ksekar |
| <rdar://problem/3792767> get_ifi_info doesn't return IPv6 interfaces on Linux |
| |
| Revision 1.24 2005/04/08 21:30:16 ksekar |
| <rdar://problem/4007457> Compiling problems with mDNSResponder-98 on Solaris/Sparc v9 |
| Patch submitted by Bernd Kuhls |
| |
| Revision 1.23 2004/12/01 04:25:05 cheshire |
| <rdar://problem/3872803> Darwin patches for Solaris and Suse |
| Provide daemon() for platforms that don't have it |
| |
| Revision 1.22 2004/11/30 22:37:01 cheshire |
| Update copyright dates and add "Mode: C; tab-width: 4" headers |
| |
| Revision 1.21 2004/11/08 22:13:59 rpantos |
| Create sockf6 lazily when v6 interface found. |
| |
| Revision 1.20 2004/10/16 00:17:01 cheshire |
| <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check |
| |
| Revision 1.19 2004/07/20 01:47:36 rpantos |
| NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr. |
| |
| Revision 1.18 2004/07/08 21:30:21 rpantos |
| |
| Revision 1.17 2004/06/25 00:26:27 rpantos |
| Changes to fix the Posix build on Solaris. |
| |
| Revision 1.16 2004/03/20 05:37:09 cheshire |
| Fix contributed by Terry Lambert & Alfred Perlstein: |
| Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x |
| |
| Revision 1.15 2004/02/14 01:09:45 rpantos |
| Just use HAVE_IPV6 rather than defined(HAVE_IPV6). |
| |
| Revision 1.14 2003/12/11 18:53:40 cheshire |
| Fix compiler warning reported by Paul Guyot |
| |
| Revision 1.13 2003/12/08 20:47:02 rpantos |
| Add support for mDNSResponder on Linux. |
| |
| Revision 1.12 2003/09/02 20:47:13 cheshire |
| Fix signed/unsigned warning |
| |
| Revision 1.11 2003/08/12 19:56:26 cheshire |
| Update to APSL 2.0 |
| |
| Revision 1.10 2003/08/06 18:20:51 cheshire |
| Makefile cleanup |
| |
| Revision 1.9 2003/07/14 18:11:54 cheshire |
| Fix stricter compiler warnings |
| |
| Revision 1.8 2003/07/02 21:19:59 cheshire |
| <rdar://problem/3313413> Update copyright notices, etc., in source code comments |
| |
| Revision 1.7 2003/03/20 21:10:31 cheshire |
| Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris |
| |
| Revision 1.6 2003/03/13 03:46:21 cheshire |
| Fixes to make the code build on Linux |
| |
| Revision 1.5 2003/02/07 03:02:02 cheshire |
| Submitted by: Mitsutaka Watanabe |
| The code saying "index += 1;" was effectively making up random interface index values. |
| The right way to find the correct interface index is if_nametoindex(); |
| |
| Revision 1.4 2002/12/23 22:13:31 jgraessl |
| |
| Reviewed by: Stuart Cheshire |
| Initial IPv6 support for mDNSResponder. |
| |
| Revision 1.3 2002/09/21 20:44:53 zarzycki |
| Added APSL info |
| |
| Revision 1.2 2002/09/19 04:20:44 cheshire |
| Remove high-ascii characters that confuse some systems |
| |
| Revision 1.1 2002/09/17 06:24:34 cheshire |
| First checkin |
| |
| */ |
| |
| #include "mDNSUNP.h" |
| |
| #include "mDNSDebug.h" |
| |
| #include <errno.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <sys/uio.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| |
| /* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P) |
| macro, usually defined in <sys/param.h> or someplace like that, to make sure the |
| CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO |
| should be set to the name of the header to include to get the ALIGN(P) macro. |
| */ |
| #ifdef NEED_ALIGN_MACRO |
| #include NEED_ALIGN_MACRO |
| #endif |
| |
| /* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but |
| other platforms don't even have that include file. So, |
| if we haven't yet got a definition, let's try to find |
| <sys/sockio.h>. |
| */ |
| |
| #ifndef SIOCGIFCONF |
| #include <sys/sockio.h> |
| #endif |
| |
| /* sockaddr_dl is only referenced if we're using IP_RECVIF, |
| so only include the header in that case. |
| */ |
| |
| #ifdef IP_RECVIF |
| #include <net/if_dl.h> |
| #endif |
| |
| #if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX |
| #if !HAVE_SOLARIS |
| #include <net/if_var.h> |
| #else |
| #include <alloca.h> |
| #ifdef HAVE_SOLARIS_ZONES |
| #include <zone.h> |
| #endif /* HAVE_SOLARIS_ZONES */ |
| #endif /* !HAVE_SOLARIS */ |
| #include <netinet/in_var.h> |
| // NOTE: netinet/in_var.h implicitly includes netinet6/in6_var.h for us |
| #endif |
| |
| #if defined(AF_INET6) && HAVE_IPV6 |
| |
| #if HAVE_LINUX |
| #include <netdb.h> |
| #include <arpa/inet.h> |
| |
| /* Converts a prefix length to IPv6 network mask */ |
| void plen_to_mask(int plen, char *addr) { |
| int i; |
| int colons=7; /* Number of colons in IPv6 address */ |
| int bits_in_block=16; /* Bits per IPv6 block */ |
| for(i=0;i<=colons;i++) { |
| int block, ones=0xffff, ones_in_block; |
| if(plen>bits_in_block) ones_in_block=bits_in_block; |
| else ones_in_block=plen; |
| block = ones & (ones << (bits_in_block-ones_in_block)); |
| i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block); |
| plen -= ones_in_block; |
| } |
| } |
| |
| /* Gets IPv6 interface information from the /proc filesystem in linux*/ |
| struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) |
| { |
| struct ifi_info *ifi, *ifihead, **ifipnext; |
| FILE *fp; |
| char addr[8][5]; |
| int flags, myflags, index, plen, scope; |
| char ifname[8], lastname[IFNAMSIZ]; |
| char addr6[32+7+1]; /* don't forget the seven ':' */ |
| struct addrinfo hints, *res0; |
| struct sockaddr_in6 *sin6; |
| struct in6_addr *addrptr; |
| int err; |
| |
| res0=NULL; |
| ifihead = NULL; |
| ifipnext = &ifihead; |
| lastname[0] = 0; |
| |
| if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { |
| while (fscanf(fp, |
| "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n", |
| addr[0],addr[1],addr[2],addr[3], |
| addr[4],addr[5],addr[6],addr[7], |
| &index, &plen, &scope, &flags, ifname) != EOF) { |
| |
| myflags = 0; |
| if (strncmp(lastname, ifname, IFNAMSIZ) == 0) { |
| if (doaliases == 0) |
| continue; /* already processed this interface */ |
| myflags = IFI_ALIAS; |
| } |
| memcpy(lastname, ifname, IFNAMSIZ); |
| ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); |
| if (ifi == NULL) { |
| goto gotError; |
| } |
| |
| *ifipnext = ifi; /* prev points to this new one */ |
| ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ |
| |
| sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", |
| addr[0],addr[1],addr[2],addr[3], |
| addr[4],addr[5],addr[6],addr[7]); |
| |
| /* Add address of the interface */ |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = AF_INET6; |
| hints.ai_flags = AI_NUMERICHOST; |
| err = getaddrinfo(addr6, NULL, &hints, &res0); |
| if (err) { |
| goto gotError; |
| } |
| ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); |
| if (ifi->ifi_addr == NULL) { |
| goto gotError; |
| } |
| memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6)); |
| |
| /* Add netmask of the interface */ |
| char ipv6addr[INET6_ADDRSTRLEN]; |
| plen_to_mask(plen, ipv6addr); |
| ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); |
| if (ifi->ifi_addr == NULL) { |
| goto gotError; |
| } |
| sin6=calloc(1, sizeof(struct sockaddr_in6)); |
| addrptr=calloc(1, sizeof(struct in6_addr)); |
| inet_pton(family, ipv6addr, addrptr); |
| sin6->sin6_family=family; |
| sin6->sin6_addr=*addrptr; |
| sin6->sin6_scope_id=scope; |
| memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6)); |
| free(sin6); |
| |
| |
| /* Add interface name */ |
| memcpy(ifi->ifi_name, ifname, IFI_NAME); |
| |
| /* Add interface index */ |
| ifi->ifi_index = index; |
| |
| /* If interface is in /proc then it is up*/ |
| ifi->ifi_flags = IFF_UP; |
| |
| freeaddrinfo(res0); |
| res0=NULL; |
| } |
| } |
| goto done; |
| |
| gotError: |
| if (ifihead != NULL) { |
| free_ifi_info(ifihead); |
| ifihead = NULL; |
| } |
| if (res0 != NULL) { |
| freeaddrinfo(res0); |
| res0=NULL; |
| } |
| done: |
| return(ifihead); /* pointer to first structure in linked list */ |
| } |
| |
| #endif /* LINUX */ |
| #endif /* defined(AF_INET6) && HAVE_IPV6 */ |
| |
| #if HAVE_SOLARIS |
| |
| /* |
| * Converts prefix length to network mask. Assumes |
| * addr points to a zeroed out buffer and prefix <= sizeof(addr) |
| * Unlike plen_to_mask returns netmask in binary form and not |
| * in text form. |
| */ |
| static void plen_to_netmask(int prefix, unsigned char *addr) { |
| for (; prefix > 8; prefix -= 8) |
| *addr++ = 0xff; |
| for (; prefix > 0; prefix--) |
| *addr = (*addr >> 1) | 0x80; |
| } |
| |
| /* |
| * This function goes through all the IP interfaces associated with a |
| * physical interface and finds the best matched one for use by mDNS. |
| * Returns NULL when none of the IP interfaces associated with a physical |
| * interface are usable. Otherwise returns the best matched interface |
| * information and a pointer to the best matched lifreq. |
| */ |
| struct ifi_info * |
| select_src_ifi_info_solaris(int sockfd, int numifs, |
| struct lifreq *lifrlist, const char *curifname, |
| struct lifreq **best_lifr) |
| { |
| struct lifreq *lifr; |
| struct lifreq lifrcopy; |
| struct ifi_info *ifi; |
| char *chptr; |
| char cmpifname[LIFNAMSIZ]; |
| int i; |
| uint64_t best_lifrflags; |
| uint64_t ifflags; |
| |
| *best_lifr = NULL; |
| |
| /* |
| * Check all logical interfaces associated with the physical |
| * interface and figure out which one works best for us. |
| */ |
| for (i = numifs, lifr = lifrlist; i > 0; --i, ++lifr) { |
| |
| if (strlcpy(cmpifname, lifr->lifr_name, sizeof(cmpifname)) >= sizeof(cmpifname)) |
| continue; /* skip interface */ |
| |
| /* Strip logical interface number before checking ifname */ |
| if ((chptr = strchr(cmpifname, ':')) != NULL) |
| *chptr = '\0'; |
| |
| /* |
| * Check ifname to see if the logical interface is associated |
| * with the physical interface we are interested in. |
| */ |
| if (strcmp(cmpifname, curifname) != 0) |
| continue; |
| |
| #ifdef HAVE_SOLARIS_ZONES |
| /* Check the zone associated with the address */ |
| lifrcopy = *lifr; |
| if (ioctl(sockfd, SIOCGLIFZONE, &lifrcopy) < 0) { |
| /* interface removed */ |
| if (errno == ENXIO) |
| continue; |
| return(NULL); |
| } |
| if (lifrcopy.lifr_zoneid != getzoneid()) |
| continue; |
| #endif |
| |
| lifrcopy = *lifr; |
| if (ioctl(sockfd, SIOCGLIFFLAGS, &lifrcopy) < 0) { |
| /* interface removed */ |
| if (errno == ENXIO) |
| continue; |
| return(NULL); |
| } |
| ifflags = lifrcopy.lifr_flags; |
| |
| /* ignore address if not up */ |
| if ((ifflags & IFF_UP) == 0) |
| continue; |
| /* |
| * Avoid address if any of the following flags are set: |
| * IFF_NOXMIT: no packets transmitted over interface |
| * IFF_NOLOCAL: no address |
| * IFF_PRIVATE: is not advertised |
| */ |
| if (ifflags & (IFF_NOXMIT | IFF_NOLOCAL | IFF_PRIVATE)) |
| continue; |
| |
| /* A DHCP client will have IFF_UP set yet the address is zero. Ignore */ |
| if (lifr->lifr_addr.ss_family == AF_INET) { |
| struct sockaddr_in *sinptr; |
| |
| sinptr = (struct sockaddr_in *) &lifr->lifr_addr; |
| if (sinptr->sin_addr.s_addr == INADDR_ANY) |
| continue; |
| } |
| |
| if (*best_lifr != NULL) { |
| /* |
| * Check if we found a better interface by checking |
| * the flags. If flags are identical we prefer |
| * the new found interface. |
| */ |
| uint64_t diff_flags = best_lifrflags ^ ifflags; |
| |
| /* If interface has a different set of flags */ |
| if (diff_flags != 0) { |
| /* Check flags in increasing order of ones we prefer */ |
| |
| /* Address temporary? */ |
| if ((diff_flags & IFF_TEMPORARY) && |
| (ifflags & IFF_TEMPORARY)) |
| continue; |
| /* Deprecated address? */ |
| if ((diff_flags & IFF_DEPRECATED) && |
| (ifflags & IFF_DEPRECATED)) |
| continue; |
| /* Last best-matched interface address has preferred? */ |
| if ((diff_flags & IFF_PREFERRED) && |
| ((ifflags & IFF_PREFERRED) == 0)) |
| continue; |
| } |
| } |
| |
| /* Set best match interface & flags */ |
| *best_lifr = lifr; |
| best_lifrflags = ifflags; |
| } |
| |
| if (*best_lifr == NULL) |
| return(NULL); |
| |
| /* Found a match: return the interface information */ |
| ifi = calloc(1, sizeof(struct ifi_info)); |
| if (ifi == NULL) |
| return(NULL); |
| |
| ifi->ifi_flags = best_lifrflags; |
| ifi->ifi_index = if_nametoindex((*best_lifr)->lifr_name); |
| if (strlcpy(ifi->ifi_name, (*best_lifr)->lifr_name, sizeof(ifi->ifi_name)) >= sizeof(ifi->ifi_name)) { |
| free(ifi); |
| return(NULL); |
| } |
| return(ifi); |
| } |
| |
| /* |
| * Returns a list of IP interface information on Solaris. The function |
| * returns all IP interfaces on the system with IPv4 address assigned |
| * when passed AF_INET and returns IP interfaces with IPv6 address assigned |
| * when AF_INET6 is passed. |
| */ |
| struct ifi_info *get_ifi_info_solaris(int family) |
| { |
| struct ifi_info *ifi, *ifihead, **ifipnext; |
| int sockfd; |
| int len; |
| char *buf; |
| char *cptr; |
| char ifname[LIFNAMSIZ], cmpifname[LIFNAMSIZ]; |
| struct sockaddr_in *sinptr; |
| struct lifnum lifn; |
| struct lifconf lifc; |
| struct lifreq *lifrp, *best_lifr; |
| struct lifreq lifrcopy; |
| int numifs, nlifr, n; |
| #if defined(AF_INET6) && HAVE_IPV6 |
| struct sockaddr_in6 *sinptr6; |
| #endif |
| |
| ifihead = NULL; |
| |
| sockfd = socket(family, SOCK_DGRAM, 0); |
| if (sockfd < 0) |
| goto gotError; |
| |
| again: |
| lifn.lifn_family = family; |
| lifn.lifn_flags = 0; |
| if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0) |
| goto gotError; |
| /* |
| * Pad interface count to detect & retrieve any |
| * additional interfaces between IFNUM & IFCONF calls. |
| */ |
| lifn.lifn_count += 4; |
| numifs = lifn.lifn_count; |
| len = numifs * sizeof (struct lifreq); |
| buf = alloca(len); |
| |
| lifc.lifc_family = family; |
| lifc.lifc_len = len; |
| lifc.lifc_buf = buf; |
| lifc.lifc_flags = 0; |
| |
| if (ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0) |
| goto gotError; |
| |
| nlifr = lifc.lifc_len / sizeof(struct lifreq); |
| if (nlifr >= numifs) |
| goto again; |
| |
| lifrp = lifc.lifc_req; |
| ifipnext = &ifihead; |
| |
| for (n = nlifr; n > 0; n--, lifrp++) { |
| |
| if (lifrp->lifr_addr.ss_family != family) |
| continue; |
| |
| /* |
| * See if we have already processed the interface |
| * by checking the interface names. |
| */ |
| if (strlcpy(ifname, lifrp->lifr_name, sizeof(ifname)) >= sizeof(ifname)) |
| goto gotError; |
| if ((cptr = strchr(ifname, ':')) != NULL) |
| *cptr = '\0'; |
| |
| /* |
| * If any of the interfaces found so far share the physical |
| * interface name then we have already processed the interface. |
| */ |
| for (ifi = ifihead; ifi != NULL; ifi = ifi->ifi_next) { |
| |
| /* Retrieve physical interface name */ |
| (void) strlcpy(cmpifname, ifi->ifi_name, sizeof(cmpifname)); |
| |
| /* Strip logical interface number before checking ifname */ |
| if ((cptr = strchr(cmpifname, ':')) != NULL) |
| *cptr = '\0'; |
| |
| if (strcmp(cmpifname, ifname) == 0) |
| break; |
| } |
| if (ifi != NULL) |
| continue; /* already processed */ |
| |
| /* |
| * New interface, find the one with the preferred source |
| * address for our use in Multicast DNS. |
| */ |
| if ((ifi = select_src_ifi_info_solaris(sockfd, nlifr, |
| lifc.lifc_req, ifname, &best_lifr)) == NULL) |
| continue; |
| |
| assert(best_lifr != NULL); |
| assert((best_lifr->lifr_addr.ss_family == AF_INET6) || |
| (best_lifr->lifr_addr.ss_family == AF_INET)); |
| |
| switch (best_lifr->lifr_addr.ss_family) { |
| |
| #if defined(AF_INET6) && HAVE_IPV6 |
| case AF_INET6: |
| sinptr6 = (struct sockaddr_in6 *) &best_lifr->lifr_addr; |
| ifi->ifi_addr = malloc(sizeof(struct sockaddr_in6)); |
| if (ifi->ifi_addr == NULL) |
| goto gotError; |
| memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); |
| |
| ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); |
| if (ifi->ifi_netmask == NULL) |
| goto gotError; |
| sinptr6 = (struct sockaddr_in6 *)(ifi->ifi_netmask); |
| sinptr6->sin6_family = AF_INET6; |
| plen_to_netmask(best_lifr->lifr_addrlen, |
| (unsigned char *) &(sinptr6->sin6_addr)); |
| break; |
| #endif |
| |
| case AF_INET: |
| sinptr = (struct sockaddr_in *) &best_lifr->lifr_addr; |
| ifi->ifi_addr = malloc(sizeof(struct sockaddr_in)); |
| if (ifi->ifi_addr == NULL) |
| goto gotError; |
| |
| memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); |
| |
| lifrcopy = *best_lifr; |
| if (ioctl(sockfd, SIOCGLIFNETMASK, &lifrcopy) < 0) { |
| /* interface removed */ |
| if (errno == ENXIO) { |
| free(ifi->ifi_addr); |
| free(ifi); |
| continue; |
| } |
| goto gotError; |
| } |
| |
| ifi->ifi_netmask = malloc(sizeof(struct sockaddr_in)); |
| if (ifi->ifi_netmask == NULL) |
| goto gotError; |
| sinptr = (struct sockaddr_in *) &lifrcopy.lifr_addr; |
| sinptr->sin_family = AF_INET; |
| memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); |
| break; |
| |
| default: |
| /* never reached */ |
| break; |
| } |
| |
| *ifipnext = ifi; /* prev points to this new one */ |
| ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ |
| } |
| |
| (void) close(sockfd); |
| return(ifihead); /* pointer to first structure in linked list */ |
| |
| gotError: |
| if (sockfd != -1) |
| (void) close(sockfd); |
| if (ifihead != NULL) |
| free_ifi_info(ifihead); |
| return(NULL); |
| } |
| |
| #endif /* HAVE_SOLARIS */ |
| |
| struct ifi_info *get_ifi_info(int family, int doaliases) |
| { |
| int junk; |
| struct ifi_info *ifi, *ifihead, **ifipnext; |
| int sockfd, sockf6, len, lastlen, flags, myflags; |
| #ifdef NOT_HAVE_IF_NAMETOINDEX |
| int index = 200; |
| #endif |
| char *ptr, *buf, lastname[IFNAMSIZ], *cptr; |
| struct ifconf ifc; |
| struct ifreq *ifr, ifrcopy; |
| struct sockaddr_in *sinptr; |
| |
| #if defined(AF_INET6) && HAVE_IPV6 |
| struct sockaddr_in6 *sinptr6; |
| #endif |
| |
| #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX |
| if(family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); |
| #elif HAVE_SOLARIS |
| return get_ifi_info_solaris(family); |
| #endif |
| |
| sockfd = -1; |
| sockf6 = -1; |
| buf = NULL; |
| ifihead = NULL; |
| |
| sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
| if (sockfd < 0) { |
| goto gotError; |
| } |
| |
| lastlen = 0; |
| len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ |
| for ( ; ; ) { |
| buf = (char*)malloc(len); |
| if (buf == NULL) { |
| goto gotError; |
| } |
| ifc.ifc_len = len; |
| ifc.ifc_buf = buf; |
| if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { |
| if (errno != EINVAL || lastlen != 0) { |
| goto gotError; |
| } |
| } else { |
| if (ifc.ifc_len == lastlen) |
| break; /* success, len has not changed */ |
| lastlen = ifc.ifc_len; |
| } |
| len += 10 * sizeof(struct ifreq); /* increment */ |
| free(buf); |
| } |
| ifihead = NULL; |
| ifipnext = &ifihead; |
| lastname[0] = 0; |
| /* end get_ifi_info1 */ |
| |
| /* include get_ifi_info2 */ |
| for (ptr = buf; ptr < buf + ifc.ifc_len; ) { |
| ifr = (struct ifreq *) ptr; |
| |
| /* Advance to next one in buffer */ |
| if (sizeof(struct ifreq) > sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr)) |
| ptr += sizeof(struct ifreq); |
| else |
| ptr += sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr); |
| |
| // fprintf(stderr, "intf %p name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family); |
| |
| if (ifr->ifr_addr.sa_family != family) |
| continue; /* ignore if not desired address family */ |
| |
| myflags = 0; |
| if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) |
| *cptr = 0; /* replace colon will null */ |
| if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { |
| if (doaliases == 0) |
| continue; /* already processed this interface */ |
| myflags = IFI_ALIAS; |
| } |
| memcpy(lastname, ifr->ifr_name, IFNAMSIZ); |
| |
| ifrcopy = *ifr; |
| if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) { |
| goto gotError; |
| } |
| |
| flags = ifrcopy.ifr_flags; |
| if ((flags & IFF_UP) == 0) |
| continue; /* ignore if interface not up */ |
| |
| ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); |
| if (ifi == NULL) { |
| goto gotError; |
| } |
| *ifipnext = ifi; /* prev points to this new one */ |
| ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ |
| |
| ifi->ifi_flags = flags; /* IFF_xxx values */ |
| ifi->ifi_myflags = myflags; /* IFI_xxx values */ |
| #ifndef NOT_HAVE_IF_NAMETOINDEX |
| ifi->ifi_index = if_nametoindex(ifr->ifr_name); |
| #else |
| ifrcopy = *ifr; |
| #ifdef SIOCGIFINDEX |
| if ( 0 >= ioctl(sockfd, SIOCGIFINDEX, &ifrcopy)) |
| ifi->ifi_index = ifrcopy.ifr_index; |
| else |
| #endif |
| ifi->ifi_index = index++; /* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */ |
| #endif |
| memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); |
| ifi->ifi_name[IFI_NAME-1] = '\0'; |
| /* end get_ifi_info2 */ |
| /* include get_ifi_info3 */ |
| switch (ifr->ifr_addr.sa_family) { |
| case AF_INET: |
| sinptr = (struct sockaddr_in *) &ifr->ifr_addr; |
| if (ifi->ifi_addr == NULL) { |
| ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); |
| if (ifi->ifi_addr == NULL) { |
| goto gotError; |
| } |
| memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); |
| |
| #ifdef SIOCGIFNETMASK |
| if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) goto gotError; |
| ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); |
| if (ifi->ifi_netmask == NULL) goto gotError; |
| sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr; |
| /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ |
| #ifndef NOT_HAVE_SA_LEN |
| sinptr->sin_len = sizeof(struct sockaddr_in); |
| #endif |
| sinptr->sin_family = AF_INET; |
| memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); |
| #endif |
| |
| #ifdef SIOCGIFBRDADDR |
| if (flags & IFF_BROADCAST) { |
| if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) { |
| goto gotError; |
| } |
| sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; |
| /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ |
| #ifndef NOT_HAVE_SA_LEN |
| sinptr->sin_len = sizeof( struct sockaddr_in ); |
| #endif |
| sinptr->sin_family = AF_INET; |
| ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); |
| if (ifi->ifi_brdaddr == NULL) { |
| goto gotError; |
| } |
| memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in)); |
| } |
| #endif |
| |
| #ifdef SIOCGIFDSTADDR |
| if (flags & IFF_POINTOPOINT) { |
| if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) { |
| goto gotError; |
| } |
| sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; |
| /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ |
| #ifndef NOT_HAVE_SA_LEN |
| sinptr->sin_len = sizeof( struct sockaddr_in ); |
| #endif |
| sinptr->sin_family = AF_INET; |
| ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); |
| if (ifi->ifi_dstaddr == NULL) { |
| goto gotError; |
| } |
| memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in)); |
| } |
| #endif |
| } |
| break; |
| |
| #if defined(AF_INET6) && HAVE_IPV6 |
| case AF_INET6: |
| sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr; |
| if (ifi->ifi_addr == NULL) { |
| ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); |
| if (ifi->ifi_addr == NULL) { |
| goto gotError; |
| } |
| |
| /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */ |
| /* We need to strip that out */ |
| if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr)) |
| sinptr6->sin6_addr.s6_addr[2] = sinptr6->sin6_addr.s6_addr[3] = 0; |
| memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); |
| |
| #ifdef SIOCGIFNETMASK_IN6 |
| { |
| struct in6_ifreq ifr6; |
| if (sockf6 == -1) |
| sockf6 = socket(AF_INET6, SOCK_DGRAM, 0); |
| bzero(&ifr6, sizeof(ifr6)); |
| memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name )); |
| memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr)); |
| if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) goto gotError; |
| ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6)); |
| if (ifi->ifi_netmask == NULL) goto gotError; |
| sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr; |
| memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6)); |
| } |
| #endif |
| } |
| break; |
| #endif |
| |
| default: |
| break; |
| } |
| } |
| goto done; |
| |
| gotError: |
| if (ifihead != NULL) { |
| free_ifi_info(ifihead); |
| ifihead = NULL; |
| } |
| |
| done: |
| if (buf != NULL) { |
| free(buf); |
| } |
| if (sockfd != -1) { |
| junk = close(sockfd); |
| assert(junk == 0); |
| } |
| if (sockf6 != -1) { |
| junk = close(sockf6); |
| assert(junk == 0); |
| } |
| return(ifihead); /* pointer to first structure in linked list */ |
| } |
| /* end get_ifi_info3 */ |
| |
| /* include free_ifi_info */ |
| void |
| free_ifi_info(struct ifi_info *ifihead) |
| { |
| struct ifi_info *ifi, *ifinext; |
| |
| for (ifi = ifihead; ifi != NULL; ifi = ifinext) { |
| if (ifi->ifi_addr != NULL) |
| free(ifi->ifi_addr); |
| if (ifi->ifi_brdaddr != NULL) |
| free(ifi->ifi_brdaddr); |
| if (ifi->ifi_dstaddr != NULL) |
| free(ifi->ifi_dstaddr); |
| if (ifi->ifi_netmask != NULL) |
| free(ifi->ifi_netmask); |
| ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */ |
| free(ifi); /* the ifi_info{} itself */ |
| } |
| } |
| /* end free_ifi_info */ |
| |
| ssize_t |
| recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, |
| struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl) |
| { |
| struct msghdr msg; |
| struct iovec iov[1]; |
| ssize_t n; |
| |
| #ifdef CMSG_FIRSTHDR |
| struct cmsghdr *cmptr; |
| union { |
| struct cmsghdr cm; |
| char control[1024]; |
| pad64_t align8; /* ensure structure is 8-byte aligned on sparc */ |
| } control_un; |
| |
| *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be |
| |
| msg.msg_control = (void *) control_un.control; |
| msg.msg_controllen = sizeof(control_un.control); |
| msg.msg_flags = 0; |
| #else |
| memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */ |
| #endif /* CMSG_FIRSTHDR */ |
| |
| msg.msg_name = (char *) sa; |
| msg.msg_namelen = *salenptr; |
| iov[0].iov_base = (char *)ptr; |
| iov[0].iov_len = nbytes; |
| msg.msg_iov = iov; |
| msg.msg_iovlen = 1; |
| |
| if ( (n = recvmsg(fd, &msg, *flagsp)) < 0) |
| return(n); |
| |
| *salenptr = msg.msg_namelen; /* pass back results */ |
| if (pktp) { |
| /* 0.0.0.0, i/f = -1 */ |
| /* We set the interface to -1 so that the caller can |
| tell whether we returned a meaningful value or |
| just some default. Previously this code just |
| set the value to 0, but I'm concerned that 0 |
| might be a valid interface value. |
| */ |
| memset(pktp, 0, sizeof(struct my_in_pktinfo)); |
| pktp->ipi_ifindex = -1; |
| } |
| /* end recvfrom_flags1 */ |
| |
| /* include recvfrom_flags2 */ |
| #ifndef CMSG_FIRSTHDR |
| #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. |
| *flagsp = 0; /* pass back results */ |
| return(n); |
| #else |
| |
| *flagsp = msg.msg_flags; /* pass back results */ |
| if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) || |
| (msg.msg_flags & MSG_CTRUNC) || pktp == NULL) |
| return(n); |
| |
| for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; |
| cmptr = CMSG_NXTHDR(&msg, cmptr)) { |
| |
| #ifdef IP_PKTINFO |
| #if in_pktinfo_definition_is_missing |
| struct in_pktinfo |
| { |
| int ipi_ifindex; |
| struct in_addr ipi_spec_dst; |
| struct in_addr ipi_addr; |
| }; |
| #endif |
| if (cmptr->cmsg_level == IPPROTO_IP && |
| cmptr->cmsg_type == IP_PKTINFO) { |
| struct in_pktinfo *tmp; |
| struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; |
| |
| tmp = (struct in_pktinfo *) CMSG_DATA(cmptr); |
| sin->sin_family = AF_INET; |
| sin->sin_addr = tmp->ipi_addr; |
| sin->sin_port = 0; |
| pktp->ipi_ifindex = tmp->ipi_ifindex; |
| continue; |
| } |
| #endif |
| |
| #ifdef IP_RECVDSTADDR |
| if (cmptr->cmsg_level == IPPROTO_IP && |
| cmptr->cmsg_type == IP_RECVDSTADDR) { |
| struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; |
| |
| sin->sin_family = AF_INET; |
| sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr); |
| sin->sin_port = 0; |
| continue; |
| } |
| #endif |
| |
| #ifdef IP_RECVIF |
| if (cmptr->cmsg_level == IPPROTO_IP && |
| cmptr->cmsg_type == IP_RECVIF) { |
| struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr); |
| #ifndef HAVE_BROKEN_RECVIF_NAME |
| int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1); |
| strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen); |
| #endif |
| (void) memcpy(&pktp->ipi_ifindex, CMSG_DATA(cmptr), sizeof(uint_t)); |
| assert(pktp->ipi_ifname[IFI_NAME - 1] == 0); |
| // null terminated because of memset above |
| continue; |
| } |
| #endif |
| |
| #ifdef IP_RECVTTL |
| if (cmptr->cmsg_level == IPPROTO_IP && |
| cmptr->cmsg_type == IP_RECVTTL) { |
| *ttl = *(u_char*)CMSG_DATA(cmptr); |
| continue; |
| } |
| else if (cmptr->cmsg_level == IPPROTO_IP && |
| cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL |
| *ttl = *(int*)CMSG_DATA(cmptr); |
| continue; |
| } |
| #endif |
| |
| #if defined(IPV6_PKTINFO) && HAVE_IPV6 |
| if (cmptr->cmsg_level == IPPROTO_IPV6 && |
| cmptr->cmsg_type == IPV6_PKTINFO) { |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr; |
| struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr); |
| |
| sin6->sin6_family = AF_INET6; |
| #ifndef NOT_HAVE_SA_LEN |
| sin6->sin6_len = sizeof(*sin6); |
| #endif |
| sin6->sin6_addr = ip6_info->ipi6_addr; |
| sin6->sin6_flowinfo = 0; |
| sin6->sin6_scope_id = 0; |
| sin6->sin6_port = 0; |
| pktp->ipi_ifindex = ip6_info->ipi6_ifindex; |
| continue; |
| } |
| #endif |
| |
| #if defined(IPV6_HOPLIMIT) && HAVE_IPV6 |
| if (cmptr->cmsg_level == IPPROTO_IPV6 && |
| cmptr->cmsg_type == IPV6_HOPLIMIT) { |
| *ttl = *(int*)CMSG_DATA(cmptr); |
| continue; |
| } |
| #endif |
| assert(0); // unknown ancillary data |
| } |
| return(n); |
| #endif /* CMSG_FIRSTHDR */ |
| } |
| |
| // ********************************************************************************************** |
| |
| // daemonize the process. Adapted from "Unix Network Programming" vol 1 by Stevens, section 12.4. |
| // Returns 0 on success, -1 on failure. |
| |
| #ifdef NOT_HAVE_DAEMON |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <signal.h> |
| |
| int daemon(int nochdir, int noclose) |
| { |
| switch (fork()) |
| { |
| case -1: return (-1); // Fork failed |
| case 0: break; // Child -- continue |
| default: _exit(0); // Parent -- exit |
| } |
| |
| if (setsid() == -1) return(-1); |
| |
| signal(SIGHUP, SIG_IGN); |
| |
| switch (fork()) // Fork again, primarily for reasons of Unix trivia |
| { |
| case -1: return (-1); // Fork failed |
| case 0: break; // Child -- continue |
| default: _exit(0); // Parent -- exit |
| } |
| |
| if (!nochdir) (void)chdir("/"); |
| umask(0); |
| |
| if (!noclose) |
| { |
| int fd = open("/dev/null", O_RDWR, 0); |
| if (fd != -1) |
| { |
| // Avoid unnecessarily duplicating a file descriptor to itself |
| if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO); |
| if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO); |
| if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO); |
| if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) |
| (void)close (fd); |
| } |
| } |
| return (0); |
| } |
| #endif /* NOT_HAVE_DAEMON */ |