summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSMacOSX/DNSSECSupport.c
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSMacOSX/DNSSECSupport.c')
-rw-r--r--mDNSResponder/mDNSMacOSX/DNSSECSupport.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSMacOSX/DNSSECSupport.c b/mDNSResponder/mDNSMacOSX/DNSSECSupport.c
new file mode 100644
index 00000000..e1255c4d
--- /dev/null
+++ b/mDNSResponder/mDNSMacOSX/DNSSECSupport.c
@@ -0,0 +1,645 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Computer, 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+// ***************************************************************************
+// DNSSECSupport.c: Platform specific support for DNSSEC like fetching root
+// trust anchor and dnssec probes etc.
+// ***************************************************************************
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h" // For mDNS_Lock, mDNS_Random
+#include "dnssec.h"
+#include "DNSSECSupport.h"
+
+#include <CommonCrypto/CommonDigest.h> // For Hash algorithms SHA1 etc.
+
+// Following are needed for fetching the root trust anchor dynamically
+#include <CoreFoundation/CoreFoundation.h>
+#include <libxml2/libxml/parser.h>
+#include <libxml2/libxml/tree.h>
+#include <libxml2/libxml/xmlmemory.h>
+#include <notify.h>
+
+// 30 days
+#define ROOT_TA_UPDATE_INTERVAL (30 * 24 * 3600) // seconds
+
+// After 100 days, the test anchors are not valid. Just an arbitrary number
+// to configure validUntil.
+#define TEST_TA_EXPIRE_INTERVAL (100 * 24 * 4600)
+
+// When we can't fetch the root TA due to network errors etc., we start off a timer
+// to fire at 60 seconds and then keep doubling it till we fetch it
+#define InitialTAFetchInterval 60
+
+#if !TARGET_OS_IPHONE
+DNSQuestion DNSSECProbeQuestion;
+#endif
+
+mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval);
+
+mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta)
+{
+ int length = 0;
+ int i;
+ mDNSu8 *p;
+ TrustAnchor **t = &m->TrustAnchors;
+ char buffer[256];
+
+ while (*t)
+ t = &((*t)->next);
+ *t = ta;
+
+ buffer[0] = 0;
+ p = ta->rds.digest;
+ for (i = 0; i < ta->digestLen; i++)
+ {
+ length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]);
+ }
+ LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag,
+ ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer);
+}
+
+mDNSlocal void DelTrustAnchor(mDNS *const m, const domainname *zone)
+{
+ TrustAnchor **ta = &m->TrustAnchors;
+ TrustAnchor *tmp;
+
+ while (*ta && !SameDomainName(&(*ta)->zone, zone))
+ ta = &(*ta)->next;
+
+ // First time, we won't find the TrustAnchor in the list as it has
+ // not been added.
+ if (!(*ta))
+ return;
+
+ tmp = *ta;
+ *ta = (*ta)->next; // Cut this record from the list
+ tmp->next = mDNSNULL;
+ if (tmp->rds.digest)
+ mDNSPlatformMemFree(tmp->rds.digest);
+ mDNSPlatformMemFree(tmp);
+}
+
+mDNSlocal void AddTrustAnchor(mDNS *const m, const domainname *zone, mDNSu16 keytag, mDNSu8 alg, mDNSu8 digestType, int diglen,
+ mDNSu8 *digest)
+{
+ TrustAnchor *ta, *tmp;
+ mDNSu32 t = (mDNSu32) time(NULL);
+
+ // Check for duplicates
+ tmp = m->TrustAnchors;
+ while (tmp)
+ {
+ if (SameDomainName(zone, &tmp->zone) && tmp->rds.keyTag == keytag && tmp->rds.alg == alg && tmp->rds.digestType == digestType &&
+ !memcmp(tmp->rds.digest, digest, diglen))
+ {
+ LogMsg("AddTrustAnchors: Found a duplicate");
+ return;
+ }
+ tmp = tmp->next;
+ }
+
+ ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor));
+ if (!ta)
+ {
+ LogMsg("AddTrustAnchor: malloc failure ta");
+ return;
+ }
+ ta->rds.keyTag = keytag;
+ ta->rds.alg = alg;
+ ta->rds.digestType = digestType;
+ ta->rds.digest = digest;
+ ta->digestLen = diglen;
+ ta->validFrom = t;
+ ta->validUntil = t + TEST_TA_EXPIRE_INTERVAL;
+ AssignDomainName(&ta->zone, zone);
+ ta->next = mDNSNULL;
+
+ LinkTrustAnchor(m, ta);
+}
+
+#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \
+ ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \
+ ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1)
+
+mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen)
+{
+ int i, j;
+ mDNSu8 *dig;
+
+ switch (digestType)
+ {
+ case SHA1_DIGEST_TYPE:
+ *diglen = CC_SHA1_DIGEST_LENGTH;
+ break;
+ case SHA256_DIGEST_TYPE:
+ *diglen = CC_SHA256_DIGEST_LENGTH;
+ break;
+ default:
+ LogMsg("ConvertDigest: digest type %d not supported", digestType);
+ return mDNSNULL;
+ }
+ dig = mDNSPlatformMemAllocate(*diglen);
+ if (!dig)
+ {
+ LogMsg("ConvertDigest: malloc failure");
+ return mDNSNULL;
+ }
+
+ for (j=0,i=0; i<*diglen*2; i+=2)
+ {
+ int l, h;
+ l = HexVal(digest[i]);
+ h = HexVal(digest[i+1]);
+ if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); return NULL;}
+ dig[j++] = (mDNSu8)((l << 4) | h);
+ }
+ return dig;
+}
+
+// All the children are in a linked list
+//
+// <TrustAnchor> has two children: <Zone> and <KeyDigest>
+// <KeyDigest> has four children <KeyTag> <Algorithm> <DigestType> <Digest>
+//
+// Returns false if failed to parse the element i.e., malformed xml document.
+// Validity of the actual values itself is done outside the function.
+mDNSlocal mDNSBool ParseElementChildren(xmlDocPtr tadoc, xmlNode *node, TrustAnchor *ta)
+{
+ xmlNode *cur_node;
+ xmlChar *val1, *val2, *val;
+ char *invalid = NULL;
+
+ val = val1 = val2 = NULL;
+
+ for (cur_node = node; cur_node; cur_node = cur_node->next)
+ {
+ invalid = NULL;
+ val1 = val2 = NULL;
+
+ val = xmlNodeListGetString(tadoc, cur_node->xmlChildrenNode, 1);
+ if (!val)
+ {
+ LogInfo("ParseElementChildren: NULL value for %s", cur_node->name);
+ continue;
+ }
+ if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Zone"))
+ {
+ // MaeDomainNameFromDNSNameString does not work for "."
+ if (!xmlStrcmp(val, (const xmlChar *)"."))
+ {
+ ta->zone.c[0] = 0;
+ }
+ else if (!MakeDomainNameFromDNSNameString(&ta->zone, (char *)val))
+ {
+ LogMsg("ParseElementChildren: Cannot parse Zone %s", val);
+ goto error;
+ }
+ else
+ {
+ LogInfo("ParseElementChildren: Element %s, value %##s", cur_node->name, ta->zone.c);
+ }
+ }
+ else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyTag"))
+ {
+ ta->rds.keyTag = strtol((const char *)val, &invalid, 10);
+ if (*invalid != '\0')
+ {
+ LogMsg("ParseElementChildren: KeyTag invalid character %d", *invalid);
+ goto error;
+ }
+ else
+ {
+ LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.keyTag);
+ }
+ }
+ else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Algorithm"))
+ {
+ ta->rds.alg = strtol((const char *)val, &invalid, 10);
+ if (*invalid != '\0')
+ {
+ LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid);
+ goto error;
+ }
+ else
+ {
+ LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.alg);
+ }
+ }
+ else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"DigestType"))
+ {
+ ta->rds.digestType = strtol((const char *)val, &invalid, 10);
+ if (*invalid != '\0')
+ {
+ LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid);
+ goto error;
+ }
+ else
+ {
+ LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.digestType);
+ }
+ }
+ else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Digest"))
+ {
+ int diglen;
+ mDNSu8 *dig = ConvertDigest((char *)val, ta->rds.digestType, &diglen);
+ if (dig)
+ {
+ LogInfo("ParseElementChildren: Element %s, digest %s", cur_node->name, val);
+ ta->digestLen = diglen;
+ ta->rds.digest = dig;
+ }
+ else
+ {
+ LogMsg("ParseElementChildren: Element %s, NULL digest", cur_node->name);
+ goto error;
+ }
+ }
+ else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest"))
+ {
+ struct tm tm;
+ val1 = xmlGetProp(cur_node, (const xmlChar *)"validFrom");
+ if (val1)
+ {
+ char *s = strptime((const char *)val1, "%Y-%m-%dT%H:%M:%S", &tm);
+ if (!s)
+ {
+ LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val1);
+ goto error;
+ }
+ else
+ {
+ ta->validFrom = (mDNSu32)timegm(&tm);
+ }
+ }
+ val2 = xmlGetProp(cur_node, (const xmlChar *)"validUntil");
+ if (val2)
+ {
+ char *s = strptime((const char *)val2, "%Y-%m-%dT%H:%M:%S", &tm);
+ if (!s)
+ {
+ LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val2);
+ goto error;
+ }
+ else
+ {
+ ta->validUntil = (mDNSu32)timegm(&tm);
+ }
+ }
+ else
+ {
+ // If there is no validUntil, set it to the next probing interval
+ mDNSu32 t = (mDNSu32) time(NULL);
+ ta->validUntil = t + ROOT_TA_UPDATE_INTERVAL;
+ }
+ LogInfo("ParseElementChildren: ValidFrom time %u, validUntil %u", (unsigned)ta->validFrom, (unsigned)ta->validUntil);
+ }
+ if (val1)
+ xmlFree(val1);
+ if (val2)
+ xmlFree(val2);
+ if (val)
+ xmlFree(val);
+ }
+ return mDNStrue;
+error:
+ if (val1)
+ xmlFree(val1);
+ if (val2)
+ xmlFree(val2);
+ if (val)
+ xmlFree(val);
+ return mDNSfalse;
+}
+
+mDNSlocal mDNSBool ValidateTrustAnchor(TrustAnchor *ta)
+{
+ time_t currTime = time(NULL);
+
+ // Currently only support trust anchor for root.
+ if (!SameDomainName(&ta->zone, (const domainname *)"\000"))
+ {
+ LogInfo("ParseElementChildren: Zone %##s not root", ta->zone.c);
+ return mDNSfalse;
+ }
+
+ switch (ta->rds.digestType)
+ {
+ case SHA1_DIGEST_TYPE:
+ if (ta->digestLen != CC_SHA1_DIGEST_LENGTH)
+ {
+ LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA1", ta->digestLen);
+ return mDNSfalse;
+ }
+ break;
+ case SHA256_DIGEST_TYPE:
+ if (ta->digestLen != CC_SHA256_DIGEST_LENGTH)
+ {
+ LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA256", ta->digestLen);
+ return mDNSfalse;
+ }
+ break;
+ default:
+ LogMsg("ValidateTrustAnchor: digest type %d not supported", ta->rds.digestType);
+ return mDNSfalse;
+ }
+ if (!ta->rds.digest)
+ {
+ LogMsg("ValidateTrustAnchor: digest NULL for %d", ta->rds.digestType);
+ return mDNSfalse;
+ }
+ switch (ta->rds.alg)
+ {
+ case CRYPTO_RSA_SHA512:
+ case CRYPTO_RSA_SHA256:
+ case CRYPTO_RSA_NSEC3_SHA1:
+ case CRYPTO_RSA_SHA1:
+ break;
+ default:
+ LogMsg("ValidateTrustAnchor: Algorithm %d not supported", ta->rds.alg);
+ return mDNSfalse;
+ }
+
+ if (DNS_SERIAL_LT(currTime, ta->validFrom))
+ {
+ LogMsg("ValidateTrustAnchor: Invalid ValidFrom time %u, currtime %u", (unsigned)ta->validFrom, (unsigned)currTime);
+ return mDNSfalse;
+ }
+ if (DNS_SERIAL_LT(ta->validUntil, currTime))
+ {
+ LogMsg("ValidateTrustAnchor: Invalid ValidUntil time %u, currtime %u", (unsigned)ta->validUntil, (unsigned)currTime);
+ return mDNSfalse;
+ }
+ return mDNStrue;
+}
+
+mDNSlocal mDNSBool ParseElement(xmlDocPtr tadoc, xmlNode * a_node, TrustAnchor *ta)
+{
+ xmlNode *cur_node = NULL;
+
+ for (cur_node = a_node; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE)
+ {
+ // There could be multiple KeyDigests per TrustAnchor. We keep parsing till we
+ // reach the last one or we encounter an error in parsing the document.
+ if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest"))
+ {
+ if (ta->rds.digest)
+ mDNSPlatformMemFree(ta->rds.digest);
+ ta->rds.digestType = 0;
+ ta->digestLen = 0;
+ }
+ if (!ParseElementChildren(tadoc, cur_node->children, ta))
+ return mDNSfalse;
+ if (!ParseElement(tadoc, cur_node->children, ta))
+ return mDNSfalse;
+ }
+ }
+ return mDNStrue;
+}
+
+mDNSlocal void TAComplete(mDNS *const m, void *context)
+{
+ TrustAnchor *ta = (TrustAnchor *)context;
+
+ DelTrustAnchor(m, &ta->zone);
+ LinkTrustAnchor(m, ta);
+}
+
+mDNSlocal void FetchRootTA(mDNS *const m)
+{
+ CFStringRef urlString = CFSTR("https://data.iana.org/root-anchors/root-anchors.xml");
+ CFDataRef xmlData;
+ CFStringRef fileRef = NULL;
+ const char *xmlFileName = NULL;
+ char buf[512];
+ CFURLRef url = NULL;
+ static unsigned int RootTAFetchInterval = InitialTAFetchInterval;
+
+ (void) m;
+
+ TrustAnchor *ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor));
+ if (!ta)
+ {
+ LogMsg("FetchRootTA: TrustAnchor alloc failed");
+ return;
+ }
+ memset(ta, 0, sizeof(TrustAnchor));
+
+ url = CFURLCreateWithString(NULL, urlString, NULL);
+ if (!url)
+ {
+ LogMsg("FetchRootTA: CFURLCreateWithString error");
+ mDNSPlatformMemFree(ta);
+ return;
+ }
+
+ // If we can't fetch the XML file e.g., network problems, trigger a timer. All other failures
+ // should hardly happen in practice for which schedule the normal interval to refetch the TA.
+ if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL))
+ {
+ LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error");
+ CFRelease(url);
+ mDNSPlatformMemFree(ta);
+ RegisterNotification(m, RootTAFetchInterval);
+ RootTAFetchInterval *= 2 + 1;
+ return;
+ }
+
+ // get the name of the last component from the url, libxml will use it if
+ // it has to report an error
+ fileRef = CFURLCopyLastPathComponent(url);
+ if (fileRef)
+ {
+ xmlFileName = CFStringGetCStringPtr(fileRef, kCFStringEncodingUTF8);
+ if (!xmlFileName)
+ {
+ if (!CFStringGetCString(fileRef, buf, sizeof(buf), kCFStringEncodingUTF8) )
+ strlcpy(buf, "nofile.xml", sizeof(buf));
+ xmlFileName = (const char *)buf;
+ }
+ }
+
+ // Parse the XML and get the CFXMLTree.
+ xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData),
+ (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0);
+
+ CFRelease(fileRef);
+ CFRelease(url);
+ CFRelease(xmlData);
+
+ if (!tadoc)
+ {
+ LogMsg("FetchRootTA: xmlReadMemory failed");
+ goto done;
+ }
+
+ xmlNodePtr root = xmlDocGetRootElement(tadoc);
+ if (!root)
+ {
+ LogMsg("FetchRootTA: Cannot get root element");
+ goto done;
+ }
+
+ if (ParseElement(tadoc, root, ta) && ValidateTrustAnchor(ta))
+ {
+ // Do the actual addition of TA on the main queue.
+ mDNSPlatformDispatchAsync(m, ta, TAComplete);
+ }
+ else
+ {
+ if (ta->rds.digest)
+ mDNSPlatformMemFree(ta->rds.digest);
+ mDNSPlatformMemFree(ta);
+ }
+done:
+ if (tadoc)
+ xmlFreeDoc(tadoc);
+ RegisterNotification(m, ROOT_TA_UPDATE_INTERVAL);
+ RootTAFetchInterval = InitialTAFetchInterval;
+ return;
+}
+
+
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
+mDNSlocal void DNSSECProbeCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ if (!AddRecord)
+ return;
+
+ mDNS_Lock(m);
+ if ((m->timenow - question->StopTime) >= 0)
+ {
+ mDNS_Unlock(m);
+ LogDNSSEC("DNSSECProbeCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype));
+ mDNS_StopQuery(m, question);
+ return;
+ }
+ mDNS_Unlock(m);
+
+ // Wait till we get the DNSSEC results. If we get a negative response e.g., no DNS servers, the
+ // question will be restarted by the core and we should have the DNSSEC results eventually.
+ if (AddRecord != QC_dnssec)
+ {
+ LogDNSSEC("DNSSECProbeCallback: Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
+ return;
+ }
+
+ LogDNSSEC("DNSSECProbeCallback: Question %##s (%s), DNSSEC status %s", question->qname.c, DNSTypeName(question->qtype),
+ DNSSECStatusName(question->ValidationStatus));
+
+ mDNS_StopQuery(m, question);
+}
+
+// Send a DNSSEC probe just for the sake of collecting DNSSEC statistics.
+mDNSexport void DNSSECProbe(mDNS *const m)
+{
+ mDNSu32 rand;
+
+ if (DNSSECProbeQuestion.ThisQInterval != -1)
+ return;
+
+ rand = mDNSRandom(0x3FFFFFFF) % 100;
+ // Probe 5% of the time
+ if (rand > 5)
+ return;
+
+ mDNS_DropLockBeforeCallback();
+ InitializeQuestion(m, &DNSSECProbeQuestion, mDNSInterface_Any, (const domainname *)"\003com", kDNSType_DS, DNSSECProbeCallback, mDNSNULL);
+ DNSSECProbeQuestion.ValidatingResponse = 0;
+ DNSSECProbeQuestion.ValidationRequired = DNSSEC_VALIDATION_SECURE;
+
+ BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeProbe, 1);
+ mDNS_StartQuery(m, &DNSSECProbeQuestion);
+ mDNS_ReclaimLockAfterCallback();
+}
+#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
+
+// For now we fetch the root trust anchor and update the local copy
+mDNSexport void UpdateTrustAnchors(mDNS *const m)
+{
+ // Register for a notification to fire immediately which in turn will update
+ // the trust anchor
+ if (RegisterNotification(m, 1))
+ {
+ LogMsg("UpdateTrustAnchors: ERROR!! failed to register for notification");
+ }
+}
+
+mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval)
+{
+ int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval
+ char buffer[len];
+ unsigned int blen;
+ int status;
+
+ // Starting "interval" second from now (+ below indicates relative) register for a notification
+ blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval);
+ if (blen >= sizeof(buffer))
+ {
+ LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer));
+ return -1;
+ }
+ LogInfo("RegisterNotification: buffer %s", buffer);
+ if (m->notifyToken)
+ {
+ notify_cancel(m->notifyToken);
+ m->notifyToken = 0;
+ }
+ status = notify_register_dispatch(buffer, &m->notifyToken,
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
+ ^(int t) { (void) t; FetchRootTA(m); });
+
+ if (status != NOTIFY_STATUS_OK)
+ {
+ LogMsg("RegisterNotification: notify_register_dispatch failed");
+ return -1;
+ }
+ return 0;
+}
+
+mDNSexport mStatus DNSSECPlatformInit(mDNS *const m)
+{
+ int diglen;
+
+ m->TrustAnchors = mDNSNULL;
+ m->notifyToken = 0;
+
+ // Add a couple of trust anchors for testing purposes.
+ mDNSlocal const domainname *testZone = (const domainname*)"\007example";
+
+ char *digest = "F122E47B5B7D2B6A5CC0A21EADA11D96BB9CC927";
+ mDNSu8 *dig = ConvertDigest(digest, 1, &diglen);
+ AddTrustAnchor(m, testZone, 23044, 5, 1, diglen, dig);
+
+ char *digest1 = "D795AE5E1AFB200C6139474199B70EAD3F3484553FD97BE5A43704B8A4791F21";
+ dig = ConvertDigest(digest1, 2, &diglen);
+ AddTrustAnchor(m, testZone, 23044, 5, 2, diglen, dig);
+
+ // Add the TA for root zone manually here. We will dynamically fetch the root TA and
+ // update it shortly. If that fails e.g., disconnected from the network, we still
+ // have something to work with.
+ char *digest2 = "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5";
+ dig = ConvertDigest(digest2, 2, &diglen);
+ AddTrustAnchor(m, (const domainname *)"\000", 19036, 8, 2, diglen, dig);
+
+#if !TARGET_OS_IPHONE
+ DNSSECProbeQuestion.ThisQInterval = -1;
+#endif
+ return mStatus_NoError;
+}