Imported Upstream version 878.70.2
[platform/upstream/mdnsresponder.git] / mDNSCore / anonymous.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include "mDNSEmbeddedAPI.h"
19 #include "CryptoAlg.h"
20 #include "anonymous.h"
21 #include "DNSCommon.h"
22
23 // Define ANONYMOUS_DISABLED to remove all the anonymous functionality
24 // and use the stub functions implemented later in this file.
25
26 #ifndef ANONYMOUS_DISABLED
27
28 #define ANON_NSEC3_ITERATIONS        1 
29
30 struct AnonInfoResourceRecord_struct
31 {
32     ResourceRecord resrec;
33     RData          rdatastorage;
34 };
35
36 typedef struct AnonInfoResourceRecord_struct AnonInfoResourceRecord;
37
38 mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt)
39 {
40     const mDNSu8 *ptr;
41     rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data;
42     mDNSu8 *tmp, *nxt;
43     unsigned short iter = ANON_NSEC3_ITERATIONS;
44     int hlen;
45     const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
46
47     // Construct the RDATA first and construct the owner name based on that.
48     ptr = (const mDNSu8 *)&salt;
49     debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c);
50
51     // Set the RDATA
52     nsec3->alg = SHA1_DIGEST_TYPE;
53     nsec3->flags = 0;
54     nsec3->iterations = swap16(iter);
55     nsec3->saltLength = 4;
56     tmp = (mDNSu8 *)&nsec3->salt;
57     *tmp++ = ptr[0];
58     *tmp++ = ptr[1];
59     *tmp++ = ptr[2];
60     *tmp++ = ptr[3];
61
62     // hashLength, nxt, bitmap
63     *tmp++ = SHA1_HASH_LENGTH;    // hash length
64     nxt = tmp;
65     tmp += SHA1_HASH_LENGTH;
66     *tmp++ = 0; // window number
67     *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length
68     mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE);
69     tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7);
70
71     // Hash the base service name + salt + AnonData
72     if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen))
73     {
74         LogMsg("InitializeNSEC3Record: NSEC3HashName failed for %##s", rr->name->c);
75         return mDNSfalse;
76     }
77     if (hlen != SHA1_HASH_LENGTH)
78     {
79         LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen);
80         return mDNSfalse;
81     }
82     mDNSPlatformMemCopy(nxt, hashName, hlen);
83
84     return mDNStrue;
85 }
86
87 mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt)
88 {
89     ResourceRecord *rr;
90     int dlen;
91     domainname *name;
92
93     // We are just allocating an RData which has StandardAuthRDSize
94     if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH)
95     {
96         LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH);
97         return mDNSNULL;
98     }
99
100     dlen = DomainNameLength(service);
101  
102     // Allocate space for the name and RData. 
103     rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData));
104     if (!rr)
105         return mDNSNULL;
106     name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord));
107     rr->RecordType        = kDNSRecordTypePacketAuth;
108     rr->InterfaceID       = mDNSInterface_Any;
109     rr->name              = (const domainname *)name;
110     rr->rrtype            = kDNSType_NSEC3;
111     rr->rrclass           = kDNSClass_IN;
112     rr->rroriginalttl     = kStandardTTL;
113     rr->rDNSServer        = mDNSNULL;
114     rr->rdlength          = MCAST_NSEC3_RDLENGTH;
115     rr->rdestimate        = MCAST_NSEC3_RDLENGTH;
116     rr->rdata             = (RData *)((mDNSu8 *)rr->name + dlen);
117
118     AssignDomainName(name, service);
119     if (!InitializeNSEC3Record(rr, AnonData, len, salt))
120     {
121         mDNSPlatformMemFree(rr);
122         return mDNSNULL;
123     }
124     return rr;
125 }
126
127 mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr)
128 {
129     AnonInfoResourceRecord *anonRR;
130     domainname *name;
131     mDNSu32 neededLen;
132     mDNSu32 extraLen;
133
134     if (rr->rdlength < MCAST_NSEC3_RDLENGTH)
135     {
136         LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH);
137         return mDNSNULL;
138     }
139     // Allocate space for the name and the rdata along with the ResourceRecord
140     neededLen = rr->rdlength + DomainNameLength(rr->name);
141     extraLen = (neededLen > sizeof(RDataBody)) ? (neededLen - sizeof(RDataBody)) : 0;
142     anonRR = (AnonInfoResourceRecord *)mDNSPlatformMemAllocate(sizeof(AnonInfoResourceRecord) + extraLen);
143     if (!anonRR)
144         return mDNSNULL;
145
146     anonRR->resrec = *rr;
147
148     anonRR->rdatastorage.MaxRDLength = rr->rdlength;
149     mDNSPlatformMemCopy(anonRR->rdatastorage.u.data, rr->rdata->u.data, rr->rdlength);
150
151     name = (domainname *)(anonRR->rdatastorage.u.data + rr->rdlength);
152     AssignDomainName(name, rr->name);
153
154     anonRR->resrec.name = name;
155     anonRR->resrec.rdata = &anonRR->rdatastorage;
156
157     si->nsec3RR = (ResourceRecord *)anonRR;
158
159     return si->nsec3RR;
160 }
161
162 // When a service is started or a browse is started with the Anonymous data, we allocate a new random
163 // number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and
164 // the anonymous data.
165 //
166 // If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can
167 // check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received.
168 mDNSexport  AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr)
169 {
170     AnonymousInfo *ai;
171     ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo));
172     if (!ai)
173     {
174         return mDNSNULL;
175     }
176     mDNSPlatformMemZero(ai, sizeof(AnonymousInfo));
177     if (rr)
178     {
179         if (!CopyNSEC3ResourceRecord(ai, rr))
180         {
181             mDNSPlatformMemFree(ai);
182             return mDNSNULL;
183         }
184         return ai;
185     }
186     ai->salt = mDNSRandom(0xFFFFFFFF);
187     ai->AnonData = mDNSPlatformMemAllocate(len);
188     if (!ai->AnonData)
189     {
190         mDNSPlatformMemFree(ai);
191         return mDNSNULL;
192     }
193     ai->AnonDataLen = len;
194     mDNSPlatformMemCopy(ai->AnonData, data, len);
195     ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt);
196     if (!ai->nsec3RR)
197     {
198         mDNSPlatformMemFree(ai);
199         return mDNSNULL;
200     }
201     return ai;
202 }
203
204 mDNSexport void FreeAnonInfo(AnonymousInfo *ai)
205 {
206     if (ai->nsec3RR)
207         mDNSPlatformMemFree(ai->nsec3RR);
208     if (ai->AnonData)
209         mDNSPlatformMemFree(ai->AnonData);
210     mDNSPlatformMemFree(ai);
211 }
212
213 mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name)
214 {
215     if (*AnonInfo)
216     {
217         AnonymousInfo *ai = *AnonInfo;
218         *AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL);
219         if (!(*AnonInfo))
220             *AnonInfo = ai;
221         else
222             FreeAnonInfo(ai);
223     }
224 }
225
226 // This function should be used only if you know that the question and
227 // the resource record belongs to the same set. The main usage is
228 // in ProcessQuery where we find the question to be part of the same
229 // set as the resource record, but it needs the AnonData to be
230 // initialized so that it can walk the cache records to see if they
231 // answer the question.
232 mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion)
233 {
234     if (!q->AnonInfo || !rr->AnonInfo)
235     {
236         LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
237         return;
238     }
239     
240     debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
241     if (ForQuestion)
242     {
243         if (q->AnonInfo->AnonDataLen < rr->AnonInfo->AnonDataLen)
244         {
245             mDNSPlatformMemFree(q->AnonInfo->AnonData);
246             q->AnonInfo->AnonData = mDNSNULL;
247         }
248
249         if (!q->AnonInfo->AnonData)
250         {
251             q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen);
252             if (!q->AnonInfo->AnonData)
253                 return;
254         }
255         mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen);
256         q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen;
257     }
258     else
259     {
260         if (rr->AnonInfo->AnonDataLen < q->AnonInfo->AnonDataLen)
261         {
262             mDNSPlatformMemFree(rr->AnonInfo->AnonData);
263             rr->AnonInfo->AnonData = mDNSNULL;
264         }
265
266         if (!rr->AnonInfo->AnonData)
267         {
268             rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen);
269             if (!rr->AnonInfo->AnonData)
270                 return;
271         }
272         mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen);
273         rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen;
274     }
275 }
276
277 // returns -1 if the caller should ignore the result
278 // returns 1 if the record answers the question
279 // returns 0 if the record does not answer the question
280 mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
281 {
282     mDNSexport mDNS mDNSStorage;
283     ResourceRecord *nsec3RR;
284     int i;
285     AnonymousInfo *qai, *rai;
286     mDNSu8 *AnonData;
287     int AnonDataLen;
288     rdataNSEC3 *nsec3;
289     int hlen;
290     int nxtLength;
291     mDNSu8 *nxtName;
292     mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
293     mDNSPlatformMemZero(hashName, sizeof(hashName));
294
295     debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c);
296
297     // Currently only PTR records can have anonymous information
298     if (q->qtype != kDNSType_PTR)
299     {
300         return -1;
301     }
302
303     // We allow anonymous questions to be answered by both normal services (without the
304     // anonymous information) and anonymous services that are part of the same set. And
305     // normal questions discover normal services and all anonymous services. 
306     //
307     // The three cases have been enumerated clearly even though they all behave the
308     // same way.
309     if (!q->AnonInfo)
310     {
311         debugf("AnonInfoAnswersQuestion: not a anonymous type question");
312         if (!rr->AnonInfo)
313         {
314             // case 1
315             return -1;
316         }
317         else
318         {
319             // case 2
320             debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c);
321             return -1;
322         }
323     }
324     else
325     {
326         // case 3
327         if (!rr->AnonInfo)
328         {
329             debugf("AnonInfoAnswersQuestion: not a anonymous type record");
330             return -1;
331         }
332     }
333
334     // case 4: We have the anonymous information both in the question and the record. We need
335     // two sets of information to validate.
336     //
337     // 1) Anonymous data that identifies the set/group
338     // 2) NSEC3 record that contains the hash and the salt
339     //
340     // If the question is a remote one, it does not have the anonymous information to validate (just
341     // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the
342     // question is local, it can come from either of them and if there is a mismatch between the
343     // question and record, it won't validate.
344
345     qai = q->AnonInfo;
346     rai = rr->AnonInfo;
347
348     if (qai->AnonData && rai->AnonData)
349     {
350         // Before a cache record is created, if there is a matching question i.e., part
351         // of the same set, then when the cache is created we also set the anonymous
352         // information. Otherwise, the cache record contains just the NSEC3 record and we
353         // won't be here for that case.
354         //
355         // It is also possible that a local question is matched against the local AuthRecord
356         // as that is also the case for which the AnonData would be non-NULL for both.
357         // We match questions against AuthRecords (rather than the cache) for LocalOnly case and 
358         // to see whether a .local query should be suppressed or not. The latter never happens
359         // because PTR queries are never suppressed.
360
361         // If they don't belong to the same anonymous set, then no point in validating.
362         if ((qai->AnonDataLen != rai->AnonDataLen) ||
363             mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0)
364         {
365             debugf("AnonInfoAnswersQuestion: AnonData mis-match for record  %s question %##s ",
366                 RRDisplayString(&mDNSStorage, rr), q->qname.c);
367             return 0;
368         }
369         // AnonData matches i.e they belong to the same group and the same service.
370         LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c,
371             rr->name->c);
372         return 1;
373     }
374     else
375     {
376         debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData);
377     }
378
379     if (qai->AnonData)
380     {
381         // If there is AnonData, then this is a local question. The
382         // NSEC3 RR comes from the resource record which could be part
383         // of the cache or local auth record. The cache entry could
384         // be from a remote host or created when we heard our own 
385         // announcements. In any case, we use that to see if it matches
386         // the question.
387         AnonData = qai->AnonData;
388         AnonDataLen = qai->AnonDataLen;
389         nsec3RR = rai->nsec3RR;
390     }
391     else
392     {
393         // Remote question or hearing our own question back
394         AnonData = rai->AnonData;
395         AnonDataLen = rai->AnonDataLen;
396         nsec3RR = qai->nsec3RR;
397     }
398
399     if (!AnonData || !nsec3RR)
400     {
401         // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for
402         // that too and we can end up here for that case.
403         debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR,
404             q->qname.c, RRDisplayString(&mDNSStorage, rr));
405         return 0;
406     }
407     debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR));
408
409
410     nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data;
411
412     if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen))
413     {
414         LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for %##s", nsec3RR->name->c);
415         return mDNSfalse;
416     }
417     if (hlen != SHA1_HASH_LENGTH)
418     {
419         LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen);
420         return mDNSfalse;
421     }
422
423     NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);
424
425     if (hlen != nxtLength)
426     {
427         LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength);
428         return mDNSfalse;
429     }
430
431     for (i = 0; i < nxtLength; i++)
432     {
433         if (nxtName[i] != hashName[i])
434         {
435             debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i);
436             return 0;
437         }
438     }
439     LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype));
440     return 1;
441 }
442
443 // Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order.
444 // Similarly we also parse the NSEC3 records in order and this mapping to the questions and records
445 // respectively.
446 mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name)
447 {
448     CacheRecord *cr;
449     CacheRecord **prev = nsec3;
450     
451     (void) m;
452
453     for (cr = *nsec3; cr; cr = cr->next)
454     {
455         if (SameDomainName(cr->resrec.name, name))
456         {
457             debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c);
458             *prev = cr->next;
459             cr->next = mDNSNULL;
460             return cr;
461         }
462         prev = &cr->next;
463     }
464     return mDNSNULL;
465 }
466
467 mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
468 {
469     CacheRecord *nsec3CR;
470
471     if (q->qtype != kDNSType_PTR)
472         return;
473
474     nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname);
475     if (nsec3CR)
476     {
477         q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec);
478         if (q->AnonInfo)
479         {
480             debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)",
481                 RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype));
482         }
483         ReleaseCacheRecord(m, nsec3CR);
484     }
485 }
486
487 mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
488 {
489     CacheRecord *nsec3CR;
490
491     if (!(*McastNSEC3Records))
492         return;
493
494     // If already initialized or not a PTR type, we don't have to do anything
495     if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR)
496         return;
497
498     nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name);
499     if (nsec3CR)
500     {
501         cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec);
502         if (cr->resrec.AnonInfo)
503         {
504             debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)",
505                 RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c,
506                 DNSTypeName(cr->resrec.rrtype));
507         }
508         ReleaseCacheRecord(m, nsec3CR);
509     }
510 }
511
512 mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2)
513 {
514     // if a1 is NULL and a2 is not NULL AND vice-versa
515     // return false as there is a change.
516     if ((a1 != mDNSNULL) != (a2 != mDNSNULL))
517         return mDNSfalse;
518
519     // Both could be NULL or non-NULL
520     if (a1 && a2)
521     {
522         // The caller already verified that the owner name is the same.
523         // Check whether the RData is same.
524         if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR))
525         {
526             debugf("IdenticalAnonInfo: nsec3RR mismatch");
527             return mDNSfalse;
528         }
529     }
530     return mDNStrue;
531 }
532
533 mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom)
534 {
535     AnonymousInfo *aifrom = crfrom->resrec.AnonInfo;
536     AnonymousInfo *aito = crto->resrec.AnonInfo;
537
538     (void) m;
539
540     if (!aifrom)
541         return;
542
543     if (aito)
544     {
545         crto->resrec.AnonInfo = aifrom;
546         FreeAnonInfo(aito);
547         crfrom->resrec.AnonInfo = mDNSNULL;
548     }
549     else
550     {
551         FreeAnonInfo(aifrom);
552         crfrom->resrec.AnonInfo = mDNSNULL;
553     }
554 }
555
556 #else // !ANONYMOUS_DISABLED
557
558 mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name)
559 {
560         (void)si;
561         (void)name;
562 }
563
564 mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr)
565 {
566         (void)service;
567         (void)AnonData;
568         (void)len;
569         (void)rr;
570
571         return mDNSNULL;
572 }
573
574 mDNSexport void FreeAnonInfo(AnonymousInfo *ai)
575 {
576         (void)ai;
577 }
578
579 mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion)
580 {
581         (void)q;
582         (void)rr;
583         (void)ForQuestion;
584 }
585
586 mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
587 {
588         (void)rr;
589         (void)q;
590
591         return mDNSfalse;
592 }
593
594 mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
595 {
596         (void)m;
597         (void)McastNSEC3Records;
598         (void)q;
599 }
600
601 mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
602 {
603         (void)m;
604         (void)McastNSEC3Records;
605         (void)cr;
606 }
607
608 mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom)
609 {
610         (void)m;
611         (void)crto;
612         (void)crfrom;
613 }
614
615 mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2)
616 {
617         (void)a1;
618         (void)a2;
619
620         return mDNStrue;
621 }
622
623 #endif // !ANONYMOUS_DISABLED