/* -*- 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. */ #include "mDNSUNP.h" #include #include #include #include #include #include #include #include #include /* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P) macro, usually defined in 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 but other platforms don't even have that include file. So, if we haven't yet got a definition, let's try to find . */ #ifndef SIOCGIFCONF #include #endif /* sockaddr_dl is only referenced if we're using IP_RECVIF, so only include the header in that case. */ #ifdef IP_RECVIF #include #endif #if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX #if !HAVE_SOLARIS #include #else #include #endif /* !HAVE_SOLARIS */ #include // Note: netinet/in_var.h implicitly includes netinet6/in6_var.h for us #endif #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX #include #include /* 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, *ifipold, **ifiptr; FILE *fp = NULL; char addr[8][5]; int flags, myflags, index, plen, scope; char ifname[9], lastname[IFNAMSIZ]; char addr6[32+7+1]; /* don't forget the seven ':' */ struct addrinfo hints, *res0; int err; int sockfd = -1; struct ifreq ifr; res0=NULL; ifihead = NULL; ifipnext = &ifihead; lastname[0] = 0; if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { sockfd = socket(AF_INET6, SOCK_DGRAM, 0); if (sockfd < 0) { goto gotError; } 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; } ifipold = *ifipnext; /* need this later */ ifiptr = ifipnext; *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_netmask == NULL) { goto gotError; } ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_family=family; ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_scope_id=scope; inet_pton(family, ipv6addr, &((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_addr); /* Add interface name */ memcpy(ifi->ifi_name, ifname, IFI_NAME); /* Add interface index */ ifi->ifi_index = index; /* Add interface flags*/ memcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { if (errno == EADDRNOTAVAIL) { /* * If the main interface is configured with no IP address but * an alias interface exists with an IP address, you get * EADDRNOTAVAIL for the main interface */ free(ifi->ifi_addr); free(ifi->ifi_netmask); free(ifi); ifipnext = ifiptr; *ifipnext = ifipold; continue; } else { goto gotError; } } ifi->ifi_flags = ifr.ifr_flags; freeaddrinfo(res0); res0=NULL; } } goto done; gotError: if (ifihead != NULL) { free_ifi_info(ifihead); ifihead = NULL; } if (res0 != NULL) { freeaddrinfo(res0); res0=NULL; } done: if (sockfd != -1) { int rv; rv = close(sockfd); assert(rv == 0); } if (fp != NULL) { fclose(fp); } return(ifihead); /* pointer to first structure in linked list */ } #endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX #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 = 0; 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; 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, *ifipold, **ifiptr; 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; } ifipold = *ifipnext; /* need this later */ ifiptr = ifipnext; *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) { if (errno == EADDRNOTAVAIL) { /* * If the main interface is configured with no IP address but * an alias interface exists with an IP address, you get * EADDRNOTAVAIL for the main interface */ free(ifi->ifi_addr); free(ifi); ifipnext = ifiptr; *ifipnext = ifipold; continue; } else { 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); memset(&ifr6, 0, 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) { if (errno == EADDRNOTAVAIL) { /* * If the main interface is configured with no IP address but * an alias interface exists with an IP address, you get * EADDRNOTAVAIL for the main interface */ free(ifi->ifi_addr); free(ifi); ifipnext = ifiptr; *ifipnext = ifipold; continue; } else { 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_netmask != NULL) free(ifi->ifi_netmask); if (ifi->ifi_brdaddr != NULL) free(ifi->ifi_brdaddr); if (ifi->ifi_dstaddr != NULL) free(ifi->ifi_dstaddr); 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 = 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 /* * the is memcpy used for sparc? no idea;) * pktp->ipi_ifindex = sdl->sdl_index; */ (void) memcpy(&pktp->ipi_ifindex, CMSG_DATA(cmptr), sizeof(uint_t)); #ifdef HAVE_BROKEN_RECVIF_NAME if (sdl->sdl_index == 0) { pktp->ipi_ifindex = *(uint_t*)sdl; } #endif 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 #include #include 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 */