/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2017 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.
#endif
#include "uDNS.h"
+#if AWD_METRICS
+#include "Metrics.h"
+#endif
+
#if (defined(_MSC_VER))
// Disable "assignment within conditional expression".
// Other compilers understand the convention that if you place the assignment expression within an extra pair
mDNSBool StrictUnicastOrdering = mDNSfalse;
// We keep track of the number of unicast DNS servers and log a message when we exceed 64.
-// Currently the unicast queries maintain a 64 bit map to track the valid DNS servers for that
+// Currently the unicast queries maintain a 128 bit map to track the valid DNS servers for that
// question. Bit position is the index into the DNS server list. This is done so to try all
// the servers exactly once before giving up. If we could allocate memory in the core, then
-// arbitrary limitation of 64 DNSServers can be removed.
+// arbitrary limitation of 128 DNSServers can be removed.
mDNSu8 NumUnicastDNSServers = 0;
-#define MAX_UNICAST_DNS_SERVERS 64
+#define MAX_UNICAST_DNS_SERVERS 128
#if APPLE_OSX_mDNSResponder
mDNSu8 NumUnreachableDNSServers = 0;
#endif
#endif
mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr,
- const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA,
- mDNSBool reqAAAA, mDNSBool reqDO)
+ const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSBool isExpensive, mDNSu16 resGroupID,
+ mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO)
{
DNSServer **p = &m->DNSServers;
DNSServer *tmp = mDNSNULL;
if (!d)
d = (const domainname *)"";
- LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A is %s req_AAAA is %s cell %s req_DO is %s",
+ LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A is %s req_AAAA is %s cell %s isExpensive %s req_DO is %s",
NumUnicastDNSServers, addr, d->c, interface, serviceID, scoped, resGroupID, reqA ? "True" : "False", reqAAAA ? "True" : "False",
- cellIntf ? "True" : "False", reqDO ? "True" : "False");
+ cellIntf ? "True" : "False", isExpensive ? "True" : "False", reqDO ? "True" : "False");
mDNS_CheckLock(m);
}
else
{
- (*p)->scoped = scoped;
- (*p)->interface = interface;
- (*p)->serviceID = serviceID;
- (*p)->addr = *addr;
- (*p)->port = port;
- (*p)->flags = DNSServer_FlagNew;
- (*p)->timeout = timeout;
- (*p)->cellIntf = cellIntf;
- (*p)->req_A = reqA;
- (*p)->req_AAAA = reqAAAA;
- (*p)->req_DO = reqDO;
+ (*p)->scoped = scoped;
+ (*p)->interface = interface;
+ (*p)->serviceID = serviceID;
+ (*p)->addr = *addr;
+ (*p)->port = port;
+ (*p)->flags = DNSServer_FlagNew;
+ (*p)->timeout = timeout;
+ (*p)->cellIntf = cellIntf;
+ (*p)->isExpensive = isExpensive;
+ (*p)->req_A = reqA;
+ (*p)->req_AAAA = reqAAAA;
+ (*p)->req_DO = reqDO;
// We start off assuming that the DNS server is not DNSSEC aware and
// when we receive the first response to a DNSSEC question, we set
// it to true.
if (!m->NATTraversals)
{
- m->retryGetAddr = NonZeroTime(m->timenow + 0x78000000);
+ m->retryGetAddr = NonZeroTime(m->timenow + FutureTime);
LogInfo("uDNS_RequestAddress: Setting retryGetAddr to future");
}
else if (m->timenow - m->retryGetAddr >= 0)
return err;
}
-mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP)
+mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP, mDNSBool unmapping)
{
mStatus err = mStatus_NoError;
info->sentNATPMP = mDNSfalse;
#ifdef _LEGACY_NAT_TRAVERSAL_
- if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort))
- {
- LNT_SendDiscoveryMsg(m);
- debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg");
- }
- else
+ // If an unmapping is being performed, then don't send an LNT discovery message or an LNT port map request.
+ if (!unmapping)
{
- mStatus lnterr = LNT_MapPort(m, info);
- if (lnterr)
- LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr);
+ if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort))
+ {
+ LNT_SendDiscoveryMsg(m);
+ debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg");
+ }
+ else
+ {
+ mStatus lnterr = LNT_MapPort(m, info);
+ if (lnterr)
+ LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr);
- err = err ? err : lnterr; // PCP error takes precedence
+ err = err ? err : lnterr; // PCP error takes precedence
+ }
}
+#else
+ (void)unmapping; // Unused
#endif // _LEGACY_NAT_TRAVERSAL_
}
}
}
}
+ // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up
+ // Before zeroing traversal->RequestedPort below, perform the LNT unmapping, which requires the mapping's external port,
+ // held by the traversal->RequestedPort variable.
+ #ifdef _LEGACY_NAT_TRAVERSAL_
+ {
+ mStatus err = LNT_UnmapPort(m, traversal);
+ if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err);
+ }
+ #endif // _LEGACY_NAT_TRAVERSAL_
+
if (traversal->ExpiryTime && unmap)
{
traversal->NATLease = 0;
traversal->RequestedPort = zeroIPPort;
traversal->NewAddress = zerov4Addr;
- uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP);
+ uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP, mDNStrue);
}
- // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up
- #ifdef _LEGACY_NAT_TRAVERSAL_
- {
- mStatus err = LNT_UnmapPort(m, traversal);
- if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err);
- }
- #endif // _LEGACY_NAT_TRAVERSAL_
-
return(mStatus_NoError);
}
err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo, mDNSfalse);
if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %d", err); err = mStatus_UnknownErr; goto exit; }
+#if AWD_METRICS
+ if (mDNSSameIPPort(tcpInfo->Port, UnicastDNSPort))
+ {
+ MetricsUpdateDNSQuerySize((mDNSu32)(end - (mDNSu8 *)&tcpInfo->request));
+ }
+#endif
// Record time we sent this question
if (q)
mDNSPlatformMemZero(info, sizeof(tcpInfo_t));
info->m = m;
- info->sock = mDNSPlatformTCPSocket(m, flags, &srcport, useBackgroundTrafficClass);
+ info->sock = mDNSPlatformTCPSocket(flags, &srcport, useBackgroundTrafficClass);
info->requestLen = 0;
info->question = question;
info->rr = rr;
DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
if (AuthInfo && AuthInfo->AutoTunnel)
{
- StartServerTunnel(m, AuthInfo);
+ StartServerTunnel(AuthInfo);
if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL);
debugf("GetServiceTarget: Returning %##s", AuthInfo->AutoTunnelHostRecord.namestorage.c);
return(&AuthInfo->AutoTunnelHostRecord.namestorage);
m->NextSRVUpdate = NonZeroTime(m->timenow);
#if APPLE_OSX_mDNSResponder
- if (RouterChanged) uuid_generate(m->asl_uuid);
UpdateAutoTunnelDomainStatuses(m);
#endif
}
mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time)
{
DomainAuthInfo *info;
- (void) m; //unused
// A record is eligible for merge, if the following properties are met.
//
// 1. uDNS Resource Record
{
// Send a NAT-PMP request for this operation as needed
// and update the state variables
- uDNS_SendNATMsg(m, n, mDNSfalse);
+ uDNS_SendNATMsg(m, n, mDNSfalse, mDNSfalse);
}
m->NextScheduledNATOp = m->timenow;
if (AddrReply->opcode == NATOp_AddrResponse)
{
#if APPLE_OSX_mDNSResponder
- static char msgbuf[16];
- mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err);
- mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, "");
+ LogInfo("uDNS_ReceiveNATPMPPacket: AddressRequest %s error %d", AddrReply->err ? "failure" : "success", AddrReply->err);
#endif
if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT-PMP AddrResponse message too short (%d bytes)", len); return; }
natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr);
{
mDNSu8 Protocol = AddrReply->opcode & 0x7F;
#if APPLE_OSX_mDNSResponder
- static char msgbuf[16];
- mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s - %d", AddrReply->opcode == NATOp_MapUDPResponse ? "UDP" : "TCP", PortMapReply->err);
- mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.PortMapRequest", PortMapReply->err ? "failure" : "success", msgbuf, "");
+ LogInfo("uDNS_ReceiveNATPMPPacket: PortMapRequest %s %s - error %d",
+ PortMapReply->err ? "failure" : "success", (AddrReply->opcode == NATOp_MapUDPResponse) ? "UDP" : "TCP", PortMapReply->err);
#endif
if (!PortMapReply->err)
{
if (QR_OP == UpdateR)
{
- mDNSu32 lease = GetPktLease(m, msg, end);
+ mDNSu32 pktlease = 0;
+ mDNSBool gotlease = GetPktLease(m, msg, end, &pktlease);
+ mDNSu32 lease = gotlease ? pktlease : 60 * 60; // If lease option missing, assume one hour
mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond;
mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10);
mDNS_Unlock(m);
}
+#ifdef DNS_PUSH_ENABLED
mDNSexport void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
{
DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext;
}
mDNS_Unlock(m);
}
-
+#endif // DNS_PUSH_ENABLED
// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
mDNSlocal void uDNS_HandleLLQState(mDNS *const m, DNSQuestion *q)
{
+#ifdef DNS_PUSH_ENABLED
// First attempt to use DNS Push Notification.
if (q->dnsPushState == DNSPUSH_INIT)
DiscoverDNSPushNotificationServer(m, q);
+#endif // DNS_PUSH_ENABLED
switch (q->state)
{
case LLQ_InitialRequest: startLLQHandshake(m, q); break;
debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d",
q, q->qname.c, DNSTypeName(q->qtype),
q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
+#if APPLE_OSX_mDNSResponder
+ // When a DNS proxy network extension initiates the close of a UDP flow (this usually happens when a DNS
+ // proxy gets disabled or crashes), mDNSResponder's corresponding UDP socket will be marked with the
+ // SS_CANTRCVMORE state flag. Reading from such a socket is no longer possible, so close the current
+ // socket pair so that we can create a new pair.
+ if (q->LocalSocket && mDNSPlatformUDPSocketEncounteredEOF(q->LocalSocket))
+ {
+ mDNSPlatformUDPClose(q->LocalSocket);
+ q->LocalSocket = mDNSNULL;
+ }
+#endif
if (!q->LocalSocket)
{
- q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+ q->LocalSocket = mDNSPlatformUDPSocket(zeroIPPort);
if (q->LocalSocket)
{
mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv4, q);
else
{
err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass);
-#if TARGET_OS_EMBEDDED
+#if AWD_METRICS
if (!err)
{
+ MetricsUpdateDNSQuerySize((mDNSu32)(end - (mDNSu8 *)&m->omsg));
if (q->metrics.answered)
{
q->metrics.querySendCount = 0;
}
newServer = GetServerForQuestion(m, q);
- DNSServerChangeForQuestion(m, q, newServer);
-
+ if (!newServer)
+ {
+ q->triedAllServersOnce = 1;
+ SetValidDNSServers(m, q);
+ newServer = GetServerForQuestion(m, q);
+ }
+ if (newServer)
+ {
+ LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%u ThisQInterval %d",
+ q, q->qname.c, DNSTypeName(q->qtype), newServer ? &newServer->addr : mDNSNULL, mDNSVal16(newServer ? newServer->port : zeroIPPort), q->ThisQInterval);
+ DNSServerChangeForQuestion(m, q, newServer);
+ }
if (q->triedAllServersOnce)
{
q->LastQTime = m->timenow;
q->LastQTime = m->timenow - q->ThisQInterval;
}
q->unansweredQueries = 0;
- q->noServerResponse = 1;
}
else
{
// passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire --
// but we want *all* of the questions to get answer callbacks.)
CacheRecord *rr;
- const mDNSu32 slot = HashSlot(&q->qname);
- CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ const mDNSu32 slot = HashSlotFromNameHash(q->qnamehash);
+ CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname);
if (!q->qDNSServer)
{
- if (!mDNSOpaque64IsZero(&q->validDNSServers))
- LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x for question %##s (%s)",
- q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype));
+ if (!mDNSOpaque128IsZero(&q->validDNSServers))
+ LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x 0x%x 0x%x for question %##s (%s)",
+ q->validDNSServers.l[3], q->validDNSServers.l[2], q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype));
// If we reached the end of list while picking DNS servers, then we don't want to deactivate the
// question. Try after 60 seconds. We find this by looking for valid DNSServers for this question,
// if we find any, then we must have tried them before we came here. This avoids maintaining
// another state variable to see if we had valid DNS servers for this question.
SetValidDNSServers(m, q);
- if (mDNSOpaque64IsZero(&q->validDNSServers))
+ if (mDNSOpaque128IsZero(&q->validDNSServers))
{
LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
q->ThisQInterval = 0;
{
mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4);
mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4);
- m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF;
+ m->NextScheduledNATOp = m->timenow + FutureTime;
if (HaveRoutable) m->ExtAddress = m->AdvertisedV4.ip.v4;
{
// we need to log a message if we can't get our socket, but only the first time (after success)
static mDNSBool needLog = mDNStrue;
- m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort);
+ m->NATMcastRecvskt = mDNSPlatformUDPSocket(NATPMPAnnouncementPort);
if (!m->NATMcastRecvskt)
{
if (needLog)
cur->retryInterval = NATMAP_INIT_RETRY;
}
- uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary
+ uDNS_SendNATMsg(m, cur, mDNStrue, mDNSfalse); // Will also do UPnP discovery for us, if necessary
if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry
NATSetNextRenewalTime(m, cur);
mDNSlocal mDNSs32 CheckRecordUpdates(mDNS *m)
{
AuthRecord *rr;
- mDNSs32 nextevent = m->timenow + 0x3FFFFFFF;
+ mDNSs32 nextevent = m->timenow + FutureTime;
CheckGroupRecordUpdates(m);
mDNSs32 nexte;
DNSServer *d;
- m->NextuDNSEvent = m->timenow + 0x3FFFFFFF;
+ m->NextuDNSEvent = m->timenow + FutureTime;
nexte = CheckRecordUpdates(m);
if (m->NextuDNSEvent - nexte > 0)
// Make sure we have the search domains from the platform layer so that if we start the WAB
// queries below, we have the latest information.
mDNS_Lock(m);
- if (!mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL, mDNSfalse))
+ if (!mDNSPlatformSetDNSConfig(mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL, mDNSfalse))
{
// If the configuration did not change, clear the flag so that we don't free the searchlist.
// We still have to start the domain enumeration queries as we may not have started them
uDNS_SetupWABQueries(m);
}
-mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+mDNSexport domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
{
SearchListElem *p = SearchList;
int count = *searchIndex;
- (void) m; // unused
if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; }
#pragma mark - DNS Push Notification functions
#endif
+#ifdef DNS_PUSH_ENABLED
mDNSlocal tcpInfo_t * GetTCPConnectionToPushServer(mDNS *m, DNSQuestion *q)
{
+ DNSPushNotificationZone *zone;
+ DNSPushNotificationServer *server;
+ DNSPushNotificationZone *newZone;
+ DNSPushNotificationServer *newServer;
+
// If we already have a question for this zone and if the server is the same, reuse it
- DNSPushNotificationZone *zone = mDNSNULL;
for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next)
{
if (SameDomainName(&q->nta->ChildName, &zone->zoneName))
}
// If we have a connection to this server but it is for a differnt zone, create a new zone entry and reuse the connection
- DNSPushNotificationServer *server = mDNSNULL;
for (server = m->DNSPushServers; server != mDNSNULL; server = server->next)
{
if (mDNSSameAddress(&q->dnsPushServerAddr, &server->serverAddr))
{
- DNSPushNotificationZone *newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone));
+ newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone));
newZone->numberOfQuestions = 1;
newZone->zoneName = q->nta->ChildName;
newZone->servers = server;
}
// If we do not have any existing connections, create a new connection
- DNSPushNotificationServer *newServer = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationServer));
- DNSPushNotificationZone *newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone));
+ newServer = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationServer));
+ newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone));
newServer->numberOfQuestions = 1;
newServer->serverAddr = q->dnsPushServerAddr;
{
DNSPushNotificationZone *zone;
DNSPushNotificationServer *server;
+ DNSPushNotificationServer *nextServer;
+ DNSPushNotificationZone *nextZone;
+
// Update the counts
for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next)
{
// Now prune the lists
server = m->DNSPushServers;
- DNSPushNotificationServer *nextServer = mDNSNULL;
+ nextServer = mDNSNULL;
while(server != mDNSNULL)
{
nextServer = server->next;
}
zone = m->DNSPushZones;
- DNSPushNotificationZone *nextZone = mDNSNULL;
+ nextZone = mDNSNULL;
while(zone != mDNSNULL)
{
nextZone = zone->next;
reconcileDNSPushConnection(m, q);
}
+#endif // DNS_PUSH_ENABLED
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#endif
return mStatus_UnsupportedErr;
}
-mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+mDNSexport domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
{
- (void) m;
(void) InterfaceID;
(void) searchIndex;
(void) ignoreDotLocal;
}
mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr,
- const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA,
- mDNSBool reqAAAA, mDNSBool reqDO)
+ const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSBool isExpensive, mDNSu16 resGroupID,
+ mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO)
{
(void) m;
(void) d;
(void) scoped;
(void) timeout;
(void) cellIntf;
+ (void) isExpensive;
(void) resGroupID;
(void) reqA;
(void) reqAAAA;