/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2012-2013 Apple 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
*
*
* 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 "mDNSEmbeddedAPI.h"
#include "CryptoAlg.h"
#include "anonymous.h"
#include "DNSCommon.h"
// Define ANONYMOUS_DISABLED to remove all the anonymous functionality
// and use the stub functions implemented later in this file.
#ifndef ANONYMOUS_DISABLED
{
};
mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt)
{
int hlen;
// Construct the RDATA first and construct the owner name based on that.
// Set the RDATA
// hashLength, nxt, bitmap
tmp += SHA1_HASH_LENGTH;
*tmp++ = 0; // window number
// Hash the base service name + salt + AnonData
{
return mDNSfalse;
}
if (hlen != SHA1_HASH_LENGTH)
{
return mDNSfalse;
}
return mDNStrue;
}
mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt)
{
int dlen;
// We are just allocating an RData which has StandardAuthRDSize
{
LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH);
return mDNSNULL;
}
// Allocate space for the name and RData.
if (!rr)
return mDNSNULL;
{
return mDNSNULL;
}
return rr;
}
{
{
LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH);
return mDNSNULL;
}
// Allocate space for the name and the rdata along with the ResourceRecord
anonRR = (AnonInfoResourceRecord *)mDNSPlatformMemAllocate(sizeof(AnonInfoResourceRecord) + extraLen);
if (!anonRR)
return mDNSNULL;
}
// 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)
{
if (!ai)
{
return mDNSNULL;
}
if (rr)
{
{
return mDNSNULL;
}
return ai;
}
{
return mDNSNULL;
}
{
return mDNSNULL;
}
return ai;
}
{
}
{
if (*AnonInfo)
{
if (!(*AnonInfo))
else
}
}
// This function should be used only if you know that the question and
// the resource record belongs to the same set. The main usage is
// in ProcessQuery where we find the question to be part of the same
// set as the resource record, but it needs the AnonData to be
// initialized so that it can walk the cache records to see if they
// answer the question.
{
{
LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
return;
}
debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
if (ForQuestion)
{
{
return;
}
}
else
{
{
return;
}
}
}
// returns -1 if the caller should ignore the result
// returns 1 if the record answers the question
// returns 0 if the record does not answer the question
{
int i;
int AnonDataLen;
int hlen;
int nxtLength;
// Currently only PTR records can have anonymous information
if (q->qtype != kDNSType_PTR)
{
return -1;
}
// We allow anonymous questions to be answered by both normal services (without the
// anonymous information) and anonymous services that are part of the same set. And
// normal questions discover normal services and all anonymous services.
//
// The three cases have been enumerated clearly even though they all behave the
// same way.
if (!q->AnonInfo)
{
debugf("AnonInfoAnswersQuestion: not a anonymous type question");
{
// case 1
return -1;
}
else
{
// case 2
debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c);
return -1;
}
}
else
{
// case 3
{
debugf("AnonInfoAnswersQuestion: not a anonymous type record");
return -1;
}
}
// case 4: We have the anonymous information both in the question and the record. We need
// two sets of information to validate.
//
// 2) NSEC3 record that contains the hash and the salt
//
// If the question is a remote one, it does not have the anonymous information to validate (just
// the NSEC3 record) and hence the anonymous data should come from the local resource record. If the
// question is local, it can come from either of them and if there is a mismatch between the
// question and record, it won't validate.
{
// Before a cache record is created, if there is a matching question i.e., part
// of the same set, then when the cache is created we also set the anonymous
// information. Otherwise, the cache record contains just the NSEC3 record and we
// won't be here for that case.
//
// It is also possible that a local question is matched against the local AuthRecord
// as that is also the case for which the AnonData would be non-NULL for both.
// We match questions against AuthRecords (rather than the cache) for LocalOnly case and
// to see whether a .local query should be suppressed or not. The latter never happens
// because PTR queries are never suppressed.
// If they don't belong to the same anonymous set, then no point in validating.
{
debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ",
return 0;
}
// AnonData matches i.e they belong to the same group and the same service.
LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c,
return 1;
}
else
{
}
{
// If there is AnonData, then this is a local question. The
// NSEC3 RR comes from the resource record which could be part
// of the cache or local auth record. The cache entry could
// be from a remote host or created when we heard our own
// announcements. In any case, we use that to see if it matches
// the question.
}
else
{
// Remote question or hearing our own question back
}
{
// AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for
// that too and we can end up here for that case.
debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR,
return 0;
}
debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR));
{
return mDNSfalse;
}
if (hlen != SHA1_HASH_LENGTH)
{
return mDNSfalse;
}
{
return mDNSfalse;
}
for (i = 0; i < nxtLength; i++)
{
{
debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i);
return 0;
}
}
LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype));
return 1;
}
// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order.
// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records
// respectively.
mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name)
{
(void) m;
{
{
return cr;
}
}
return mDNSNULL;
}
mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
{
if (q->qtype != kDNSType_PTR)
return;
if (nsec3CR)
{
if (q->AnonInfo)
{
debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)",
}
}
}
mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
{
if (!(*McastNSEC3Records))
return;
// If already initialized or not a PTR type, we don't have to do anything
return;
if (nsec3CR)
{
{
debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)",
}
}
}
{
// if a1 is NULL and a2 is not NULL AND vice-versa
// return false as there is a change.
return mDNSfalse;
// Both could be NULL or non-NULL
{
// The caller already verified that the owner name is the same.
// Check whether the RData is same.
{
debugf("IdenticalAnonInfo: nsec3RR mismatch");
return mDNSfalse;
}
}
return mDNStrue;
}
{
(void) m;
if (!aifrom)
return;
if (aito)
{
}
else
{
}
}
#else // !ANONYMOUS_DISABLED
{
(void)si;
(void)name;
}
mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr)
{
(void)service;
(void)AnonData;
(void)len;
(void)rr;
return mDNSNULL;
}
{
(void)ai;
}
{
(void)q;
(void)rr;
(void)ForQuestion;
}
{
(void)rr;
(void)q;
return mDNSfalse;
}
mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
{
(void)m;
(void)McastNSEC3Records;
(void)q;
}
mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
{
(void)m;
(void)McastNSEC3Records;
(void)cr;
}
{
(void)m;
(void)crto;
(void)crfrom;
}
{
(void)a1;
(void)a2;
return mDNStrue;
}
#endif // !ANONYMOUS_DISABLED