// When a service is started or a browse is started with the Anonymous data, we allocate a new random
// number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and
// the anonymous data.
//
// If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can
// check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received.
mDNSexport  AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr)
{
    AnonymousInfo *ai;
    ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo));
    if (!ai)
    {
        return mDNSNULL;
    }
    mDNSPlatformMemZero(ai, sizeof(AnonymousInfo));
    if (rr)
    {
        if (!CopyNSEC3ResourceRecord(ai, rr))
        {
            mDNSPlatformMemFree(ai);
            return mDNSNULL;
        }
        return ai;
    }
    ai->salt = mDNSRandom(0xFFFFFFFF);
    ai->AnonData = mDNSPlatformMemAllocate(len);
    if (!ai->AnonData)
    {
        mDNSPlatformMemFree(ai);
        return mDNSNULL;
    }
    ai->AnonDataLen = len;
    mDNSPlatformMemCopy(ai->AnonData, data, len);
    ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt);
    if (!ai->nsec3RR)
    {
        mDNSPlatformMemFree(ai);
        return mDNSNULL;
    }
    return ai;
}
Beispiel #2
0
static void DNSSD_API DNSServiceGetAddrInfoResponse(
    DNSServiceRef inRef,
    DNSServiceFlags inFlags,
    uint32_t inInterfaceIndex,
    DNSServiceErrorType inErrorCode,
    const char *        inFullName,
    uint16_t inRRType,
    uint16_t inRRClass,
    uint16_t inRDLen,
    const void *        inRData,
    uint32_t inTTL,
    void *              inContext )
{
    mDNS_DirectOP_GetAddrInfo *     x = (mDNS_DirectOP_GetAddrInfo*)inContext;
    struct sockaddr_in sa4;

    mDNSPlatformMemZero(&sa4, sizeof(sa4));
    if (inErrorCode == kDNSServiceErr_NoError && inRRType == kDNSServiceType_A)
    {
        sa4.sin_family = AF_INET;
        mDNSPlatformMemCopy(&sa4.sin_addr.s_addr, inRData, 4);
    }

    x->callback((DNSServiceRef)x, inFlags, inInterfaceIndex, inErrorCode, inFullName,
                (const struct sockaddr *) &sa4, inTTL, x->context);
}
Beispiel #3
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);
	}
Beispiel #4
0
mDNSexport int embedded_mDNSInit() {
	mStatus err;
	if (mDNSIsInitialized != 0) {
		return 0;
	}

	mDNSPlatformMemZero( &mDNSStorage, sizeof(mDNSStorage));
	mDNSPlatformMemZero( &platformSupport, sizeof(platformSupport));

//	mDNSStorage.AutoTargetServices = 1;

	err = mDNS_Init(
		&mDNSStorage,
		&platformSupport,
		rrcachestorage,
		RR_CACHE_SIZE,
		mDNS_Init_AdvertiseLocalAddresses,
		mDNS_Init_NoInitCallback,
		mDNS_Init_NoInitCallbackContext
	);
	if (err)
		return err;

#ifdef WIN32
	platformSupport.reportStatusFunc = embedded_mDNSInit_ReportStatus;
	err = SetupInterfaceList( &mDNSStorage );
	if (err)
		return err;
	err = uDNS_SetupDNSConfig( &mDNSStorage );
#endif

	if (err == 0) {
		mDNSIsInitialized = 1;
	}
	return err;
}
mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt)
{
    const mDNSu8 *ptr;
    rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data;
    mDNSu8 *tmp, *nxt;
    unsigned short iter = ANON_NSEC3_ITERATIONS;
    int hlen;
    const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];

    // Construct the RDATA first and construct the owner name based on that.
    ptr = (const mDNSu8 *)&salt;
    debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c);

    // Set the RDATA
    nsec3->alg = SHA1_DIGEST_TYPE;
    nsec3->flags = 0;
    nsec3->iterations = swap16(iter);
    nsec3->saltLength = 4;
    tmp = (mDNSu8 *)&nsec3->salt;
    *tmp++ = ptr[0];
    *tmp++ = ptr[1];
    *tmp++ = ptr[2];
    *tmp++ = ptr[3];

    // hashLength, nxt, bitmap
    *tmp++ = SHA1_HASH_LENGTH;    // hash length
    nxt = tmp;
    tmp += SHA1_HASH_LENGTH;
    *tmp++ = 0; // window number
    *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length
    mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE);
    tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7);

    // Hash the base service name + salt + AnonData
    if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen))
    {
        LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c);
        return mDNSfalse;
    }
    if (hlen != SHA1_HASH_LENGTH)
    {
        LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen);
        return mDNSfalse;
    }
    mDNSPlatformMemCopy(nxt, hashName, hlen);

    return mDNStrue;
}
mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *n)
	{
	char        externalPort[10];
	Property    propArgs[3];
	tcpLNTInfo  *info;
	tcpLNTInfo  **infoPtr = &m->tcpInfoUnmapList;
	mStatus     err;

	// If no NAT gateway to talk to, no need to do all this work for nothing
	if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError;

	mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort));

	mDNSPlatformMemZero(propArgs, sizeof(propArgs));
	propArgs[0].name  = "NewRemoteHost";
	propArgs[0].type  = "string";
	propArgs[0].value = "";
	propArgs[1].name  = "NewExternalPort";
	propArgs[1].type  = "ui2";
	propArgs[1].value = externalPort;
	propArgs[2].name  = "NewProtocol";
	propArgs[2].type  = "string";
	propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";

	n->tcpInfo.parentNATInfo = n;

	// clean up previous port mapping requests and allocations
	if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection");
	if (n->tcpInfo.sock   ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock    = mDNSNULL; }
	if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request);         n->tcpInfo.Request = mDNSNULL; }
	if (n->tcpInfo.Reply  ) { mDNSPlatformMemFree(n->tcpInfo.Reply);           n->tcpInfo.Reply   = mDNSNULL; }
	
	// make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns)
	if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL)
		{ LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); }
	*info = n->tcpInfo;
	
	while (*infoPtr) infoPtr = &(*infoPtr)->next;	// find the end of the list
	*infoPtr = info;    // append

	err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp);
	if (err) DisposeInfoFromUnmapList(m, info);
	return err;
	}
mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op)
	{
	mStatus err = mStatus_NoError;
	mDNSIPPort srcport = zeroIPPort;

	if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port))
	    { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); }
	info->m         = m;
	info->Address   = *Addr;
	info->Port      = Port;
	info->op        = op;
	info->nread     = 0;
	info->replyLen  = LNT_MAXBUFSIZE;
	if      (info->Reply != mDNSNULL)  mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE);   // reuse previously allocated buffer
	else if ((info->Reply = (mDNSs8 *) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); }

	if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; }
	info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport);
	if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); }
	LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port));
	err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpConnectionCallback, info);

	if      (err == mStatus_ConnPending) err = mStatus_NoError;
	else if (err == mStatus_ConnEstablished)
		{
		mDNS_DropLockBeforeCallback();
		tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError);
		mDNS_ReclaimLockAfterCallback();
		err = mStatus_NoError;
		}
	else
		{
		// Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
		LogInfo("LNT MakeTCPConnection: connection failed");
		mDNSPlatformTCPCloseConnection(info->sock);	// Dispose the socket we created with mDNSPlatformTCPSocket() above
		info->sock = mDNSNULL;
		mDNSPlatformMemFree(info->Reply);
		info->Reply = mDNSNULL;
		}
	return(err);
	}
mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info)
	{
	// Device description format -
	//  - device description URL
	//  - host/port
	static const char szSSDPMsgDescribeDeviceFMT[] =
		"GET %s HTTP/1.1\r\n"
		"Accept: text/xml, application/xml\r\n"
		"User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
		"Host: %s\r\n"
		"Connection: close\r\n"
		"\r\n";

	if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need
	
	if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL)     { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); }

	// build message
	if      (info->Request != mDNSNULL)  mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer
	else if ((info->Request = (mDNSs8 *) mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); }
	info->requestLen = mDNS_snprintf((char *)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString);
	LogInfo("Describe Device: [%s]", info->Request);
	return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp);
	}
mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context)
{
    int ret;
    struct sockaddr_storage from;
    struct sockaddr_storage to;
    mDNSAddr senderAddr, destAddr;
    mDNSIPPort senderPort;
    ProxyTCPInfo_t *ti = (ProxyTCPInfo_t *)context;
    TCPSocket *sock = &ti->sock;
    KQSocketSet *kq = &sock->ss;

    (void) filter;

    ret = ProxyTCPRead(ti);
    if (ret == -1)
    {
        mDNSPlatformDisposeProxyContext(ti);
        return; 
    }
    else if (!ret)
    {
        debugf("ProxyTCPReceive: Not yet read completely Actual length %d, Read length %d", ti->replyLen, ti->nread);
        return;
    }
    // We read all the data and hence not interested in read events anymore
    KQueueSet(s1, EV_DELETE, EVFILT_READ, sock->kqEntry);

    mDNSPlatformMemZero(&to, sizeof(to));
    mDNSPlatformMemZero(&from, sizeof(from));
    socklen_t len = sizeof(to);
    ret = getsockname(s1, (struct sockaddr*) &to, &len);
    if (ret < 0)
    {
        LogMsg("ProxyTCPReceive: getsockname(fd=%d) errno %d", s1, errno);
        mDNSPlatformDisposeProxyContext(ti);
        return;
    }
    ret = getpeername(s1, (struct sockaddr*) &from, &len);
    if (ret < 0)
    {
        LogMsg("ProxyTCPReceive: getpeername(fd=%d) errno %d", s1, errno);
        mDNSPlatformDisposeProxyContext(ti);
        return;
    }

    if (from.ss_family == AF_INET)
    {
        struct sockaddr_in *s = (struct sockaddr_in*)&from;

        senderAddr.type = mDNSAddrType_IPv4;
        senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr;
        senderPort.NotAnInteger = s->sin_port;

        s = (struct sockaddr_in *)&to;
        destAddr.type = mDNSAddrType_IPv4;
        destAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr;

        LogInfo("ProxyTCPReceive received IPv4 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL);
    }
    else if (from.ss_family == AF_INET6)
    {
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from;
        senderAddr.type = mDNSAddrType_IPv6;
        senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
        senderPort.NotAnInteger = sin6->sin6_port;

        sin6 = (struct sockaddr_in6 *)&to;
        destAddr.type = mDNSAddrType_IPv6;
        destAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;

        LogInfo("ProxyTCPReceive received IPv6 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL);
    }
    else
    {
        LogMsg("ProxyTCPReceive from is unknown address family %d", from.ss_family);
        mDNSPlatformDisposeProxyContext(ti);
        return;
    }

    // We pass sock for the TCPSocket and the "ti" for context as that's what we want to free at the end.
    // In the UDP case, there is just a single socket and nothing to free. Hence, the context (last argument)
    // would be NULL.
    kq->m->p->TCPProxyCallback(kq->m, sock, ti->reply, (mDNSu8 *)ti->reply + ti->replyLen, &senderAddr, senderPort, &destAddr,
        UnicastDNSPort, 0, ti);
}
mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context)
{
    int newfd;
    struct sockaddr_storage ss;
    socklen_t sslen = sizeof(ss);
    const int on = 1;
    KQSocketSet *listenSet = (KQSocketSet *)context;

    (void) filter;

    while ((newfd = accept(s1, (struct sockaddr *)&ss, &sslen)) != -1)
    {
        int err;
        int *s;
        KQueueEntry *k;
        KQSocketSet *kq;

        // Even though we just need a single KQueueEntry, for simplicity we re-use
        // the KQSocketSet
        ProxyTCPInfo_t *ti = mallocL("ProxyTCPContext", sizeof(ProxyTCPInfo_t));
        if (!ti)
        {
            LogMsg("ProxyTCPAccept: cannot allocate TCPSocket");
            close(newfd);
            return;
        }
        mDNSPlatformMemZero(ti, sizeof(ProxyTCPInfo_t));
        TCPSocket *sock = &ti->sock;

        kq = &sock->ss;
        kq->sktv4 = -1;
        kq->sktv6 = -1;
        kq->m = listenSet->m;

        fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
        if (ss.ss_family == AF_INET)
        {
            s =  &kq->sktv4;
            k =  &kq->kqsv4;
            // Receive interface identifiers
            err = setsockopt(newfd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
            if (err)
            {
                LogMsg("ProxyTCPAccept: IP_RECVIF %d errno %d (%s)", newfd, errno, strerror(errno));
                mDNSPlatformDisposeProxyContext(ti);
                return;
            }
        }
        else
        {
            s =  &kq->sktv6;
            k =  &kq->kqsv6;
            // We want to receive destination addresses and receive interface identifiers
            err = setsockopt(newfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
            if (err)
            {
                LogMsg("ProxyTCPAccept: IP_RECVPKTINFO %d errno %d (%s)", newfd, errno, strerror(errno));
                mDNSPlatformDisposeProxyContext(ti);
                return;
            }
        }
        *s = newfd;
        // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address
        // from which we can infer the destination address family. Hence we need to remember that here.
        // Instead of remembering the address family, we remember the right fd.
        sock->fd = newfd;
        sock->kqEntry = k;

        k->KQcallback = ProxyTCPSocketCallBack;
        k->KQcontext  = ti;
        k->KQtask     = "TCP Proxy packet reception";
#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        k->readSource = mDNSNULL;
        k->writeSource = mDNSNULL;
        k->fdClosed = mDNSfalse;
#endif
        KQueueSet(*s, EV_ADD, EVFILT_READ, k);
    }
}
Beispiel #11
0
// 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;
	}
Beispiel #12
0
// If the answer was result of a wildcard match, then this function proves
// that a proper wildcard was used to answer the question and that the
// original name does not exist
mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
{
    CacheRecord *ncr;
    CacheRecord **rp;
    const domainname *ce;
    DNSQuestion q;
    CacheRecord **nsec3 = mDNSNULL;

    LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
    //
    // RFC 4035: Section 3.1.3.3
    //
    // 1) We used a wildcard because the qname does not exist, so verify
    //    that the qname does not exist
    //
    // 2) Is the wildcard the right one ?
    //
    // Unfortunately, this is not well explained in that section. Refer to
    // RFC 5155 section 7.2.6.

    // Walk the list of nsecs we received and see if they prove that
    // the name does not exist

    mDNSPlatformMemZero(&q, sizeof(DNSQuestion));
    q.ThisQInterval = -1;
    InitializeQuestion(m, &q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);

    ncr = NSECParentForQuestion(m, &q);
    if (!ncr)
    {
        LogMsg("WildcardAnswerProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
        goto error;
    }
    else
    {
        LogDNSSEC("WildcardAnswerProof: found %s", CRDisplayString(m, ncr));
    }
    rp = &(ncr->nsec);
    while (*rp)
    {
        if ((*rp)->resrec.rrtype == kDNSType_NSEC)
        {
            CacheRecord *cr = *rp;
            if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
                break;
        }
        else if ((*rp)->resrec.rrtype == kDNSType_NSEC3)
        {
            nsec3 = rp;
        }
        rp=&(*rp)->next;
    }
    if (!(*rp))
    {
        mDNSBool ret = mDNSfalse;
        if (nsec3)
        {
            ret = NSEC3WildcardAnswerProof(m, ncr, dv);
        }
        if (!ret)
        {
            LogDNSSEC("WildcardAnswerProof: NSEC3 wildcard proof failed for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
            goto error;
        }
        rp = nsec3;
    }
    else
    {
        ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
        if (!ce)
        {
            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
            goto error;
        }
        if (!SameDomainName(ce, dv->wildcardName))
        {
            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
            goto error;
        }
    }

    VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
    return;
error:
    dv->DVCallback(m, dv, DNSSEC_Bogus);
}
// Build port mapping request with new port (up to max) and send it
mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
	{
	char              externalPort[6];
	char              internalPort[6];
	char              localIPAddrString[30];
	char              publicPortString[40];
	Property          propArgs[8];
	mDNSu16           ReqPortNum = RequestedPortNum(n);
	NATTraversalInfo *n2 = m->NATTraversals;

	// Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique.
	// UPnP gateways will report conflicts if different devices request the same external port, but if two
	// clients on the same device request the same external port the second one just stomps over the first.
	// One way this can happen is like this:
	// 1. Client A binds local port 80
	// 2. Client A requests external port 80 -> internal port 80
	// 3. UPnP NAT gateway refuses external port 80 (some other client already has it)
	// 4. Client A tries again, and successfully gets external port 80 -> internal port 81
	// 5. Client B on same machine tries to bind local port 80, and fails
	// 6. Client B tries again, and successfully binds local port 81
	// 7. Client B now requests external port 81 -> internal port 81
	// 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping

	while (n2)
		{
		if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next;
		else
			{
			if (n->tcpInfo.retries < 100)
				{
				n->tcpInfo.retries++;
				ReqPortNum = RequestedPortNum(n);	// Pick a new port number
				n2 = m->NATTraversals;				// And re-scan the list looking for conflicts
				}
			else
				{
				natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
				return mStatus_NoError;
				}
			}
		}

	// create strings to use in the message
	mDNS_snprintf(externalPort,      sizeof(externalPort),      "%u",   ReqPortNum);
	mDNS_snprintf(internalPort,      sizeof(internalPort),      "%u",   mDNSVal16(n->IntPort));
	mDNS_snprintf(publicPortString,  sizeof(publicPortString),  "iC%u", ReqPortNum);
	mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u",
	    m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]);

	// build the message
	mDNSPlatformMemZero(propArgs, sizeof(propArgs));
	propArgs[0].name  = "NewRemoteHost";
	propArgs[0].type  = "string";
	propArgs[0].value = "";
	propArgs[1].name  = "NewExternalPort";
	propArgs[1].type  = "ui2";
	propArgs[1].value = externalPort;
	propArgs[2].name  = "NewProtocol";
	propArgs[2].type  = "string";
	propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
	propArgs[3].name  = "NewInternalPort";
	propArgs[3].type  = "ui2";
	propArgs[3].value = internalPort;
	propArgs[4].name  = "NewInternalClient";
	propArgs[4].type  = "string";
	propArgs[4].value = localIPAddrString;
	propArgs[5].name  = "NewEnabled";
	propArgs[5].type  = "boolean";
	propArgs[5].value = "1";
	propArgs[6].name  = "NewPortMappingDescription";
	propArgs[6].type  = "string";
	propArgs[6].value = publicPortString;
	propArgs[7].name  = "NewLeaseDuration";
	propArgs[7].type  = "ui4";
	propArgs[7].value = "0";

	LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum);
	return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp);
	}