#include "uDNS.h"
#if(defined(_MSC_VER))
#pragma warning(disable:4706)
#endif
#define umalloc(x) mDNSPlatformMemAllocate(x) // short hands for common routines
#define ufree(x) mDNSPlatformMemFree(x)
#define ubzero(x,y) mDNSPlatformMemZero(x,y)
#define umemcpy(x, y, l) mDNSPlatformMemCopy(y, x, l) // uses memcpy(2) arg ordering
typedef enum
{
zoneDataResult
} AsyncOpResultType;
typedef struct
{
domainname zoneName;
mDNSAddr primaryAddr;
mDNSu16 zoneClass;
mDNSIPPort llqPort;
mDNSIPPort updatePort;
} zoneData_t;
typedef struct
{
AsyncOpResultType type;
zoneData_t zoneData;
} AsyncOpResult;
typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const AsyncOpResult *result);
mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m);
mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, AsyncOpCallback callback, void *callbackInfo);
mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID);
mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr);
mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs);
mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs);
mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result);
mDNSlocal void SendRecordUpdate(mDNS *m, AuthRecord *rr, uDNS_RegInfo *info);
mDNSlocal mStatus RegisterService(mDNS *m, ServiceRecordSet *srs);
mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive);
mDNSlocal void RestartQueries(mDNS *m);
mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer);
mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context);
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Temporary workaround
#endif
mDNSlocal mDNSs32 mDNSPlatformTimeNow(mDNS *m)
{
if (m->mDNS_busy && m->timenow) return(m->timenow);
LogMsg("ERROR: uDNS.c code executing without holding main mDNS lock");
return(mDNS_TimeNow(m));
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - General Utility Functions
#endif
mDNSlocal int CountLabels(const domainname *d)
{
int count = 0;
const mDNSu8 *ptr;
for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
return count;
}
mDNSlocal mDNSOpaque16 newMessageID(uDNS_GlobalInfo *u)
{
static mDNSBool randomized = mDNSfalse;
if (!randomized) { u->NextMessageID = mDNSRandom(~0); randomized = mDNStrue; }
return mDNSOpaque16fromIntVal(u->NextMessageID++);
}
mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr)
{
AuthRecord *rptr, *prev = mDNSNULL;
for (rptr = *list; rptr; rptr = rptr->next)
{
if (rptr == rr)
{
if (prev) prev->next = rptr->next;
else *list = rptr->next;
rptr->next = mDNSNULL;
return mStatus_NoError;
}
prev = rptr;
}
LogMsg("ERROR: unlinkAR - no such active record");
return mStatus_UnknownErr;
}
mDNSlocal void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs)
{
ServiceRecordSet **p;
for (p = &u->ServiceRegistrations; *p; p = &(*p)->next)
if (*p == srs) { *p = srs->next; srs->next = mDNSNULL; return; }
LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list");
}
mDNSlocal void LinkActiveQuestion(uDNS_GlobalInfo *u, DNSQuestion *q)
{
if (uDNS_IsActiveQuery(q, u))
{ LogMsg("LinkActiveQuestion - %##s (%d) already in list!", q->qname.c, q->qtype); return; }
q->next = u->ActiveQueries;
u->ActiveQueries = q;
}
mDNSlocal void SwapRData(mDNS *m, AuthRecord *rr, mDNSBool DeallocOld)
{
RData *oldrd = rr->resrec.rdata;
mDNSu16 oldrdlen = rr->resrec.rdlength;
if (!rr->uDNS_info.UpdateRData) { LogMsg("SwapRData invoked with NULL UpdateRData field"); return; }
SetNewRData(&rr->resrec, rr->uDNS_info.UpdateRData, rr->uDNS_info.UpdateRDLen);
if (DeallocOld)
{
rr->uDNS_info.UpdateRData = mDNSNULL; if (rr->uDNS_info.UpdateRDCallback) rr->uDNS_info.UpdateRDCallback(m, rr, oldrd); }
else
{
rr->uDNS_info.UpdateRData = oldrd;
rr->uDNS_info.UpdateRDLen = oldrdlen;
}
}
mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr)
{
rr->LastAPTime = mDNSPlatformTimeNow(m);
if (rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) { rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; return; }
if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) { rr->ThisAPInterval *= 2; return; }
if (rr->ThisAPInterval != MAX_UCAST_POLL_INTERVAL) { rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL; }
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Name Server List Management
#endif
mDNSexport void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *addr, const domainname *d)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSServer *s, **p = &u->Servers;
mDNS_Lock(m);
if (!d) d = (domainname *)"";
while (*p) {
if (mDNSSameAddress(&(*p)->addr, addr) && SameDomainName(&(*p)->domain, d))
LogMsg("Note: DNS Server %#a for domain %##s registered more than once", addr, d->c);
p=&(*p)->next;
}
s = umalloc(sizeof(*s));
if (!s) { LogMsg("Error: mDNS_AddDNSServer - malloc"); goto end; }
s->addr = *addr;
AssignDomainName(s->domain, *d);
s->next = mDNSNULL;
*p = s;
end:
mDNS_Unlock(m);
}
mDNSexport void mDNS_DeleteDNSServers(mDNS *const m)
{
DNSServer *s;
mDNS_Lock(m);
s = m->uDNS_info.Servers;
m->uDNS_info.Servers = mDNSNULL;
while (s)
{
DNSServer *tmp = s;
s = s->next;
ufree(tmp);
}
mDNS_Unlock(m);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - authorization management
#endif
mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone)
{
uDNS_AuthInfo *ptr;
while (zone->c[0])
{
for (ptr = u->AuthInfoList; ptr; ptr = ptr->next)
if (SameDomainName(&ptr->zone, zone)) return(ptr);
zone = (const domainname *)(zone->c + 1 + zone->c[0]);
}
return mDNSNULL;
}
mDNSlocal void DeleteAuthInfoForZone(uDNS_GlobalInfo *u, const domainname *zone)
{
uDNS_AuthInfo *ptr, *prev = mDNSNULL;
for (ptr = u->AuthInfoList; ptr; ptr = ptr->next)
{
if (SameDomainName(&ptr->zone, zone))
{
if (prev) prev->next = ptr->next;
else u->AuthInfoList = ptr->next;
ufree(ptr);
return;
}
prev = ptr;
}
}
mDNSexport mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64)
{
uDNS_AuthInfo *info;
mDNSu8 keybuf[1024];
mDNSs32 keylen;
uDNS_GlobalInfo *u = &m->uDNS_info;
mStatus status = mStatus_NoError;
mDNS_Lock(m);
if (GetAuthInfoForZone(u, zone)) DeleteAuthInfoForZone(u, zone);
if (!key) goto exit;
info = (uDNS_AuthInfo*)umalloc(sizeof(uDNS_AuthInfo) + ssLen);
if (!info) { LogMsg("ERROR: umalloc"); status = mStatus_NoMemoryErr; goto exit; }
ubzero(info, sizeof(uDNS_AuthInfo));
AssignDomainName(info->zone, *zone);
AssignDomainName(info->keyname, *key);
if (base64)
{
keylen = DNSDigest_Base64ToBin((const char*)sharedSecret, keybuf, 1024);
if (keylen < 0)
{
LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication - could not convert shared secret from base64");
ufree(info);
status = mStatus_UnknownErr;
goto exit;
}
DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
}
else DNSDigest_ConstructHMACKey(info, sharedSecret, ssLen);
info->next = m->uDNS_info.AuthInfoList;
m->uDNS_info.AuthInfoList = info;
exit:
mDNS_Unlock(m);
return status;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - NAT Traversal
#endif
mDNSlocal mDNSBool MapServicePort(mDNS *m)
{
uDNS_HostnameInfo *i;
for (i = m->uDNS_info.Hostnames; i; i= i->next)
if (i->ar->uDNS_info.NATinfo && i->ar->uDNS_info.NATinfo->state != NATState_Error) return mDNStrue;
return mDNSfalse;
}
mDNSlocal mDNSBool DomainContainsLabelString(const domainname *d, const char *str)
{
const domainlabel *l;
domainlabel buf;
if (!MakeDomainLabelFromLiteralString(&buf, str)) return mDNSfalse;
for (l = (const domainlabel *)d; l->c[0]; l = (const domainlabel *)(l->c + l->c[0]+1))
if (SameDomainLabel(l->c, buf.c)) return mDNStrue;
return mDNSfalse;
}
mDNSlocal NATTraversalInfo *AllocNATInfo(mDNS *const m, NATOp_t op, NATResponseHndlr callback)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *info = umalloc(sizeof(NATTraversalInfo));
if (!info) { LogMsg("ERROR: malloc"); return mDNSNULL; }
ubzero(info, sizeof(NATTraversalInfo));
info->next = u->NATTraversals;
u->NATTraversals = info;
info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY;
info->op = op;
info->state = NATState_Init;
info->ReceiveResponse = callback;
info->PublicPort.NotAnInteger = 0;
return info;
}
mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n)
{
NATTraversalInfo *ptr, *prev = mDNSNULL;
if (n == m->uDNS_info.LLQNatInfo) m->uDNS_info.LLQNatInfo = mDNSNULL;
ptr = m->uDNS_info.NATTraversals;
while (ptr)
{
if (ptr == n)
{
if (prev) prev->next = ptr->next;
else m->uDNS_info.NATTraversals = ptr->next;
ufree(n);
return mDNStrue;
}
prev = ptr;
ptr = ptr->next;
}
LogMsg("FreeNATInfo: NATTraversalInfo not found in list");
return mDNSfalse;
}
mDNSlocal void SendNATMsg(NATTraversalInfo *info, mDNS *m)
{
mStatus err;
mDNSAddr dst;
mDNSIPPort dstport;
uDNS_GlobalInfo *u = &m->uDNS_info;
if (info->state != NATState_Request && info->state != NATState_Refresh)
{ LogMsg("SendNATMsg: Bad state %d", info->state); return; }
if (u->Router.ip.v4.NotAnInteger)
{
dst.type = u->Router.type;
dst.ip.v4 = u->Router.ip.v4;
dstport = mDNSOpaque16fromIntVal(NATMAP_PORT);
err = mDNSPlatformSendUDP(m, info->request, info->request+info->requestlen, 0, &dst, dstport);
if (!err) (info->ntries++); }
if (info->RetryInterval < NATMAP_INIT_RETRY) info->RetryInterval = NATMAP_INIT_RETRY;
else if (info->RetryInterval * 2 > NATMAP_MAX_RETRY) info->RetryInterval = NATMAP_MAX_RETRY;
else info->RetryInterval *= 2;
info->retry = mDNSPlatformTimeNow(m) + info->RetryInterval;
}
mDNSlocal void ReceiveNATAddrResponse(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len)
{
NATErr_t NatErr = NATErr_None;
mStatus err = mStatus_NoError;
AuthRecord *rr = mDNSNULL;
mDNSAddr addr;
if (n->state != NATState_Request)
{
LogMsg("ReceiveNATAddrResponse: bad state %d", n->state);
err = mStatus_UnknownErr;
goto end;
}
rr = n->reg.RecordRegistration;
if (!rr)
{
LogMsg("ReceiveNATAddrResponse: registration cancelled");
err = mStatus_UnknownErr;
goto end;
}
addr.type = mDNSAddrType_IPv4;
addr.ip.v4 = rr->resrec.rdata->u.ipv4;
if (!pkt) {
#ifdef _LEGACY_NAT_TRAVERSAL_
err = LNT_GetPublicIP(&addr.ip.v4);
if (err) goto end;
else n->state = NATState_Legacy;
#else
debugf("ReceiveNATAddrResponse: timeout");
err = mStatus_NATTraversal;
goto end;
#endif // _LEGACY_NAT_TRAVERSAL_
}
else
{
if (len < ADDR_REPLY_PKTLEN)
{
LogMsg("ReceiveNATAddrResponse: response too short (%d bytes)", len);
err = mStatus_NATTraversal;
goto end;
}
if (pkt[0] != NATMAP_VERS)
{
LogMsg("ReceiveNATAddrResponse: received version %d (expect version %d)", pkt[0], NATMAP_VERS);
err = mStatus_NATTraversal;
goto end;
}
if (pkt[1] != (NATOp_AddrRequest | NATMAP_RESPONSE_MASK))
{
LogMsg("ReceiveNATAddrResponse: bad response code %d", pkt[1]);
err = mStatus_NATTraversal;
goto end;
}
NatErr = (NATErr_t)((NATErr_t)pkt[2] << 8 | (NATErr_t)pkt[3]);
if (NatErr) { LogMsg("ReceiveAddrResponse: received error %d", err); err = mStatus_NATTraversal; goto end; }
addr.ip.v4.b[0] = pkt[4];
addr.ip.v4.b[1] = pkt[5];
addr.ip.v4.b[2] = pkt[6];
addr.ip.v4.b[3] = pkt[7];
n->state = NATState_Established;
}
if (IsPrivateV4Addr(&addr))
{
LogMsg("ReceiveNATAddrResponse: Double NAT");
err = mStatus_DblNAT;
goto end;
}
end:
if (err)
{
FreeNATInfo(m, n);
if (rr)
{
rr->uDNS_info.NATinfo = mDNSNULL;
rr->uDNS_info.state = regState_Unregistered; rr->RecordCallback(m, rr, mStatus_NATTraversal);
}
return;
}
else LogMsg("Received public IP address %d.%d.%d.%d from NAT.", addr.ip.v4.b[0], addr.ip.v4.b[1], addr.ip.v4.b[2], addr.ip.v4.b[3]);
rr->resrec.rdata->u.ipv4 = addr.ip.v4; uDNS_RegisterRecord(m, rr);
}
mDNSlocal void StartGetPublicAddr(mDNS *m, uDNS_HostnameInfo *hInfo)
{
mDNSu8 *msg;
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *info = AllocNATInfo(m, NATOp_AddrRequest, ReceiveNATAddrResponse);
if (!info) { uDNS_RegisterRecord(m, hInfo->ar); return; }
hInfo->ar->uDNS_info.NATinfo = info;
info->reg.RecordRegistration = hInfo->ar;
info->state = NATState_Request;
msg = info->request;
msg[0] = NATMAP_VERS;
msg[1] = NATOp_AddrRequest;
info->requestlen = ADDR_REQUEST_PKTLEN;
if (!u->Router.ip.v4.NotAnInteger)
{
debugf("No router. Will retry NAT traversal in %ld ticks", NATMAP_INIT_RETRY);
return;
}
SendNATMsg(info, m);
}
mDNSlocal void RefreshNATMapping(NATTraversalInfo *n, mDNS *m)
{
n->state = NATState_Refresh;
n->RetryInterval = NATMAP_INIT_RETRY;
n->ntries = 0;
SendNATMsg(n, m);
}
mDNSlocal void LLQNatMapComplete(mDNS *m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
LLQ_Info *llqInfo;
NATTraversalInfo *n = u->LLQNatInfo;
if (!n) { LogMsg("Error: LLQNatMapComplete called with NULL LLQNatInfo"); return; }
if (n->state != NATState_Established && n->state != NATState_Legacy && n->state != NATState_Error)
{ LogMsg("LLQNatMapComplete - bad nat state %d", n->state); return; }
u->CurrentQuery = u->ActiveQueries;
while (u->CurrentQuery)
{
DNSQuestion *q = u->CurrentQuery;
u->CurrentQuery = u->CurrentQuery->next;
llqInfo = q->uDNS_info.llq;
if (q->LongLived && llqInfo->state == LLQ_NatMapWait)
{
if (n->state == NATState_Error)
{
llqInfo->NATMap = mDNSfalse;
llqInfo->question->uDNS_info.responseCallback = llqResponseHndlr;
llqInfo->state = LLQ_Poll;
llqInfo->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); llqInfo->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
}
else { llqInfo->state = LLQ_GetZoneInfo; startLLQHandshake(m, llqInfo, mDNSfalse); }
}
}
}
mDNSlocal void ReceivePortMapReply(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len)
{
NATErr_t err;
ServiceRecordSet *srs;
mDNSIPPort priv, pktpriv, pub;
mDNSu32 lease;
mDNSBool deletion;
if (n->state != NATState_Request && n->state != NATState_Refresh)
{ LogMsg("ReceivePortMapReply: bad state %d", n->state); return; }
deletion = !(n->request[8] | n->request[9] | n->request[10] | n->request[11]); if (deletion) { n->state = NATState_Deleted; return; }
srs = n->reg.ServiceRegistration;
if (!srs && n != m->uDNS_info.LLQNatInfo)
{
debugf("ReceivePortMapReply: registration cancelled");
FreeNATInfo(m, n);
return;
}
priv = srs ? srs->RR_SRV.resrec.rdata->u.srv.port : m->UnicastPort4;
if (!pkt) {
#ifdef _LEGACY_NAT_TRAVERSAL_
int ntries = 0;
mStatus err;
mDNSBool tcp = (srs && DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp"));
pub = priv; while (1)
{
err = LNT_MapPort(priv, pub, tcp);
if (!err)
{
n->PublicPort = pub;
n->state = NATState_Legacy;
goto end;
}
else if (err != mStatus_AlreadyRegistered || ++ntries > LEGACY_NATMAP_MAX_TRIES)
{
n->state = NATState_Error;
goto end;
}
else
{
mDNSu16 RandPort = mDNSRandom(DYN_PORT_MAX - DYN_PORT_MIN) + DYN_PORT_MIN;
pub = mDNSOpaque16fromIntVal(RandPort);
}
}
#else
goto end;
#endif // _LEGACY_NAT_TRAVERSAL_
}
if (len < PORTMAP_PKTLEN) { LogMsg("ReceivePortMapReply: response too short (%d bytes)", len); goto end; }
if (pkt[0] != NATMAP_VERS) { LogMsg("ReceivePortMapReply: received version %d (expect version %d)", pkt[0], NATMAP_VERS); goto end; }
if (pkt[1] != (n->op | NATMAP_RESPONSE_MASK)) { LogMsg("ReceivePortMapReply: bad response code %d", pkt[1]); goto end; }
err = (NATErr_t)((NATErr_t)pkt[2] << 8 | (NATErr_t)pkt[3]);
if (err) { LogMsg("ReceivePortMapReply: received error %d", err); goto end; }
pktpriv.b[0] = pkt[4];
pktpriv.b[1] = pkt[5];
pub.b[0] = pkt[6];
pub.b[1] = pkt[7];
lease = (mDNSu32) ((mDNSu32)pkt[8] << 24 | (mDNSu32)pkt[9] << 16 | (mDNSu32)pkt[10] << 8 | pkt[11]);
if (lease > 0x70000000UL / mDNSPlatformOneSecond)
lease = 0x70000000UL / mDNSPlatformOneSecond;
if (priv.NotAnInteger != pktpriv.NotAnInteger)
{ LogMsg("ReceivePortMapReply: reply private port does not match requested private port"); goto end; }
if (n->state == NATState_Refresh && pub.NotAnInteger != n->PublicPort.NotAnInteger)
LogMsg("ReceivePortMapReply: NAT refresh changed public port from %d to %d", mDNSVal16(n->PublicPort), mDNSVal16(pub));
n->PublicPort = pub;
n->retry = mDNSPlatformTimeNow(m) + ((mDNSs32)lease * mDNSPlatformOneSecond/2);
if (n->state == NATState_Refresh) { n->state = NATState_Established; return; }
n->state = NATState_Established;
end:
if (n->state != NATState_Established && n->state != NATState_Legacy)
{
LogMsg("NAT Port Mapping: timeout");
n->state = NATState_Error;
if (!srs) { LLQNatMapComplete(m); return; }
FreeNATInfo(m, n);
srs->uDNS_info.NATinfo = mDNSNULL;
unlinkSRS(&m->uDNS_info, srs);
srs->uDNS_info.state = regState_Unregistered;
srs->ServiceCallback(m, srs, mStatus_NATTraversal);
return; }
LogMsg("Mapped private port %d to public port %d", mDNSVal16(priv), mDNSVal16(n->PublicPort));
if (!srs) { LLQNatMapComplete(m); return; }
srs->uDNS_info.state = regState_FetchingZoneData;
startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
}
mDNSlocal void FormatPortMaprequest(NATTraversalInfo *info, mDNSIPPort port)
{
mDNSu8 *msg = info->request;
mDNSOpaque32 lease = mDNSOpaque32fromIntVal(NATMAP_DEFAULT_LEASE);
msg[0] = NATMAP_VERS;
msg[1] = info->op;
msg[2] = 0; msg[3] = 0; msg[4] = port.b[0]; msg[5] = port.b[1];
msg[6] = port.b[0]; msg[7] = port.b[1];
msg[8] = lease.b[0];
msg[9] = lease.b[1];
msg[10] = lease.b[2];
msg[11] = lease.b[3];
}
mDNSlocal void SendInitialPMapReq(mDNS *m, NATTraversalInfo *info)
{
if (!m->uDNS_info.Router.ip.v4.NotAnInteger)
{
debugf("No router. Will retry NAT traversal in %ld seconds", NATMAP_INIT_RETRY);
info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY;
info->RetryInterval = NATMAP_INIT_RETRY;
return;
}
SendNATMsg(info, m);
return;
}
mDNSlocal void StartNATPortMap(mDNS *m, ServiceRecordSet *srs)
{
NATOp_t op;
NATTraversalInfo *info;
if (DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp")) op = NATOp_MapTCP;
else if (DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_udp")) op = NATOp_MapUDP;
else { LogMsg("StartNATPortMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name.c); goto error; }
info = AllocNATInfo(m, op, ReceivePortMapReply);
srs->uDNS_info.NATinfo = info;
info->reg.ServiceRegistration = srs;
info->state = NATState_Request;
info->requestlen = PORTMAP_PKTLEN;
FormatPortMaprequest(info, srs->RR_SRV.resrec.rdata->u.srv.port);
SendInitialPMapReq(m, info);
return;
error:
startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
}
mDNSlocal void DeleteNATPortMapping(mDNS *m, NATTraversalInfo *nat, ServiceRecordSet *srs)
{
if (nat->state == NATState_Established) {
nat->request[8] = 0;
nat->request[9] = 0;
nat->request[10] = 0 ;
nat->request[11] = 0;
nat->state = NATState_Request;
SendNATMsg(nat, m);
}
#ifdef _LEGACY_NAT_TRAVERSAL_
else if (nat->state == NATState_Legacy)
{
mStatus err = mStatus_NoError;
mDNSBool tcp = srs ? DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp") : mDNSfalse;
err = LNT_UnmapPort(nat->PublicPort, tcp);
if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %ld", err);
}
#else
(void)srs; #endif // _LEGACY_NAT_TRAVERSAL_
}
mDNSlocal void StartLLQNatMap(mDNS *m)
{
NATTraversalInfo *info = AllocNATInfo(m, NATOp_MapUDP, ReceivePortMapReply);
uDNS_GlobalInfo *u = &m->uDNS_info;
u->LLQNatInfo = info;
info->reg.RecordRegistration = mDNSNULL;
info->reg.ServiceRegistration = mDNSNULL;
info->state = NATState_Request;
info->requestlen = PORTMAP_PKTLEN;
FormatPortMaprequest(info, m->UnicastPort4);
SendInitialPMapReq(m, info);
return;
}
mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m)
{
NATTraversalInfo *nat = m->uDNS_info.LLQNatInfo;
DNSQuestion *q;
if (!nat) return;
for (q = m->uDNS_info.ActiveQueries; q; q = q->next)
if (q->LongLived && q->uDNS_info.llq->NATMap) return;
if (nat->state == NATState_Established || nat->state == NATState_Legacy)
DeleteNATPortMapping(m, nat, mDNSNULL); FreeNATInfo(m, nat); }
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - host name and interface management
#endif
mDNSlocal mDNSBool GetServiceTarget(uDNS_GlobalInfo *u, AuthRecord *srv, domainname *dst)
{
uDNS_HostnameInfo *hi = u->Hostnames;
(void)srv;
dst->c[0] = 0;
while (hi)
{
if (hi->ar->uDNS_info.state == regState_Registered || hi->ar->uDNS_info.state == regState_Refresh)
{ AssignDomainName(*dst, hi->ar->resrec.name); return mDNStrue; }
hi = hi->next;
}
if (u->StaticHostname.c[0]) { AssignDomainName(*dst, u->StaticHostname); return mDNStrue; }
return mDNSfalse;
}
mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
ExtraResourceRecord *e;
domainname newtarget;
domainname *curtarget = &srs->RR_SRV.resrec.rdata->u.srv.target;
mDNSBool havetarget = GetServiceTarget(u, &srs->RR_SRV, &newtarget);
if ((!havetarget && srs->uDNS_info.state == regState_NoTarget) || (havetarget && SameDomainName(curtarget, &newtarget))) return;
switch(srs->uDNS_info.state)
{
case regState_FetchingZoneData:
case regState_Cancelled:
case regState_DeregPending:
case regState_DeregDeferred:
case regState_Unregistered:
case regState_NATMap:
case regState_ExtraQueued:
return;
case regState_Pending:
case regState_Refresh:
case regState_UpdatePending:
srs->uDNS_info.SRVUpdateDeferred = mDNStrue;
return;
case regState_NoTarget:
if (havetarget) SendServiceRegistration(m, srs);
return;
case regState_Registered:
for (e = srs->Extras; e; e = e->next) e->r.uDNS_info.state = regState_ExtraQueued;
srs->uDNS_info.LostTarget = mDNStrue;
SendServiceDeregistration(m, srs);
return;
}
}
mDNSlocal void UpdateSRVRecords(mDNS *m)
{
ServiceRecordSet *srs;
for (srs = m->uDNS_info.ServiceRegistrations; srs; srs = srs->next) UpdateSRV(m, srs);
}
mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
uDNS_HostnameInfo *hi = (uDNS_HostnameInfo *)rr->RecordContext;
mDNSu8 *ip = rr->resrec.rdata->u.ipv4.b;
if (result == mStatus_MemFree)
{
debugf("MemFree: %##s IP %d.%d.%d.%d", rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]);
if (hi) ufree(hi);
ufree(rr);
return;
}
if (result == mStatus_NameConflict && rr->resrec.RecordType == kDNSRecordTypeUnique)
{
debugf("Name in use - retrying as type KnownUnique");
rr->resrec.RecordType = kDNSRecordTypeKnownUnique;
uDNS_RegisterRecord(m, rr);
return;
}
if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
{
if (result == mStatus_NoSuchRecord) result = mStatus_NameConflict;
}
if (result)
{
LogMsg("HostnameCallback: Error %ld for registration of %##s IP %d.%d.%d.%d", result, rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]);
if (!hi) { ufree(rr); return; }
if (hi->ar->uDNS_info.state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!");
(const void *)rr->RecordContext = hi->StatusContext;
if (hi->StatusCallback)
hi->StatusCallback(m, rr, result); rr->RecordContext = (void *)hi;
return;
}
UpdateSRVRecords(m);
if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; }
LogMsg("Registered hostname %##s IP %d.%d.%d.%d", rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]);
(const void *)rr->RecordContext = hi->StatusContext;
if (hi->StatusCallback)
hi->StatusCallback(m, rr, result); rr->RecordContext = (void *)hi;
}
mDNSlocal void AdvertiseHostname(mDNS *m, uDNS_HostnameInfo *h)
{
if (IsPrivateV4Addr(&m->uDNS_info.PrimaryIP))
StartGetPublicAddr(m, h);
else
{
mDNSu8 *ip = m->uDNS_info.PrimaryIP.ip.v4.b;
LogMsg("Advertising %##s IP %d.%d.%d.%d", h->ar->resrec.name.c, ip[0], ip[1], ip[2], ip[3]);
uDNS_RegisterRecord(m, h->ar);
}
}
mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
const domainname *pktname = &answer->rdata->u.name;
domainname *storedname = &m->uDNS_info.StaticHostname;
(void)question;
debugf("FoundStaticHostname: %##s -> %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "added" : "removed");
if (AddRecord && !SameDomainName(pktname, storedname))
{
AssignDomainName(*storedname, *pktname);
UpdateSRVRecords(m);
}
else if (!AddRecord && SameDomainName(pktname, storedname))
{
storedname->c[0] = 0;
UpdateSRVRecords(m);
}
}
mDNSlocal void GetStaticHostname(mDNS *m)
{
char buf[MAX_ESCAPED_DOMAIN_NAME];
DNSQuestion *q = &m->uDNS_info.ReverseMap;
mDNSu8 *ip = m->uDNS_info.PrimaryIP.ip.v4.b;
mStatus err;
ubzero(q, sizeof(*q));
if (m->uDNS_info.ReverseMapActive)
{
uDNS_StopQuery(m, q);
m->uDNS_info.ReverseMapActive = mDNSfalse;
}
if (!m->uDNS_info.PrimaryIP.ip.v4.NotAnInteger) return;
mDNS_snprintf(buf, MAX_ESCAPED_DOMAIN_NAME, "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]);
if (!MakeDomainNameFromDNSNameString(&q->qname, buf)) { LogMsg("Error: GetStaticHostname - bad name %s", buf); return; }
q->InterfaceID = mDNSInterface_Any;
q->Target = zeroAddr;
q->qtype = kDNSType_PTR;
q->qclass = kDNSClass_IN;
q->LongLived = mDNSfalse;
q->ExpectUnique = mDNSfalse;
q->ForceMCast = mDNSfalse;
q->QuestionCallback = FoundStaticHostname;
q->QuestionContext = mDNSNULL;
err = uDNS_StartQuery(m, q);
if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err);
else m->uDNS_info.ReverseMapActive = mDNStrue;
}
mDNSlocal void UpdateHostnameRegistrations(mDNS *m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
AuthRecord *new;
uDNS_HostnameInfo *i;
for (i = u->Hostnames; i; i = i->next)
{
if (i->ar->uDNS_info.state == regState_Unregistered) new = i->ar;
else
{
new = umalloc(sizeof(AuthRecord));
if (!new) { LogMsg("ERROR: UpdateHostnameRegistration - malloc"); return; }
mDNS_SetupResourceRecord(new, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeUnique, HostnameCallback, i);
}
AssignDomainName(new->resrec.name, i->ar->resrec.name);
new->resrec.rdata->u.ipv4 = u->PrimaryIP.ip.v4;
if (i->ar->uDNS_info.state != regState_Unregistered)
{
i->ar->RecordContext = mDNSNULL; uDNS_DeregisterRecord(m, i->ar);
i->ar = new;
}
AdvertiseHostname(m, i);
}
}
mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
uDNS_HostnameInfo *ptr, *new;
mDNS_Lock(m);
for (ptr = u->Hostnames; ptr; ptr = ptr->next)
{
if (SameDomainName(fqdn, &ptr->ar->resrec.name))
{ LogMsg("Host Domain %##s already in list", fqdn->c); goto exit; }
}
new = umalloc(sizeof(*new));
if (new) new->ar = umalloc(sizeof(AuthRecord));
if (!new || !new->ar) { LogMsg("ERROR: mDNS_AddDynDNSHostname - malloc"); goto exit; }
new->StatusCallback = StatusCallback;
new->StatusContext = StatusContext;
mDNS_SetupResourceRecord(new->ar, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeUnique, HostnameCallback, new);
AppendDomainName(&new->ar->resrec.name, fqdn);
new->next = u->Hostnames;
u->Hostnames = new;
if (u->PrimaryIP.ip.v4.NotAnInteger)
{
if (u->MappedPrimaryIP.ip.v4.NotAnInteger) new->ar->resrec.rdata->u.ipv4 = u->MappedPrimaryIP.ip.v4; else new->ar->resrec.rdata->u.ipv4 = u->PrimaryIP.ip.v4;
AdvertiseHostname(m, new);
}
else new->ar->uDNS_info.state = regState_Unregistered;
exit:
mDNS_Unlock(m);
}
mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
uDNS_HostnameInfo **ptr = &u->Hostnames;
mDNS_Lock(m);
while (*ptr && !SameDomainName(fqdn, &(*ptr)->ar->resrec.name)) ptr = &(*ptr)->next;
if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c);
else
{
uDNS_HostnameInfo *hi = *ptr;
*ptr = (*ptr)->next; hi->ar->RecordContext = mDNSNULL; if (hi->ar->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->ar);
else { ufree(hi->ar); hi->ar = mDNSNULL; }
ufree(hi);
}
UpdateSRVRecords(m);
mDNS_Unlock(m);
}
mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *addr, const mDNSAddr *router)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSBool AddrChanged, RouterChanged;
if (addr && addr->type !=mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 address. Discarding."); return; }
if (router && router->type !=mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 address. Discarding."); return; }
mDNS_Lock(m);
AddrChanged = addr ? (addr->ip.v4.NotAnInteger != u->PrimaryIP.ip.v4.NotAnInteger) : u->PrimaryIP.ip.v4.NotAnInteger;
RouterChanged = router ? (router->ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger) : u->Router.ip.v4.NotAnInteger;
#if MDNS_DEBUGMSGS
if (addr && (AddrChanged || RouterChanged))
LogMsg("mDNS_SetPrimaryInterfaceInfo: address changed from %d.%d.%d.%d to %d.%d.%d.%d:%d",
u->PrimaryIP.ip.v4.b[0], u->PrimaryIP.ip.v4.b[1], u->PrimaryIP.ip.v4.b[2], u->PrimaryIP.ip.v4.b[3],
addr->ip.v4.b[0], addr->ip.v4.b[1], addr->ip.v4.b[2], addr->ip.v4.b[3], mDNSVal16(m->UnicastPort4));
#endif // MDNS_DEBUGMSGS
if (addr) u->PrimaryIP = *addr;
if (router) u->Router = *router;
else u->Router.ip.v4.NotAnInteger = 0;
if (AddrChanged)
{
if (addr)
{
UpdateHostnameRegistrations(m);
UpdateSRVRecords(m);
}
GetStaticHostname(m); }
mDNS_Unlock(m);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Incoming Message Processing
#endif
mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr)
{
CacheRecord *ptr;
for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next)
if (SameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue;
return mDNSfalse;
}
mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr)
{
CacheRecord *ptr, *prev = mDNSNULL;
for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next)
{
if (SameResourceRecord(&ptr->resrec, &rr->resrec))
{
if (prev) prev->next = ptr->next;
else question->uDNS_info.knownAnswers = ptr->next;
ufree(ptr);
return;
}
prev = ptr;
}
LogMsg("removeKnownAnswer() called for record not in KA list");
}
mDNSlocal void addKnownAnswer(DNSQuestion *question, const CacheRecord *rr)
{
CacheRecord *newCR = mDNSNULL;
mDNSu32 size;
size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize;
newCR = (CacheRecord *)umalloc(size);
if (!newCR) { LogMsg("ERROR: addKnownAnswer - malloc"); return; }
umemcpy(newCR, rr, size);
newCR->resrec.rdata = (RData*)&newCR->rdatastorage;
newCR->resrec.rdata->MaxRDLength = rr->resrec.rdlength;
newCR->next = question->uDNS_info.knownAnswers;
question->uDNS_info.knownAnswers = newCR;
}
mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question)
{
const mDNSu8 *ptr;
int i;
CacheRecord *fptr, *ka, *cr, *answers = mDNSNULL, *prev = mDNSNULL;
LargeCacheRecord *lcr;
if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: deriveGoodbyes called without CurrentQuery set!"); return; }
ptr = LocateAnswers(msg, end);
if (!ptr) goto pkt_error;
if (!msg->h.numAnswers)
{
ka = question->uDNS_info.knownAnswers;
while (ka)
{
debugf("deriving goodbye for %##s", ka->resrec.name.c);
m->mDNS_reentrancy++; question->QuestionCallback(m, question, &ka->resrec, mDNSfalse);
m->mDNS_reentrancy--; if (question != m->uDNS_info.CurrentQuery)
{
debugf("deriveGoodbyes - question removed via callback. returning.");
return;
}
fptr = ka;
ka = ka->next;
ufree(fptr);
}
question->uDNS_info.knownAnswers = mDNSNULL;
return;
}
for (i = 0; i < msg->h.numAnswers; i++)
{
lcr = (LargeCacheRecord *)umalloc(sizeof(LargeCacheRecord));
if (!lcr) goto malloc_error;
ubzero(lcr, sizeof(LargeCacheRecord));
ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, lcr);
if (!ptr) goto pkt_error;
cr = &lcr->r;
if (ResourceRecordAnswersQuestion(&cr->resrec, question))
{
cr->next = answers;
answers = cr;
}
else ufree(cr);
}
ka = question->uDNS_info.knownAnswers;
while (ka)
{
for (cr = answers; cr; cr = cr->next)
{ if (SameResourceRecord(&ka->resrec, &cr->resrec)) break; }
if (!cr)
{
if (prev) prev->next = ka->next;
else question->uDNS_info.knownAnswers = ka->next;
debugf("deriving goodbye for %##s", ka->resrec.name.c);
m->mDNS_reentrancy++; question->QuestionCallback(m, question, &ka->resrec, mDNSfalse);
m->mDNS_reentrancy--; if (question != m->uDNS_info.CurrentQuery)
{
debugf("deriveGoodbyes - question removed via callback. returning.");
return;
}
fptr = ka;
ka = ka->next;
ufree(fptr);
}
else
{
prev = ka;
ka = ka->next;
}
}
cr = answers;
while (cr) { fptr = cr; cr = cr->next; ufree(fptr); }
return;
pkt_error:
LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %##s (%d)",
question->qname.c, question->qtype);
return;
malloc_error:
LogMsg("ERROR: Malloc");
}
mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, mDNSBool llq)
{
const mDNSu8 *ptr;
int i;
LargeCacheRecord lcr;
CacheRecord *cr = &lcr.r;
mDNSBool goodbye, inKAList, followedCName = mDNSfalse;
LLQ_Info *llqInfo = question->uDNS_info.llq;
domainname origname;
if (question != m->uDNS_info.CurrentQuery)
{ LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; }
question->uDNS_info.Answered = mDNStrue;
ptr = LocateAnswers(msg, end);
if (!ptr) goto pkt_error;
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) goto pkt_error;
if (ResourceRecordAnswersQuestion(&cr->resrec, question))
{
if (cr->resrec.rrtype == kDNSType_CNAME)
{
if (followedCName) LogMsg("Error: multiple CNAME referals for question %##s", question->qname.c);
else
{
debugf("Following cname %##s -> %##s", question->qname.c, cr->resrec.rdata->u.name.c);
AssignDomainName(origname, question->qname);
AssignDomainName(question->qname, cr->resrec.rdata->u.name);
question->qnamehash = DomainNameHashValue(&question->qname);
followedCName = mDNStrue;
i = -1; ptr = LocateAnswers(msg, end);
continue;
}
}
goodbye = llq ? ((mDNSs32)cr->resrec.rroriginalttl == -1) : mDNSfalse;
inKAList = kaListContainsAnswer(question, cr);
if ((goodbye && !inKAList) || (!goodbye && inKAList)) continue; if (!inKAList) addKnownAnswer(question, cr);
if (goodbye) removeKnownAnswer(question, cr);
m->mDNS_reentrancy++; question->QuestionCallback(m, question, &cr->resrec, !goodbye);
m->mDNS_reentrancy--; if (question != m->uDNS_info.CurrentQuery)
{
debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning");
return;
}
}
else if (!followedCName || !SameDomainName(&cr->resrec.name, &origname))
LogMsg("Question %##s type %d - unexpected answer %##s type %d", question->qname.c, question->qtype, cr->resrec.name.c, cr->resrec.rrtype);
}
if (!llq || llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume)
{
deriveGoodbyes(m, msg, end,question);
if (llq && llqInfo->deriveRemovesOnResume) llqInfo->deriveRemovesOnResume = mDNSfalse;
}
if (question->ThisQInterval < MAX_UCAST_POLL_INTERVAL) question->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
return;
pkt_error:
LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %##s (%d)",
question->qname.c, question->qtype);
return;
}
mDNSlocal void simpleResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context)
{
(void)context; pktResponseHndlr(m, msg, end, question, mDNSfalse);
}
mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context)
{
(void)context; pktResponseHndlr(m, msg, end, question, mDNStrue);
}
mDNSlocal mStatus ParseTSIGError(mDNS *m, const DNSMessage *msg, const mDNSu8 *end, const domainname *displayname)
{
LargeCacheRecord lcr;
const mDNSu8 *ptr;
mStatus err = mStatus_NoError;
int i;
ptr = LocateAdditionals(msg, end);
if (!ptr) goto finish;
for (i = 0; i < msg->h.numAdditionals; i++)
{
ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);;
if (!ptr) goto finish;
if (lcr.r.resrec.rrtype == kDNSType_TSIG)
{
mDNSu32 macsize;
mDNSu8 *rd = lcr.r.resrec.rdata->u.data;
mDNSu8 *rdend = rd + MaximumRDSize;
int alglen = DomainNameLength(&lcr.r.resrec.rdata->u.name);
if (rd + alglen > rdend) goto finish;
rd += alglen; if (rd + 6 > rdend) goto finish;
rd += 6; if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
rd += sizeof(mDNSOpaque16); if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
macsize = mDNSVal16(*(mDNSOpaque16 *)rd);
rd += sizeof(mDNSOpaque16); if (rd + macsize > rdend) goto finish;
rd += macsize;
if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
rd += sizeof(mDNSOpaque16); if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
err = mDNSVal16(*(mDNSOpaque16 *)rd);
if (err == TSIG_ErrBadSig) { LogMsg("%##s: bad signature", displayname->c); err = mStatus_BadSig; }
else if (err == TSIG_ErrBadKey) { LogMsg("%##s: bad key", displayname->c); err = mStatus_BadKey; }
else if (err == TSIG_ErrBadTime) { LogMsg("%##s: bad time", displayname->c); err = mStatus_BadTime; }
else if (err) { LogMsg("%##s: unknown tsig error %d", err); err = mStatus_UnknownErr; }
goto finish;
}
}
finish:
return err;
}
mDNSlocal mStatus checkUpdateResult(domainname *displayname, mDNSu8 rcode, mDNS *m, const DNSMessage *msg, const mDNSu8 *end)
{
(void)msg; if (!rcode) return mStatus_NoError;
else if (rcode == kDNSFlag1_RC_YXDomain)
{
LogMsg("name in use: %##s", displayname->c);
return mStatus_NameConflict;
}
else if (rcode == kDNSFlag1_RC_Refused)
{
LogMsg("Update %##s refused", displayname->c);
return mStatus_Refused;
}
else if (rcode == kDNSFlag1_RC_NXRRSet)
{
LogMsg("Reregister refused (NXRRSET): %##s", displayname->c);
return mStatus_NoSuchRecord;
}
else if (rcode == kDNSFlag1_RC_NotAuth)
{
mStatus tsigerr = ParseTSIGError(m, msg, end, displayname);
if (!tsigerr)
{
LogMsg("Permission denied (NOAUTH): %##s", displayname->c);
return mStatus_UnknownErr;
}
else return tsigerr;
}
else if (rcode == kDNSFlag1_RC_FmtErr)
{
mStatus tsigerr = ParseTSIGError(m, msg, end, displayname);
if (!tsigerr)
{
LogMsg("Format Error: %##s", displayname->c);
return mStatus_UnknownErr;
}
else return tsigerr;
}
else
{
LogMsg("Update %##s failed with rcode %d", displayname->c, rcode);
return mStatus_UnknownErr;
}
}
mDNSlocal void hndlServiceUpdateReply(mDNS * const m, ServiceRecordSet *srs, mStatus err)
{
mDNSBool InvokeCallback = mDNSfalse;
AuthRecord *UpdateR = mDNSNULL;
uDNS_RegInfo *info = &srs->uDNS_info;
NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
ExtraResourceRecord **e = &srs->Extras;
switch (info->state)
{
case regState_Pending:
if (err == mStatus_NameConflict && !info->TestForSelfConflict)
{
info->TestForSelfConflict = mDNStrue;
debugf("checking for self-conflict of service %##s", srs->RR_SRV.resrec.name.c);
SendServiceRegistration(m, srs);
return;
}
else if (info->TestForSelfConflict)
{
info->TestForSelfConflict = mDNSfalse;
if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; if (err) info->state = regState_Unregistered;
else info->state = regState_Registered;
InvokeCallback = mDNStrue;
break;
}
else if (err == mStatus_UnknownErr && info->lease)
{
LogMsg("Re-trying update of service %##s without lease option", srs->RR_SRV.resrec.name.c);
info->lease = mDNSfalse;
info->expire = -1;
SendServiceRegistration(m, srs);
return;
}
else
{
if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name.c); info->state = regState_Unregistered; } else info->state = regState_Registered;
InvokeCallback = mDNStrue;
break;
}
case regState_Refresh:
if (err)
{
LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name.c);
InvokeCallback = mDNStrue;
info->state = regState_Unregistered;
}
else info->state = regState_Registered;
break;
case regState_DeregPending:
if (err) LogMsg("Error %ld for deregistration of service %##s", err, srs->RR_SRV.resrec.name.c);
if (info->LostTarget)
{
info->state = regState_NoTarget;
break;
}
err = mStatus_MemFree;
InvokeCallback = mDNStrue;
if (nat)
{
if (nat->state == NATState_Deleted) { FreeNATInfo(m, nat); info->NATinfo = mDNSNULL; } else nat->reg.ServiceRegistration = mDNSNULL; }
info->state = regState_Unregistered;
break;
case regState_DeregDeferred:
if (err) { debugf("Error %ld received prior to deferred derigstration of %##s", err, srs->RR_SRV.resrec.name.c); }
debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name.c);
info->state = regState_Registered; SendServiceDeregistration(m, srs);
return;
case regState_UpdatePending:
UpdateR = &srs->RR_TXT;
if (err)
{
LogMsg("hndlServiceUpdateReply: error updating resource record");
UpdateR->uDNS_info.state = regState_Unregistered;
InvokeCallback = mDNStrue; }
else
{
UpdateR->uDNS_info.state = regState_Registered;
SwapRData(m, UpdateR, mDNStrue);
}
info->state = regState_Registered;
break;
case regState_FetchingZoneData:
case regState_Registered:
case regState_Cancelled:
case regState_Unregistered:
case regState_NATMap:
case regState_NoTarget:
case regState_ExtraQueued:
LogMsg("hndlServiceUpdateReply called for service %##s in unexpected state %d with error %ld. Unlinking.",
srs->RR_SRV.resrec.name.c, info->state, err);
err = mStatus_UnknownErr;
}
if ((info->LostTarget || info->SRVUpdateDeferred) && (info->state == regState_NoTarget || info->state == regState_Registered))
{
UpdateSRV(m, srs);
return;
}
while (*e)
{
uDNS_RegInfo *einfo = &(*e)->r.uDNS_info;
if (einfo->state == regState_ExtraQueued)
{
if (info->state == regState_Registered && !err)
{
AssignDomainName(einfo->zone, info->zone);
einfo->ns = info->ns;
einfo->port = info->port;
einfo->lease = info->lease;
sendRecordRegistration(m, &(*e)->r);
e = &(*e)->next;
}
else if (err && einfo->state != regState_Unregistered)
{
einfo->state = regState_Unregistered;
*e = (*e)->next;
}
else e = &(*e)->next;
}
else e = &(*e)->next;
}
srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; if (srs->RR_TXT.uDNS_info.UpdateQueued) SendRecordUpdate(m, &srs->RR_TXT, &srs->uDNS_info);
if (info->state == regState_Unregistered) unlinkSRS(&m->uDNS_info, srs);
m->mDNS_reentrancy++; if (InvokeCallback) srs->ServiceCallback(m, srs, err);
else if (info->ClientCallbackDeferred)
{
info->ClientCallbackDeferred = mDNSfalse;
srs->ServiceCallback(m, srs, info->DeferredStatus);
}
m->mDNS_reentrancy--; }
mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
if (rr->uDNS_info.state == regState_UpdatePending)
{
if (err)
{
LogMsg("Update record failed for %##s (err %d)", rr->resrec.name.c, err);
rr->uDNS_info.state = regState_Unregistered;
}
else
{
debugf("Update record %##s - success", rr->resrec.name.c);
rr->uDNS_info.state = regState_Registered;
SwapRData(m, rr, mDNStrue);
}
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
m->mDNS_reentrancy--; return;
}
if (rr->uDNS_info.state == regState_DeregPending)
{
debugf("Received reply for deregister record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype);
if (err) LogMsg("ERROR: Deregistration of record %##s type %d failed with error %ld",
rr->resrec.name.c, rr->resrec.rrtype, err);
err = mStatus_MemFree;
if (unlinkAR(&m->uDNS_info.RecordRegistrations, rr))
LogMsg("ERROR: Could not unlink resource record following deregistration");
rr->uDNS_info.state = regState_Unregistered;
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
m->mDNS_reentrancy--; return;
}
if (rr->uDNS_info.state == regState_DeregDeferred)
{
if (err)
{
LogMsg("Cancelling deferred deregistration record %##s type %d due to registration error %ld",
rr->resrec.name.c, rr->resrec.rrtype, err);
unlinkAR(&m->uDNS_info.RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
return;
}
LogMsg("Calling deferred deregistration of record %##s type %d",
rr->resrec.name.c, rr->resrec.rrtype);
rr->uDNS_info.state = regState_Registered;
uDNS_DeregisterRecord(m, rr);
return;
}
if (rr->uDNS_info.state == regState_Pending || rr->uDNS_info.state == regState_Refresh)
{
if (err)
{
if (rr->uDNS_info.lease && err == mStatus_UnknownErr)
{
LogMsg("Re-trying update of record %##s without lease option", rr->resrec.name.c);
rr->uDNS_info.lease = mDNSfalse;
rr->uDNS_info.expire = -1;
sendRecordRegistration(m, rr);
return;
}
LogMsg("Registration of record %##s type %d failed with error %ld",
rr->resrec.name.c, rr->resrec.rrtype, err);
unlinkAR(&u->RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
m->mDNS_reentrancy--; return;
}
else
{
if (rr->uDNS_info.UpdateQueued)
{
debugf("%##s: sending queued update", rr->resrec.name.c);
rr->uDNS_info.state = regState_Registered;
SendRecordUpdate(m ,rr, &rr->uDNS_info);
return;
}
if (rr->uDNS_info.state == regState_Refresh)
rr->uDNS_info.state = regState_Registered;
else
{
rr->uDNS_info.state = regState_Registered;
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
m->mDNS_reentrancy--; }
return;
}
}
LogMsg("Received unexpected response for record %##s type %d, in state %d, with response error %ld",
rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state, err);
}
mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, uDNS_RegInfo *info)
{
LargeCacheRecord lcr;
const mDNSu8 *ptr;
int i;
mDNSu32 lease = 0;
mDNSs32 expire;
ptr = LocateAdditionals(msg, end);
if (info->lease && (ptr = LocateAdditionals(msg, end)))
{
for (i = 0; i < msg->h.numAdditionals; i++)
{
ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
if (!ptr) break;
if (lcr.r.resrec.rrtype == kDNSType_OPT)
{
if (lcr.r.resrec.rdlength < LEASE_OPT_SIZE) continue;
if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue;
lease = lcr.r.resrec.rdata->u.opt.OptData.lease;
break;
}
}
}
if (lease > 0)
{
expire = (mDNSPlatformTimeNow(m) + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4);
if (info->state == regState_UpdatePending)
{ if (expire - info->expire < 0) info->expire = expire; }
else info->expire = expire;
}
else info->expire = -1;
}
mDNSexport void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *ptr, *cur;
NATOp_t op;
if (len < ADDR_REPLY_PKTLEN && len < PORTMAP_PKTLEN) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; }
if (pkt[0] != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expect version %d)", pkt[0], NATMAP_VERS); return; }
op = pkt[1];
if (!(op & NATMAP_RESPONSE_MASK)) { LogMsg("Received NAT Traversal message that is not a response (opcode %d)", op); return; }
ptr = u->NATTraversals;
while (ptr)
{
cur = ptr;
ptr = ptr->next;
if ((cur->state == NATState_Request || cur->state == NATState_Refresh) &&
(cur->op | NATMAP_RESPONSE_MASK) == op)
cur->ReceiveResponse(cur, m, pkt, len); }
}
mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr,
const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
{
DNSQuestion *qptr;
AuthRecord *rptr;
ServiceRecordSet *sptr;
mStatus err = mStatus_NoError;
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC);
mDNSs32 timenow = mDNSPlatformTimeNow(m);
(void)dstaddr;
(void)dstport;
(void)InterfaceID;
if (QR_OP == StdR)
{
if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return;
for (qptr = u->ActiveQueries; qptr; qptr = qptr->next)
{
if (qptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
{
if (timenow - (qptr->LastQTime + RESPONSE_WINDOW) > 0)
{ LogMsg("uDNS_ReceiveMsg - response received after maximum allowed window. Discarding"); return; }
if (msg->h.flags.b[0] & kDNSFlag0_TC)
{ hndlTruncatedAnswer(qptr, srcaddr, m); return; }
else
{
u->CurrentQuery = qptr;
qptr->uDNS_info.responseCallback(m, msg, end, qptr, qptr->uDNS_info.context);
u->CurrentQuery = mDNSNULL;
return;
}
}
}
}
if (QR_OP == UpdateR)
{
for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next)
{
if (sptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
{
err = checkUpdateResult(&sptr->RR_SRV.resrec.name, rcode, m, msg, end);
if (!err) SetUpdateExpiration(m, msg, end, &sptr->uDNS_info);
hndlServiceUpdateReply(m, sptr, err);
return;
}
}
for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next)
{
if (rptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
{
err = checkUpdateResult(&rptr->resrec.name, rcode, m, msg, end);
if (!err) SetUpdateExpiration(m, msg, end, &rptr->uDNS_info);
hndlRecordUpdateReply(m, rptr, err);
return;
}
}
}
debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id));
}
mDNSlocal mDNSBool GetServerForName(uDNS_GlobalInfo *u, const domainname *name, mDNSAddr *addr)
{
DNSServer *curmatch = mDNSNULL, *p = u->Servers;
int i, ncount, scount, curmatchlen = -1;
*addr = zeroAddr;
ncount = name ? CountLabels(name) : 0;
while (p)
{
scount = CountLabels(&p->domain);
if (scount <= ncount && scount > curmatchlen)
{
const domainname *tail = name;
for (i = 0; i < ncount - scount; i++)
tail = (domainname *)(tail->c + 1 + tail->c[0]); if (SameDomainName(tail, &p->domain)) { curmatch = p; curmatchlen = scount; }
}
p = p->next;
}
if (curmatch)
{
*addr = curmatch->addr;
return mDNStrue;
}
else return mDNSfalse;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Query Routines
#endif
#define sameID(x,y) mDNSPlatformMemSame(x,y,8)
mDNSlocal void initializeQuery(DNSMessage *msg, DNSQuestion *question)
{
mDNSOpaque16 flags = QueryFlags;
ubzero(msg, sizeof(msg));
flags.b[0] |= kDNSFlag0_RD; InitializeDNSMessage(&msg->h, question->uDNS_info.id, flags);
}
mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question)
{
initializeQuery(msg, question);
*endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
if (!*endPtr)
{
LogMsg("ERROR: Unicast query out of space in packet");
return mStatus_UnknownErr;
}
return mStatus_NoError;
}
mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *question, LLQOptData *data, mDNSBool includeQuestion)
{
AuthRecord rr;
ResourceRecord *opt = &rr.resrec;
rdataOpt *optRD;
if (includeQuestion)
{
ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; }
}
ubzero(&rr, sizeof(AuthRecord));
opt->rdata = &rr.rdatastorage;
opt->RecordType = kDNSRecordTypeKnownUnique; opt->rrtype = kDNSType_OPT;
opt->rdlength = LLQ_OPT_SIZE;
opt->rdestimate = LLQ_OPT_SIZE;
optRD = &rr.resrec.rdata->u.opt;
optRD->opt = kDNSOpt_LLQ;
optRD->optlen = sizeof(LLQOptData);
umemcpy(&optRD->OptData.llq, data, sizeof(LLQOptData));
ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0);
if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; }
return ptr;
}
mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LLQOptData *llq, int index)
{
LargeCacheRecord lcr;
int i;
const mDNSu8 *ptr;
ptr = LocateAdditionals(msg, end);
if (!ptr) return mDNSfalse;
for (i = 0; i < msg->h.numAdditionals; i++)
{ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; if (lcr.r.resrec.rrtype == kDNSType_OPT) break; }
if (lcr.r.resrec.rrtype != kDNSType_OPT) return mDNSfalse;
if (lcr.r.resrec.rdlength < (index + 1) * LLQ_OPT_SIZE) return mDNSfalse; umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(LLQOptData)), sizeof(LLQOptData)); return mDNStrue;
}
mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *q)
{
LLQ_Info *qInfo;
LLQOptData pktData;
qInfo = q->uDNS_info.llq;
if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; }
if (pktData.llqOp != kLLQOp_Refresh) return;
if (!sameID(pktData.id, qInfo->id)) { LogMsg("recvRefreshReply - ID mismatch. Discarding"); return; }
if (pktData.err != LLQErr_NoError) { LogMsg("recvRefreshReply: received error %d from server", pktData.err); return; }
qInfo->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond);
qInfo->retry = qInfo->expire - ((mDNSs32)pktData.lease * mDNSPlatformOneSecond/2);
qInfo->origLease = pktData.lease;
qInfo->state = LLQ_Established;
}
mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
{
DNSMessage msg;
mDNSu8 *end;
LLQOptData llq;
LLQ_Info *info = q->uDNS_info.llq;
mStatus err;
mDNSs32 timenow;
timenow = mDNSPlatformTimeNow(m);
if ((info->state == LLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) ||
info->expire - timenow < 0)
{
LogMsg("Unable to refresh LLQ %##s - will retry in %d minutes", q->qname.c, kLLQ_DEF_RETRY/60);
info->state = LLQ_Retry;
info->retry = mDNSPlatformTimeNow(m) + kLLQ_DEF_RETRY * mDNSPlatformOneSecond;
info->deriveRemovesOnResume = mDNStrue;
return;
}
llq.vers = kLLQ_Vers;
llq.llqOp = kLLQOp_Refresh;
llq.err = LLQErr_NoError;
umemcpy(llq.id, info->id, 8);
llq.lease = lease;
initializeQuery(&msg, q);
end = putLLQ(&msg, msg.data, q, &llq, mDNStrue);
if (!end) { LogMsg("ERROR: sendLLQRefresh - putLLQ"); return; }
err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL);
if (err) LogMsg("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err);
if (info->state == LLQ_Established) info->ntries = 1;
else info->ntries++;
info->state = LLQ_Refresh;
q->LastQTime = timenow;
info->retry = (info->expire - q->LastQTime) / 2;
}
mDNSlocal mDNSBool recvLLQEvent(mDNS *m, DNSQuestion *q, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, mDNSInterfaceID InterfaceID)
{
DNSMessage ack;
mDNSu8 *ackEnd = ack.data;
mStatus err;
LLQOptData opt;
(void)InterfaceID;
if (!getLLQAtIndex(m, msg, end, &opt, 0)) { debugf("Pkt does not contain LLQ Opt"); return mDNSfalse; }
if (opt.llqOp != kLLQOp_Event) { LogMsg("recvLLQEvent - Bad LLQ Opcode %d", opt.llqOp); return mDNSfalse; }
if (!q->uDNS_info.llq) { LogMsg("Error: recvLLQEvent - question onject does not contain LLQ metadata"); return mDNSfalse; }
if (!sameID(opt.id, q->uDNS_info.llq->id)) { LogMsg("recvLLQEvent - incorrect ID. Discarding"); return mDNSfalse; }
m->uDNS_info.CurrentQuery = q;
q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context);
if (m->uDNS_info.CurrentQuery != q) return mDNStrue;
InitializeDNSMessage(&ack.h, msg->h.id, ResponseFlags);
ackEnd = putQuestion(&ack, ack.data, ack.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putQuestion"); return mDNSfalse; }
err = mDNSSendDNSMessage(m, &ack, ackEnd, mDNSInterface_Any, srcaddr, srcport, -1, mDNSNULL);
if (err) LogMsg("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %ld", err);
return mDNStrue;
}
mDNSlocal void hndlChallengeResponseAck(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q)
{
LLQ_Info *info = q->uDNS_info.llq;
if (llq->err) { LogMsg("hndlChallengeResponseAck - received error %d from server", llq->err); goto error; }
if (!sameID(info->id, llq->id)) { LogMsg("hndlChallengeResponseAck - ID changed. discarding"); return; } info->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
info->retry = info->expire - ((mDNSs32)llq->lease * mDNSPlatformOneSecond / 2);
info->origLease = llq->lease;
info->state = LLQ_Established;
q->uDNS_info.responseCallback = llqResponseHndlr;
llqResponseHndlr(m, pktMsg, end, q, mDNSNULL);
return;
error:
info->state = LLQ_Error;
}
mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq)
{
LLQ_Info *info = q->uDNS_info.llq;
DNSMessage response;
mDNSu8 *responsePtr = response.data;
mStatus err;
LLQOptData llqBuf;
mDNSs32 timenow = mDNSPlatformTimeNow(m);
if (info->ntries++ == kLLQ_MAX_TRIES)
{
LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s. Will re-try in %d minutes",
kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60);
info->state = LLQ_Retry;
info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond);
return;
}
if (!llq)
{
llq = &llqBuf;
llq->vers = kLLQ_Vers;
llq->llqOp = kLLQOp_Setup;
llq->err = LLQErr_NoError;
umemcpy(llq->id, info->id, 8);
llq->lease = info->origLease;
}
q->LastQTime = timenow;
info->retry = timenow + (kLLQ_INIT_RESEND * info->ntries * mDNSPlatformOneSecond);
if (constructQueryMsg(&response, &responsePtr, q)) goto error;
responsePtr = putLLQ(&response, responsePtr, q, llq, mDNSfalse);
if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; }
err = mDNSSendDNSMessage(m, &response, responsePtr, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL);
if (err) LogMsg("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err);
return;
error:
info->state = LLQ_Error;
}
mDNSlocal void hndlRequestChallenge(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q)
{
LLQ_Info *info = q->uDNS_info.llq;
mDNSs32 timenow = mDNSPlatformTimeNow(m);
switch(llq->err)
{
case LLQErr_NoError: break;
case LLQErr_ServFull:
LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %##s. Retry in %lu sec", q->qname.c, llq->lease);
info->retry = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
info->state = LLQ_Retry;
simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); info->deriveRemovesOnResume = mDNStrue;
case LLQErr_Static:
info->state = LLQ_Static;
LogMsg("LLQ %##s: static", q->qname.c);
simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL);
return;
case LLQErr_FormErr:
LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %##s", q->qname.c);
goto error;
case LLQErr_BadVers:
LogMsg("ERROR: hndlRequestChallenge - received BadVers from server");
goto error;
case LLQErr_UnknownErr:
LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %##s", q->qname.c);
goto error;
default:
LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %##s", llq->err, q->qname.c);
goto error;
}
if (info->origLease != llq->lease)
LogMsg("hndlRequestChallenge: requested lease %lu, granted lease %lu", info->origLease, llq->lease);
info->origLease = llq->lease;
info->expire = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
info->state = LLQ_SecondaryRequest;
umemcpy(info->id, llq->id, 8);
info->ntries = 0;
sendChallengeResponse(m, q, llq);
return;
error:
info->state = LLQ_Error;
}
mDNSlocal void recvSetupResponse(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, DNSQuestion *q, void *clientContext)
{
DNSQuestion pktQuestion;
LLQOptData llq;
const mDNSu8 *ptr = pktMsg->data;
LLQ_Info *info = q->uDNS_info.llq;
mDNSu8 rcode = (mDNSu8)(pktMsg->h.flags.b[1] & kDNSFlag1_RC);
(void)clientContext;
if (rcode && rcode != kDNSFlag1_RC_NXDomain) goto poll;
ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion);
if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto poll; }
if (!SameDomainName(&q->qname, &pktQuestion.qname))
{ LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %##s", q->qname.c); goto poll; }
if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { debugf("recvSetupResponse - GetLLQAtIndex"); goto poll; }
if (llq.llqOp != kLLQOp_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto poll; }
if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto poll; }
if (info->state == LLQ_InitialRequest) { hndlRequestChallenge(m, pktMsg, end, &llq, q); return; }
if (info->state == LLQ_SecondaryRequest) { hndlChallengeResponseAck(m, pktMsg, end, &llq, q); return; }
LogMsg("recvSetupResponse - bad state %d", info->state);
poll:
info->state = LLQ_Poll;
q->uDNS_info.responseCallback = llqResponseHndlr;
info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
}
mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer)
{
DNSMessage msg;
mDNSu8 *end;
LLQOptData llqData;
DNSQuestion *q = info->question;
mStatus err = mStatus_NoError;
mDNSs32 timenow = mDNSPlatformTimeNow(m);
uDNS_GlobalInfo *u = &m->uDNS_info;
if (IsPrivateV4Addr(&u->PrimaryIP))
{
if (!u->LLQNatInfo)
{
info->state = LLQ_NatMapWait;
StartLLQNatMap(m);
return;
}
if (u->LLQNatInfo->state == NATState_Error) goto poll;
if (u->LLQNatInfo->state != NATState_Established && u->LLQNatInfo->state != NATState_Legacy)
{ info->state = LLQ_NatMapWait; info->NATMap = mDNStrue; return; }
info->NATMap = mDNStrue; }
if (info->ntries++ >= kLLQ_MAX_TRIES)
{
debugf("startLLQHandshake: %d failed attempts for LLQ %##s. Polling.", kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60);
goto poll;
}
llqData.vers = kLLQ_Vers;
llqData.llqOp = kLLQOp_Setup;
llqData.err = LLQErr_NoError;
ubzero(llqData.id, 8);
llqData.lease = kLLQ_DefLease;
initializeQuery(&msg, q);
end = putLLQ(&msg, msg.data, q, &llqData, mDNStrue);
if (!end)
{
LogMsg("ERROR: startLLQHandshake - putLLQ");
info->state = LLQ_Error;
return;
}
if (!defer) {
err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL);
if (err) LogMsg("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err);
}
info->state = LLQ_InitialRequest;
info->origLease = kLLQ_DefLease;
info->retry = timenow + (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
q->LastQTime = timenow;
q->uDNS_info.responseCallback = recvSetupResponse;
q->uDNS_info.internal = mDNStrue;
return;
poll:
info->question->uDNS_info.responseCallback = llqResponseHndlr;
info->state = LLQ_Poll;
info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
}
mDNSlocal void startLLQHandshakeCallback(mStatus err, mDNS *const m, void *llqInfo, const AsyncOpResult *result)
{
LLQ_Info *info = (LLQ_Info *)llqInfo;
const zoneData_t *zoneInfo = mDNSNULL;
if (info->state == LLQ_Cancelled)
{
debugf("startLLQHandshake - LLQ Cancelled.");
info->question = mDNSNULL; ufree(info);
return;
}
if (!info->question)
{ LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL question"); goto error; }
if (info->state != LLQ_GetZoneInfo)
{ LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); goto error; }
if (err)
{ LogMsg("ERROR: startLLQHandshakeCallback invoked with error code %ld", err); goto poll; }
if (!result)
{ LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code"); goto error; }
zoneInfo = &result->zoneData;
if (!zoneInfo->llqPort.NotAnInteger)
{ debugf("LLQ port lookup failed - reverting to polling"); goto poll; }
info->servAddr.type = zoneInfo->primaryAddr.type;
info->servAddr.ip.v4.NotAnInteger = zoneInfo->primaryAddr.ip.v4.NotAnInteger;
info->servPort.NotAnInteger = zoneInfo->llqPort.NotAnInteger;
info->ntries = 0;
if (info->state == LLQ_SuspendDeferred) info->state = LLQ_Suspended;
else startLLQHandshake(m, info, mDNSfalse);
return;
poll:
info->question->uDNS_info.responseCallback = llqResponseHndlr;
info->state = LLQ_Poll;
info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
return;
error:
info->state = LLQ_Error;
}
mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question)
{
LLQ_Info *info;
mStatus err = mStatus_NoError;
info = umalloc(sizeof(LLQ_Info));
if (!info) { LogMsg("ERROR: startLLQ - malloc"); return mStatus_NoMemoryErr; }
ubzero(info, sizeof(LLQ_Info));
info->state = LLQ_GetZoneInfo;
info->question = question;
question->uDNS_info.llq = info;
question->uDNS_info.responseCallback = llqResponseHndlr;
err = startGetZoneData(&question->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, info);
if (err)
{
LogMsg("ERROR: startLLQ - startGetZoneData returned %ld", err);
info->question = mDNSNULL;
ufree(info);
question->uDNS_info.llq = mDNSNULL;
return err;
}
LinkActiveQuestion(&m->uDNS_info, question);
return err;
}
mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
{
DNSQuestion pktQ, *q;
uDNS_GlobalInfo *u = &m->uDNS_info;
const mDNSu8 *ptr = msg->data;
LLQ_Info *llqInfo;
if (!msg->h.numQuestions) return mDNSfalse;
ptr = getQuestion(msg, ptr, end, 0, &pktQ);
if (!ptr) return mDNSfalse;
pktQ.uDNS_info.id = msg->h.id;
q = u->ActiveQueries;
while (q)
{
llqInfo = q->uDNS_info.llq;
if (q->LongLived &&
llqInfo &&
q->qnamehash == pktQ.qnamehash &&
q->qtype == pktQ.qtype &&
SameDomainName(&q->qname, &pktQ.qname))
{
u->CurrentQuery = q;
if (llqInfo->state == LLQ_Established || (llqInfo->state == LLQ_Refresh && msg->h.numAnswers))
{ if (recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID)) return mDNStrue; }
else if (msg->h.id.NotAnInteger == q->uDNS_info.id.NotAnInteger)
{
if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers)
{ recvRefreshReply(m, msg, end, q); return mDNStrue; }
if (llqInfo->state < LLQ_Static)
{
if ((llqInfo->state != LLQ_InitialRequest && llqInfo->state != LLQ_SecondaryRequest) || mDNSSameAddress(srcaddr, &llqInfo->servAddr))
{ q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; }
}
}
}
q = q->next;
}
return mDNSfalse;
}
mDNSexport mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u)
{
DNSQuestion *q;
for (q = u->ActiveQueries; q; q = q->next)
{
if (q == question)
{
if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&question->qname))
LogMsg("Warning: Question %##s in Active Unicast Query list with id %d, interfaceID %p",
question->qname.c, question->uDNS_info.id.NotAnInteger, question->InterfaceID);
return mDNStrue;
}
}
return mDNSfalse;
}
mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question)
{
LLQ_Info *info = question->uDNS_info.llq;
(void)m;
if (!question->LongLived) { LogMsg("ERROR: stopLLQ - LongLived flag not set"); return; }
if (!info) { LogMsg("ERROR: stopLLQ - llq info is NULL"); return; }
switch (info->state)
{
case LLQ_UnInit:
LogMsg("ERROR: stopLLQ - state LLQ_UnInit");
return;
case LLQ_GetZoneInfo:
case LLQ_SuspendDeferred:
info->question = mDNSNULL; info->state = LLQ_Cancelled;
return;
case LLQ_Established:
case LLQ_Refresh:
sendLLQRefresh(m, question, 0);
goto end;
default:
debugf("stopLLQ - silently discarding LLQ in state %d", info->state);
goto end;
}
end:
if (info->NATMap) info->NATMap = mDNSfalse;
CheckForUnreferencedLLQMapping(m);
info->question = mDNSNULL;
ufree(info);
question->uDNS_info.llq = mDNSNULL;
question->LongLived = mDNSfalse;
}
mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSQuestion *qptr, *prev = mDNSNULL;
CacheRecord *ka;
qptr = u->ActiveQueries;
while (qptr)
{
if (qptr == question)
{
if (question->LongLived && question->uDNS_info.llq)
stopLLQ(m, question);
if (m->uDNS_info.CurrentQuery == question)
m->uDNS_info.CurrentQuery = m->uDNS_info.CurrentQuery->next;
while (question->uDNS_info.knownAnswers)
{
ka = question->uDNS_info.knownAnswers;
question->uDNS_info.knownAnswers = question->uDNS_info.knownAnswers->next;
ufree(ka);
}
if (prev) prev->next = question->next;
else u->ActiveQueries = question->next;
return mStatus_NoError;
}
prev = qptr;
qptr = qptr->next;
}
LogMsg("uDNS_StopQuery: no such active query (%##s)", question->qname.c);
return mStatus_UnknownErr;
}
mDNSlocal mStatus startQuery(mDNS *const m, DNSQuestion *const question, mDNSBool internal)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSMessage msg;
mDNSu8 *endPtr;
mStatus err = mStatus_NoError;
mDNSAddr server;
if (!ValidateDomainName(&question->qname))
{
LogMsg("Attempt to start query with invalid qname %##s %##s", question->qname.c, DNSTypeName(question->qtype));
return mStatus_Invalid;
}
question->next = mDNSNULL;
question->qnamehash = DomainNameHashValue(&question->qname); question->uDNS_info.id = newMessageID(u);
question->uDNS_info.Answered = mDNSfalse;
if (question->LongLived) return startLLQ(m, question);
err = constructQueryMsg(&msg, &endPtr, question);
if (err) return err;
question->LastQTime = mDNSPlatformTimeNow(m);
question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
question->uDNS_info.internal = internal;
LinkActiveQuestion(u, question);
question->uDNS_info.knownAnswers = mDNSNULL;
if (GetServerForName(u, &question->qname, &server))
{
err = mDNSSendDNSMessage(m, &msg, endPtr, mDNSInterface_Any, &server, UnicastDNSPort, -1, mDNSNULL);
if (err) { debugf("ERROR: startQuery - %ld (keeping question in list for retransmission", err); }
}
return mStatus_NoError; }
mDNSexport mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
{
ubzero(&question->uDNS_info, sizeof(uDNS_QuestionInfo));
question->uDNS_info.responseCallback = simpleResponseHndlr;
question->uDNS_info.context = mDNSNULL;
return startQuery(m, question, 0);
}
mDNSlocal mStatus startInternalQuery(DNSQuestion *q, mDNS *m, InternalResponseHndlr callback, void *hndlrContext)
{
ubzero(&q->uDNS_info, sizeof(uDNS_QuestionInfo));
q->QuestionContext = hndlrContext;
q->uDNS_info.responseCallback = callback;
q->uDNS_info.context = hndlrContext;
return startQuery(m, q, 1);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Domain -> Name Server Conversion
#endif
typedef enum
{
init,
lookupSOA,
foundZone,
lookupNS,
foundNS,
lookupA,
foundA,
lookupPort,
foundPort,
complete
} ntaState;
typedef enum
{
smContinue, smBreak, smError } smAction;
typedef struct
{
domainname origName; domainname *curSOA; ntaState state; mDNS *m;
domainname zone; mDNSu16 zoneClass;
domainname ns; mDNSv4Addr addr; DNSQuestion question; DNSQuestion extraQuestion; mDNSBool questionActive; mDNSBool findUpdatePort;
mDNSBool findLLQPort;
mDNSIPPort updatePort;
mDNSIPPort llqPort;
AsyncOpCallback *callback; void *callbackInfo;
} ntaContext;
mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr);
mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr);
mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort,
AsyncOpCallback callback, void *callbackInfo)
{
ntaContext *context = (ntaContext*)umalloc(sizeof(ntaContext));
if (!context) { LogMsg("ERROR: startGetZoneData - umalloc failed"); return mStatus_NoMemoryErr; }
ubzero(context, sizeof(ntaContext));
AssignDomainName(context->origName, *name);
context->state = init;
context->m = m;
context->callback = callback;
context->callbackInfo = callbackInfo;
context->findUpdatePort = findUpdatePort;
context->findLLQPort = findLLQPort;
getZoneData(m, mDNSNULL, mDNSNULL, mDNSNULL, context);
return mStatus_NoError;
}
mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr)
{
AsyncOpResult result;
ntaContext *context = (ntaContext*)contextPtr;
smAction action;
(void)m;
(void)question;
if (context->questionActive)
{
uDNS_StopQuery(context->m, &context->question);
context->questionActive = mDNSfalse;
}
if (msg && msg->h.flags.b[2] >> 4 && msg->h.flags.b[2] >> 4 != kDNSFlag1_RC_NXDomain)
{
LogMsg("ERROR: getZoneData - received response w/ rcode %d", msg->h.flags.b[2] >> 4);
goto error;
}
switch (context->state)
{
case init:
case lookupSOA:
action = hndlLookupSOA(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
case foundZone:
case lookupNS:
action = confirmNS(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
case foundNS:
case lookupA:
action = lookupNSAddr(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
case foundA:
if (!context->findUpdatePort && !context->findLLQPort)
{
context->state = complete;
break;
}
case lookupPort:
action = hndlLookupPorts(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
if (action == smContinue) context->state = complete;
case foundPort:
case complete: break;
}
if (context->state != complete)
{
LogMsg("ERROR: getZoneData - exited state machine with state %d", context->state);
goto error;
}
result.type = zoneDataResult;
result.zoneData.primaryAddr.ip.v4.NotAnInteger = context->addr.NotAnInteger;
result.zoneData.primaryAddr.type = mDNSAddrType_IPv4;
AssignDomainName(result.zoneData.zoneName, context->zone);
result.zoneData.zoneClass = context->zoneClass;
result.zoneData.llqPort = context->findLLQPort ? context->llqPort : zeroIPPort;
result.zoneData.updatePort = context->findUpdatePort ? context->updatePort : zeroIPPort;
context->callback(mStatus_NoError, context->m, context->callbackInfo, &result);
goto cleanup;
error:
if (context && context->callback)
context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, mDNSNULL);
cleanup:
if (context && context->questionActive)
{
uDNS_StopQuery(context->m, &context->question);
context->questionActive = mDNSfalse;
}
if (context) ufree(context);
}
mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
{
mStatus err;
LargeCacheRecord lcr;
ResourceRecord *rr = &lcr.r.resrec;
DNSQuestion *query = &context->question;
const mDNSu8 *ptr;
if (msg)
{
int i;
ptr = LocateAnswers(msg, end);
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Answers - GetLargeResourceRecord returned NULL"); return smError; }
if (rr->rrtype == kDNSType_SOA && SameDomainName(context->curSOA, &rr->name))
{
processSOA(context, rr);
return smContinue;
}
}
ptr = LocateAuthorities(msg, end);
for (i = 0; i < msg->h.numAuthorities; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Authority - GetLargeResourceRecord returned NULL"); return smError; }
if (rr->rrtype == kDNSType_SOA)
{
processSOA(context, rr);
return smContinue;
}
}
}
if (context->state != init && !context->curSOA->c[0])
{
LogMsg("ERROR: hndlLookupSOA - recursed to root label of %##s without finding SOA",
context->origName.c);
return smError;
}
ubzero(query, sizeof(DNSQuestion));
if (context->state == init) context->curSOA = &context->origName;
else context->curSOA = (domainname *)(context->curSOA->c + context->curSOA->c[0]+1);
context->state = lookupSOA;
AssignDomainName(query->qname, *context->curSOA);
query->qtype = kDNSType_SOA;
query->qclass = kDNSClass_IN;
err = startInternalQuery(query, context->m, getZoneData, context);
context->questionActive = mDNStrue;
if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err);
return smBreak; }
mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr)
{
AssignDomainName(context->zone, rr->name);
context->zoneClass = rr->rrclass;
AssignDomainName(context->ns, rr->rdata->u.soa.mname);
context->state = foundZone;
}
mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
{
DNSQuestion *query = &context->question;
mStatus err;
LargeCacheRecord lcr;
const ResourceRecord *const rr = &lcr.r.resrec;
const mDNSu8 *ptr;
int i;
if (context->state == foundZone)
{
AssignDomainName(query->qname, context->zone);
query->qtype = kDNSType_NS;
query->qclass = kDNSClass_IN;
err = startInternalQuery(query, context->m, getZoneData, context);
context->questionActive = mDNStrue;
if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission", err);
context->state = lookupNS;
return smBreak; }
else if (context->state == lookupNS)
{
ptr = LocateAnswers(msg, end);
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: confirmNS, Answers - GetLargeResourceRecord returned NULL"); return smError; }
if (rr->rrtype == kDNSType_NS &&
SameDomainName(&context->zone, &rr->name) && SameDomainName(&context->ns, &rr->rdata->u.name))
{
context->state = foundNS;
return smContinue; }
}
LogMsg("ERROR: could not confirm existence of record %##s NS %##s", context->zone.c, context->ns.c);
return smError;
}
else { LogMsg("ERROR: confirmNS - bad state %d", context->state); return smError; }
}
mDNSlocal smAction queryNSAddr(ntaContext *context)
{
mStatus err;
DNSQuestion *query = &context->question;
AssignDomainName(query->qname, context->ns);
query->qtype = kDNSType_A;
query->qclass = kDNSClass_IN;
err = startInternalQuery(query, context->m, getZoneData, context);
context->questionActive = mDNStrue;
if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err);
context->state = lookupA;
return smBreak;
}
mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
{
const mDNSu8 *ptr;
int i;
LargeCacheRecord lcr;
ResourceRecord *rr = &lcr.r.resrec;
if (context->state == foundNS)
{
if (!msg->h.numAdditionals) return queryNSAddr(context);
ptr = LocateAdditionals(msg, end);
if (!ptr)
{
LogMsg("ERROR: lookupNSAddr - LocateAdditionals returned NULL, expected %d additionals", msg->h.numAdditionals);
return queryNSAddr(context);
}
else
{
for (i = 0; i < msg->h.numAdditionals; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr)
{
LogMsg("ERROR: lookupNSAddr, Additionals - GetLargeResourceRecord returned NULL");
return queryNSAddr(context);
}
if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name))
{
context->addr.NotAnInteger = rr->rdata->u.ipv4.NotAnInteger;
context->state = foundA;
return smContinue;
}
}
}
return queryNSAddr(context);
}
else if (context->state == lookupA)
{
ptr = LocateAnswers(msg, end);
if (!ptr) { LogMsg("ERROR: lookupNSAddr: LocateAnswers returned NULL"); return smError; }
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: lookupNSAddr, Answers - GetLargeResourceRecord returned NULL"); break; }
if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name))
{
context->addr.NotAnInteger = rr->rdata->u.ipv4.NotAnInteger;
context->state = foundA;
return smContinue;
}
}
LogMsg("ERROR: lookupNSAddr: Address record not found in answer section");
return smError;
}
else { LogMsg("ERROR: lookupNSAddr - bad state %d", context->state); return smError; }
}
mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext *context, char *portName, mDNSIPPort *port)
{
int i;
LargeCacheRecord lcr;
const mDNSu8 *ptr;
DNSQuestion *q;
mStatus err;
if (context->state == lookupPort) {
if (!msg) { LogMsg("ERROR: hndlLookupUpdatePort - NULL message"); return smError; }
ptr = LocateAnswers(msg, end);
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: hndlLookupUpdatePort - GetLargeResourceRecord returned NULL"); return smError; }
if (ResourceRecordAnswersQuestion(&lcr.r.resrec, &context->question))
{
port->NotAnInteger = lcr.r.resrec.rdata->u.srv.port.NotAnInteger;
context->state = foundPort;
return smContinue;
}
}
debugf("hndlLookupUpdatePort - no answer for type %s", portName);
port->NotAnInteger = 0;
context->state = foundPort;
return smContinue;
}
context->state = lookupPort;
q = &context->question;
MakeDomainNameFromDNSNameString(&q->qname, portName);
AppendDomainName(&q->qname, &context->zone);
q->qtype = kDNSType_SRV;
q->qclass = kDNSClass_IN;
err = startInternalQuery(q, context->m, getZoneData, context);
context->questionActive = mDNStrue;
if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err);
return smBreak; }
mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
{
smAction action;
if (context->findUpdatePort && !context->updatePort.NotAnInteger)
{
action = lookupDNSPort(msg, end, context, UPDATE_PORT_NAME, &context->updatePort);
if (action != smContinue) return action;
}
if (context->findLLQPort && !context->llqPort.NotAnInteger)
return lookupDNSPort(msg, end, context, LLQ_PORT_NAME, &context->llqPort);
return smContinue;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Truncation Handling
#endif
typedef struct
{
DNSQuestion *question;
DNSMessage reply;
mDNSu16 replylen;
int nread;
mDNS *m;
} tcpInfo_t;
mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstablished)
{
mStatus err = 0;
char msgbuf[356]; DNSMessage *msg;
mDNSu8 *end;
tcpInfo_t *info = (tcpInfo_t *)context;
DNSQuestion *question = info->question;
int n;
mDNS *m = info->m;
mDNS_Lock(m);
if (ConnectionEstablished)
{
msg = (DNSMessage *)&msgbuf;
err = constructQueryMsg(msg, &end, question);
if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %ld", err); goto error; }
err = mDNSSendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL);
question->LastQTime = mDNSPlatformTimeNow(m);
if (err) { LogMsg("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %ld", err); goto error; }
}
else
{
if (!info->nread)
{
n = mDNSPlatformReadTCP(sd, &info->replylen, 2);
if (n != 2)
{
LogMsg("ERROR:conQueryCallback - attempt to read message length failed (read returned %d)", n);
goto error;
}
}
n = mDNSPlatformReadTCP(sd, ((char *)&info->reply) + info->nread, info->replylen - info->nread);
if (n < 0) { LogMsg("ERROR: conQueryCallback - read returned %d", n); goto error; }
info->nread += n;
if (info->nread == info->replylen)
{
uDNS_ReceiveMsg(m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, question->InterfaceID);
mDNSPlatformTCPCloseConnection(sd);
ufree(info);
}
}
mDNS_Unlock(m);
return;
error:
mDNSPlatformTCPCloseConnection(sd);
ufree(info);
mDNS_Unlock(m);
}
mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m)
{
mStatus connectionStatus;
uDNS_QuestionInfo *info = &question->uDNS_info;
int sd;
tcpInfo_t *context;
if (!src) { LogMsg("hndlTruncatedAnswer: TCP DNS response had TC bit set: ignoring"); return; }
context = (tcpInfo_t *)umalloc(sizeof(tcpInfo_t));
if (!context) { LogMsg("ERROR: hndlTruncatedAnswer - memallocate failed"); return; }
ubzero(context, sizeof(tcpInfo_t));
context->question = question;
context->m = m;
info->id = newMessageID(&m->uDNS_info);
connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd);
if (connectionStatus == mStatus_ConnEstablished) {
conQueryCallback(sd, context, mDNStrue);
return;
}
if (connectionStatus == mStatus_ConnPending) return; LogMsg("hndlTruncatedAnswer: connection failed");
uDNS_StopQuery(m, question); }
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Dynamic Updates
#endif
mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr)
{
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSOpaque16 id;
uDNS_RegInfo *regInfo = &rr->uDNS_info;
mStatus err = mStatus_UnknownErr;
id = newMessageID(u);
InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
rr->uDNS_info.id.NotAnInteger = id.NotAnInteger;
ptr = putZone(&msg, ptr, end, ®Info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) goto error;
if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->uDNS_info.state == regState_Refresh)
{
ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &rr->resrec, 0);
if (!ptr) goto error;
}
else if (rr->resrec.RecordType != kDNSRecordTypeShared)
{
ptr = putPrereqNameNotInUse(&rr->resrec.name, &msg, ptr, end);
if (!ptr) goto error;
}
ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl);
if (!ptr) goto error;
if (rr->uDNS_info.lease)
{ ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; }
rr->uDNS_info.expire = -1;
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, ®Info->ns, regInfo->port, -1, GetAuthInfoForZone(u, ®Info->zone));
if (err) LogMsg("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr);
if (regInfo->state != regState_Refresh) regInfo->state = regState_Pending;
return;
error:
LogMsg("sendRecordRegistration: Error formatting message");
if (rr->uDNS_info.state != regState_Unregistered)
{
unlinkAR(&u->RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
}
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
m->mDNS_reentrancy--; }
mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *authPtr, const AsyncOpResult *result)
{
AuthRecord *newRR = (AuthRecord*)authPtr;
const zoneData_t *zoneData = mDNSNULL;
uDNS_GlobalInfo *u = &m->uDNS_info;
AuthRecord *ptr;
for (ptr = u->RecordRegistrations; ptr; ptr = ptr->next)
if (ptr == newRR) break;
if (!ptr) { LogMsg("RecordRegistrationCallback - RR no longer in list. Discarding."); return; }
if (err) { LogMsg("RecordRegistrationCallback: error %ld", err); goto error; }
if (!result) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); goto error; }
else zoneData = &result->zoneData;
if (newRR->uDNS_info.state == regState_Cancelled)
{
debugf("Registration of %##s type %d cancelled prior to update",
newRR->resrec.name.c, newRR->resrec.rrtype);
newRR->uDNS_info.state = regState_Unregistered;
unlinkAR(&u->RecordRegistrations, newRR);
return;
}
if (result->type != zoneDataResult)
{
LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type);
goto error;
}
if (newRR->resrec.rrclass != zoneData->zoneClass)
{
LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)",
newRR->resrec.rrclass, zoneData->zoneClass);
goto error;
}
AssignDomainName(newRR->uDNS_info.zone, zoneData->zoneName);
newRR->uDNS_info.ns.type = mDNSAddrType_IPv4;
newRR->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger;
if (zoneData->updatePort.NotAnInteger) newRR->uDNS_info.port = zoneData->updatePort;
else
{
debugf("Update port not advertised via SRV - guessing port 53, no lease option");
newRR->uDNS_info.port = UnicastDNSPort;
newRR->uDNS_info.lease = mDNSfalse;
}
sendRecordRegistration(m, newRR);
return;
error:
if (newRR->uDNS_info.state != regState_Unregistered)
{
unlinkAR(&u->RecordRegistrations, newRR);
newRR->uDNS_info.state = regState_Unregistered;
}
m->mDNS_reentrancy++; if (newRR->RecordCallback)
newRR->RecordCallback(m, newRR, err);
m->mDNS_reentrancy--; }
mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs)
{
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSOpaque16 id;
uDNS_RegInfo *rInfo = &srs->uDNS_info;
mStatus err = mStatus_UnknownErr;
mDNSIPPort privport;
NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
mDNSBool mapped = mDNSfalse;
domainname target;
AuthRecord *srv = &srs->RR_SRV;
id = newMessageID(u);
InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
SetNewRData(&srs->RR_PTR.resrec, mDNSNULL, 0);
SetNewRData(&srs->RR_TXT.resrec, mDNSNULL, 0);
if (nat && nat->PublicPort.NotAnInteger &&
(nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy))
{
privport = srv->resrec.rdata->u.srv.port;
srv->resrec.rdata->u.srv.port = nat->PublicPort;
mapped = mDNStrue;
}
ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srv->resrec.rrclass));
if (!ptr) goto error;
if (srs->uDNS_info.TestForSelfConflict)
{
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0))) goto error;
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_TXT.resrec, 0))) goto error;
}
else if (srs->uDNS_info.state != regState_Refresh)
{
ptr = putPrereqNameNotInUse(&srv->resrec.name, &msg, ptr, end);
if (!ptr) goto error;
}
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec, srs->RR_PTR.resrec.rroriginalttl))) goto error;
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) goto error;
if (!GetServiceTarget(u, srv, &target))
{
debugf("Couldn't get target for service %##s", srv->resrec.name.c);
rInfo->state = regState_NoTarget;
return;
}
if (!SameDomainName(&target, &srv->resrec.rdata->u.srv.target))
{
AssignDomainName(srv->resrec.rdata->u.srv.target, target);
SetNewRData(&srv->resrec, mDNSNULL, 0);
}
ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srv->resrec, srv->resrec.rroriginalttl);
if (!ptr) goto error;
if (srs->uDNS_info.lease)
{ ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; }
srs->uDNS_info.expire = -1;
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rInfo->ns, rInfo->port, -1, GetAuthInfoForZone(u, &rInfo->zone));
if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err); goto error; }
if (rInfo->state != regState_Refresh)
rInfo->state = regState_Pending;
SetRecordRetry(m, &srs->RR_SRV);
rInfo->id.NotAnInteger = id.NotAnInteger;
if (mapped) srv->resrec.rdata->u.srv.port = privport;
return;
error:
LogMsg("SendServiceRegistration - Error formatting message");
if (mapped) srv->resrec.rdata->u.srv.port = privport;
unlinkSRS(u, srs);
rInfo->state = regState_Unregistered;
m->mDNS_reentrancy++; srs->ServiceCallback(m, srs, err);
m->mDNS_reentrancy--; }
mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result)
{
ServiceRecordSet *srs = (ServiceRecordSet *)srsPtr;
const zoneData_t *zoneData = mDNSNULL;
uDNS_GlobalInfo *u = &m->uDNS_info;
if (err) goto error;
if (!result) { LogMsg("ERROR: serviceRegistrationCallback invoked with NULL result and no error"); goto error; }
else zoneData = &result->zoneData;
if (result->type != zoneDataResult)
{
LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type);
goto error;
}
if (srs->uDNS_info.state == regState_Cancelled)
{
srs->uDNS_info.state = regState_Unregistered;
unlinkSRS(u, srs);
m->mDNS_reentrancy++; srs->ServiceCallback(m, srs, mStatus_MemFree);
m->mDNS_reentrancy--; return;
}
if (srs->RR_SRV.resrec.rrclass != zoneData->zoneClass)
{
LogMsg("Service %##s - class does not match zone", srs->RR_SRV.resrec.name.c);
goto error;
}
AssignDomainName(srs->uDNS_info.zone, zoneData->zoneName);
srs->uDNS_info.ns.type = mDNSAddrType_IPv4;
srs->uDNS_info.ns = zoneData->primaryAddr;
if (zoneData->updatePort.NotAnInteger) srs->uDNS_info.port = zoneData->updatePort;
else
{
debugf("Update port not advertised via SRV - guessing port 53, no lease option");
srs->uDNS_info.port = UnicastDNSPort;
srs->uDNS_info.lease = mDNSfalse;
}
SendServiceRegistration(m, srs);
return;
error:
unlinkSRS(u, srs);
srs->uDNS_info.state = regState_Unregistered;
m->mDNS_reentrancy++; srs->ServiceCallback(m, srs, err);
m->mDNS_reentrancy--; }
mDNSlocal mStatus SetupRecordRegistration(mDNS *m, AuthRecord *rr)
{
domainname *target = GetRRDomainNameTarget(&rr->resrec);
AuthRecord *ptr = m->uDNS_info.RecordRegistrations;
while (ptr && ptr != rr) ptr = ptr->next;
if (ptr) { LogMsg("Error: SetupRecordRegistration - record %##s already in list!", rr->resrec.name.c); return mStatus_AlreadyRegistered; }
if (rr->uDNS_info.state == regState_FetchingZoneData ||
rr->uDNS_info.state == regState_Pending ||
rr->uDNS_info.state == regState_Registered)
{
LogMsg("Requested double-registration of physical record %##s type %d",
rr->resrec.name.c, rr->resrec.rrtype);
return mStatus_AlreadyRegistered;
}
rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
if (!ValidateDomainName(&rr->resrec.name))
{
LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr));
return mStatus_Invalid;
}
if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata))
{
LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr));
return mStatus_Invalid;
}
rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name);
rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
rr->resrec.rdnamehash = target ? DomainNameHashValue(target) : 0;
rr->uDNS_info.state = regState_FetchingZoneData;
rr->next = m->uDNS_info.RecordRegistrations;
m->uDNS_info.RecordRegistrations = rr;
rr->uDNS_info.lease = mDNStrue;
return mStatus_NoError;
}
mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr)
{
mStatus err = SetupRecordRegistration(m, rr);
if (err) return err;
else return startGetZoneData(&rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr);
}
mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
mStatus err;
InitializeDNSMessage(&msg.h, rr->uDNS_info.id, UpdateReqFlags);
ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) goto error;
if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error;
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForZone(u, &rr->uDNS_info.zone));
if (err) LogMsg("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr);
rr->uDNS_info.state = regState_DeregPending;
return;
error:
LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet");
unlinkAR(&u->RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
}
mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *n = rr->uDNS_info.NATinfo;
switch (rr->uDNS_info.state)
{
case regState_NATMap:
if (!n) LogMsg("uDNS_DeregisterRecord: no NAT info context");
else FreeNATInfo(m, n); rr->uDNS_info.NATinfo = mDNSNULL;
rr->uDNS_info.state = regState_Unregistered;
break;
case regState_ExtraQueued:
rr->uDNS_info.state = regState_Unregistered;
break;
case regState_FetchingZoneData:
rr->uDNS_info.state = regState_Cancelled;
return mStatus_NoError;
case regState_Refresh:
case regState_Pending:
case regState_UpdatePending:
rr->uDNS_info.state = regState_DeregDeferred;
LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name.c);
return mStatus_NoError;
case regState_Registered:
case regState_DeregPending:
break;
case regState_DeregDeferred:
case regState_Cancelled:
LogMsg("Double deregistration of record %##s type %d",
rr->resrec.name.c, rr->resrec.rrtype);
return mStatus_UnknownErr;
case regState_Unregistered:
LogMsg("Requested deregistration of unregistered record %##s type %d",
rr->resrec.name.c, rr->resrec.rrtype);
return mStatus_UnknownErr;
case regState_NoTarget:
LogMsg("ERROR: uDNS_DeregisterRecord called for record %##s with bad state (regState_NoTarget)", rr->resrec.name.c);
return mStatus_UnknownErr;
}
if (rr->uDNS_info.state == regState_Unregistered)
{
unlinkAR(&u->RecordRegistrations, rr);
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree);
m->mDNS_reentrancy--; return mStatus_NoError;
}
if (n) FreeNATInfo(m, n);
rr->uDNS_info.NATinfo = mDNSNULL;
SendRecordDeregistration(m, rr);
return mStatus_NoError;
}
mDNSlocal mStatus RegisterService(mDNS *m, ServiceRecordSet *srs)
{
uDNS_RegInfo *info = &srs->uDNS_info;
srs->RR_SRV.resrec.rroriginalttl = 3;
srs->RR_TXT.resrec.rroriginalttl = 3;
srs->RR_PTR.resrec.rroriginalttl = 3;
info->lease = mDNStrue;
if (srs->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger && MapServicePort(m))
{
info->state = regState_NATMap;
StartNATPortMap(m, srs);
return mStatus_NoError;
}
info->state = regState_FetchingZoneData;
return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
}
mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs)
{
ServiceRecordSet **p = &m->uDNS_info.ServiceRegistrations;
while (*p && *p != srs) p=&(*p)->next;
if (*p) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs, srs->RR_SRV.resrec.name.c); return(mStatus_AlreadyRegistered); }
ubzero(&srs->uDNS_info, sizeof(uDNS_RegInfo));
srs->uDNS_info.state = regState_Unregistered;
*p = srs;
srs->next = mDNSNULL;
return RegisterService(m, srs);
}
mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
{
uDNS_RegInfo *info = &srs->uDNS_info;
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSMessage msg;
mDNSOpaque16 id;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
mStatus err = mStatus_UnknownErr;
id = newMessageID(u);
InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass));
if (!ptr) { LogMsg("ERROR: SendServiceDeregistration - putZone"); goto error; }
if (!(ptr = putDeleteAllRRSets(&msg, ptr, &srs->RR_SRV.resrec.name))) goto error; if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error;
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForZone(u, &info->zone));
if (err) { LogMsg("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto error; }
SetRecordRetry(m, &srs->RR_SRV);
info->id.NotAnInteger = id.NotAnInteger;
info->state = regState_DeregPending;
return;
error:
unlinkSRS(u, srs);
info->state = regState_Unregistered;
}
mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
AuthRecord **r = &u->RecordRegistrations;
char *errmsg = "Unknown State";
while (*r)
{
if (SameDomainName(&srs->RR_SRV.resrec.name, &(*r)->resrec.name)) *r = (*r)->next;
else r = &(*r)->next;
}
srs->uDNS_info.LostTarget = srs->uDNS_info.SRVUpdateDeferred = mDNSfalse;
switch (srs->uDNS_info.state)
{
case regState_NATMap:
if (!nat) LogMsg("uDNS_DeregisterRecord: no NAT info context");
else { nat->reg.ServiceRegistration = mDNSNULL; FreeNATInfo(m, nat); }
unlinkSRS(u, srs);
srs->uDNS_info.state = regState_Unregistered;
m->mDNS_reentrancy++; srs->ServiceCallback(m, srs, mStatus_MemFree);
m->mDNS_reentrancy--; return mStatus_NoError;
case regState_Unregistered:
errmsg = "service not registered";
goto error;
case regState_FetchingZoneData:
srs->uDNS_info.state = regState_Cancelled;
return mStatus_NoError; case regState_Pending:
case regState_Refresh:
case regState_UpdatePending:
srs->uDNS_info.state = regState_DeregDeferred;
return mStatus_NoError;
case regState_DeregPending:
case regState_DeregDeferred:
case regState_Cancelled:
debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name.c);
return mStatus_NoError;
case regState_NoTarget:
unlinkSRS(u, srs);
srs->uDNS_info.state = regState_Unregistered;
m->mDNS_reentrancy++; srs->ServiceCallback(m, srs, mStatus_MemFree);
m->mDNS_reentrancy--; return mStatus_NoError;
case regState_Registered:
if (nat) DeleteNATPortMapping(m, nat, srs);
srs->uDNS_info.state = regState_DeregPending;
SendServiceDeregistration(m, srs);
return mStatus_NoError;
case regState_ExtraQueued: errmsg = "bad state (regState_ExtraQueued)";
goto error;
}
error:
LogMsg("Error, uDNS_DeregisterService: %s", errmsg);
return mStatus_BadReferenceErr;
}
mDNSlocal void SendRecordUpdate(mDNS *m, AuthRecord *rr, uDNS_RegInfo *info)
{
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSOpaque16 id;
mStatus err = mStatus_UnknownErr;
rr->uDNS_info.UpdateQueued = mDNSfalse; id = newMessageID(u);
InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
info->id.NotAnInteger = id.NotAnInteger;
ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) goto error;
ptr = putDeletionRecord(&msg, ptr, &rr->resrec);
if (!ptr) goto error;
SwapRData(m, rr, mDNSfalse);
ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl);
SwapRData(m, rr, mDNSfalse); if (!ptr) goto error;
if (info->lease)
{ ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; }
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForZone(u, &info->zone));
if (err) debugf("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr);
rr->uDNS_info.state = regState_UpdatePending;
if (&rr->uDNS_info != info) info->state = regState_UpdatePending; return;
error:
LogMsg("ERROR: SendRecordUpdate. Error formatting update message.");
info ->state = regState_Registered;
}
mDNSexport mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra)
{
mStatus err = mStatus_UnknownErr;
extra->r.resrec.RecordType = kDNSRecordTypeShared; extra->r.RecordCallback = mDNSNULL;
if (sr->uDNS_info.state == regState_Registered || sr->uDNS_info.state == regState_Refresh)
err = uDNS_RegisterRecord(m, &extra->r);
else
{
err = SetupRecordRegistration(m, &extra->r);
extra->r.uDNS_info.state = regState_ExtraQueued;
}
if (!err)
{
extra->next = sr->Extras;
sr->Extras = extra;
}
return err;
}
mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
ServiceRecordSet *sptr, *parent = mDNSNULL;
AuthRecord *rptr;
uDNS_RegInfo *info = mDNSNULL;
for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next)
if (&sptr->RR_TXT == rr) { info = &sptr->uDNS_info; parent = sptr; break; }
if (!parent)
{
for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next)
if (rptr == rr) { info = &rr->uDNS_info; break; }
}
if (!info) goto unreg_error;
rr->uDNS_info.UpdateRData = rr->NewRData;
rr->uDNS_info.UpdateRDLen = rr->newrdlength;
rr->uDNS_info.UpdateRDCallback = rr->UpdateCallback;
rr->NewRData = mDNSNULL;
switch(info->state)
{
case regState_DeregPending:
case regState_DeregDeferred:
case regState_Cancelled:
case regState_Unregistered:
goto unreg_error;
case regState_FetchingZoneData:
case regState_NATMap:
case regState_ExtraQueued:
SwapRData(m, rr, mDNStrue);
return mStatus_NoError;
case regState_Pending:
case regState_Refresh:
case regState_UpdatePending:
rr->uDNS_info.UpdateQueued = mDNStrue; return mStatus_NoError;
case regState_Registered:
SendRecordUpdate(m, rr, info);
return mStatus_NoError;
case regState_NoTarget:
LogMsg("Bad state (regState_NoTarget)"); return mStatus_UnknownErr; }
unreg_error:
LogMsg("Requested update of record %##s type %d, part of service not currently registered",
rr->resrec.name.c, rr->resrec.rrtype);
return mStatus_Invalid;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Periodic Execution Routines
#endif
mDNSlocal mDNSs32 CheckNATMappings(mDNS *m, mDNSs32 timenow)
{
NATTraversalInfo *ptr, *cur;
mDNSs32 nextevent;
ptr = m->uDNS_info.NATTraversals;
nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
while (ptr)
{
cur = ptr;
ptr = ptr->next;
if (cur->retry - timenow < 0)
{
if (cur->state == NATState_Established) RefreshNATMapping(cur, m);
else if (cur->state == NATState_Request || cur->state == NATState_Refresh)
{
if (cur->ntries >= NATMAP_MAX_TRIES) cur->ReceiveResponse(cur, m, mDNSNULL, 0); else SendNATMsg(cur, m);
}
}
else if (cur->retry - nextevent < 0) nextevent = cur->retry;
}
return nextevent;
}
mDNSlocal mDNSs32 CheckQueries(mDNS *m, mDNSs32 timenow)
{
DNSQuestion *q;
uDNS_GlobalInfo *u = &m->uDNS_info;
LLQ_Info *llq;
mDNSs32 sendtime;
mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
DNSMessage msg;
mStatus err;
mDNSu8 *end;
uDNS_QuestionInfo *info;
u->CurrentQuery = u->ActiveQueries;
while (u->CurrentQuery)
{
q = u->CurrentQuery;
info = &q->uDNS_info;
llq = info->llq;
if (!info->internal && ((!q->LongLived && !info->Answered) || (llq && llq->state < LLQ_Established)) &&
info->RestartTime + RESTART_GOODBYE_DELAY - timenow < 0)
{
while (info->knownAnswers)
{
CacheRecord *cr = info->knownAnswers;
info->knownAnswers = info->knownAnswers->next;
m->mDNS_reentrancy++; q->QuestionCallback(m, q, &cr->resrec, mDNSfalse);
m->mDNS_reentrancy--; ufree(cr);
if (q != u->CurrentQuery) { debugf("CheckQueries - question removed via callback."); break; }
}
}
if (q != u->CurrentQuery) continue;
if (q->LongLived && llq->state != LLQ_Poll)
{
if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Established)
{
if (llq->retry - timenow < 0)
{
if (!llq->retry)
LogMsg("ERROR: retry timer not set for LLQ %##s in state %d", q->qname.c, llq->state);
else if (llq->state == LLQ_Established || llq->state == LLQ_Refresh)
sendLLQRefresh(m, q, llq->origLease);
else if (llq->state == LLQ_InitialRequest)
startLLQHandshake(m, llq, mDNSfalse);
else if (llq->state == LLQ_SecondaryRequest)
sendChallengeResponse(m, q, mDNSNULL);
else if (llq->state == LLQ_Retry)
{ llq->ntries = 0; startLLQHandshake(m, llq, mDNSfalse); }
}
else if (llq->retry - nextevent < 0) nextevent = llq->retry;
}
}
else
{
sendtime = q->LastQTime + q->ThisQInterval;
if (sendtime - timenow < 0)
{
mDNSAddr server;
if (GetServerForName(&m->uDNS_info, &q->qname, &server))
{
err = constructQueryMsg(&msg, &end, q);
if (err) LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %##s", q->qname.c);
else
{
err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &server, UnicastDNSPort, -1, mDNSNULL);
if (err) { debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); } q->LastQTime = timenow;
if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2;
}
}
}
else if (sendtime - nextevent < 0) nextevent = sendtime;
}
u->CurrentQuery = u->CurrentQuery->next;
}
return nextevent;
}
mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m, mDNSs32 timenow)
{
AuthRecord *rr;
uDNS_RegInfo *rInfo;
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
for (rr = u->RecordRegistrations; rr; rr = rr->next)
{
rInfo = &rr->uDNS_info;
if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_UpdatePending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh)
{
if (rr->LastAPTime + rr->ThisAPInterval - timenow < 0)
{
#if MDNS_DEBUGMSGS
char *op = "(unknown operation)";
if (rInfo->state == regState_Pending) op = "registration";
else if (rInfo->state == regState_DeregPending) op = "deregistration";
else if (rInfo->state == regState_Refresh) op = "refresh";
debugf("Retransmit record %s %##s", op, rr->resrec.name.c);
#endif
if (rInfo->state == regState_DeregPending) SendRecordDeregistration(m, rr);
else if (rInfo->state == regState_UpdatePending) SendRecordUpdate(m, rr, rInfo);
else sendRecordRegistration(m, rr);
}
if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval;
}
if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0)
{
if (rInfo->expire - timenow < 0)
{
debugf("refreshing record %##s", rr->resrec.name.c);
rInfo->state = regState_Refresh;
sendRecordRegistration(m, rr);
}
if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire;
}
}
return nextevent;
}
mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m, mDNSs32 timenow)
{
ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations;
uDNS_RegInfo *rInfo;
mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
while (s)
{
ServiceRecordSet *srs = s;
s = s->next;
rInfo = &srs->uDNS_info;
if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh)
{
if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - timenow < 0)
{
#if MDNS_DEBUGMSGS
char *op = "unknown";
if (rInfo->state == regState_Pending) op = "registration";
else if (rInfo->state == regState_DeregPending) op = "deregistration";
else if (rInfo->state == regState_Refresh) op = "refresh";
debugf("Retransmit service %s %##s", op, srs->RR_SRV.resrec.name.c);
#endif
if (rInfo->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; }
else SendServiceRegistration (m, srs);
}
if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0)
nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval;
}
if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0)
{
if (rInfo->expire - timenow < 0)
{
debugf("refreshing service %##s", srs->RR_SRV.resrec.name.c);
rInfo->state = regState_Refresh;
SendServiceRegistration(m, srs);
}
if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire;
}
}
return nextevent;
}
mDNSexport void uDNS_Execute(mDNS *const m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSs32 nexte, timenow = mDNSPlatformTimeNow(m);
u->nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
nexte = CheckNATMappings(m, timenow);
if (nexte - u->nextevent < 0) u->nextevent = nexte;
nexte = CheckQueries(m, timenow);
if (nexte - u->nextevent < 0) u->nextevent = nexte;
nexte = CheckRecordRegistrations(m, timenow);
if (nexte - u->nextevent < 0) u->nextevent = nexte;
nexte = CheckServiceRegistrations(m, timenow);
if (nexte - u->nextevent < 0) u->nextevent = nexte;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Startup, Shutdown, and Sleep
#endif
mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive)
{
DNSQuestion *q;
LLQ_Info *llq;
for (q = m->uDNS_info.ActiveQueries; q; q = q->next)
{
llq = q->uDNS_info.llq;
if (q->LongLived && llq)
{
if (llq->state == LLQ_GetZoneInfo)
{
debugf("Marking %##s suspend-deferred", q->qname.c);
llq->state = LLQ_SuspendDeferred; }
else if (llq->state < LLQ_Suspended)
{
if (DeregisterActive && (llq->state == LLQ_Established || llq->state == LLQ_Refresh))
{ debugf("Deleting LLQ %##s", q->qname.c); sendLLQRefresh(m, q, 0); }
debugf("Marking %##s suspended", q->qname.c);
llq->state = LLQ_Suspended;
ubzero(llq->id, 8);
}
else if (llq->state == LLQ_Poll) { debugf("Marking %##s suspended-poll", q->qname.c); llq->state = LLQ_SuspendedPoll; }
if (llq->NATMap) llq->NATMap = mDNSfalse; }
}
CheckForUnreferencedLLQMapping(m);
}
mDNSlocal void RestartQueries(mDNS *m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSQuestion *q;
LLQ_Info *llqInfo;
mDNSs32 timenow = mDNSPlatformTimeNow(m);
u->CurrentQuery = u->ActiveQueries;
while (u->CurrentQuery)
{
q = u->CurrentQuery;
u->CurrentQuery = u->CurrentQuery->next;
llqInfo = q->uDNS_info.llq;
q->uDNS_info.RestartTime = timenow;
q->uDNS_info.Answered = mDNSfalse;
if (q->LongLived)
{
if (!llqInfo) { LogMsg("Error: RestartQueries - %##s long-lived with NULL info", q->qname.c); continue; }
if (llqInfo->state == LLQ_Suspended || llqInfo->state == LLQ_NatMapWait)
{
llqInfo->ntries = -1;
llqInfo->deriveRemovesOnResume = mDNStrue;
startLLQHandshake(m, llqInfo, mDNStrue); }
else if (llqInfo->state == LLQ_SuspendDeferred)
llqInfo->state = LLQ_GetZoneInfo; else if (llqInfo->state == LLQ_SuspendedPoll)
{
llqInfo->ntries = 0;
llqInfo->deriveRemovesOnResume = mDNStrue;
llqInfo->state = LLQ_GetZoneInfo;
startGetZoneData(&q->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, llqInfo);
}
}
else { q->LastQTime = timenow; q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } }
}
mDNSexport void mDNS_UpdateLLQs(mDNS *m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNS_Lock(m);
if (u->LLQNatInfo)
{
NATTraversalInfo *nat = u->LLQNatInfo;
u->LLQNatInfo = mDNSNULL;
DeleteNATPortMapping(m, nat, mDNSNULL);
}
SuspendLLQs(m, mDNStrue);
RestartQueries(m);
mDNS_Unlock(m);
}
mDNSlocal void SleepRecordRegistrations(mDNS *m)
{
DNSMessage msg;
AuthRecord *rr = m->uDNS_info.RecordRegistrations;
mDNSs32 timenow = mDNSPlatformTimeNow(m);
while (rr)
{
if (rr->uDNS_info.state == regState_Registered ||
rr->uDNS_info.state == regState_Refresh)
{
mDNSu8 *ptr = msg.data, *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), UpdateReqFlags);
ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; }
ptr = putDeletionRecord(&msg, ptr, &rr->resrec);
if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; }
mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForZone(&m->uDNS_info, &rr->uDNS_info.zone));
rr->uDNS_info.state = regState_Refresh;
rr->LastAPTime = timenow;
rr->ThisAPInterval = 300 * mDNSPlatformOneSecond;
}
rr = rr->next;
}
}
mDNSlocal void WakeRecordRegistrations(mDNS *m)
{
mDNSs32 timenow = mDNSPlatformTimeNow(m);
AuthRecord *rr = m->uDNS_info.RecordRegistrations;
while (rr)
{
if (rr->uDNS_info.state == regState_Refresh)
{
rr->LastAPTime = timenow;
rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
}
rr = rr->next;
}
}
mDNSlocal void SleepServiceRegistrations(mDNS *m)
{
mDNSs32 timenow = mDNSPlatformTimeNow(m);
ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations;
while(srs)
{
if (srs->uDNS_info.state == regState_Registered ||
srs->uDNS_info.state == regState_Refresh)
{
mDNSOpaque16 origid = srs->uDNS_info.id;
srs->uDNS_info.state = regState_DeregPending; SendServiceDeregistration(m, srs);
srs->uDNS_info.id = origid;
srs->uDNS_info.state = regState_Refresh;
srs->RR_SRV.LastAPTime = timenow;
srs->RR_SRV.ThisAPInterval = 300 * mDNSPlatformOneSecond;
}
srs = srs->next;
}
}
mDNSlocal void WakeServiceRegistrations(mDNS *m)
{
mDNSs32 timenow = mDNSPlatformTimeNow(m);
ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations;
while(srs)
{
if (srs->uDNS_info.state == regState_Refresh)
{
srs->RR_SRV.LastAPTime = timenow;
srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
}
srs = srs->next;
}
}
mDNSexport void uDNS_Init(mDNS *const m)
{
mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo));
m->uDNS_info.nextevent = m->timenow_last + 0x78000000;
}
mDNSexport void uDNS_Sleep(mDNS *m)
{
SuspendLLQs(m, mDNStrue);
SleepServiceRegistrations(m);
SleepRecordRegistrations(m);
}
mDNSexport void uDNS_Wake(mDNS *m)
{
RestartQueries(m);
WakeServiceRegistrations(m);
WakeRecordRegistrations(m);
}