mDNSexport void ExampleClientEventLoop(mDNS *const m) { signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C signal(SIGTERM, HandleSIG); while (!StopNow) { int nfds = 0; fd_set readfds; struct timeval timeout; int result; // 1. Set up the fd_set as usual here. // This example client has no file descriptors of its own, // but a real application would call FD_SET to add them to the set here FD_ZERO(&readfds); // 2. Set up the timeout. // This example client has no other work it needs to be doing, // so we set an effectively infinite timeout timeout.tv_sec = 0x3FFFFFFF; timeout.tv_usec = 0; // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout mDNSPosixGetFDSet(m, &nfds, &readfds, &timeout); // 4. Call select as normal verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); result = select(nfds, &readfds, NULL, NULL, &timeout); if (result < 0) { verbosedebugf("select() returned %d errno %d", result, errno); if (errno != EINTR) StopNow = mDNStrue; } else { // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work mDNSPosixProcessFDSet(m, &readfds); // 6. This example client has no other work it needs to be doing, // but a real client would do its work here // ... (do work) ... } } debugf("Exiting"); }
static void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort) { switch (sa->sa_family) { case AF_INET: { struct sockaddr_in* sin = (struct sockaddr_in*)sa; ipAddr->type = mDNSAddrType_IPv4; ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; if (ipPort) ipPort->NotAnInteger = sin->sin_port; break; } #ifdef mDNSIPv6Support case AF_INET6: { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; assert(sin6->sin6_len == sizeof(*sin6)); ipAddr->type = mDNSAddrType_IPv6; ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; break; } #endif default: verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); ipAddr->type = mDNSAddrType_None; if (ipPort) ipPort->NotAnInteger = 0; break; } }
mDNSlocal int NSEC3SameName(const mDNSu8 *name, int namelen, const mDNSu8 *nsecName, int nsecLen) { int i; // Note: With NSEC3, the lengths should always be same. if (namelen != nsecLen) { LogMsg("NSEC3SameName: ERROR!! namelen %d, nsecLen %d", namelen, nsecLen); return ((namelen < nsecLen) ? -1 : 1); } for (i = 0; i < namelen; i++) { mDNSu8 ac = *name++; mDNSu8 bc = *nsecName++; if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; if (ac != bc) { verbosedebugf("NSEC3SameName: returning ac %c, bc %c", ac, bc); return ((ac < bc) ? -1 : 1); } } return 0; }
// mDNS core calls this routine when it needs to send a packet. mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) { int err; struct sockaddr_storage to; PosixNetworkInterface * thisIntf; assert(m != NULL); assert(msg != NULL); assert(end != NULL); assert( (((char *) end) - ((char *) msg)) > 0 ); assert(InterfaceID != 0); // Can't send from zero source address assert(srcPort.NotAnInteger != 0); // Nor from a zero source port assert(dstPort.NotAnInteger != 0); // Nor from a zero source port if (dst->type == mDNSAddrType_IPv4) { struct sockaddr_in *sin = (struct sockaddr_in*)&to; #ifndef NOT_HAVE_SA_LEN sin->sin_len = sizeof(*sin); #endif sin->sin_family = AF_INET; sin->sin_port = dstPort.NotAnInteger; sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; } #ifdef mDNSIPv6Support else if (dst->type == mDNSAddrType_IPv6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; mDNSPlatformMemZero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = dstPort.NotAnInteger; sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; } #endif err = 0; thisIntf = (PosixNetworkInterface *)(InterfaceID); if (dst->type == mDNSAddrType_IPv4) err = sendto(thisIntf->multicastSocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); #ifdef mDNSIPv6Support else if (dst->type == mDNSAddrType_IPv6) err = sendto(thisIntf->multicastSocketv6, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); #endif if (err > 0) err = 0; else if (err < 0) verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); return PosixErrorToStatus(err); }
// Sets up a multicast send/receive socket for the specified // port on the interface specified by the IP addrelss intfAddr. static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) { int err = 0; static const int kOn = 1; static const int kIntTwoFiveFive = 255; static const unsigned char kByteTwoFiveFive = 255; (void) interfaceIndex; // Unused assert(intfAddr != NULL); assert(sktPtr != NULL); assert(*sktPtr == -1); // Open the socket... if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); #ifdef mDNSIPv6Support else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); #endif else return EINVAL; if (*sktPtr < 0) { err = errno; perror("socket"); } // ... with a shared UDP port if (err == 0) { #if defined(SO_REUSEPORT) err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); #elif defined(SO_REUSEADDR) err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); #else #error This platform has no way to avoid address busy errors on multicast. #endif if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } } // We want to receive destination addresses and interface identifiers. if (intfAddr->sa_family == AF_INET) { struct ip_mreq imr; struct sockaddr_in bindAddr; if (err == 0) { #if defined(IP_PKTINFO) // Linux err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris #if defined(IP_RECVDSTADDR) err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } #endif #if defined(IP_RECVIF) if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } } #endif #else #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts #endif } // Add multicast group membership on this interface if (err == 0) { imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } } // Specify outgoing interface too if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } } // Per the mDNS spec, send unicast packets with TTL 255 if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } } // and multicast packets with TTL 255 too // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); if (err < 0 && errno == EINVAL) err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } } // And start listening for packets if (err == 0) { bindAddr.sin_family = AF_INET; bindAddr.sin_port = port.NotAnInteger; bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); if (err < 0) { err = errno; perror("bind"); fflush(stderr); } } } // endif (intfAddr->sa_family == AF_INET) #ifdef mDNSIPv6Support else if (intfAddr->sa_family == AF_INET6) { struct ipv6_mreq imr6; struct sockaddr_in6 bindAddr6; if (err == 0) { #if defined(IPV6_PKTINFO) err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_PKTINFO, &kOn, sizeof(kOn)); if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } #else #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts #endif } // Add multicast group membership on this interface if (err == 0) { imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; imr6.ipv6mr_interface = interfaceIndex; err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); if (err < 0) { err = errno; verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); perror("setsockopt - IPV6_JOIN_GROUP"); } } // Specify outgoing interface too if (err == 0) { u_int multicast_if = interfaceIndex; err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } } // We want to receive only IPv6 packets on this socket. // Without this option, we may get IPv4 addresses as mapped addresses. if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } } // Per the mDNS spec, send unicast packets with TTL 255 if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } } // and multicast packets with TTL 255 too // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. if (err == 0) { err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); if (err < 0 && errno == EINVAL) err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } } // And start listening for packets if (err == 0) { mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); bindAddr6.sin6_len = sizeof(bindAddr6); bindAddr6.sin6_family = AF_INET6; bindAddr6.sin6_port = port.NotAnInteger; bindAddr6.sin6_flowinfo = 0; // bindAddr6.sin6_addr.s_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket bindAddr6.sin6_scope_id = 0; err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); if (err < 0) { err = errno; perror("bind"); fflush(stderr); } } } // endif (intfAddr->sa_family == AF_INET6) #endif // Set the socket to non-blocking. if (err == 0) { err = fcntl(*sktPtr, F_GETFL, 0); if (err < 0) err = errno; else { err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); if (err < 0) err = errno; } } // Clean up if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } assert( (err == 0) == (*sktPtr != -1) ); return err; }
// This routine is called when the main loop detects that data is available on a socket. static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) { mDNSAddr senderAddr, destAddr; mDNSIPPort senderPort; ssize_t packetLen; DNSMessage packet; struct my_in_pktinfo packetInfo; struct sockaddr_storage from; socklen_t fromLen; int flags; mDNSBool reject; assert(m != NULL); assert(intf != NULL); assert(skt >= 0); fromLen = sizeof(from); flags = 0; packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo); if (packetLen >= 0) { SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); // If we have broken IP_RECVDSTADDR functionality (so far // I've only seen this on OpenBSD) then apply a hack to // convince mDNS Core that this isn't a spoof packet. // Basically what we do is check to see whether the // packet arrived as a multicast and, if so, set its // destAddr to the mDNS address. // // I must admit that I could just be doing something // wrong on OpenBSD and hence triggering this problem // but I'm at a loss as to how. // // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have // no way to tell the destination address or interface this packet arrived on, // so all we can do is just assume it's a multicast #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) ) { destAddr.type == senderAddr.type; if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup; else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6; } #endif // We only accept the packet if the interface on which it came // in matches the interface associated with this socket. // We do this match by name or by index, depending on which // information is available. recvfrom_flags sets the name // to "" if the name isn't available, or the index to -1 // if the index is available. This accomodates the various // different capabilities of our target platforms. reject = mDNSfalse; if ( packetInfo.ipi_ifname[0] != 0 ) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); else if ( packetInfo.ipi_ifindex != -1 ) reject = (packetInfo.ipi_ifindex != intf->index); if (reject) { verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d", &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, &intf->coreIntf.ip, intf->intfName, intf->index); packetLen = -1; num_pkts_rejected++; if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) { fprintf(stderr, "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); num_pkts_accepted = 0; num_pkts_rejected = 0; } } else { verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d", &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index); num_pkts_accepted++; } } if (packetLen >= 0 && packetLen < (ssize_t)sizeof(DNSMessageHeader)) { debugf("SocketDataReady packet length (%d) too short", packetLen); packetLen = -1; } if (packetLen >= 0) mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, &senderAddr, senderPort, &destAddr, MulticastDNSPort, intf->coreIntf.InterfaceID, 255); }
int main(int argc, char **argv) { mStatus status; int result; // Parse our command line arguments. This won't come back if there's an error. ParseArguments(argc, argv); // If we're told to run as a daemon, then do that straight away. // Note that we don't treat the inability to create our PID // file as an error. Also note that we assign getpid to a long // because printf has no format specified for pid_t. if (gDaemon) { if (gMDNSPlatformPosixVerboseLevel > 0) { fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); } daemon(0,0); { FILE *fp; int junk; fp = fopen(gPIDFile, "w"); if (fp != NULL) { fprintf(fp, "%ld\n", (long) getpid()); junk = fclose(fp); assert(junk == 0); } } } else { if (gMDNSPlatformPosixVerboseLevel > 0) { fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid()); } } status = mDNS_Init(&mDNSStorage, &PlatformStorage, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (status != mStatus_NoError) return(2); status = RegisterOurServices(); if (status != mStatus_NoError) return(2); signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid> signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid> while (!gStopNow) { int nfds = 0; fd_set readfds; struct timeval timeout; int result; // 1. Set up the fd_set as usual here. // This example client has no file descriptors of its own, // but a real application would call FD_SET to add them to the set here FD_ZERO(&readfds); // 2. Set up the timeout. // This example client has no other work it needs to be doing, // so we set an effectively infinite timeout timeout.tv_sec = 0x3FFFFFFF; timeout.tv_usec = 0; // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); // 4. Call select as normal verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); result = select(nfds, &readfds, NULL, NULL, &timeout); if (result < 0) { verbosedebugf("select() returned %d errno %d", result, errno); if (errno != EINTR) gStopNow = mDNStrue; else { if (gReceivedSigUsr1) { gReceivedSigUsr1 = mDNSfalse; gMDNSPlatformPosixVerboseLevel += 1; if (gMDNSPlatformPosixVerboseLevel > 2) gMDNSPlatformPosixVerboseLevel = 0; if ( gMDNSPlatformPosixVerboseLevel > 0 ) fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); } if (gReceivedSigHup) { if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "\nSIGHUP\n"); gReceivedSigHup = mDNSfalse; DeregisterOurServices(); status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); if (status != mStatus_NoError) break; status = RegisterOurServices(); if (status != mStatus_NoError) break; } } } else { // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work mDNSPosixProcessFDSet(&mDNSStorage, &readfds); // 6. This example client has no other work it needs to be doing, // but a real client would do its work here // ... (do work) ... } } debugf("Exiting"); DeregisterOurServices(); mDNS_Close(&mDNSStorage); if (status == mStatus_NoError) { result = 0; } else { result = 2; } if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result); } return result; }