#include "uDNS.h"
#if(defined(_MSC_VER))
#pragma warning(disable:4706)
#endif
#ifndef NULL
#define NULL mDNSNULL
#endif // NULL
#define ustrcpy(d,s) mDNSPlatformStrCopy(s,d) // use strcpy(2) param ordering
#define ustrlen(s) mDNSPlatformStrLen(s)
#define umalloc(x) mDNSPlatformMemAllocate(x) // short hands for common routines
#define ufree(x) mDNSPlatformMemFree(x)
#define ubzero(x,y) mDNSPlatformMemZero(x,y)
#define umemcpy(x, y, l) mDNSPlatformMemCopy(y, x, l) // uses memcpy(2) arg ordering
typedef enum
{
zoneDataResult
} AsyncOpResultType;
typedef struct
{
domainname zoneName;
mDNSAddr primaryAddr;
mDNSu16 zoneClass;
mDNSIPPort llqPort;
mDNSIPPort updatePort;
} zoneData_t;
typedef struct
{
AsyncOpResultType type;
zoneData_t zoneData;
} AsyncOpResult;
typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const AsyncOpResult *result);
mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m);
mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort,
AsyncOpCallback callback, void *callbackInfo);
mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID);
mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr);
mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs);
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - General Utility Functions
#endif
mDNSlocal mDNSOpaque16 newMessageID(uDNS_GlobalInfo *u)
{
if (!u->NextMessageID || u->NextMessageID == (mDNSu16)~0) u->NextMessageID = 1;
return mDNSOpaque16fromIntVal(u->NextMessageID++);
}
mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr)
{
AuthRecord *rptr, *prev = NULL;
for (rptr = *list; rptr; rptr = rptr->next)
{
if (rptr == rr)
{
if (prev) prev->next = rptr->next;
else *list = rptr->next;
rptr->next = NULL;
return mStatus_NoError;
}
prev = rptr;
}
LogMsg("ERROR: unlinkAR - no such active record");
return mStatus_UnknownErr;
}
mDNSlocal void LinkActiveQuestion(uDNS_GlobalInfo *u, DNSQuestion *q)
{
if (IsActiveUnicastQuery(q, u))
{ LogMsg("LinkActiveQuestion - %s (%d) already in list!", q->qname.c, q->qtype); return; }
q->next = u->ActiveQueries;
u->ActiveQueries = q;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Name Server List Management
#endif
mDNSexport void mDNS_RegisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
int i;
if (!dnsAddr->NotAnInteger)
{
LogMsg("ERROR: attempt to register DNS with IP address 0");
return;
}
for (i = 0; i < 32; i++)
{
if (!u->Servers[i].ip.v4.NotAnInteger)
{
u->Servers[i].ip.v4.NotAnInteger = dnsAddr->NotAnInteger;
u->Servers[i].type = mDNSAddrType_IPv4;
return;
}
if (u->Servers[i].ip.v4.NotAnInteger == dnsAddr->NotAnInteger)
{
LogMsg("ERROR: mDNS_RegisterDNS - DNS already registered");
return;
}
}
if (i == 32) { LogMsg("ERROR: mDNS_RegisterDNS - too many registered servers"); }
}
mDNSexport void mDNS_DeregisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
int i;
if (!dnsAddr->NotAnInteger)
{
LogMsg("ERROR: attempt to deregister DNS with IP address 0");
return;
}
for (i = 0; i < 32; i++)
{
if (u->Servers[i].ip.v4.NotAnInteger == dnsAddr->NotAnInteger)
{
u->Servers[i].ip.v4.NotAnInteger = 0;
return;
}
}
if (i == 32) { LogMsg("ERROR: mDNS_DeregisterDNS - no such DNS registered"); }
}
mDNSexport void mDNS_DeregisterDNSList(mDNS *const m)
{
ubzero(m->uDNS_info.Servers, 32 * sizeof(mDNSAddr));
}
mDNSexport mDNSBool mDNS_DNSRegistered(mDNS *const m)
{
int i;
for (i = 0; i < 32; i++) if (m->uDNS_info.Servers[i].ip.v4.NotAnInteger) return mDNStrue;
return mDNSfalse;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - authorization management
#endif
mDNSexport mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname *zone, domainname *key,
mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64)
{
uDNS_AuthInfo *info;
mDNSu8 keybuf[1024];
mDNSs32 keylen;
info = (uDNS_AuthInfo*)umalloc(sizeof(uDNS_AuthInfo) + ssLen);
if (!info) { LogMsg("ERROR: umalloc"); return mStatus_NoMemoryErr; }
ubzero(info, sizeof(uDNS_AuthInfo));
ustrcpy(info->zone.c, zone->c);
ustrcpy(info->keyname.c, key->c);
if (base64)
{
keylen = DNSDigest_Base64ToBin((const char*)sharedSecret, keybuf, 1024);
if (keylen < 0)
{
LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication - could not convert shared secret from base64");
ufree(info);
return mStatus_UnknownErr;
}
DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
}
else DNSDigest_ConstructHMACKey(info, sharedSecret, ssLen);
info->next = m->uDNS_info.AuthInfoList;
m->uDNS_info.AuthInfoList = info;
return mStatus_NoError;
}
mDNSexport void mDNS_ClearAuthenticationList(mDNS *m)
{
uDNS_AuthInfo *fptr, *ptr = m->uDNS_info.AuthInfoList;
while (ptr)
{
fptr = ptr;
ptr = ptr->next;
ufree(fptr);
}
m->uDNS_info.AuthInfoList = NULL;
}
mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone)
{
uDNS_AuthInfo *ptr;
domainname *z;
mDNSu32 zoneLen, ptrZoneLen;
zoneLen = ustrlen(zone->c);
for (ptr = u->AuthInfoList; ptr; ptr = ptr->next)
{
z = &ptr->zone;
ptrZoneLen = ustrlen(z->c);
if (zoneLen < ptrZoneLen) continue;
if (mDNSPlatformMemSame(z->c, zone->c + (zoneLen - ptrZoneLen), ptrZoneLen)) return ptr;
}
return NULL;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - host name and interface management
#endif
mDNSlocal void hostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
if (result == mStatus_MemFree) return;
if (result == mStatus_NameConflict && rr->resrec.RecordType == kDNSRecordTypeUnique)
{
rr->resrec.RecordType = kDNSRecordTypeKnownUnique;
uDNS_RegisterRecord(m, rr);
return;
}
if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
{
if (result == mStatus_NoSuchRecord) result = mStatus_NameConflict;
rr->resrec.RecordType = kDNSRecordTypeUnique;
}
if (!result) rr->resrec.RecordType = kDNSRecordTypeVerified;
if (result)
((NetworkInterfaceInfo *)(rr->RecordContext))->uDNS_info.registered = mDNSfalse;
mDNS_HostNameCallback(m, rr, result);
}
mDNSlocal void deadvertiseIfCallback(mDNS *const m, AuthRecord *const rr, mStatus err)
{
(void)m;
if (err == mStatus_MemFree) ufree(rr);
else LogMsg("deadvertiseIfCallback - error %s for record %s", err, rr->resrec.name.c);
}
mDNSexport void uDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
{
AuthRecord *copy;
AuthRecord *rr = &set->uDNS_info.RR_A;
if (set->uDNS_info.registered)
{
copy = (AuthRecord*)umalloc(sizeof(AuthRecord)); if (!copy) { LogMsg("ERROR: Malloc"); return; }
umemcpy(copy, rr, sizeof(AuthRecord)); copy->resrec.rdata = ©->rdatastorage; if (rr->resrec.rdata != &rr->rdatastorage)
{ LogMsg("ERROR: uDNS_DeadvertiseInterface - expected local rdata storage. Aborting deregistration"); return; }
copy->next = m->uDNS_info.RecordRegistrations;
m->uDNS_info.RecordRegistrations = copy;
copy->RecordCallback = deadvertiseIfCallback;
unlinkAR(&m->uDNS_info.RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
set->uDNS_info.registered = mDNSfalse;
uDNS_DeregisterRecord(m, copy);
}
else debugf("uDNS_DeadvertiseInterface - interface not registered");
return;
}
mDNSexport void uDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
{
mDNSu8 *ip = set->ip.ip.v4.b;
AuthRecord *a = &set->uDNS_info.RR_A;
a->RecordContext = set;
if (set->ip.type != mDNSAddrType_IPv4 || (ip[0] == 169 && ip[1] == 254) || (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1)) return;
if (set->uDNS_info.registered && SameDomainName(&m->uDNS_info.hostname, &set->uDNS_info.regname))
return;
if (!m->uDNS_info.hostname.c[0])
{
set->uDNS_info.registered = mDNSfalse;
return;
}
set->uDNS_info.registered = mDNStrue;
ustrcpy(set->uDNS_info.regname.c, m->uDNS_info.hostname.c);
mDNS_SetupResourceRecord(a, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeShared , hostnameCallback, set);
ustrcpy(a->resrec.name.c, m->uDNS_info.hostname.c);
a->resrec.rdata->u.ip = set->ip.ip.v4;
LogMsg("uDNS_AdvertiseInterface: advertising %s", m->uDNS_info.hostname.c);
uDNS_RegisterRecord(m, a);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Incoming Message Processing
#endif
mDNSlocal mDNSBool sameResourceRecord(ResourceRecord *r1, ResourceRecord *r2)
{
return (r1->namehash == r2->namehash &&
r1->rrtype == r2->rrtype &&
SameDomainName(&r1->name, &r2->name) &&
SameRData(r1, r2));
}
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 = NULL;
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 = NULL;
mDNSu32 size;
size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize;
newCR = (CacheRecord *)umalloc(size);
if (!newCR) { LogMsg("ERROR: addKnownAnswer - malloc"); return; }
umemcpy(newCR, rr, size);
newCR->resrec.rdata = (RData*)&newCR->rdatastorage;
newCR->resrec.rdata->MaxRDLength = rr->resrec.rdlength;
newCR->next = question->uDNS_info.knownAnswers;
question->uDNS_info.knownAnswers = newCR;
}
mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question)
{
const mDNSu8 *ptr;
int i;
CacheRecord *fptr, *ka, *cr, *answers = NULL, *prev = NULL;
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);
question->QuestionCallback(m, question, &ka->resrec, mDNSfalse);
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 = NULL;
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);
question->QuestionCallback(m, question, &ka->resrec, mDNSfalse);
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;
LLQ_Info *llqInfo = question->uDNS_info.llq;
if (question != m->uDNS_info.CurrentQuery)
{ LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; }
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))
{
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);
question->QuestionCallback(m, question, &cr->resrec, !goodbye);
if (question != m->uDNS_info.CurrentQuery)
{
debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning");
return;
}
}
else
{
LogMsg("unexpected answer: %s", cr->resrec.name.c);
}
}
if (llq && (llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume))
{ deriveGoodbyes(m, msg, end,question); llqInfo->deriveRemovesOnResume = mDNSfalse; }
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 void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs)
{
ServiceRecordSet *ptr, *prev = NULL;
for (ptr = u->ServiceRegistrations; ptr; ptr = ptr->next)
{
if (ptr == srs)
{
if (prev) prev->next = ptr->next;
else u->ServiceRegistrations = ptr->next;
ptr->next = NULL;
return;
}
prev = ptr;
}
LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list");
}
mDNSlocal mStatus checkUpdateResult(domainname *name, mDNSu8 rcode, const DNSMessage *msg)
{
(void)msg; if (!rcode) return mStatus_NoError;
else if (rcode == kDNSFlag1_RC_YXDomain)
{
LogMsg("Name in use: %s", name->c);
return mStatus_NameConflict;
}
else if (rcode == kDNSFlag1_RC_Refused)
{
LogMsg("Update %s refused", name->c);
return mStatus_Refused;
}
else if (rcode == kDNSFlag1_RC_NXRRSet)
{
LogMsg("Reregister refusted (NXRRSET): %s", name->c);
return mStatus_NoSuchRecord;
}
else if (rcode == kDNSFlag1_RC_NotAuth)
{
LogMsg("Permission denied (NOAUTH): %s", name->c);
return mStatus_NoAuth;
}
else if (rcode == kDNSFlag1_RC_FmtErr)
{
LogMsg("Format Error: %s", name->c);
return mStatus_UnknownErr;
}
else
{
LogMsg("Update %s failed with rcode %d", name->c, rcode);
return mStatus_UnknownErr;
}
}
mDNSlocal void hndlServiceUpdateReply(mDNS * const m, ServiceRecordSet *srs, mStatus err)
{
switch (srs->uDNS_info.state)
{
case regState_Pending:
case regState_Refresh:
if (err)
{
if (srs->uDNS_info.lease && err == mStatus_UnknownErr)
{
LogMsg("Re-trying update of service %s without lease option", srs->RR_SRV.resrec.name.c);
srs->uDNS_info.lease = mDNSfalse;
srs->uDNS_info.expire = -1;
SendServiceRegistration(m, srs);
return;
}
else
{
LogMsg("hndlServiceUpdateReply: Error %d returned for registration of %s",
err, srs->RR_SRV.resrec.name.c);
srs->uDNS_info.state = regState_Unregistered;
break;
}
}
else
{
if (srs->uDNS_info.state == regState_Refresh)
{
srs->uDNS_info.state = regState_Registered;
return;
}
srs->uDNS_info.state = regState_Registered;
break;
}
case regState_DeregPending:
if (err) LogMsg("hndlServiceUpdateReply: Error %d returned for dereg of %s",
err, srs->RR_SRV.resrec.name.c);
else err = mStatus_MemFree;
break;
case regState_DeregDeferred:
if (err) LogMsg("hndlServiceUpdateReply: Error %d received prior to deferred derigstration of %s",
err, srs->RR_SRV.resrec.name.c);
LogMsg("Performing deferred deregistration of %s", srs->RR_SRV.resrec.name.c);
uDNS_DeregisterService(m, srs);
return;
case regState_TargetChange:
if (err)
{
LogMsg("hdnlServiceUpdateReply: Error %d returned for host target update of %s",
err, srs->RR_SRV.resrec.name.c);
srs->uDNS_info.state = regState_Unregistered;
}
else srs->uDNS_info.state = regState_Registered;
break;
default:
LogMsg("hndlServiceUpdateReply called for service %s in unexpected state %d with error %d. Unlinking.",
srs->RR_SRV.resrec.name.c, srs->uDNS_info.state, err);
err = mStatus_UnknownErr;
}
if (err)
{
unlinkSRS(&m->uDNS_info, srs); srs->uDNS_info.state = regState_Unregistered;
}
srs->ServiceCallback(m, srs, err);
}
mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
if (rr->uDNS_info.state == regState_DeregPending)
{
debugf("Received reply for deregister record %s type %d", rr->resrec.name.c, rr->resrec.rrtype);
if (err) LogMsg("ERROR: Deregistration of record %s type %s failed with error %d",
rr->resrec.name.c, rr->resrec.rrtype, err);
else err = mStatus_MemFree;
if (unlinkAR(&m->uDNS_info.RecordRegistrations, rr))
LogMsg("ERROR: Could not unlink resource record following deregistration");
rr->uDNS_info.state = regState_Unregistered;
rr->RecordCallback(m, rr, err);
return;
}
if (rr->uDNS_info.state == regState_DeregDeferred)
{
if (err)
{
LogMsg("Cancelling deferred deregistration record %s type %d due to registration error %d",
rr->resrec.name.c, rr->resrec.rrtype, err);
unlinkAR(&m->uDNS_info.RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
return;
}
LogMsg("Calling deferred deregistration of record %s type %d",
rr->resrec.name.c, rr->resrec.rrtype);
rr->uDNS_info.state = regState_Registered;
uDNS_DeregisterRecord(m, rr);
return;
}
if (rr->uDNS_info.state == regState_Pending || rr->uDNS_info.state == regState_Refresh)
{
if (err)
{
if (rr->uDNS_info.lease && err == mStatus_UnknownErr)
{
LogMsg("Re-trying update of record %s without lease option", rr->resrec.name.c);
rr->uDNS_info.lease = mDNSfalse;
rr->uDNS_info.expire = -1;
sendRecordRegistration(m, rr);
return;
}
LogMsg("Registration of record %s type %d failed with error %d",
rr->resrec.name.c, rr->resrec.rrtype, err);
unlinkAR(&u->RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
}
else
{
if (rr->uDNS_info.state == regState_Refresh)
rr->uDNS_info.state = regState_Registered;
else
{
rr->uDNS_info.state = regState_Registered;
rr->RecordCallback(m, rr, err);
}
return;
}
}
LogMsg("Received unexpected response for record %s type %d, in state %d, with response error %d",
rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state, err);
}
mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, uDNS_RegInfo *info)
{
LargeCacheRecord lcr;
const mDNSu8 *ptr;
int i;
mDNSu32 lease = 0;
ptr = LocateAdditionals(msg, end);
if (info->lease && (ptr = LocateAdditionals(msg, end)))
{
for (i = 0; i < msg->h.numAdditionals; i++)
{
ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
if (!ptr) break;
if (lcr.r.resrec.rrtype == kDNSType_OPT)
{
if (lcr.r.resrec.rdlength < LEASE_OPT_SIZE) continue;
if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue;
lease = lcr.r.resrec.rdata->u.opt.OptData.lease;
break;
}
}
}
if (lease > 0)
info->expire = (mDNSPlatformTimeNow() + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4);
else info->expire = -1;
}
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, mDNSu8 ttl)
{
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_OP_Update | kDNSFlag0_QR_Response;
mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC);
(void)srcaddr;
(void)srcport;
(void)dstaddr;
(void)dstport;
(void)ttl;
(void)InterfaceID;
if (QR_OP == StdR)
{
if (recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return;
for (qptr = u->ActiveQueries; qptr; qptr = qptr->next)
{
if (qptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
{
if (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 = NULL;
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, msg);
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, msg);
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 void receiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
const mDNSInterfaceID InterfaceID)
{
mDNSAddr *sa = NULL, *da = NULL;
mDNSIPPort sp, dp;
mDNSu8 ttl = 0;
sp.NotAnInteger = 0;
dp.NotAnInteger = 0;
uDNS_ReceiveMsg(m, msg, end, sa, sp, da, dp, InterfaceID, ttl);
}
mDNSlocal const mDNSAddr *getInitializedDNS(uDNS_GlobalInfo *u)
{
int i;
for (i = 0; i < 32; i++)
if (u->Servers[i].ip.v4.NotAnInteger) return &u->Servers[i];
return NULL;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Query Routines
#endif
#define sameID(x,y) mDNSPlatformMemSame(x,y,8)
mDNSlocal void initializeQuery(DNSMessage *msg, DNSQuestion *question)
{
mDNSOpaque16 flags = QueryFlags;
ubzero(msg, sizeof(msg));
flags.b[0] |= kDNSFlag0_RD; InitializeDNSMessage(&msg->h, question->uDNS_info.id, flags);
}
mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question)
{
initializeQuery(msg, question);
*endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
if (!*endPtr)
{
LogMsg("ERROR: Unicast query out of space in packet");
return mStatus_UnknownErr;
}
return mStatus_NoError;
}
mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *question, LLQOptData *data, mDNSBool includeQuestion)
{
AuthRecord rr;
ResourceRecord *opt = &rr.resrec;
rdataOpt *optRD;
if (includeQuestion)
{
ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return NULL; }
}
ubzero(&rr, sizeof(AuthRecord));
opt->rdata = &rr.rdatastorage;
opt->RecordType = kDNSRecordTypeKnownUnique; opt->rrtype = kDNSType_OPT;
opt->rdlength = LLQ_OPT_SIZE;
opt->rdestimate = LLQ_OPT_SIZE;
optRD = &rr.resrec.rdata->u.opt;
optRD->opt = kDNSOpt_LLQ;
optRD->optlen = sizeof(LLQOptData);
umemcpy(&optRD->OptData.llq, data, sizeof(LLQOptData));
ptr = PutResourceRecordTTL(msg, ptr, &msg->h.numAdditionals, opt, 0);
if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTL"); return NULL; }
return ptr;
}
mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LLQOptData *llq, int index)
{
LargeCacheRecord lcr;
int i;
const mDNSu8 *ptr;
ptr = LocateAdditionals(msg, end);
if (!ptr) return mDNSfalse;
for (i = 0; i < msg->h.numAdditionals; i++)
{ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; if (lcr.r.resrec.rrtype == kDNSType_OPT) break; }
if (lcr.r.resrec.rrtype != kDNSType_OPT) return mDNSfalse;
if (lcr.r.resrec.rdlength < (index + 1) * LLQ_OPT_SIZE) return mDNSfalse; umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(LLQOptData)), sizeof(LLQOptData));
return mDNStrue;
}
mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *q)
{
LLQ_Info *qInfo;
LLQOptData pktData;
qInfo = q->uDNS_info.llq;
if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; }
if (pktData.llqOp != kLLQ_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() + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond);
qInfo->retry = qInfo->expire + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond * 3/4);
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;
if (info->state == kLLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES)
{
LogMsg("sendLLQRefresh - %d failed attempts for llq %s", info->ntries, q->qname.c);
info->state = LLQ_Retry;
info->retry = mDNSPlatformTimeNow() + kLLQ_DEF_RETRY * mDNSPlatformOneSecond;
info->deriveRemovesOnResume = mDNStrue;
return;
}
llq.vers = kLLQ_Vers;
llq.llqOp = kLLQ_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, q->InterfaceID, &info->servAddr, info->servPort);
if (err) LogMsg("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %d", err);
if (info->state == LLQ_Established) info->ntries = 1;
else info->ntries++;
info->state = LLQ_Refresh;
q->LastQTime = mDNSPlatformTimeNow();
info->retry = (info->expire - q->LastQTime) / 2;
}
mDNSlocal void 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;
m->uDNS_info.CurrentQuery = q;
q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context);
if (m->uDNS_info.CurrentQuery != q) return;
InitializeDNSMessage(&ack.h, msg->h.id, ResponseFlags);
ackEnd = putQuestion(&ack, ack.data, ack.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putQuestion"); return; }
err = mDNSSendDNSMessage(m, &ack, ackEnd, InterfaceID, srcaddr, srcport);
if (err) LogMsg("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %d", err);
}
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() + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
info->retry = info->expire + ((mDNSs32)llq->lease * mDNSPlatformOneSecond * 3/4);
info->origLease = llq->lease;
info->state = LLQ_Established;
q->uDNS_info.responseCallback = llqResponseHndlr;
llqResponseHndlr(m, pktMsg, end, q, NULL);
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();
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 = kLLQ_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, q->InterfaceID, &info->servAddr, info->servPort);
if (err) LogMsg("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %d", 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();
switch(llq->err)
{
case LLQErr_NoError: break;
case LLQErr_ServFull:
LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %s. Retry in %d sec", q->qname.c, llq->lease);
info->retry = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
info->state = LLQ_Retry;
simpleResponseHndlr(m, pktMsg, end, q, NULL); info->deriveRemovesOnResume = mDNStrue;
case LLQErr_Static:
info->state = LLQ_Static;
LogMsg("LLQ %s: static", q->qname.c);
simpleResponseHndlr(m, pktMsg, end, q, NULL);
return;
case LLQErr_FormErr:
LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %s", q->qname.c);
goto error;
case LLQErr_BadVers:
LogMsg("ERROR: hndlRequestChallenge - received BadVers from server");
goto error;
case LLQErr_UnknownErr:
LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %s", q->qname.c);
goto error;
default:
LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %s", llq->err, q->qname.c);
goto error;
}
if (info->origLease != llq->lease)
LogMsg("hndlRequestChallenge: requested lease %d, granted lease %d", 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)
{
LogMsg("LLQ Setup for %s failed with rcode %d. Reverting to polling mode", q->qname.c, rcode);
info->state = LLQ_Poll;
q->uDNS_info.responseCallback = simpleResponseHndlr;
q->LastQTime = mDNSPlatformTimeNow();
q->ThisQInterval = 1;
return;
}
ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion);
if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto error; }
if (!SameDomainName(&q->qname, &pktQuestion.qname))
{ LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %s", q->qname.c); goto error; }
if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { LogMsg("ERROR: recvSetupResponse - GetLLQAtIndex"); goto error; }
if (llq.llqOp != kLLQ_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto error; }
if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto error; }
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);
error:
info->state = LLQ_Error;
}
mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info)
{
DNSMessage msg;
mDNSu8 *end;
LLQOptData llqData;
DNSQuestion *q = info->question;
mStatus err;
mDNSs32 timenow = mDNSPlatformTimeNow();
if (info->ntries++ == kLLQ_MAX_TRIES)
{
LogMsg("startLLQHandshake: %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;
}
llqData.vers = kLLQ_Vers;
llqData.llqOp = kLLQ_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;
}
err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, &info->servAddr, info->servPort);
if (err) LogMsg("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %d", 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;
}
mDNSlocal void startLLQHandshakeCallback(mStatus err, mDNS *const m, void *llqInfo, const AsyncOpResult *result)
{
LLQ_Info *info = (LLQ_Info *)llqInfo;
const zoneData_t *zoneInfo = &result->zoneData;
if (err)
{
LogMsg("ERROR: startLLQHandshakeCallback invoked with error code %d", err);
info->state = LLQ_Poll;
info->question->LastQTime = 0; info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
return;
}
if (info->state == LLQ_Cancelled)
{
LogMsg("startLLQHandshake - LLQ Cancelled.");
info->question = NULL; ufree(info);
return;
}
if (info->state != LLQ_GetZoneInfo)
{
LogMsg("ERROR: startLLQHandshake - bad state %d", info->state);
return;
}
info->servAddr.type = zoneInfo->primaryAddr.type;
info->servAddr.ip.v4.NotAnInteger = zoneInfo->primaryAddr.ip.v4.NotAnInteger;
info->servPort.NotAnInteger = zoneInfo->llqPort.NotAnInteger;
info->ntries = 0;
startLLQHandshake(m, info);
}
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 %d", err);
info->question = NULL;
ufree(info);
question->uDNS_info.llq = NULL;
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))
{ recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID); return mDNStrue; }
else if (msg->h.id.NotAnInteger != q->uDNS_info.id.NotAnInteger)
{ q = q->next; continue; }
else if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers)
{ recvRefreshReply(m, msg, end, q); return mDNStrue; }
else if (llqInfo->state < LLQ_Static)
{ q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; }
}
q = q->next;
}
return mDNSfalse;
}
mDNSexport mDNSBool IsActiveUnicastQuery(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 || IsLocalDomain(&question->qname))
LogMsg("Warning: Question %s in Active Unicast Query list with id %d, interfaceID %x",
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:
info->question = NULL; info->state = LLQ_Cancelled;
return;
case LLQ_Established:
case LLQ_Refresh:
sendLLQRefresh(m, question, 0);
goto free_info;
default:
debugf("stopLLQ - silently discarding LLQ in state %d", info->state);
goto free_info;
}
free_info:
info->question = NULL;
ufree(info);
question->uDNS_info.llq = NULL;
}
mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSQuestion *qptr, *prev = NULL;
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;
}
mDNSexport void uDNS_SuspendLLQs(mDNS *m)
{
DNSQuestion *q;
LLQ_Info *llq;
for (q = m->uDNS_info.ActiveQueries; q; q = q->next)
{
llq = q->uDNS_info.llq;
if (q->LongLived && llq && llq->state < LLQ_Suspended)
{
if (llq->state == LLQ_Established || llq->state == LLQ_Refresh)
sendLLQRefresh(m, q, 0);
if (llq->state != LLQ_Retry) llq->state = LLQ_Suspended;
}
}
}
extern void uDNS_RestartLLQs(mDNS *m)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSQuestion *q;
LLQ_Info *llqInfo;
u->CurrentQuery = u->ActiveQueries;
while (u->CurrentQuery)
{
q = u->CurrentQuery;
u->CurrentQuery = u->CurrentQuery->next;
llqInfo = q->uDNS_info.llq;
if (q->LongLived && llqInfo && llqInfo->state == LLQ_Suspended)
{ llqInfo->ntries = 0; llqInfo->deriveRemovesOnResume = mDNStrue; startLLQHandshake(m, llqInfo); }
}
}
mDNSlocal mStatus startQuery(mDNS *const m, DNSQuestion *const question, mDNSBool internal)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSMessage msg;
mDNSu8 *endPtr;
mStatus err = mStatus_NoError;
const mDNSAddr *server;
if (!ValidateDomainName(&question->qname))
{
LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype));
return mStatus_Invalid;
}
question->next = NULL;
question->qnamehash = DomainNameHashValue(&question->qname); question->uDNS_info.id = newMessageID(u);
if (question->LongLived) return startLLQ(m, question);
err = constructQueryMsg(&msg, &endPtr, question);
if (err) return err;
question->LastQTime = mDNSPlatformTimeNow();
question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
question->uDNS_info.timestamp = question->LastQTime;
question->uDNS_info.internal = internal;
LinkActiveQuestion(u, question);
question->uDNS_info.knownAnswers = NULL;
server = getInitializedDNS(u);
if (!server) { LogMsg("startQuery - no initialized DNS"); err = mStatus_NotInitializedErr; }
else err = mDNSSendDNSMessage(m, &msg, endPtr, question->InterfaceID, server, UnicastDNSPort);
if (err) { LogMsg("ERROR: startQuery - %d (keeping question in list for retransmission", err); }
return err;
}
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 = NULL;
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));
ustrcpy(context->origName.c, name->c);
context->state = init;
context->m = m;
context->callback = callback;
context->callbackInfo = callbackInfo;
context->findUpdatePort = findUpdatePort;
context->findLLQPort = findLLQPort;
getZoneData(m, NULL, NULL, NULL, context);
return mStatus_NoError;
}
mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr)
{
AsyncOpResult result;
ntaContext *context = (ntaContext*)contextPtr;
smAction action;
(void)m;
(void)question;
if (context->questionActive)
{
uDNS_StopQuery(context->m, &context->question);
context->questionActive = mDNSfalse;
}
if (msg && msg->h.flags.b[2] >> 4 && msg->h.flags.b[2] >> 4 != kDNSFlag1_RC_NXDomain)
{
LogMsg("ERROR: getZoneData - received response w/ rcode %d", msg->h.flags.b[2] >> 4);
goto error;
}
switch (context->state)
{
case init:
case lookupSOA:
action = hndlLookupSOA(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
case foundZone:
case lookupNS:
action = confirmNS(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
case foundNS:
case lookupA:
action = lookupNSAddr(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
case foundA:
if (!context->findUpdatePort && !context->findLLQPort)
{
context->state = complete;
break;
}
case lookupPort:
action = hndlLookupPorts(msg, end, context);
if (action == smError) goto error;
if (action == smBreak) return;
if (action == smContinue) context->state = complete;
case foundPort:
case complete: break;
}
if (context->state != complete)
{
LogMsg("ERROR: getZoneData - exited state machine with state %d", context->state);
goto error;
}
result.type = zoneDataResult;
result.zoneData.primaryAddr.ip.v4.NotAnInteger = context->addr.NotAnInteger;
result.zoneData.primaryAddr.type = mDNSAddrType_IPv4;
ustrcpy(result.zoneData.zoneName.c, context->zone.c);
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, NULL);
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;
ustrcpy(query->qname.c, context->curSOA->c);
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 %d (breaking until next periodic retransmission)", err);
return smBreak; }
mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr)
{
ustrcpy(context->zone.c, rr->name.c);
context->zoneClass = rr->rrclass;
ustrcpy(context->ns.c, rr->rdata->u.soa.mname.c);
context->state = foundZone;
}
mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
{
DNSQuestion *query = &context->question;
mStatus err;
LargeCacheRecord lcr;
ResourceRecord *rr = &lcr.r.resrec;
const mDNSu8 *ptr;
int i;
if (context->state == foundZone)
{
ustrcpy(query->qname.c, context->zone.c);
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 %d (breaking until next periodic retransmission", err);
context->state = lookupNS;
return smBreak; }
else if (context->state == lookupNS)
{
ptr = LocateAnswers(msg, end);
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: confirmNS, Answers - GetLargeResourceRecord returned NULL"); return smError; }
if (rr->rrtype == kDNSType_NS &&
SameDomainName(&context->zone, &rr->name) && SameDomainName(&context->ns, &rr->rdata->u.name))
{
context->state = foundNS;
return smContinue; }
}
LogMsg("ERROR: could not confirm existance of NS record %s", context->zone.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;
ustrcpy(query->qname.c, context->ns.c);
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 %d (breaking until next periodic retransmission)", err);
context->state = lookupA;
return smBreak;
}
mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
{
const mDNSu8 *ptr;
int i;
LargeCacheRecord lcr;
ResourceRecord *rr = &lcr.r.resrec;
if (context->state == foundNS)
{
if (!msg->h.numAdditionals) return queryNSAddr(context);
ptr = LocateAdditionals(msg, end);
if (!ptr)
{
LogMsg("ERROR: lookupNSAddr - LocateAdditionals returned NULL, expected %d additionals", msg->h.numAdditionals);
return queryNSAddr(context);
}
else
{
for (i = 0; i < msg->h.numAdditionals; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr)
{
LogMsg("ERROR: lookupNSAddr, Additionals - GetLargeResourceRecord returned NULL");
return queryNSAddr(context);
}
if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name))
{
context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger;
context->state = foundA;
return smContinue;
}
}
}
return queryNSAddr(context);
}
else if (context->state == lookupA)
{
ptr = LocateAnswers(msg, end);
if (!ptr) { LogMsg("ERROR: lookupNSAddr: LocateAnswers returned NULL"); return smError; }
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: lookupNSAddr, Answers - GetLargeResourceRecord returned NULL"); break; }
if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name))
{
context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger;
context->state = foundA;
return smContinue;
}
}
LogMsg("ERROR: lookupNSAddr: Address record not found in answer section");
return smError;
}
else { LogMsg("ERROR: lookupNSAddr - bad state %d", context->state); return smError; }
}
mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext *context, char *portName, mDNSIPPort *port)
{
int i;
LargeCacheRecord lcr;
const mDNSu8 *ptr;
DNSQuestion *q;
mStatus err;
if (context->state == lookupPort) {
if (!msg) { LogMsg("ERROR: hndlLookupUpdatePort - NULL message"); return smError; }
ptr = LocateAnswers(msg, end);
for (i = 0; i < msg->h.numAnswers; i++)
{
ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr) { LogMsg("ERROR: hndlLookupUpdatePort - GetLargeResourceRecord returned NULL"); return smError; }
if (ResourceRecordAnswersQuestion(&lcr.r.resrec, &context->question))
{
port->NotAnInteger = lcr.r.resrec.rdata->u.srv.port.NotAnInteger;
context->state = foundPort;
return smContinue;
}
}
LogMsg("hndlLookupUpdatePort %s - answer not contained in reply. Guessing port %d", portName, UnicastDNSPort);
*port = UnicastDNSPort;
context->state = foundPort;
return smContinue;
}
context->state = lookupPort;
q = &context->question;
MakeDomainNameFromDNSNameString(&q->qname, portName);
ustrcpy((q->qname.c + ustrlen(q->qname.c)), context->zone.c);
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 %d (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;
question->uDNS_info.id.NotAnInteger = (mDNSu16)~0;
if (ConnectionEstablished)
{
msg = (DNSMessage *)&msgbuf;
err = constructQueryMsg(msg, &end, question);
if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %d", err); goto error; }
err = mDNSSendDNSMessage_tcp(info->m, msg, end, sd);
if (err) { LogMsg("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %d", err); goto error; }
return;
}
else
{
if (!info->nread)
{
n = mDNSPlatformReadTCP(sd, &info->replylen, 2);
if (n != 2)
{
LogMsg("ERROR:conQueryCallback - attempt to read message length failed (read returned %d)", n);
goto error;
}
}
n = mDNSPlatformReadTCP(sd, ((char *)&info->reply) + info->nread, info->replylen - info->nread);
if (n < 0) { LogMsg("ERROR: conQueryCallback - read returned %d", n); goto error; }
info->nread += n;
if (info->nread == info->replylen)
{
receiveMsg(info->m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, question->InterfaceID);
mDNSPlatformTCPCloseConnection(sd);
ufree(info);
return;
}
else return;
}
return;
error:
mDNSPlatformTCPCloseConnection(sd);
ufree(info);
}
mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m)
{
mStatus connectionStatus;
uDNS_QuestionInfo *info = &question->uDNS_info;
int sd;
tcpInfo_t *context;
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.NotAnInteger = (mDNSu16)~0; info->timestamp = mDNSPlatformTimeNow();
connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd);
if (connectionStatus == mStatus_ConnectionEstablished) {
conQueryCallback(sd, context, mDNStrue);
return;
}
if (connectionStatus == mStatus_ConnectionPending) return; LogMsg("hndlTruncatedAnswer: connection failed");
uDNS_StopQuery(m, question); }
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Dynamic Updates
#endif
mDNSlocal mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
{
ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
if (!ptr || ptr + 4 > limit) return NULL; ((mDNSOpaque16 *)ptr)->NotAnInteger = kDNSType_SOA;
ptr += 2;
((mDNSOpaque16 *)ptr)->NotAnInteger = zoneClass.NotAnInteger;
ptr += 2;
msg->h.mDNS_numZones++;
return ptr;
}
mDNSlocal mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end)
{
AuthRecord prereq;
ubzero(&prereq, sizeof(AuthRecord));
ustrcpy(prereq.resrec.name.c, name->c);
prereq.resrec.rrtype = kDNSQType_ANY;
prereq.resrec.rrclass = kDNSClass_NONE;
ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
return ptr;
}
mDNSlocal mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
{
mDNSu16 origclass;
origclass = rr->rrclass;
rr->rrclass = kDNSClass_NONE;
ptr = PutResourceRecordTTL(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
rr->rrclass = origclass;
return ptr;
}
mDNSlocal mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end)
{
AuthRecord rr;
ResourceRecord *opt = &rr.resrec;
rdataOpt *optRD;
ubzero(&rr, sizeof(AuthRecord));
opt->rdata = &rr.rdatastorage;
opt->RecordType = kDNSRecordTypeKnownUnique; opt->rrtype = kDNSType_OPT;
opt->rdlength = LEASE_OPT_SIZE;
opt->rdestimate = LEASE_OPT_SIZE;
optRD = &rr.resrec.rdata->u.opt;
optRD->opt = kDNSOpt_Lease;
optRD->optlen = sizeof(mDNSs32);
optRD->OptData.lease = kUpdate_DefLease;
end = PutResourceRecordTTL(msg, end, &msg->h.numAdditionals, opt, 0);
if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return NULL; }
return end;
}
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_AuthInfo *authInfo;
uDNS_RegInfo *regInfo = &rr->uDNS_info;
mStatus err = mStatus_UnknownErr;
id = newMessageID(u);
InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
rr->uDNS_info.id.NotAnInteger = id.NotAnInteger;
ptr = putZone(&msg, ptr, end, ®Info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) goto error;
if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->uDNS_info.state == regState_Refresh)
{
ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &rr->resrec, 0);
if (!ptr) goto error;
}
else if (rr->resrec.RecordType != kDNSRecordTypeShared)
{
ptr = putPrereqNameNotInUse(&rr->resrec.name, &msg, ptr, end);
if (!ptr) goto error;
}
ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec);
if (!ptr) goto error;
if (rr->uDNS_info.lease)
ptr = putUpdateLease(&msg, ptr);
rr->uDNS_info.expire = -1;
authInfo = GetAuthInfoForZone(u, ®Info->zone);
if (authInfo)
{
err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, ®Info->ns, regInfo->port, authInfo);
if (err) { LogMsg("ERROR: sendRecordRegistration - mDNSSendSignedDNSMessage - %d", err); goto error; }
}
else
{
err = mDNSSendDNSMessage(m, &msg, ptr, 0, ®Info->ns, regInfo->port);
if (err) { LogMsg("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %d", err); goto error; }
}
if (regInfo->state != regState_Refresh) regInfo->state = regState_Pending;
return;
error:
if (rr->uDNS_info.state != regState_Unregistered)
{
unlinkAR(&u->RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
}
rr->RecordCallback(m, rr, err);
}
mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *authPtr, const AsyncOpResult *result)
{
AuthRecord *newRR = (AuthRecord*)authPtr;
const zoneData_t *zoneData = &result->zoneData;
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 %d", err); goto error; }
if (newRR->uDNS_info.state == regState_Cancelled)
{
LogMsg("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;
}
ustrcpy(newRR->uDNS_info.zone.c, zoneData->zoneName.c);
newRR->uDNS_info.ns.type = mDNSAddrType_IPv4;
newRR->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger;
newRR->uDNS_info.port.NotAnInteger = zoneData->updatePort.NotAnInteger;
sendRecordRegistration(m, newRR);
return;
error:
if (newRR->uDNS_info.state != regState_Unregistered)
{
unlinkAR(&u->RecordRegistrations, newRR);
newRR->uDNS_info.state = regState_Unregistered;
}
newRR->RecordCallback(m, newRR, err);
}
mDNSlocal mDNSBool setHostTarget(AuthRecord *rr, mDNS *m)
{
domainname *target;
if (!rr->HostTarget)
{
debugf("Service %s - not updating host target", rr->resrec.name.c);
return mDNSfalse;
}
target = GetRRDomainNameTarget(&rr->resrec);
if (!target)
{
LogMsg("ERROR: setHostTarget: Can't set target of rrtype %d", rr->resrec.rrtype);
return mDNSfalse;
}
if (SameDomainName(target, &m->uDNS_info.hostname))
{
debugf("Host target for %s unchanged", rr->resrec.name.c);
return mDNSfalse;
}
AssignDomainName(*target, m->uDNS_info.hostname);
SetNewRData(&rr->resrec, NULL, 0);
return mDNStrue;
}
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_AuthInfo *authInfo;
uDNS_RegInfo *rInfo = &srs->uDNS_info;
mStatus err = mStatus_UnknownErr;
id = newMessageID(u);
InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
rInfo->id.NotAnInteger = id.NotAnInteger;
if (setHostTarget(&srs->RR_SRV, m))
SetNewRData(&srs->RR_SRV.resrec, NULL, 0);
SetNewRData(&srs->RR_PTR.resrec, NULL, 0);
SetNewRData(&srs->RR_TXT.resrec, NULL, 0);
ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass));
if (!ptr) goto error;
if (srs->uDNS_info.state == regState_Refresh)
{
ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0);
if (!ptr) goto error;
}
else
{
ptr = putPrereqNameNotInUse(&srs->RR_SRV.resrec.name, &msg, ptr, end);
if (!ptr) goto error;
}
if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec))) goto error;
if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_SRV.resrec))) goto error;
if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec))) goto error;
if (srs->uDNS_info.lease)
ptr = putUpdateLease(&msg, ptr);
srs->uDNS_info.expire = -1;
authInfo = GetAuthInfoForZone(u, &rInfo->zone);
if (authInfo)
{
err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &rInfo->ns, rInfo->port, authInfo);
if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendSignedDNSMessage - %d", err); goto error; }
}
else
{
err = mDNSSendDNSMessage(m, &msg, ptr, 0, &rInfo->ns, rInfo->port);
if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %d", err); goto error; }
}
if (rInfo->state != regState_Refresh)
rInfo->state = regState_Pending;
return;
error:
unlinkSRS(u, srs);
rInfo->state = regState_Unregistered;
srs->ServiceCallback(m, srs, err);
}
mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result)
{
ServiceRecordSet *srs = (ServiceRecordSet *)srsPtr;
const zoneData_t *zoneData = &result->zoneData;
uDNS_GlobalInfo *u = &m->uDNS_info;
if (err) goto error;
if (result->type != zoneDataResult)
{
LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type);
goto error;
}
if (srs->uDNS_info.state == regState_Cancelled)
{
srs->uDNS_info.state = regState_Unregistered;
unlinkSRS(u, srs);
srs->ServiceCallback(m, srs, mStatus_MemFree);
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;
}
ustrcpy(srs->uDNS_info.zone.c, zoneData->zoneName.c);
srs->uDNS_info.ns.type = mDNSAddrType_IPv4;
srs->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger;
srs->uDNS_info.port.NotAnInteger = zoneData->updatePort.NotAnInteger;
SendServiceRegistration(m, srs);
return;
error:
unlinkSRS(u, srs);
srs->uDNS_info.state = regState_Unregistered;
srs->ServiceCallback(m, srs, err);
}
mDNSexport void uDNS_UpdateServiceTargets(mDNS *const m)
{
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
uDNS_GlobalInfo *u = &m->uDNS_info;
ServiceRecordSet *srs;
AuthRecord *rr;
mStatus err = mStatus_NoError;
if (!m->uDNS_info.hostname.c[0])
{
LogMsg("ERROR: uDNS_UpdateServiceTargets called before registration of hostname");
return;
}
for (srs = u->ServiceRegistrations; srs; srs = srs->next)
{
if (err) srs = u->ServiceRegistrations;
rr = &srs->RR_SRV;
if (srs->uDNS_info.state != regState_Registered)
{
LogMsg("ERROR: uDNS_UpdateServiceTargets - service %s not registered", rr->resrec.name.c);
continue;
}
InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags);
ptr = putZone(&msg, ptr, end, &srs->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (ptr) ptr = putDeletionRecord(&msg, ptr, &rr->resrec); if (!setHostTarget(rr, m)) continue;
if (ptr) ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec); if (!ptr) err = mStatus_UnknownErr;
else err = mDNSSendDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port);
if (err)
{
LogMsg("ERROR: uDNS_UpdateServiceTargets - %s", ptr ? "mDNSSendDNSMessage" : "message formatting error");
unlinkSRS(u, srs);
srs->uDNS_info.state = regState_Unregistered;
srs->ServiceCallback(m, srs, err);
}
else srs->uDNS_info.state = regState_TargetChange;
}
}
mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr)
{
domainname *target = GetRRDomainNameTarget(&rr->resrec);
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 %s",
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", GetRRDisplayString(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", GetRRDisplayString(m, rr));
return mStatus_Invalid;
}
rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name);
rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
rr->resrec.rdnamehash = target ? DomainNameHashValue(target) : 0;
rr->uDNS_info.state = regState_FetchingZoneData;
rr->next = m->uDNS_info.RecordRegistrations;
m->uDNS_info.RecordRegistrations = rr;
rr->uDNS_info.lease = mDNStrue;
return startGetZoneData(&rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr);
}
mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
mStatus err;
uDNS_AuthInfo *authInfo;
switch (rr->uDNS_info.state)
{
case regState_FetchingZoneData:
rr->uDNS_info.state = regState_Cancelled;
return mStatus_NoError;
case regState_Pending:
rr->uDNS_info.state = regState_DeregDeferred;
debugf("Deferring deregistration of record %s until registration completes", rr->resrec.name.c);
return mStatus_NoError;
case regState_Registered:
break;
case regState_DeregPending:
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;
default:
LogMsg("ERROR: uDNS_DeregisterRecord called for record %s type %d with unknown state %d",
rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state);
return mStatus_UnknownErr;
}
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;
authInfo = GetAuthInfoForZone(u, &rr->uDNS_info.zone);
if (authInfo)
{
err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &rr->uDNS_info.ns, rr->uDNS_info.port, authInfo);
if (err) { LogMsg("ERROR: uDNS_DeregiserRecord - mDNSSendSignedDNSMessage - %d", err); goto error; }
}
else
{
err = mDNSSendDNSMessage(m, &msg, ptr, 0, &rr->uDNS_info.ns, rr->uDNS_info.port);
if (err) { LogMsg("ERROR: uDNS_DeregisterRecord - mDNSSendDNSMessage - %d", err); goto error; }
}
return mStatus_NoError;
error:
if (rr->uDNS_info.state != regState_Unregistered)
{
unlinkAR(&u->RecordRegistrations, rr);
rr->uDNS_info.state = regState_Unregistered;
}
return mStatus_UnknownErr;
}
mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs)
{
if (!*m->uDNS_info.NameRegDomain)
{
LogMsg("ERROR: uDNS_RegisterService - cannot register unicast service "
"without setting the NameRegDomain via mDNSResponder.conf");
srs->uDNS_info.state = regState_Unregistered;
return mStatus_UnknownErr;
}
srs->RR_SRV.resrec.rroriginalttl = 3;
srs->RR_TXT.resrec.rroriginalttl = 3;
srs->RR_PTR.resrec.rroriginalttl = 3;
srs->uDNS_info.state = regState_FetchingZoneData;
srs->next = m->uDNS_info.ServiceRegistrations;
m->uDNS_info.ServiceRegistrations = srs;
srs->uDNS_info.lease = mDNStrue;
return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
}
mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
{
uDNS_GlobalInfo *u = &m->uDNS_info;
DNSMessage msg;
mDNSu8 *ptr = msg.data;
mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
mStatus err = mStatus_UnknownErr;
uDNS_AuthInfo *authInfo;
switch (srs->uDNS_info.state)
{
case regState_Unregistered:
LogMsg("ERROR: uDNS_DeregisterService - service not registerd");
return mStatus_UnknownErr;
case regState_FetchingZoneData:
case regState_Pending:
srs->uDNS_info.state = regState_Cancelled;
return mStatus_NoError; case regState_DeregPending:
case regState_DeregDeferred:
case regState_Cancelled:
LogMsg("uDNS_DeregisterService - deregistration in process");
return mStatus_UnknownErr;
}
srs->uDNS_info.state = regState_DeregPending;
InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags);
ptr = putZone(&msg, ptr, end, &srs->uDNS_info.zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass));
if (!ptr) { LogMsg("ERROR: uDNS_DeregisterService - putZone"); goto error; }
ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0);
if (!ptr) { LogMsg("ERROR: uDNS_DeregisterService - PutResourceRecordTTL"); goto error; }
if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_SRV.resrec))) goto error;
if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_TXT.resrec))) goto error;
if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error;
authInfo = GetAuthInfoForZone(u, &srs->uDNS_info.zone);
if (authInfo)
{
err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port, authInfo);
if (err) { LogMsg("ERROR: uDNS_DeregiserService - mDNSSendSignedDNSMessage - %d", err); goto error; }
}
else
{
err = mDNSSendDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port);
if (err) { LogMsg("ERROR: uDNS_DeregisterService - mDNSSendDNSMessage - %d", err); goto error; }
}
return mStatus_NoError;
error:
unlinkSRS(u, srs);
srs->uDNS_info.state = regState_Unregistered;
return err;
}
mDNSexport void uDNS_Execute(mDNS *const m)
{
DNSQuestion *q;
DNSMessage msg;
mStatus err;
mDNSu8 *end;
mDNSs32 sendtime;
LLQ_Info *llq;
AuthRecord *rr;
ServiceRecordSet *srs;
uDNS_RegInfo *rInfo;
uDNS_GlobalInfo *u = &m->uDNS_info;
const mDNSAddr *server = getInitializedDNS(&m->uDNS_info);
mDNSs32 timenow = mDNSPlatformTimeNow();
u->nextevent = timenow + 0x78000000;
if (!server) { debugf("uDNS_Execute - no DNS server"); return; }
for (q = u->ActiveQueries; q; q = q->next)
{
llq = q->uDNS_info.llq;
if (q->LongLived && llq->state != LLQ_Poll)
{
if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Suspended && llq->retry <= timenow)
{
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);
else if (llq->state == LLQ_SecondaryRequest)
sendChallengeResponse(m, q, NULL);
else if (llq->state == LLQ_Retry)
{ llq->ntries = 0; startLLQHandshake(m, llq); }
}
}
else
{
sendtime = q->LastQTime + q->ThisQInterval;
if (sendtime <= timenow)
{
err = constructQueryMsg(&msg, &end, q);
if (err)
{
LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %s",
q->qname.c);
continue;
}
err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, server, UnicastDNSPort);
if (err) { debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %d", err); } q->LastQTime = timenow;
if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2;
}
else if (u->nextevent - sendtime > 0) u->nextevent = sendtime;
}
}
for (rr = u->RecordRegistrations; rr; rr = rr->next)
{
rInfo = &rr->uDNS_info;
if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0)
{
if (rInfo->expire < timenow)
{
debugf("refreshing record %s", rr->resrec.name.c);
rInfo->state = regState_Refresh;
sendRecordRegistration(m, rr);
}
else if (u->nextevent - rInfo->expire > 0) u->nextevent = rInfo->expire;
}
}
for (srs = u->ServiceRegistrations; srs; srs = srs->next)
{
rInfo = &srs->uDNS_info;
if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0)
{
if (rInfo->expire < timenow)
{
debugf("refreshing service %s", srs->RR_SRV.resrec.name.c);
rInfo->state = regState_Refresh;
SendServiceRegistration(m, srs);
}
else if (u->nextevent - rInfo->expire > 0) u->nextevent = rInfo->expire;
}
}
}
mDNSexport void uDNS_Init(mDNS *const m)
{
mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo));
m->uDNS_info.nextevent = mDNSPlatformTimeNow() + 0x78000000;
}