Imported Upstream version 878.70.2
[platform/upstream/mdnsresponder.git] / mDNSMacOSX / DNSSECSupport.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 // ***************************************************************************
19 // DNSSECSupport.c: Platform specific support for DNSSEC like fetching root
20 // trust anchor and dnssec probes etc.
21 // ***************************************************************************
22
23 #include "mDNSEmbeddedAPI.h"
24 #include "DNSCommon.h"                  // For mDNS_Lock, mDNS_Random
25 #include "dnssec.h"
26 #include "DNSSECSupport.h"
27
28 #include <CommonCrypto/CommonDigest.h>  // For Hash algorithms SHA1 etc.
29
30 // Following are needed for fetching the root trust anchor dynamically
31 #include <CoreFoundation/CoreFoundation.h>
32 #include <libxml2/libxml/parser.h>
33 #include <libxml2/libxml/tree.h>
34 #include <libxml2/libxml/xmlmemory.h>
35 #include <notify.h>
36
37 // 30 days
38 #define ROOT_TA_UPDATE_INTERVAL  (30 * 24 * 3600)   // seconds
39
40 // After 100 days, the test anchors are not valid. Just an arbitrary number
41 // to configure validUntil. 
42 #define TEST_TA_EXPIRE_INTERVAL  (100 * 24 * 4600)
43
44 // When we can't fetch the root TA due to network errors etc., we start off a timer
45 // to fire at 60 seconds and then keep doubling it till we fetch it
46 #define InitialTAFetchInterval 60
47 #define DNSSECProbePercentage 1
48
49
50 #if !TARGET_OS_IPHONE
51 DNSQuestion DNSSECProbeQuestion;
52 #endif
53
54 mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval);
55
56 mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta)
57 {
58     int length = 0;
59     int i;
60     mDNSu8 *p;
61     TrustAnchor **t = &m->TrustAnchors;
62     char buffer[256];
63
64     while (*t)
65         t = &((*t)->next);
66     *t = ta;
67
68     buffer[0] = 0;
69     p = ta->rds.digest;
70     for (i = 0; i < ta->digestLen; i++)
71     {
72         length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]);
73     }
74     LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag,
75         ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer);
76 }
77
78 mDNSlocal void DelTrustAnchor(mDNS *const m, const domainname *zone)
79 {
80     TrustAnchor **ta = &m->TrustAnchors;
81     TrustAnchor *tmp;
82
83     while (*ta && !SameDomainName(&(*ta)->zone, zone))
84         ta = &(*ta)->next;
85
86     // First time, we won't find the TrustAnchor in the list as it has
87     // not been added.
88     if (!(*ta))
89         return;
90
91     tmp = *ta;
92     *ta = (*ta)->next;                  // Cut this record from the list
93     tmp->next = mDNSNULL;
94     if (tmp->rds.digest)
95         mDNSPlatformMemFree(tmp->rds.digest);
96     mDNSPlatformMemFree(tmp);
97 }
98
99 mDNSlocal void AddTrustAnchor(mDNS *const m, const domainname *zone, mDNSu16 keytag, mDNSu8 alg, mDNSu8 digestType, int diglen,
100     mDNSu8 *digest)
101 {
102     TrustAnchor *ta, *tmp;
103     mDNSu32 t = (mDNSu32) time(NULL); 
104
105     // Check for duplicates
106     tmp = m->TrustAnchors;
107     while (tmp)
108     {
109         if (SameDomainName(zone, &tmp->zone) && tmp->rds.keyTag == keytag && tmp->rds.alg == alg && tmp->rds.digestType == digestType &&
110             !memcmp(tmp->rds.digest, digest, diglen))
111         {
112             LogMsg("AddTrustAnchors: Found a duplicate");
113             return;
114         }
115         tmp = tmp->next;
116     }
117
118     ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor));
119     if (!ta)
120     {
121         LogMsg("AddTrustAnchor: malloc failure ta");
122         return;
123     }
124     ta->rds.keyTag = keytag;
125     ta->rds.alg = alg;
126     ta->rds.digestType = digestType;
127     ta->rds.digest = digest;
128     ta->digestLen = diglen;
129     ta->validFrom = t;
130     ta->validUntil = t + TEST_TA_EXPIRE_INTERVAL;
131     AssignDomainName(&ta->zone, zone);
132     ta->next = mDNSNULL;
133
134     LinkTrustAnchor(m, ta);
135 }
136
137 #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0'     ) :   \
138                     ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) :   \
139                     ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1)
140
141 mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen)
142 {
143     int i, j;
144     mDNSu8 *dig;
145
146     switch (digestType)
147     {
148     case SHA1_DIGEST_TYPE:
149         *diglen = CC_SHA1_DIGEST_LENGTH;
150         break;
151     case SHA256_DIGEST_TYPE:
152         *diglen = CC_SHA256_DIGEST_LENGTH;
153         break;
154     default:
155         LogMsg("ConvertDigest: digest type %d not supported", digestType);
156         return mDNSNULL;
157     }
158     dig = mDNSPlatformMemAllocate(*diglen);
159     if (!dig)
160     {
161         LogMsg("ConvertDigest: malloc failure");
162         return mDNSNULL;
163     }
164
165     for (j=0,i=0; i<*diglen*2; i+=2)
166     {
167         int l, h;
168         l = HexVal(digest[i]);
169         h = HexVal(digest[i+1]);
170         if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); mDNSPlatformMemFree(dig); return NULL;}
171         dig[j++] = (mDNSu8)((l << 4) | h);
172     }
173     return dig;
174 }
175
176 // All the children are in a linked list
177 //
178 // <TrustAnchor> has two children: <Zone> and <KeyDigest>
179 // <KeyDigest> has four children <KeyTag> <Algorithm> <DigestType> <Digest>
180 //
181 // Returns false if failed to parse the element i.e., malformed xml document.
182 // Validity of the actual values itself is done outside the function.
183 mDNSlocal mDNSBool ParseElementChildren(xmlDocPtr tadoc, xmlNode *node, TrustAnchor *ta)
184 {
185     xmlNode *cur_node;
186     xmlChar *val1, *val2, *val;
187     char *invalid = NULL;
188
189     val = val1 = val2 = NULL;
190
191     for (cur_node = node; cur_node; cur_node = cur_node->next)
192     {
193         invalid = NULL;
194         val1 = val2 = NULL;
195         
196         val = xmlNodeListGetString(tadoc, cur_node->xmlChildrenNode, 1);
197         if (!val)
198         {
199            LogInfo("ParseElementChildren: NULL value for %s", cur_node->name);
200            continue; 
201         }
202         if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Zone"))
203         {
204             // MaeDomainNameFromDNSNameString does not work for "."
205             if (!xmlStrcmp(val, (const xmlChar *)"."))
206             {
207                 ta->zone.c[0] = 0;
208             }
209             else if (!MakeDomainNameFromDNSNameString(&ta->zone, (char *)val))
210             {
211                 LogMsg("ParseElementChildren: Cannot parse Zone %s", val);
212                 goto error;
213             }
214             else
215             {
216                 LogInfo("ParseElementChildren: Element %s, value %##s", cur_node->name, ta->zone.c);
217             }
218         }
219         else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyTag"))
220         {
221             ta->rds.keyTag = strtol((const char *)val, &invalid, 10);
222             if (*invalid != '\0')
223             {
224                 LogMsg("ParseElementChildren: KeyTag invalid character %d", *invalid);
225                 goto error;
226             }
227             else
228             {
229                 LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.keyTag);
230             }
231         }
232         else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Algorithm"))
233         {
234             ta->rds.alg = strtol((const char *)val, &invalid, 10);
235             if (*invalid != '\0')
236             {
237                 LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid);
238                 goto error;
239             }
240             else
241             {
242                 LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.alg);
243             }
244         }
245         else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"DigestType"))
246         {
247             ta->rds.digestType = strtol((const char *)val, &invalid, 10);
248             if (*invalid != '\0')
249             {
250                 LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid);
251                 goto error;
252             }
253             else
254             {
255                 LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.digestType);
256             }
257         }
258         else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Digest"))
259         {
260             int diglen;
261             mDNSu8 *dig = ConvertDigest((char *)val, ta->rds.digestType, &diglen);
262             if (dig)
263             { 
264                 LogInfo("ParseElementChildren: Element %s, digest %s", cur_node->name, val);
265                 ta->digestLen = diglen;
266                 ta->rds.digest = dig;
267             }
268             else
269             {
270                 LogMsg("ParseElementChildren: Element %s, NULL digest", cur_node->name);
271                 goto error;
272             }
273         }
274         else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest"))
275         {
276             struct tm tm;
277             val1 = xmlGetProp(cur_node, (const xmlChar *)"validFrom");
278             if (val1)
279             {
280                 char *s = strptime((const char *)val1, "%Y-%m-%dT%H:%M:%S", &tm);
281                 if (!s)
282                 {
283                     LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val1);
284                     goto error;
285                 }
286                 else
287                 {
288                     ta->validFrom = (mDNSu32)timegm(&tm);
289                 }
290             }
291             val2 = xmlGetProp(cur_node, (const xmlChar *)"validUntil");
292             if (val2)
293             {
294                 char *s = strptime((const char *)val2, "%Y-%m-%dT%H:%M:%S", &tm);
295                 if (!s)
296                 {
297                     LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val2);
298                     goto error;
299                 }
300                 else
301                 {
302                     ta->validUntil = (mDNSu32)timegm(&tm);
303                 }
304             }
305             else
306             {
307                 // If there is no validUntil, set it to the next probing interval
308                 mDNSu32 t = (mDNSu32) time(NULL); 
309                 ta->validUntil = t + ROOT_TA_UPDATE_INTERVAL;
310             }
311             LogInfo("ParseElementChildren: ValidFrom time %u, validUntil %u", (unsigned)ta->validFrom, (unsigned)ta->validUntil);
312         }
313         if (val1)
314             xmlFree(val1);
315         if (val2)
316             xmlFree(val2);
317         if (val)
318             xmlFree(val);
319     }
320     return mDNStrue;
321 error:
322     if (val1)
323         xmlFree(val1);
324     if (val2)
325         xmlFree(val2);
326     if (val)
327         xmlFree(val);
328     return mDNSfalse;
329 }
330
331 mDNSlocal mDNSBool ValidateTrustAnchor(TrustAnchor *ta)
332 {
333     time_t currTime = time(NULL);
334
335     // Currently only support trust anchor for root.
336     if (!SameDomainName(&ta->zone, (const domainname *)"\000"))
337     {
338         LogInfo("ParseElementChildren: Zone %##s not root", ta->zone.c);
339         return mDNSfalse;
340     }
341
342     switch (ta->rds.digestType)
343     {
344     case SHA1_DIGEST_TYPE:
345         if (ta->digestLen != CC_SHA1_DIGEST_LENGTH) 
346         {
347             LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA1", ta->digestLen);
348             return mDNSfalse;
349         }
350         break;
351     case SHA256_DIGEST_TYPE:
352         if (ta->digestLen != CC_SHA256_DIGEST_LENGTH) 
353         {
354             LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA256", ta->digestLen);
355             return mDNSfalse;
356         }
357         break;
358     default:
359         LogMsg("ValidateTrustAnchor: digest type %d not supported", ta->rds.digestType);
360         return mDNSfalse;
361     }
362     if (!ta->rds.digest)
363     {
364         LogMsg("ValidateTrustAnchor: digest NULL for %d", ta->rds.digestType);
365         return mDNSfalse;
366     }
367     switch (ta->rds.alg)
368     {
369     case CRYPTO_RSA_SHA512:
370     case CRYPTO_RSA_SHA256:
371     case CRYPTO_RSA_NSEC3_SHA1:
372     case CRYPTO_RSA_SHA1:
373         break;
374     default:
375         LogMsg("ValidateTrustAnchor: Algorithm %d not supported", ta->rds.alg);
376         return mDNSfalse;
377     }
378     
379     if (DNS_SERIAL_LT(currTime, ta->validFrom))
380     {
381         LogMsg("ValidateTrustAnchor: Invalid ValidFrom time %u, currtime %u", (unsigned)ta->validFrom, (unsigned)currTime);
382         return mDNSfalse;
383     }
384     if (DNS_SERIAL_LT(ta->validUntil, currTime))
385     {
386         LogMsg("ValidateTrustAnchor: Invalid ValidUntil time %u, currtime %u", (unsigned)ta->validUntil, (unsigned)currTime);
387         return mDNSfalse;
388     }
389     return mDNStrue;
390 }
391
392 mDNSlocal mDNSBool ParseElement(xmlDocPtr tadoc, xmlNode * a_node, TrustAnchor *ta)
393 {
394     xmlNode *cur_node = NULL;
395
396     for (cur_node = a_node; cur_node; cur_node = cur_node->next)
397     {
398         if (cur_node->type == XML_ELEMENT_NODE)
399         {
400             // There could be multiple KeyDigests per TrustAnchor. We keep parsing till we
401             // reach the last one or we encounter an error in parsing the document.
402             if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest"))
403             {
404                 if (ta->rds.digest)
405                     mDNSPlatformMemFree(ta->rds.digest);
406                 ta->rds.digestType = 0;
407                 ta->digestLen = 0;
408             }
409             if (!ParseElementChildren(tadoc, cur_node->children, ta))
410                 return mDNSfalse;
411             if (!ParseElement(tadoc, cur_node->children, ta))
412                 return mDNSfalse;
413         }
414     }
415     return mDNStrue;
416 }
417
418 mDNSlocal void TAComplete(mDNS *const m, void *context)
419 {
420     TrustAnchor *ta = (TrustAnchor *)context;
421
422     DelTrustAnchor(m, &ta->zone);
423     LinkTrustAnchor(m, ta);
424 }
425
426 mDNSlocal void FetchRootTA(mDNS *const m)
427 {
428     CFStringRef urlString = CFSTR("https://data.iana.org/root-anchors/root-anchors.xml");
429     CFDataRef xmlData;
430     CFStringRef fileRef = NULL;
431     const char *xmlFileName = NULL;
432     char buf[512];
433     CFURLRef url = NULL;
434     static unsigned int RootTAFetchInterval = InitialTAFetchInterval;
435
436     (void) m;
437
438     TrustAnchor *ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor));
439     if (!ta)
440     {
441         LogMsg("FetchRootTA: TrustAnchor alloc failed");
442         return;
443     }
444     memset(ta, 0, sizeof(TrustAnchor));
445
446     url = CFURLCreateWithString(NULL, urlString, NULL);
447     if (!url)
448     {
449         LogMsg("FetchRootTA: CFURLCreateWithString error");
450         mDNSPlatformMemFree(ta);
451         return;
452     }
453
454 #pragma clang diagnostic push
455 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
456     // If we can't fetch the XML file e.g., network problems, trigger a timer. All other failures
457     // should hardly happen in practice for which schedule the normal interval to refetch the TA.
458     Boolean success = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL);
459 #pragma clang diagnostic pop
460     if (!success)
461     {
462         LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error");
463         CFRelease(url);
464         mDNSPlatformMemFree(ta);
465         RegisterNotification(m, RootTAFetchInterval);
466         RootTAFetchInterval *= 2 + 1;
467         return;
468     }
469
470     // get the name of the last component from the url, libxml will use it if
471     // it has to report an error
472     fileRef = CFURLCopyLastPathComponent(url);
473     if (fileRef)
474     {
475         xmlFileName = CFStringGetCStringPtr(fileRef, kCFStringEncodingUTF8);
476         if (!xmlFileName)
477         {
478             if (!CFStringGetCString(fileRef, buf, sizeof(buf), kCFStringEncodingUTF8) )
479                 strlcpy(buf, "nofile.xml", sizeof(buf));
480             xmlFileName = (const char *)buf;
481         }
482     }
483
484     // Parse the XML and get the CFXMLTree.
485     xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData),
486         (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0);        
487
488     if (fileRef)
489         CFRelease(fileRef);
490     CFRelease(url);
491     CFRelease(xmlData);
492
493     if (!tadoc)
494     {
495         LogMsg("FetchRootTA: xmlReadMemory failed");
496         goto done;
497     }
498
499     xmlNodePtr root = xmlDocGetRootElement(tadoc);
500     if (!root)
501     {
502         LogMsg("FetchRootTA: Cannot get root element");
503         goto done;
504     }
505
506     if (ParseElement(tadoc, root, ta) && ValidateTrustAnchor(ta))
507     {
508         // Do the actual addition of TA on the main queue.
509         mDNSPlatformDispatchAsync(m, ta, TAComplete);
510     }
511     else
512     {
513         if (ta->rds.digest)
514             mDNSPlatformMemFree(ta->rds.digest);
515         mDNSPlatformMemFree(ta);
516     }
517 done:
518     if (tadoc)
519         xmlFreeDoc(tadoc);
520     RegisterNotification(m, ROOT_TA_UPDATE_INTERVAL);
521     RootTAFetchInterval = InitialTAFetchInterval;
522     return;
523 }
524
525
526 #if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
527 mDNSlocal void DNSSECProbeCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
528 {
529     if (!AddRecord)
530         return;
531
532     mDNS_Lock(m);
533     if ((m->timenow - question->StopTime) >= 0)
534     {
535         mDNS_Unlock(m);
536         LogDNSSEC("DNSSECProbeCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype));
537         mDNS_StopQuery(m, question);
538         return;
539     }
540     mDNS_Unlock(m);
541
542     // Wait till we get the DNSSEC results. If we get a negative response e.g., no DNS servers, the
543     // question will be restarted by the core and we should have the DNSSEC results eventually.
544     if (AddRecord != QC_dnssec)
545     {
546         LogDNSSEC("DNSSECProbeCallback: Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
547         return;
548     }
549
550     LogDNSSEC("DNSSECProbeCallback: Question %##s (%s), DNSSEC status %s", question->qname.c, DNSTypeName(question->qtype),
551             DNSSECStatusName(question->ValidationStatus));
552
553     mDNS_StopQuery(m, question);
554 }
555
556 // Send a DNSSEC probe just for the sake of collecting DNSSEC statistics.
557 mDNSexport void DNSSECProbe(mDNS *const m)
558 {
559     mDNSu32 rand;
560
561     if (DNSSECProbeQuestion.ThisQInterval != -1)
562         return;
563     
564     rand = mDNSRandom(FutureTime) % 100;
565     // Probe 1% of the time
566     if (rand >= DNSSECProbePercentage)
567         return;
568     
569     mDNS_DropLockBeforeCallback();
570     InitializeQuestion(m, &DNSSECProbeQuestion, mDNSInterface_Any, (const domainname *)"\003com", kDNSType_DS, DNSSECProbeCallback, mDNSNULL);
571     DNSSECProbeQuestion.ValidatingResponse = 0;
572     DNSSECProbeQuestion.ValidationRequired = DNSSEC_VALIDATION_SECURE;
573
574     BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeProbe, 1);
575     mDNS_StartQuery(m, &DNSSECProbeQuestion);
576     mDNS_ReclaimLockAfterCallback(); 
577 }
578 #endif // APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
579
580 // For now we fetch the root trust anchor and update the local copy
581 mDNSexport void UpdateTrustAnchors(mDNS *const m)
582 {
583     // Register for a notification to fire immediately which in turn will update
584     // the trust anchor
585     if (RegisterNotification(m, 1))
586     {
587         LogMsg("UpdateTrustAnchors: ERROR!! failed to register for notification");
588     }
589 }
590
591 mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval)
592 {
593     int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval
594     char buffer[len];
595     unsigned int blen;
596     int status;
597
598     // Starting "interval" second from now (+ below indicates relative) register for a notification
599     blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval);
600     if (blen >= sizeof(buffer))
601     {
602         LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer));
603         return -1;
604     } 
605     LogInfo("RegisterNotification: buffer %s", buffer);
606     if (m->notifyToken)
607     {
608         notify_cancel(m->notifyToken);
609         m->notifyToken = 0;
610     }
611     status = notify_register_dispatch(buffer, &m->notifyToken,
612                 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
613                 ^(int t) { (void) t; FetchRootTA(m); });
614
615     if (status != NOTIFY_STATUS_OK)
616     {
617         LogMsg("RegisterNotification: notify_register_dispatch failed");
618         return -1;
619     }
620     return 0;
621 }
622
623 mDNSexport mStatus DNSSECPlatformInit(mDNS *const m)
624 {
625     int diglen;
626
627     m->TrustAnchors = mDNSNULL;
628     m->notifyToken  = 0;
629
630     // Add a couple of trust anchors for testing purposes.
631     mDNSlocal const domainname *testZone  = (const domainname*)"\007example";
632
633     char *digest = "F122E47B5B7D2B6A5CC0A21EADA11D96BB9CC927";
634     mDNSu8 *dig = ConvertDigest(digest, 1, &diglen);
635     AddTrustAnchor(m, testZone, 23044, 5, 1, diglen, dig);
636
637     char *digest1 = "D795AE5E1AFB200C6139474199B70EAD3F3484553FD97BE5A43704B8A4791F21";
638     dig = ConvertDigest(digest1, 2, &diglen);
639     AddTrustAnchor(m, testZone, 23044, 5, 2, diglen, dig);
640
641     // Add the TA for root zone manually here. We will dynamically fetch the root TA and
642     // update it shortly. If that fails e.g., disconnected from the network, we still
643     // have something to work with.
644     char *digest2 = "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5";
645     dig = ConvertDigest(digest2, 2, &diglen);
646     AddTrustAnchor(m, (const domainname *)"\000", 19036, 8, 2, diglen, dig);
647
648 #if !TARGET_OS_IPHONE
649     DNSSECProbeQuestion.ThisQInterval = -1;
650 #endif
651     return mStatus_NoError;
652 }