#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 mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n);
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 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 = (mDNSu16)mDNSRandom(0xFFFF); randomized = mDNStrue; }
if (u->NextMessageID == 0) u->NextMessageID++;
return mDNSOpaque16fromIntVal(u->NextMessageID++);
}
mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr)
{
while (*list && *list != rr) list = &(*list)->next;
if (*list) { *list = rr->next; rr->next = mDNSNULL; return(mStatus_NoError); }
LogMsg("ERROR: unlinkAR - no such active record %##s", rr->resrec.name->c);
return(mStatus_NoSuchRecord);
}
mDNSlocal void unlinkSRS(mDNS *m, ServiceRecordSet *srs)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
ServiceRecordSet **p;
NATTraversalInfo *n = u->NATTraversals;
while (n)
{
if (n->reg.ServiceRegistration == srs)
{
NATTraversalInfo *tmp = n;
n = n->next;
LogMsg("ERROR: Unlinking service record set %##s still referenced by NAT traversal object!", srs->RR_SRV.resrec.name->c);
FreeNATInfo(m, tmp);
}
else n = n->next;
}
for (p = &u->ServiceRegistrations; *p; p = &(*p)->next)
if (*p == srs)
{
ExtraResourceRecord *e;
*p = srs->next;
srs->next = mDNSNULL;
for (e=srs->Extras; e; e=e->next)
if (unlinkAR(&u->RecordRegistrations, &e->r))
LogMsg("unlinkSRS: extra record %##s not found", e->r.resrec.name->c);
return;
}
LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list %##s", srs->RR_SRV.resrec.name->c);
}
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 SetRecordRetry(mDNS *const m, AuthRecord *rr, mStatus SendErr)
{
rr->LastAPTime = mDNSPlatformTimeNow(m);
if (SendErr == mStatus_TransientErr || rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
else if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) rr->ThisAPInterval *= 2;
else 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;
s->del = mDNSfalse;
s->teststate = DNSServer_Untested;
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 *GetAuthInfoForName(const uDNS_GlobalInfo *u, const domainname *name)
{
uDNS_AuthInfo *ptr;
while (name->c[0])
{
for (ptr = u->AuthInfoList; ptr; ptr = ptr->next)
if (SameDomainName(&ptr->zone, name)) return(ptr);
name = (const domainname *)(name->c + 1 + name->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 char *sharedSecret)
{
uDNS_AuthInfo *info;
mDNSu8 keybuf[1024];
mDNSs32 keylen;
uDNS_GlobalInfo *u = &m->uDNS_info;
mStatus status = mStatus_NoError;
mDNS_Lock(m);
if (GetAuthInfoForName(u, zone)) DeleteAuthInfoForZone(u, zone);
if (!key) goto exit;
info = (uDNS_AuthInfo*)umalloc(sizeof(*info));
if (!info) { LogMsg("ERROR: umalloc"); status = mStatus_NoMemoryErr; goto exit; }
ubzero(info, sizeof(*info));
AssignDomainName(&info->zone, zone);
AssignDomainName(&info->keyname, key);
keylen = DNSDigest_Base64ToBin(sharedSecret, keybuf, 1024);
if (keylen < 0)
{
LogMsg("ERROR: mDNS_SetSecretForZone - could not convert shared secret %s from base64", sharedSecret);
ufree(info);
status = mStatus_UnknownErr;
goto exit;
}
DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
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 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;
info->Router = u->Router;
return info;
}
mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n)
{
NATTraversalInfo *ptr, *prev = mDNSNULL;
ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations;
while (s)
{
if (s->uDNS_info.NATinfo == n)
{
LogMsg("Error: Freeing NAT info object still referenced by Service Record Set %##s!", s->RR_SRV.resrec.name->c);
s->uDNS_info.NATinfo = mDNSNULL;
}
s = s->next;
}
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)
{
const mDNSu8 *end = (mDNSu8 *)&info->request;
if (info->op == NATOp_AddrRequest) end += sizeof(NATAddrRequest);
else end += sizeof(NATPortMapRequest);
dst.type = u->Router.type;
dst.ip.v4 = u->Router.ip.v4;
dstport = mDNSOpaque16fromIntVal(NATMAP_PORT);
err = mDNSPlatformSendUDP(m, &info->request, end, 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 mDNSBool ReceiveNATAddrResponse(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len)
{
mStatus err = mStatus_NoError;
AuthRecord *rr = mDNSNULL;
NATAddrReply *response = (NATAddrReply *)pkt;
mDNSAddr addr;
if (n->state != NATState_Request)
{
LogMsg("ReceiveNATAddrResponse: bad state %d", n->state);
return mDNSfalse;
}
rr = n->reg.RecordRegistration;
if (!rr)
{
LogMsg("ReceiveNATAddrResponse: registration cancelled");
return mDNSfalse;
}
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 < sizeof(*response))
{
LogMsg("ReceiveNATAddrResponse: response too short (%d bytes)", len);
return mDNSfalse;
}
if (response->vers != NATMAP_VERS)
{
LogMsg("ReceiveNATAddrResponse: received version %d (expect version %d)", pkt[0], NATMAP_VERS);
return mDNSfalse;
}
if (response->opcode != (NATOp_AddrRequest | NATMAP_RESPONSE_MASK))
{
LogMsg("ReceiveNATAddrResponse: bad response code %d", response->opcode);
return mDNSfalse;
}
if (response->err.NotAnInteger)
{ LogMsg("ReceiveAddrResponse: received error %d", mDNSVal16(response->err)); err = mStatus_NATTraversal; goto end; }
addr.ip.v4 = response->PubAddr;
n->state = NATState_Established;
}
if (IsPrivateV4Addr(&addr))
{
LogMsg("ReceiveNATAddrResponse: Double NAT");
err = mStatus_DoubleNAT;
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 mDNStrue;
}
else LogOperation("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);
return mDNStrue;
}
mDNSlocal void StartGetPublicAddr(mDNS *m, AuthRecord *AddressRec)
{
NATAddrRequest *req;
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *info = AllocNATInfo(m, NATOp_AddrRequest, ReceiveNATAddrResponse);
if (!info) { uDNS_RegisterRecord(m, AddressRec); return; }
AddressRec->uDNS_info.NATinfo = info;
info->reg.RecordRegistration = AddressRec;
info->state = NATState_Request;
req = &info->request.AddrReq;
req->vers = NATMAP_VERS;
req->opcode = NATOp_AddrRequest;
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 mDNSBool ReceivePortMapReply(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len)
{
ServiceRecordSet *srs = n->reg.ServiceRegistration;
mDNSIPPort priv = srs ? srs->RR_SRV.resrec.rdata->u.srv.port : m->UnicastPort4;
mDNSu32 lease;
mDNSBool deletion = !n->request.PortReq.lease.NotAnInteger;
NATPortMapReply *reply = (NATPortMapReply *)pkt;
mDNSu8 *service = srs ? srs->RR_SRV.resrec.name->c : (mDNSu8 *)"\016LLQ event port";
if (n->state != NATState_Request && n->state != NATState_Refresh)
{ LogMsg("ReceivePortMapReply (%##s): bad state %d", service, n->state); return mDNSfalse; }
if (!pkt && !deletion) {
#ifdef _LEGACY_NAT_TRAVERSAL_
mDNSIPPort pub;
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 < sizeof(*reply)) { LogMsg("ReceivePortMapReply: response too short (%d bytes)", len); return mDNSfalse; }
if (reply->vers != NATMAP_VERS) { LogMsg("ReceivePortMapReply: received version %d (expect version %d)", pkt[0], NATMAP_VERS); return mDNSfalse; }
if (reply->opcode != (n->op | NATMAP_RESPONSE_MASK)) { LogMsg("ReceivePortMapReply: bad response code %d", pkt[1]); return mDNSfalse; }
if (reply->err.NotAnInteger) { LogMsg("ReceivePortMapReply: received error %d", mDNSVal16(reply->err)); return mDNSfalse; }
if (priv.NotAnInteger != reply->priv.NotAnInteger) return mDNSfalse;
if (!srs && n != m->uDNS_info.LLQNatInfo)
{
LogMsg("ReceivePortMapReply: registration cancelled"); FreeNATInfo(m, n);
return mDNStrue;
}
if (deletion) { n->state = NATState_Deleted; return mDNStrue; }
lease = (mDNSu32)mDNSVal32(reply->lease);
if (lease > 0x70000000UL / mDNSPlatformOneSecond) lease = 0x70000000UL / mDNSPlatformOneSecond;
if (n->state == NATState_Refresh && reply->pub.NotAnInteger != n->PublicPort.NotAnInteger)
LogMsg("ReceivePortMapReply: NAT refresh changed public port from %d to %d", mDNSVal16(n->PublicPort), mDNSVal16(reply->pub));
n->PublicPort = reply->pub;
if (reply->pub.NotAnInteger != n->request.PortReq.pub.NotAnInteger) n->request.PortReq.pub = reply->pub;
n->retry = mDNSPlatformTimeNow(m) + ((mDNSs32)lease * mDNSPlatformOneSecond / 2);
if (n->state == NATState_Refresh) { n->state = NATState_Established; return mDNStrue; }
n->state = NATState_Established;
end:
if (n->state != NATState_Established && n->state != NATState_Legacy)
{
LogMsg("NAT Port Mapping (%##s): timeout", service);
if (pkt) LogMsg("!!! timeout with non-null packet");
n->state = NATState_Error;
if (srs)
{
uDNS_HostnameInfo *hi = m->uDNS_info.Hostnames;
while (hi)
{
if (hi->arv6 && (hi->arv6->uDNS_info.state == regState_Registered || hi->arv6->uDNS_info.state == regState_Refresh)) break;
else hi = hi->next;
}
if (hi)
{
debugf("Port map failed for service %##s - using IPv6 service target", service);
srs->uDNS_info.NATinfo = mDNSNULL;
FreeNATInfo(m, n);
goto register_service;
}
else srs->uDNS_info.state = regState_NATError;
}
else LLQNatMapComplete(m);
return mDNStrue;
}
else LogOperation("Mapped private port %d to public port %d", mDNSVal16(priv), mDNSVal16(n->PublicPort));
if (!srs) { LLQNatMapComplete(m); return mDNStrue; }
register_service:
if (srs->uDNS_info.ns.ip.v4.NotAnInteger) SendServiceRegistration(m, srs); else
{
srs->uDNS_info.state = regState_FetchingZoneData;
startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
}
return mDNStrue;
}
mDNSlocal void FormatPortMaprequest(NATTraversalInfo *info, mDNSIPPort port)
{
NATPortMapRequest *req = &info->request.PortReq;
req->vers = NATMAP_VERS;
req->opcode = info->op;
req->unused.NotAnInteger = 0;
req->priv = port;
req->pub = port;
req->lease = mDNSOpaque32fromIntVal(NATMAP_DEFAULT_LEASE);
}
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; }
if (srs->uDNS_info.NATinfo) { LogMsg("Error: StartNATPortMap - NAT info already initialized!"); FreeNATInfo(m, srs->uDNS_info.NATinfo); }
info = AllocNATInfo(m, op, ReceivePortMapReply);
srs->uDNS_info.NATinfo = info;
info->reg.ServiceRegistration = srs;
info->state = NATState_Request;
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.PortReq.lease.NotAnInteger = 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;
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->arv4 && (hi->arv4->uDNS_info.state == regState_Registered || hi->arv4->uDNS_info.state == regState_Refresh))
{
AssignDomainName(dst, hi->arv4->resrec.name);
return mDNStrue;
}
if (hi->arv6 && (hi->arv6->uDNS_info.state == regState_Registered || hi->arv6->uDNS_info.state == regState_Refresh))
{
AssignDomainName(dst, hi->arv4->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);
mDNSBool TargetChanged = (HaveTarget && srs->uDNS_info.state == regState_NoTarget) || (curtarget->c[0] && !HaveTarget) || !SameDomainName(curtarget, &newtarget);
mDNSBool HaveZoneData = srs->uDNS_info.ns.ip.v4.NotAnInteger ? mDNStrue : mDNSfalse;
NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
mDNSIPPort port = srs->RR_SRV.resrec.rdata->u.srv.port;
mDNSBool NATChanged = mDNSfalse;
mDNSBool NowBehindNAT = port.NotAnInteger && IsPrivateV4Addr(&u->AdvertisedV4);
mDNSBool WereBehindNAT = nat != mDNSNULL;
mDNSBool NATRouterChanged = nat && nat->Router.ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger;
mDNSBool PortWasMapped = nat && (nat->state == NATState_Established || nat->state == NATState_Legacy) && nat->PublicPort.NotAnInteger != port.NotAnInteger;
if (WereBehindNAT && NowBehindNAT && NATRouterChanged) NATChanged = mDNStrue;
else if (!NowBehindNAT && PortWasMapped) NATChanged = mDNStrue;
else if (!WereBehindNAT && NowBehindNAT) NATChanged = mDNStrue;
if (!TargetChanged && !NATChanged) return;
debugf("UpdateSRV (%##s) HadZoneData=%d, TargetChanged=%d, HaveTarget=%d, NowBehindNAT=%d, WereBehindNAT=%d, NATRouterChanged=%d, PortWasMapped=%d",
srs->RR_SRV.resrec.name->c, HaveZoneData, TargetChanged, HaveTarget, NowBehindNAT, WereBehindNAT, NATRouterChanged, PortWasMapped);
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_NATError:
if (!NATChanged) return;
case regState_NoTarget:
if (HaveTarget)
{
debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowBehindNAT ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c);
if (!HaveZoneData)
{
srs->uDNS_info.state = regState_FetchingZoneData;
startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
}
else
{
if (nat && (NATChanged || !NowBehindNAT)) { srs->uDNS_info.NATinfo = mDNSNULL; FreeNATInfo(m, nat); }
if (NATChanged && NowBehindNAT) { srs->uDNS_info.state = regState_NATMap; StartNATPortMap(m, srs); }
else SendServiceRegistration(m, srs);
}
}
return;
case regState_Registered:
debugf("UpdateSRV: SRV record changed for service %##s - deregistering (will re-register with new SRV)", srs->RR_SRV.resrec.name->c);
for (e = srs->Extras; e; e = e->next) e->r.uDNS_info.state = regState_ExtraQueued; srs->uDNS_info.SRVChanged = 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;
if (result == mStatus_MemFree)
{
if (hi)
{
if (hi->arv4 == rr) hi->arv4 = mDNSNULL;
else if (hi->arv4 == rr) hi->arv6 = mDNSNULL;
rr->RecordContext = mDNSNULL;
if (!hi->arv4 && !hi->arv6) ufree(hi); }
ufree(rr);
return;
}
if (result)
{
if (rr->resrec.rrtype == kDNSType_A)
LogMsg("HostnameCallback: Error %ld for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4);
else
LogMsg("HostnameCallback: Error %ld for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6);
if (!hi) { ufree(rr); return; }
if (rr->uDNS_info.state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!");
if ((!hi->arv4 || hi->arv4->uDNS_info.state == regState_Unregistered) &&
(!hi->arv6 || hi->arv6->uDNS_info.state == regState_Unregistered))
{
rr->RecordContext = (void *)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; }
if (rr->resrec.rrtype == kDNSType_A)
LogMsg("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4);
else
LogMsg("Registered hostname %##s IP %.16a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv6);
rr->RecordContext = (void *)hi->StatusContext;
if (hi->StatusCallback)
hi->StatusCallback(m, rr, result); rr->RecordContext = (void *)hi;
}
mDNSlocal void AdvertiseHostname(mDNS *m, uDNS_HostnameInfo *h)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
if (u->AdvertisedV4.ip.v4.NotAnInteger && h->arv4->uDNS_info.state == regState_Unregistered)
{
if (IsPrivateV4Addr(&u->AdvertisedV4))
StartGetPublicAddr(m, h->arv4);
else
{
LogMsg("Advertising %##s IP %.4a", h->arv4->resrec.name->c, &u->AdvertisedV4.ip.v4);
uDNS_RegisterRecord(m, h->arv4);
}
}
if (u->AdvertisedV6.ip.v6.b[0] && h->arv6->uDNS_info.state == regState_Unregistered)
{
LogMsg("Advertising %##s IP %.16a", h->arv4->resrec.name->c, &u->AdvertisedV6.ip.v6);
uDNS_RegisterRecord(m, h->arv6);
}
}
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;
uDNS_HostnameInfo *h = m->uDNS_info.Hostnames;
(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);
while (h)
{
if ((h->arv4 && (h->arv4->uDNS_info.state == regState_FetchingZoneData || h->arv4->uDNS_info.state == regState_Pending || h->arv4->uDNS_info.state == regState_NATMap)) ||
(h->arv6 && (h->arv6->uDNS_info.state == regState_FetchingZoneData || h->arv6->uDNS_info.state == regState_Pending)))
{
m->uDNS_info.DelaySRVUpdate = mDNStrue;
m->uDNS_info.NextSRVUpdate = mDNSPlatformTimeNow(m) + (5 * mDNSPlatformOneSecond);
return;
}
h = h->next;
}
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.AdvertisedV4.ip.v4.b;
mStatus err;
if (m->uDNS_info.ReverseMapActive)
{
uDNS_StopQuery(m, q);
m->uDNS_info.ReverseMapActive = mDNSfalse;
}
m->uDNS_info.StaticHostname.c[0] = 0;
if (!m->uDNS_info.AdvertisedV4.ip.v4.NotAnInteger) return;
ubzero(q, sizeof(*q));
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 AssignHostnameInfoAuthRecord(mDNS *m, uDNS_HostnameInfo *hi, int type)
{
AuthRecord **dst = (type == mDNSAddrType_IPv4 ? &hi->arv4 : &hi->arv6);
AuthRecord *ar = umalloc(sizeof(*ar));
uDNS_GlobalInfo *u = &m->uDNS_info;
if (type != mDNSAddrType_IPv4 && type != mDNSAddrType_IPv6) { LogMsg("ERROR: AssignHostnameInfoAuthRecord - bad type %d", type); return; }
if (!ar) { LogMsg("ERROR: AssignHostnameInfoAuthRecord - malloc"); return; }
mDNS_SetupResourceRecord(ar, mDNSNULL, 0, type == mDNSAddrType_IPv4 ? kDNSType_A : kDNSType_AAAA, 1, kDNSRecordTypeKnownUnique, HostnameCallback, hi);
AssignDomainName(ar->resrec.name, &hi->fqdn);
if (type == mDNSAddrType_IPv4 && u->AdvertisedV4.ip.v4.NotAnInteger)
{
if (u->MappedV4.ip.v4.NotAnInteger) ar->resrec.rdata->u.ipv4 = u->MappedV4.ip.v4;
else ar->resrec.rdata->u.ipv4 = u->AdvertisedV4.ip.v4;
}
else if (type == mDNSAddrType_IPv6 && u->AdvertisedV6.ip.v6.b[0])
{
ar->resrec.rdata->u.ipv6 = u->AdvertisedV6.ip.v6;
}
ar->uDNS_info.state = regState_Unregistered;
if (*dst)
{
LogMsg("ERROR: AssignHostnameInfoAuthRecord - overwriting %s AuthRec", type == mDNSAddrType_IPv4 ? "IPv4" : "IPv6");
unlinkAR(&u->RecordRegistrations, *dst);
(*dst)->RecordContext = mDNSNULL; }
*dst = ar;
}
mDNSlocal void UpdateHostnameRegistrations(mDNS *m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
uDNS_HostnameInfo *i;
for (i = u->Hostnames; i; i = i->next)
{
if (i->arv4 && i->arv4->uDNS_info.state != regState_Unregistered &&
i->arv4->resrec.rdata->u.ipv4.NotAnInteger != u->AdvertisedV4.ip.v4.NotAnInteger &&
i->arv4->resrec.rdata->u.ipv4.NotAnInteger !=u->MappedV4.ip.v4.NotAnInteger)
{
uDNS_DeregisterRecord(m, i->arv4);
i->arv4 = mDNSNULL;
}
if (i->arv6 && !mDNSPlatformMemSame(i->arv6->resrec.rdata->u.ipv6.b, u->AdvertisedV6.ip.v6.b, 16) && i->arv6->uDNS_info.state != regState_Unregistered)
{
uDNS_DeregisterRecord(m, i->arv6);
i->arv6 = mDNSNULL;
}
if (!i->arv4 && u->AdvertisedV4.ip.v4.NotAnInteger) AssignHostnameInfoAuthRecord(m, i, mDNSAddrType_IPv4);
else if (i->arv4 && i->arv4->uDNS_info.state == regState_Unregistered) i->arv4->resrec.rdata->u.ipv4 = u->AdvertisedV4.ip.v4; if (!i->arv6 && u->AdvertisedV6.ip.v6.b[0]) AssignHostnameInfoAuthRecord(m, i, mDNSAddrType_IPv6);
else if (i->arv6 &&i->arv6->uDNS_info.state == regState_Unregistered) i->arv6->resrec.rdata->u.ipv6 = u->AdvertisedV6.ip.v6;
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->fqdn))
{ LogMsg("Host Domain %##s already in list", fqdn->c); goto exit; }
}
new = umalloc(sizeof(*new));
if (!new) { LogMsg("ERROR: mDNS_AddDynDNSHostname - malloc"); goto exit; }
ubzero(new, sizeof(*new));
new->next = u->Hostnames;
u->Hostnames = new;
AssignDomainName(&new->fqdn, fqdn);
new->StatusCallback = StatusCallback;
new->StatusContext = StatusContext;
if (u->AdvertisedV4.ip.v4.NotAnInteger) AssignHostnameInfoAuthRecord(m, new, mDNSAddrType_IPv4);
else new->arv4 = mDNSNULL;
if (u->AdvertisedV6.ip.v6.b[0]) AssignHostnameInfoAuthRecord(m, new, mDNSAddrType_IPv6);
else new->arv6 = mDNSNULL;
if (u->AdvertisedV6.ip.v6.b[0] || u->AdvertisedV4.ip.v4.NotAnInteger) AdvertiseHostname(m, new);
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)->fqdn)) ptr = &(*ptr)->next;
if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c);
else
{
uDNS_HostnameInfo *hi = *ptr;
*ptr = (*ptr)->next; if (hi->arv4)
{
hi->arv4->RecordContext = mDNSNULL; if (hi->arv4->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->arv4);
else { ufree(hi->arv4); hi->arv4 = mDNSNULL; }
}
if (hi->arv6)
{
hi->arv6->RecordContext = mDNSNULL; if (hi->arv6->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->arv6);
else { ufree(hi->arv6); hi->arv6 = mDNSNULL; }
}
ufree(hi);
}
UpdateSRVRecords(m);
mDNS_Unlock(m);
}
mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
mDNSBool v4Changed, v6Changed, RouterChanged;
if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo V4 address - incorrect type. Discarding."); return; }
if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo V6 address - incorrect type. Discarding."); return; }
if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 router. Discarding."); return; }
mDNS_Lock(m);
v4Changed = (v4addr ? v4addr->ip.v4.NotAnInteger : 0) != u->AdvertisedV4.ip.v4.NotAnInteger;
v6Changed = v6addr ? !mDNSPlatformMemSame(v6addr, &u->AdvertisedV6, sizeof(*v6addr)) : (u->AdvertisedV6.ip.v6.b[0] != 0);
RouterChanged = (router ? router->ip.v4.NotAnInteger : 0) != u->Router.ip.v4.NotAnInteger;
#if MDNS_DEBUGMSGS
if (v4addr && (v4Changed || RouterChanged))
LogMsg("mDNS_SetPrimaryInterfaceInfo: address changed from %d.%d.%d.%d to %d.%d.%d.%d:%d",
u->AdvertisedV4.ip.v4.b[0], u->AdvertisedV4.ip.v4.b[1], u->AdvertisedV4.ip.v4.b[2], u->AdvertisedV4.ip.v4.b[3],
v4addr->ip.v4.b[0], v4addr->ip.v4.b[1], v4addr->ip.v4.b[2], v4addr->ip.v4.b[3]);
#endif // MDNS_DEBUGMSGS
if ((v4Changed || RouterChanged) && u->MappedV4.ip.v4.NotAnInteger) u->MappedV4.ip.v4.NotAnInteger = 0;
if (v4addr) u->AdvertisedV4 = *v4addr; else u->AdvertisedV4.ip.v4.NotAnInteger = 0;
if (v6addr) u->AdvertisedV6 = *v6addr; else ubzero(u->AdvertisedV6.ip.v6.b, 16);
if (router) u->Router = *router; else u->Router.ip.v4.NotAnInteger = 0;
if ((v4Changed || RouterChanged || v6Changed) && v4addr)
{
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->resrec.name = &question->qname;
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;
origname.c[0] = 0;
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 %X (%s) %##s unexpected answer %##s %X (%s)",
question->qname.c, question->qnamehash, DNSTypeName(question->qtype), origname.c,
cr->resrec.name->c, cr->resrec.namehash, DNSTypeName(cr->resrec.rrtype));
}
if (!llq || llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume)
{
deriveGoodbyes(m, msg, end,question);
if (llq && llqInfo->deriveRemovesOnResume) llqInfo->deriveRemovesOnResume = mDNSfalse;
}
if (llq && llqInfo->state == LLQ_Poll && llqInfo->servPort.NotAnInteger) question->ThisQInterval = LLQ_POLL_INTERVAL;
else 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", displayname->c, 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)
{
debugf("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;
uDNS_RegInfo *info = &srs->uDNS_info;
NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
ExtraResourceRecord **e = &srs->Extras;
AuthRecord *txt = &srs->RR_TXT;
uDNS_RegInfo *txtInfo = &txt->uDNS_info;
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;
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->SRVChanged)
{
info->state = regState_NoTarget; break;
}
err = mStatus_MemFree;
InvokeCallback = mDNStrue;
if (nat)
{
if (nat->state == NATState_Deleted) { info->NATinfo = mDNSNULL; FreeNATInfo(m, nat); } 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);
err = mStatus_MemFree;
InvokeCallback = mDNStrue;
info->state = regState_Unregistered;
break;
}
else
{
debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name->c);
info->state = regState_Registered;
SendServiceDeregistration(m, srs);
return;
}
case regState_UpdatePending:
if (err)
{
LogMsg("hndlServiceUpdateReply: error updating TXT record for service %##s", srs->RR_SRV.resrec.name->c);
info->state = regState_Unregistered;
InvokeCallback = mDNStrue;
}
else
{
info->state = regState_Registered;
if (txtInfo->UpdateRDCallback) txtInfo->UpdateRDCallback(m, txt, txtInfo->OrigRData);
SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen);
txtInfo->OrigRData = mDNSNULL;
txtInfo->InFlightRData = mDNSNULL;
}
break;
case regState_FetchingZoneData:
case regState_Registered:
case regState_Cancelled:
case regState_Unregistered:
case regState_NATMap:
case regState_NoTarget:
case regState_ExtraQueued:
case regState_NATError:
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->SRVChanged || info->SRVUpdateDeferred) && (info->state == regState_NoTarget || info->state == regState_Registered))
{
if (InvokeCallback)
{
info->ClientCallbackDeferred = mDNStrue;
info->DeferredStatus = err;
}
info->SRVChanged = mDNSfalse;
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 (info->state == regState_Unregistered) unlinkSRS(m, srs);
else if (txtInfo->QueuedRData && info->state == regState_Registered)
{
if (InvokeCallback)
{
info->ClientCallbackDeferred = mDNStrue;
info->DeferredStatus = err;
}
info->state = regState_UpdatePending;
txtInfo->InFlightRData = txtInfo->QueuedRData;
txtInfo->InFlightRDLen = txtInfo->QueuedRDLen;
info->OrigRData = txt->resrec.rdata;
info->OrigRDLen = txt->resrec.rdlength;
txtInfo->QueuedRData = mDNSNULL;
SendServiceRegistration(m, srs);
return;
}
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_RegInfo *info = &rr->uDNS_info;
mDNSBool InvokeCallback = mDNStrue;
if (info->state == regState_UpdatePending)
{
if (err)
{
LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
info->state = regState_Unregistered;
}
else
{
debugf("Update record %##s - success", rr->resrec.name->c);
info->state = regState_Registered;
if (info->UpdateRDCallback) info->UpdateRDCallback(m, rr, info->OrigRData);
SetNewRData(&rr->resrec, info->InFlightRData, info->InFlightRDLen);
info->OrigRData = mDNSNULL;
info->InFlightRData = mDNSNULL;
}
}
if (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;
info->state = regState_Unregistered;
}
if (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);
info->state = regState_Unregistered;
}
debugf("Calling deferred deregistration of record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype);
info->state = regState_Registered;
uDNS_DeregisterRecord(m, rr);
return;
}
if (info->state == regState_Pending || info->state == regState_Refresh)
{
if (!err)
{
info->state = regState_Registered;
if (info->state == regState_Refresh) InvokeCallback = mDNSfalse;
}
else
{
if (info->lease && err == mStatus_UnknownErr)
{
LogMsg("Re-trying update of record %##s without lease option", rr->resrec.name->c);
info->lease = mDNSfalse;
sendRecordRegistration(m, rr);
return;
}
LogMsg("Registration of record %##s type %d failed with error %ld", rr->resrec.name->c, rr->resrec.rrtype, err);
info->state = regState_Unregistered;
}
}
if (info->state == regState_Unregistered) unlinkAR(&m->uDNS_info.RecordRegistrations, rr);
else rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1;
if (info->QueuedRData && info->state == regState_Registered)
{
info->state = regState_UpdatePending;
info->InFlightRData = info->QueuedRData;
info->InFlightRDLen = info->QueuedRDLen;
info->OrigRData = rr->resrec.rdata;
info->OrigRDLen = rr->resrec.rdlength;
info->QueuedRData = mDNSNULL;
sendRecordRegistration(m, rr);
return;
}
if (InvokeCallback)
{
m->mDNS_reentrancy++; if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
m->mDNS_reentrancy--; }
}
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_RDLEN) 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->lease = mDNSfalse;
}
mDNSexport void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
NATTraversalInfo *ptr = u->NATTraversals;
NATOp_t op;
if (len < sizeof(NATPortMapReply) && len < sizeof(NATAddrReply)) { 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; }
while (ptr)
{
if ((ptr->state == NATState_Request || ptr->state == NATState_Refresh) && (ptr->op | NATMAP_RESPONSE_MASK) == op)
if (ptr->ReceiveResponse(ptr, m, pkt, len)) break; ptr = ptr->next;
}
}
mDNSlocal const domainname *DNSRelayTestQuestion = (domainname*)
"\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest"
"\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa";
mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
const mDNSAddr *const srcaddr, const mDNSInterfaceID InterfaceID)
{
const mDNSu8 *ptr = msg->data;
DNSQuestion q;
DNSServer *s;
mDNSu32 result = 0;
mDNSBool found = mDNSfalse;
if (msg->h.numQuestions != 1) return(mDNSfalse);
ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
if (!ptr) return(mDNSfalse);
if (q.qtype != kDNSType_PTR || q.qclass != kDNSClass_IN) return(mDNSfalse);
if (!SameDomainName(&q.qname, DNSRelayTestQuestion)) return(mDNSfalse);
if ((msg->h.flags.b[1] & kDNSFlag1_RC) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0)
result = DNSServer_Failed;
else
result = DNSServer_Passed;
for (s = m->uDNS_info.Servers; s; s = s->next)
if (mDNSSameAddress(srcaddr, &s->addr) && s->teststate != result)
{ s->teststate = result; found = mDNStrue; }
if (found && result == DNSServer_Failed)
LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a.", srcaddr);
return(mDNStrue); }
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;
if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, 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)
{ debugf("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 DNSServer *GetServerForName(uDNS_GlobalInfo *u, const domainname *name)
{
DNSServer *curmatch = mDNSNULL, *p = u->Servers;
int i, curmatchlen = -1;
int ncount = name ? CountLabels(name) : 0;
while (p)
{
int 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;
}
return(curmatch);
}
#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)
{
ubzero(msg, sizeof(msg));
InitializeDNSMessage(&msg->h, question->uDNS_info.id, uQueryFlags);
}
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));
mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
opt->rdlength = LLQ_OPT_RDLEN;
opt->rdestimate = LLQ_OPT_RDLEN;
optRD = &rr.resrec.rdata->u.opt;
optRD->opt = kDNSOpt_LLQ;
optRD->optlen = LLQ_OPTLEN;
umemcpy(&optRD->OptData.llq, data, sizeof(*data));
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;
ubzero(&lcr, sizeof(lcr));
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_RDLEN) return mDNSfalse; umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(*llq)), sizeof(*llq));
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) debugf("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 (!q->uDNS_info.llq) { LogMsg("Error: recvLLQEvent - question object does not contain LLQ metadata"); return mDNSfalse; }
if (!sameID(opt.id, q->uDNS_info.llq->id)) { return mDNSfalse; }
if (opt.llqOp != kLLQOp_Event) { if (!q->uDNS_info.llq->ntries) LogMsg("recvLLQEvent - Bad LLQ Opcode %d", opt.llqOp); 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 = putLLQ(&ack, ack.data, mDNSNULL, &opt, mDNSfalse);
if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putLLQ"); return mDNSfalse; }
err = mDNSSendDNSMessage(m, &ack, ackEnd, mDNSInterface_Any, srcaddr, srcport, -1, mDNSNULL);
if (err) debugf("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) debugf("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)
debugf("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->AdvertisedV4))
{
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) debugf("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 %##s invoked with error code %ld", info->question->qname.c, 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"); info->servPort.NotAnInteger = 0; goto poll; }
info->servAddr = zoneInfo->primaryAddr;
info->servPort = zoneInfo->llqPort;
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;
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);
question->ThisQInterval = INIT_UCAST_POLL_INTERVAL / 2;
question->LastQTime = mDNSPlatformTimeNow(m) - question->ThisQInterval;
question->uDNS_info.internal = internal;
LinkActiveQuestion(u, question);
question->uDNS_info.knownAnswers = mDNSNULL;
LogOperation("uDNS startQuery: %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
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 = context->addr;
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; }
}
debugf("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 = rr->rdata->u.ipv4;
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 = rr->rdata->u.ipv4;
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 = lcr.r.resrec.rdata->u.srv.port;
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) { debugf("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %ld", err); goto error; }
}
else
{
if (!info->nread)
{
mDNSu8 lenbuf[2];
n = mDNSPlatformReadTCP(sd, lenbuf, 2);
if (n != 2)
{
LogMsg("ERROR:conQueryCallback - attempt to read message length failed (read returned %d)", n);
goto error;
}
info->replylen = (mDNSu16)((mDNSu16)lenbuf[0] << 8 | lenbuf[1]);
if (info->replylen < sizeof(DNSMessageHeader))
{ LogMsg("ERROR: conQueryCallback - length too short (%d bytes)", info->replylen); goto error; }
info->reply = umalloc(info->replylen);
if (!info->reply) { LogMsg("ERROR: conQueryCallback - malloc failed"); 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)
{
DNSMessage *msg = info->reply;
mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
uDNS_ReceiveMsg(m, msg, (mDNSu8 *)msg + info->replylen, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, question->InterfaceID);
mDNSPlatformTCPCloseConnection(sd);
ufree(info->reply);
ufree(info);
}
}
mDNS_Unlock(m);
return;
error:
mDNSPlatformTCPCloseConnection(sd);
if (info->reply) ufree(info->reply);
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 = id;
ptr = putZone(&msg, ptr, end, ®Info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) goto error;
if (regInfo->state == regState_UpdatePending)
{
SetNewRData(&rr->resrec, regInfo->OrigRData, regInfo->OrigRDLen);
if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error;
SetNewRData(&rr->resrec, regInfo->InFlightRData, regInfo->InFlightRDLen);
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl))) goto error;
}
else
{
if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
{
ptr = putDeleteRRSet(&msg, ptr, rr->resrec.name, rr->resrec.rrtype);
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, DEFAULT_UPDATE_LEASE); if (!ptr) goto error; }
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, ®Info->ns, regInfo->port, -1, GetAuthInfoForName(u, rr->resrec.name));
if (err) debugf("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr, err);
if (regInfo->state != regState_Refresh && regInfo->state != regState_DeregDeferred && regInfo->state != regState_UpdatePending)
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;
}
if (zoneData->zoneName.c[0] == 0)
{
LogMsg("ERROR: Only name server claiming responsibility for \"%##s\" is \"%##s\"!",
newRR->resrec.name->c, zoneData->zoneName.c);
err = mStatus_NoSuchNameErr;
goto error;
}
AssignDomainName(&newRR->uDNS_info.zone, &zoneData->zoneName);
newRR->uDNS_info.ns = zoneData->primaryAddr;
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;
mDNSu32 i;
privport = zeroIPPort;
if (!rInfo->ns.ip.v4.NotAnInteger) { LogMsg("SendServiceRegistration - NS not set!"); return; }
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 = putDeleteRRSet(&msg, ptr, srs->RR_TXT.resrec.name, srs->RR_TXT.resrec.rrtype))) goto error;
}
else if (srs->uDNS_info.state != regState_Refresh && srs->uDNS_info.state != regState_UpdatePending)
{
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;
for (i = 0; i < srs->NumSubTypes; i++)
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->SubTypes[i].resrec, srs->SubTypes[i].resrec.rroriginalttl))) goto error;
if (rInfo->state == regState_UpdatePending) {
AuthRecord *txt = &srs->RR_TXT;
uDNS_RegInfo *txtInfo = &txt->uDNS_info;
SetNewRData(&txt->resrec, txtInfo->OrigRData, txtInfo->OrigRDLen);
if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_TXT.resrec))) goto error;
SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen);
if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) goto error;
}
else
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, DEFAULT_UPDATE_LEASE); if (!ptr) goto error; }
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rInfo->ns, rInfo->port, -1, GetAuthInfoForName(u, srs->RR_SRV.resrec.name));
if (err) debugf("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err);
if (rInfo->state != regState_Refresh && rInfo->state != regState_DeregDeferred && srs->uDNS_info.state != regState_UpdatePending)
rInfo->state = regState_Pending;
SetRecordRetry(m, &srs->RR_SRV, err);
rInfo->id = id;
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(m, 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;
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(m, 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;
}
if (srs->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger && IsPrivateV4Addr(&m->uDNS_info.AdvertisedV4))
{ srs->uDNS_info.state = regState_NATMap; StartNATPortMap(m, srs); }
else SendServiceRegistration(m, srs);
return;
error:
unlinkSRS(m, 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 = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
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, GetAuthInfoForName(u, rr->resrec.name));
if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr, err);
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:
rr->uDNS_info.NATinfo = mDNSNULL;
if (!n) LogMsg("uDNS_DeregisterRecord: no NAT info context");
else FreeNATInfo(m, n); 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_NATError:
case regState_NoTarget:
LogMsg("ERROR: uDNS_DeregisterRecord called for record %##s with bad state %s", rr->resrec.name->c, rr->uDNS_info.state == regState_NoTarget ? "regState_NoTarget" : "regState_NATError");
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;
}
rr->uDNS_info.NATinfo = mDNSNULL;
if (n) FreeNATInfo(m, n);
SendRecordDeregistration(m, rr);
return mStatus_NoError;
}
mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs)
{
mDNSu32 i;
domainname target;
uDNS_RegInfo *info = &srs->uDNS_info;
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(info, sizeof(*info));
*p = srs;
srs->next = mDNSNULL;
srs->RR_SRV.resrec.rroriginalttl = kWideAreaTTL;
srs->RR_TXT.resrec.rroriginalttl = kWideAreaTTL;
srs->RR_PTR.resrec.rroriginalttl = kWideAreaTTL;
for (i = 0; i < srs->NumSubTypes;i++) srs->SubTypes[i].resrec.rroriginalttl = kWideAreaTTL;
info->lease = mDNStrue;
srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0;
if (!GetServiceTarget(&m->uDNS_info, &srs->RR_SRV, &target))
{
debugf("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c);
info->state = regState_NoTarget;
return mStatus_NoError;
}
info->state = regState_FetchingZoneData;
return startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, 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;
mDNSu32 i;
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;
for (i = 0; i < srs->NumSubTypes; i++)
if (!(ptr = putDeletionRecord(&msg, ptr, &srs->SubTypes[i].resrec))) goto error;
err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForName(u, srs->RR_SRV.resrec.name));
if (err && err != mStatus_TransientErr) { debugf("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto error; }
SetRecordRetry(m, &srs->RR_SRV, err);
info->id = id;
info->state = regState_DeregPending;
return;
error:
unlinkSRS(m, srs);
info->state = regState_Unregistered;
}
mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
{
NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
char *errmsg = "Unknown State";
srs->uDNS_info.SRVChanged = srs->uDNS_info.SRVUpdateDeferred = mDNSfalse;
if (nat)
{
if (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy)
DeleteNATPortMapping(m, nat, srs);
nat->reg.ServiceRegistration = mDNSNULL;
srs->uDNS_info.NATinfo = mDNSNULL;
FreeNATInfo(m, nat);
}
switch (srs->uDNS_info.state)
{
case regState_Unregistered:
debugf("uDNS_DeregisterService - service %##s not registered", srs->RR_SRV.resrec.name->c);
return mStatus_BadReferenceErr;
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_NATError: case regState_NATMap: case regState_NoTarget: unlinkSRS(m, 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:
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;
}
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 *parent = mDNSNULL;
AuthRecord *rptr;
uDNS_RegInfo *info = &rr->uDNS_info;
regState_t *stateptr = mDNSNULL;
for (parent = u->ServiceRegistrations; parent; parent = parent->next)
if (&parent->RR_TXT == rr) { stateptr = &parent->uDNS_info.state; break; }
if (!parent)
{
for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next)
if (rptr == rr) { stateptr = &rr->uDNS_info.state; break; }
if (!rptr) goto unreg_error;
}
switch(*stateptr)
{
case regState_DeregPending:
case regState_DeregDeferred:
case regState_Cancelled:
case regState_Unregistered:
goto unreg_error;
case regState_FetchingZoneData:
case regState_NATMap:
case regState_ExtraQueued:
case regState_NoTarget:
if (info->UpdateRDCallback) info->UpdateRDCallback(m, rr, rr->resrec.rdata);
SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
rr->NewRData = mDNSNULL;
return mStatus_NoError;
case regState_Pending:
case regState_Refresh:
case regState_UpdatePending:
if (info->QueuedRData && info->UpdateRDCallback)
info->UpdateRDCallback(m, rr, info->QueuedRData);
info->QueuedRData = rr->NewRData;
info->QueuedRDLen = rr->newrdlength;
rr->NewRData = mDNSNULL;
return mStatus_NoError;
case regState_Registered:
info->OrigRData = rr->resrec.rdata;
info->OrigRDLen = rr->resrec.rdlength;
info->InFlightRData = rr->NewRData;
info->InFlightRDLen = rr->newrdlength;
rr->NewRData = mDNSNULL;
*stateptr = regState_UpdatePending;
if (parent) SendServiceRegistration(m, parent);
else sendRecordRegistration(m, rr);
return mStatus_NoError;
case regState_NATError:
LogMsg("ERROR: uDNS_UpdateRecord called for record %##s with bad state regState_NATError", rr->resrec.name->c);
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 = m->uDNS_info.NATTraversals;
mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
while (ptr)
{
NATTraversalInfo *cur = ptr;
ptr = ptr->next;
if (cur->op != NATOp_AddrRequest || cur->state != NATState_Established) {
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 = mStatus_NoError;
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 (m->SuppressStdPort53Queries &&
sendtime - m->SuppressStdPort53Queries < 0) sendtime = m->SuppressStdPort53Queries;
if (sendtime - timenow < 0)
{
DNSServer *server = GetServerForName(&m->uDNS_info, &q->qname);
if (server)
{
if (server->teststate == DNSServer_Untested)
{
InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), uQueryFlags);
end = putQuestion(&msg, msg.data, msg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN);
}
else
err = constructQueryMsg(&msg, &end, q);
if (err) LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %##s", q->qname.c);
else
{
if (server->teststate != DNSServer_Failed)
err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &server->addr, UnicastDNSPort, -1, mDNSNULL);
m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
q->LastQTime = timenow;
if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); else 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 sendRecordRegistration(m, rr);
}
if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval;
}
if (rInfo->lease && rInfo->state == regState_Registered)
{
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 || rInfo->state == regState_UpdatePending)
{
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";
else if (rInfo->state == regState_UpdatePending) op = "txt record update";
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)
{
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;
if (u->DelaySRVUpdate && u->NextSRVUpdate - timenow < 0)
{
u->DelaySRVUpdate = mDNSfalse;
UpdateSRVRecords(m);
}
nexte = CheckNATMappings(m, timenow);
if (nexte - u->nextevent < 0) u->nextevent = nexte;
if (m->SuppressStdPort53Queries && m->timenow - m->SuppressStdPort53Queries >= 0)
m->SuppressStdPort53Queries = 0;
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)
{
DeleteNATPortMapping(m, u->LLQNatInfo, mDNSNULL);
FreeNATInfo(m, u->LLQNatInfo); }
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, GetAuthInfoForName(&m->uDNS_info, rr->resrec.name));
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)
{
ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations;
while(srs)
{
uDNS_RegInfo *info = &srs->uDNS_info;
NATTraversalInfo *nat = info->NATinfo;
if (nat)
{
if (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy)
DeleteNATPortMapping(m, nat, srs);
nat->reg.ServiceRegistration = mDNSNULL;
srs->uDNS_info.NATinfo = mDNSNULL;
FreeNATInfo(m, nat);
}
if (info->state == regState_UpdatePending)
{
AuthRecord *txt = &srs->RR_TXT;
uDNS_RegInfo *txtInfo = &txt->uDNS_info;
info->state = regState_Registered;
if (txtInfo->UpdateRDCallback) txtInfo->UpdateRDCallback(m, txt, txtInfo->OrigRData);
SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen);
txtInfo->OrigRData = mDNSNULL;
txtInfo->InFlightRData = mDNSNULL;
}
if (info->state == regState_Registered || info->state == regState_Refresh)
{
mDNSOpaque16 origid = srs->uDNS_info.id;
info->state = regState_DeregPending; SendServiceDeregistration(m, srs);
info->id = origid;
info->state = regState_NoTarget; srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0;
}
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 *const m)
{
SuspendLLQs(m, mDNStrue);
SleepServiceRegistrations(m);
SleepRecordRegistrations(m);
}
mDNSexport void uDNS_Wake(mDNS *const m)
{
RestartQueries(m);
WakeServiceRegistrations(m);
WakeRecordRegistrations(m);
}