diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..920cdfc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Clients/build +mDNSPosix/build +mDNSPosix/objects + diff --git a/mDNSPosix/PosixDaemon.c b/mDNSPosix/PosixDaemon.c index 88b3292..e86a6c7 100644 --- 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); diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index 3996b7b..e58d8eb 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -603,7 +603,8 @@ static mStatus RegisterServicesInFile(const char *filePath) status = mStatus_UnknownErr; } - assert(0 == fclose(fp)); + int rv = fclose(fp); + assert(0 == rv); return status; } diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index 953bf64..4e481ea 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -136,7 +136,7 @@ mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipA // 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; @@ -574,9 +574,17 @@ mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf) { 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 free(intf); } @@ -703,6 +711,29 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf 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) { @@ -784,6 +815,29 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf 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) { @@ -815,7 +869,12 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf } // 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; } @@ -994,7 +1053,7 @@ mDNSlocal mStatus OpenIfNotifySocket(int *pFD) /* 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; @@ -1072,11 +1131,18 @@ mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) 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) @@ -1247,8 +1313,12 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) 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); @@ -1281,9 +1351,17 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) { 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 } @@ -1533,14 +1611,14 @@ mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void) mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) { (void) sock; // unused - + return (mDNSu16)-1; } mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) { (void) InterfaceID; // unused - + return mDNSfalse; } diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c index b392fc7..9f85e0e 100755 --- a/mDNSPosix/mDNSUNP.c +++ b/mDNSPosix/mDNSUNP.c @@ -61,154 +61,86 @@ #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; - } -} +#include +#include + -/* 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; + struct ifaddrs *ifap, *ifa; + struct ifi_info *ifi = NULL, *head = NULL; - if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { - sockfd = socket(AF_INET6, SOCK_DGRAM, 0); - if (sockfd < 0) { - goto gotError; + /* doaliases seems always true in the calls in current code */ + assert(doaliases); + + if (getifaddrs(&ifap) < 0) + { + return NULL; + } + 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)); } - 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)); + if (!(ifi->ifi_addr = malloc(sizeof(struct sockaddr_in6)))) + break; + memcpy(ifi->ifi_addr, ifa->ifa_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; -gotError: - if (ifihead != NULL) { - free_ifi_info(ifihead); - ifihead = NULL; - } - if (res0 != NULL) { - freeaddrinfo(res0); - res0=NULL; - } -done: - if (sockfd != -1) { - assert(close(sockfd) == 0); + 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, int doaliases) #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; diff --git a/mDNSPosix/mDNSUNP.h b/mDNSPosix/mDNSUNP.h index cc81b7d..e710087 100755 --- 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 diff --git a/mDNSShared/dnsextd_parser.y b/mDNSShared/dnsextd_parser.y index 18c5990..d4b63ce 100644 --- 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 ); }