--- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Clients/build +mDNSPosix/build +mDNSPosix/objects + --- a/mDNSPosix/PosixDaemon.c +++ b/mDNSPosix/PosixDaemon.c @@ -37,6 +37,11 @@ #include #include #include +#ifdef __linux__ +#include /* !!! We require libcap-dev for this. Oh well. */ +/* prctl is required to enable inheriting of capabilities across setuid */ +#include +#endif /* __linux__ */ #if __APPLE__ #undef daemon @@ -184,16 +189,50 @@ int main(int argc, char **argv) Reconfigure(&mDNSStorage); +#ifdef __linux__ + /* + * SO_BINDTODEVICE is privileged operation; however, we can get + * around it using capabilities instead of remaining root. + */ + if (mStatus_NoError == err) + { + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) + perror("prctl PR_SET_KEEPCAPS"); + } +#endif /* __linux__ */ + // Now that we're finished with anything privileged, switch over to running as "nobody" if (mStatus_NoError == err) { const struct passwd *pw = getpwnam("nobody"); if (pw != NULL) + { setuid(pw->pw_uid); +#ifdef __linux__ + struct __user_cap_header_struct ch; + struct __user_cap_data_struct cd[_LINUX_CAPABILITY_U32S_3]; + + memset(&ch, 0, sizeof(ch)); + ch.version = _LINUX_CAPABILITY_VERSION_3; + ch.pid = getpid(); + memset(&cd[0], 0, sizeof(cd)); + /* CAP_NET_RAW is required to use SO_BINDTODEVICE */ + int caps = CAP_TO_MASK(CAP_NET_RAW); + cd[0].permitted = caps; + cd[0].effective = caps; + if (capset(&ch, &cd[0]) < 0) + perror("capset"); +#endif /* __linux__ */ + } else LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist"); } +#ifdef __linux__ + if (mStatus_NoError == err) + err = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); +#endif /* __linux__ */ + if (mStatus_NoError == err) err = MainLoop(&mDNSStorage); --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -603,7 +603,8 @@ static mStatus RegisterServicesInFile(co status = mStatus_UnknownErr; } - assert(0 == fclose(fp)); + int rv = fclose(fp); + assert(0 == rv); return status; } --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -138,7 +138,7 @@ mDNSlocal void SockAddrTomDNSAddr(const // mDNS core calls this routine when it needs to send a packet. mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) { int err = 0; @@ -583,9 +583,17 @@ mDNSlocal void FreePosixNetworkInterface { assert(intf != NULL); if (intf->intfName != NULL) free((void *)intf->intfName); - if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); + if (intf->multicastSocket4 != -1) + { + int rv = close(intf->multicastSocket4); + assert(rv == 0); + } #if HAVE_IPV6 - if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); + if (intf->multicastSocket6 != -1) + { + int rv = close(intf->multicastSocket6); + assert(rv == 0); + } #endif // Move interface to the RecentInterfaces list for a minute @@ -724,6 +732,29 @@ mDNSlocal int SetupSocket(struct sockadd if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } } +#ifdef __linux__ +#ifdef SO_BINDTODEVICE + if (err == 0 && interfaceIndex) + { + char ifname[IFNAMSIZ]; + if (if_indextoname(interfaceIndex, ifname)) + { + err = setsockopt(*sktPtr, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + if (err < 0) + { + err = errno; + perror("setsockopt - SO_BINDTODEVICE"); + } + } + else + { + err = errno; + perror("if_indextoname"); + } + } +#endif /* SO_BINDTODEVICE */ +#endif /* __linux__ */ + // And start listening for packets if (err == 0) { @@ -805,6 +836,29 @@ mDNSlocal int SetupSocket(struct sockadd if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } } +#ifdef __linux__ +#ifdef SO_BINDTODEVICE + if (err == 0 && interfaceIndex) + { + char ifname[IFNAMSIZ]; + if (if_indextoname(interfaceIndex, ifname)) + { + err = setsockopt(*sktPtr, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + if (err < 0) + { + err = errno; + perror("setsockopt - SO_BINDTODEVICE"); + } + } + else + { + err = errno; + perror("if_indextoname"); + } + } +#endif /* SO_BINDTODEVICE */ +#endif /* __linux__ */ + // And start listening for packets if (err == 0) { @@ -836,7 +890,12 @@ mDNSlocal int SetupSocket(struct sockadd } // Clean up - if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } + if (err != 0 && *sktPtr != -1) + { + int rv = close(*sktPtr); + assert(rv == 0); + *sktPtr = -1; + } assert((err == 0) == (*sktPtr != -1)); return err; } @@ -1026,7 +1085,7 @@ mDNSlocal mStatus OpenIfNotifySocket(int /* Subscribe the socket to Link & IP addr notifications. */ mDNSPlatformMemZero(&snl, sizeof snl); snl.nl_family = AF_NETLINK; - snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; ret = bind(sock, (struct sockaddr *) &snl, sizeof snl); if (0 == ret) *pFD = sock; @@ -1104,11 +1163,18 @@ mDNSlocal mDNSu32 ProcessRoutingNo PrintNetLinkMsg(pNLMsg); #endif + // this result isn't used anywhere as a number, just as + // non-zero - however, I have seen devices with more than 32 + // interfaces at some point.. + // (on Linux, every tunnel increases index for example) + // Process the NetLink message if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) - result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index; + result |= 1; + // << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index; else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) - result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index; + result |= 1; + // << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index; // Advance pNLMsg to the next message in the buffer if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) @@ -1279,8 +1345,12 @@ mDNSexport mStatus mDNSPlatformInit(mDNS if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); #endif + // In Linux case, we can't set up sockets with different owner - + // it blows up SO_REUSEPORT. So we do this step bit later. +#ifndef __linux__ // Tell mDNS core about the network interfaces on this machine. if (err == mStatus_NoError) err = SetupInterfaceList(m); +#endif /* !__linux__ */ // Tell mDNS core about DNS Servers mDNS_Lock(m); @@ -1313,9 +1383,17 @@ mDNSexport void mDNSPlatformClose(mDNS * { assert(m != NULL); ClearInterfaceList(m); - if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); + if (m->p->unicastSocket4 != -1) + { + int rv = close(m->p->unicastSocket4); + assert(rv == 0); + } #if HAVE_IPV6 - if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); + if (m->p->unicastSocket6 != -1) + { + int rv = close(m->p->unicastSocket6); + assert(rv == 0); + } #endif } @@ -1571,14 +1649,14 @@ mDNSexport mStatus mDNSPlatformClearS mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) { (void) sock; // unused - + return (mDNSu16)-1; } mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) { (void) InterfaceID; // unused - + return mDNSfalse; } --- a/mDNSPosix/mDNSUNP.c +++ b/mDNSPosix/mDNSUNP.c @@ -61,154 +61,86 @@ #endif #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX -#include -#include +#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) +/* Correct way to deal with this is just to use getifaddrs (glibc + * 2.3.3+ and various BSDs, but BSDs are 'slightly different' just to + * make life interesting). We assume Linux getifaddrs is available, + * and if not, please upgrade. */ +struct ifi_info *get_ifi_info_linuxv6(int doaliases) { - struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; - FILE *fp; - 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; - struct sockaddr_in6 *sin6; - struct in6_addr *addrptr; - 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_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; - - /* 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); - ifipnext = ifiptr; - *ifipnext = ifipold; - continue; - } else { - goto gotError; - } - } - ifi->ifi_flags = ifr.ifr_flags; - freeaddrinfo(res0); - res0=NULL; - } - } - goto done; + struct ifaddrs *ifap, *ifa; + struct ifi_info *ifi = NULL, *head = NULL; -gotError: - if (ifihead != NULL) { - free_ifi_info(ifihead); - ifihead = NULL; - } - if (res0 != NULL) { - freeaddrinfo(res0); - res0=NULL; + /* doaliases seems always true in the calls in current code */ + assert(doaliases); + + if (getifaddrs(&ifap) < 0) + { + return NULL; } -done: - if (sockfd != -1) { - assert(close(sockfd) == 0); + for (ifa = ifap ; ifa ; ifa = ifa->ifa_next) + { + /* Care only about IPv6 addresses on non-point-to-point links. */ + if (!ifa->ifa_addr + || ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifi = calloc(1, sizeof(*ifi)); + if (!ifi) + break; + strncpy(ifi->ifi_name, ifa->ifa_name, IFI_NAME); + /* We ignore ifi_{haddr,hlen}, everyone else does too */ + ifi->ifi_flags = ifa->ifa_flags; + /* We ignore ifi_myflags; IFI_ALIAS isn't used anywhere */ + ifi->ifi_index = if_nametoindex(ifa->ifa_name); + if (!(ifi->ifi_addr = malloc(sizeof(struct sockaddr_in6)))) + break; + memcpy(ifi->ifi_addr, ifa->ifa_addr, sizeof(struct sockaddr_in6)); + + if (ifa->ifa_netmask) + { + if (!(ifi->ifi_netmask = malloc(sizeof(struct sockaddr_in6)))) + break; + memcpy(ifi->ifi_netmask, ifa->ifa_netmask, + sizeof(struct sockaddr_in6)); + } + + if (!(ifi->ifi_addr = malloc(sizeof(struct sockaddr_in6)))) + break; + memcpy(ifi->ifi_addr, ifa->ifa_addr, sizeof(struct sockaddr_in6)); + + + if (ifa->ifa_flags & IFF_POINTOPOINT && ifa->ifa_dstaddr) + { + if (!(ifi->ifi_dstaddr = malloc(sizeof(struct sockaddr_in6)))) + break; + memcpy(ifi->ifi_dstaddr, ifa->ifa_dstaddr, + sizeof(struct sockaddr_in6)); + } + else if (ifa->ifa_broadaddr) + { + if (!(ifi->ifi_brdaddr = malloc(sizeof(struct sockaddr_in6)))) + break; + memcpy(ifi->ifi_brdaddr, ifa->ifa_broadaddr, + sizeof(struct sockaddr_in6)); + } + ifi->ifi_next = head; + head = ifi; + ifi = NULL; + }; + if (ifi) + { + /* An error occurred. Let's bail out. */ + ifi->ifi_next = head; + free_ifi_info(head); + return NULL; } - return(ifihead); /* pointer to first structure in linked list */ + freeifaddrs(ifap); + return head; } + #endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX struct ifi_info *get_ifi_info(int family, int doaliases) @@ -229,7 +161,7 @@ struct ifi_info *get_ifi_info(int family #endif #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX - if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); + if (family == AF_INET6) return get_ifi_info_linuxv6(doaliases); #endif sockfd = -1; --- a/mDNSPosix/mDNSUNP.h +++ b/mDNSPosix/mDNSUNP.h @@ -97,8 +97,7 @@ struct ifi_info { }; #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX -#define PROC_IFINET6_PATH "/proc/net/if_inet6" -extern struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases); +extern struct ifi_info *get_ifi_info_linuxv6(int doaliases); #endif #if defined(AF_INET6) && HAVE_IPV6 --- a/mDNSShared/dnsextd_parser.y +++ b/mDNSShared/dnsextd_parser.y @@ -15,6 +15,8 @@ * limitations under the License. */ +%parse-param { void *context } + %{ #include #include @@ -23,7 +25,7 @@ #include "DebugServices.h" #include "dnsextd.h" -void yyerror( const char* error ); +void yyerror( void *context, const char* error ); int yylex(void); @@ -378,7 +380,7 @@ int yywrap(void); extern int yylineno; -void yyerror( const char *str ) +void yyerror( void *context, const char *str ) { fprintf( stderr,"%s:%d: error: %s\n", g_filename, yylineno, str ); }