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