#include "uDNS.h"
#if(defined(_MSC_VER))
#pragma warning(disable:4706)
#endif
typedef struct tcpInfo_t
{
mDNS *m;
TCPSocket *sock;
DNSMessage request;
int requestLen;
DNSQuestion *question; ServiceRecordSet *srs; AuthRecord *rr; mDNSAddr Addr;
mDNSIPPort Port;
DNSMessage *reply;
mDNSu16 replylen;
unsigned long nread;
int numReplies;
} tcpInfo_t;
typedef struct SearchListElem
{
struct SearchListElem *next;
domainname domain;
int flag; DNSQuestion BrowseQ;
DNSQuestion DefBrowseQ;
DNSQuestion AutomaticBrowseQ;
DNSQuestion RegisterQ;
DNSQuestion DefRegisterQ;
ARListElem *AuthRecs;
} SearchListElem;
static SearchListElem *SearchList = mDNSNULL;
static ServiceRecordSet *CurrentServiceRecordSet = mDNSNULL;
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - General Utility Functions
#endif
mDNSlocal mStatus UnlinkAuthRecord(mDNS *const m, AuthRecord *const rr)
{
AuthRecord **list = &m->ResourceRecords;
if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
while (*list && *list != rr) list = &(*list)->next;
if (!*list)
{
list = &m->DuplicateRecords;
while (*list && *list != rr) list = &(*list)->next;
}
if (*list) { *list = rr->next; rr->next = mDNSNULL; return(mStatus_NoError); }
LogMsg("ERROR: UnlinkAuthRecord - no such active record %##s", rr->resrec.name->c);
return(mStatus_NoSuchRecord);
}
mDNSlocal void unlinkSRS(mDNS *const m, ServiceRecordSet *srs)
{
ServiceRecordSet **p;
if (srs->NATinfo.clientContext)
{
mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.clientContext = mDNSNULL;
}
for (p = &m->ServiceRegistrations; *p; p = &(*p)->uDNS_next)
if (*p == srs)
{
ExtraResourceRecord *e;
*p = srs->uDNS_next;
if (CurrentServiceRecordSet == srs)
CurrentServiceRecordSet = srs->uDNS_next;
srs->uDNS_next = mDNSNULL;
for (e=srs->Extras; e; e=e->next)
if (UnlinkAuthRecord(m, &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 SetRecordRetry(mDNS *const m, AuthRecord *rr, mStatus SendErr)
{
rr->LastAPTime = m->timenow;
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 rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Name Server List Management
#endif
mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port)
{
DNSServer **p = &m->DNSServers;
if (!d) d = (const domainname *)"";
LogOperation("mDNS_AddDNSServer: Adding %#a for %##s", addr, d->c);
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
while (*p) {
if ((*p)->interface == interface && (*p)->teststate != DNSServer_Disabled &&
mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d))
{
if (!(*p)->del) LogMsg("Note: DNS Server %#a for domain %##s registered more than once", addr, d->c);
(*p)->del = mDNSfalse;
return(*p);
}
p=&(*p)->next;
}
*p = mDNSPlatformMemAllocate(sizeof(**p));
if (!*p) LogMsg("Error: mDNS_AddDNSServer - malloc");
else
{
(*p)->interface = interface;
(*p)->addr = *addr;
(*p)->port = port;
(*p)->del = mDNSfalse;
(*p)->teststate = DNSServer_Untested;
(*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL;
AssignDomainName(&(*p)->domain, d);
(*p)->next = mDNSNULL;
}
return(*p);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - authorization management
#endif
mDNSlocal DomainAuthInfo *GetAuthInfoForName_direct(mDNS *m, const domainname *const name)
{
const domainname *n = name;
while (n->c[0])
{
DomainAuthInfo *ptr;
for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
if (SameDomainName(&ptr->domain, n))
{
debugf("GetAuthInfoForName %##s Matched %##s Key name %##s", name->c, ptr->domain.c, ptr->keyname.c);
return(ptr);
}
n = (const domainname *)(n->c + 1 + n->c[0]);
}
return mDNSNULL;
}
mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name)
{
DomainAuthInfo **p = &m->AuthInfoList;
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("GetAuthInfoForName_internal: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
while (*p)
{
if ((*p)->deltime && m->timenow - (*p)->deltime >= 0 && AutoTunnelUnregistered(*p))
{
DNSQuestion *q;
DomainAuthInfo *info = *p;
LogOperation("GetAuthInfoForName_internal deleting expired key %##s %##s", info->domain.c, info->keyname.c);
*p = info->next; for (q = m->Questions; q; q=q->next)
if (q->AuthInfo == info)
{
q->AuthInfo = GetAuthInfoForName_direct(m, &q->qname);
debugf("GetAuthInfoForName_internal updated q->AuthInfo from %##s to %##s for %##s (%s)",
info->domain.c, q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
}
mDNSPlatformMemZero(info, sizeof(*info));
mDNSPlatformMemFree(info);
}
else
p = &(*p)->next;
}
return(GetAuthInfoForName_direct(m, name));
}
mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name)
{
DomainAuthInfo *d;
mDNS_Lock(m);
d = GetAuthInfoForName_internal(m, name);
mDNS_Unlock(m);
return(d);
}
mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
{
DNSQuestion *q;
DomainAuthInfo **p = &m->AuthInfoList;
if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); }
LogOperation("mDNS_SetSecretForDomain: domain %##s key %##s%s", domain->c, keyname->c, AutoTunnel ? " AutoTunnel" : "");
info->AutoTunnel = AutoTunnel;
AssignDomainName(&info->domain, domain);
AssignDomainName(&info->keyname, keyname);
mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata);
if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0)
{
LogMsg("mDNS_SetSecretForDomain: ERROR: Could not convert shared secret from base64: domain %##s key %##s %s",
domain->c, keyname->c, LogAllOperations ? b64keydata : "");
return(mStatus_BadParamErr);
}
info->deltime = 0;
while (*p && (*p) != info) p=&(*p)->next;
if (*p) return(mStatus_AlreadyRegistered);
info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered;
info->AutoTunnelHostRecord.namestorage.c[0] = 0;
info->AutoTunnelTarget .resrec.RecordType = kDNSRecordTypeUnregistered;
info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered;
info->AutoTunnelService .resrec.RecordType = kDNSRecordTypeUnregistered;
info->AutoTunnelNAT.clientContext = mDNSNULL;
info->next = mDNSNULL;
*p = info;
for (q = m->Questions; q; q=q->next)
{
if (q->QuestionCallback != GetZoneData_QuestionCallback)
{
DomainAuthInfo *newinfo = GetAuthInfoForName_internal(m, &q->qname);
if (q->AuthInfo != newinfo)
{
debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)",
q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL,
newinfo ? newinfo ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
q->AuthInfo = newinfo;
}
}
}
return(mStatus_NoError);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - NAT Traversal
#endif
mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info)
{
mStatus err = mStatus_NoError;
if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSv4AddrIsRFC1918(&m->Router.ip.v4))
{
union { NATAddrRequest NATAddrReq; NATPortMapRequest NATPortReq; } u = { { NATMAP_VERS, NATOp_AddrRequest } } ;
const mDNSu8 *end = (mDNSu8 *)&u + sizeof(NATAddrRequest);
if (info) {
mDNSu8 *p = (mDNSu8 *)&u.NATPortReq.NATReq_lease;
u.NATPortReq.opcode = info->Protocol;
u.NATPortReq.unused = zeroID;
u.NATPortReq.intport = info->IntPort;
u.NATPortReq.extport = info->RequestedPort;
p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF);
p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF);
p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF);
p[3] = (mDNSu8)( info->NATLease & 0xFF);
end = (mDNSu8 *)&u + sizeof(NATPortMapRequest);
}
err = mDNSPlatformSendUDP(m, (mDNSu8 *)&u, end, 0, &m->Router, NATPMPPort);
#ifdef _LEGACY_NAT_TRAVERSAL_
if (mDNSIPPortIsZero(m->UPnPSOAPPort)) LNT_SendDiscoveryMsg(m);
else if (info) err = LNT_MapPort(m, info);
else err = LNT_GetExternalAddress(m);
#endif // _LEGACY_NAT_TRAVERSAL_
}
return(err);
}
mDNSlocal void RecreateNATMappings(mDNS *const m)
{
NATTraversalInfo *n;
for (n = m->NATTraversals; n; n=n->next)
{
n->ExpiryTime = 0; n->retryInterval = NATMAP_INIT_RETRY;
n->retryPortMap = m->timenow;
#ifdef _LEGACY_NAT_TRAVERSAL_
if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
#endif // _LEGACY_NAT_TRAVERSAL_
}
m->NextScheduledNATOp = m->timenow; }
#ifdef _LEGACY_NAT_TRAVERSAL_
mDNSlocal void ClearUPnPState(mDNS *const m)
{
if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; }
if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; }
m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; }
#else
#define ClearUPnPState(X)
#endif // _LEGACY_NAT_TRAVERSAL_
mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr)
{
if (err) LogMsg("Error getting external address %d", err);
else if (!mDNSSameIPv4Address(m->ExternalAddress, ExtAddr))
{
LogOperation("Received external IP address %.4a from NAT", &ExtAddr);
if (mDNSv4AddrIsRFC1918(&ExtAddr))
LogMsg("Double NAT (external NAT gateway address %.4a is also a private RFC 1918 address)", &ExtAddr);
m->ExternalAddress = ExtAddr;
RecreateNATMappings(m); }
if (err || mDNSIPv4AddressIsZero(ExtAddr)) m->retryIntervalGetAddr = NATMAP_INIT_RETRY * 32; else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
if (m->NextScheduledNATOp - m->retryIntervalGetAddr > 0)
m->NextScheduledNATOp = m->retryIntervalGetAddr;
}
mDNSlocal void NATSetNextRenewalTime(mDNS *const m, NATTraversalInfo *n)
{
n->retryInterval = (n->ExpiryTime - m->timenow)/2;
if (n->retryInterval < NATMAP_MIN_RETRY_INTERVAL) n->retryInterval = NATMAP_MIN_RETRY_INTERVAL;
n->retryPortMap = m->timenow + n->retryInterval;
}
mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease)
{
n->NewResult = err;
if (err || lease == 0 || mDNSIPPortIsZero(extport))
{
LogOperation("natTraversalHandlePortMapReply: received error making port mapping error %d port %d", err, mDNSVal16(extport));
n->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL;
if (err == NATErr_Refused) n->NewResult = mStatus_NATPortMappingDisabled;
else if (err > NATErr_None && err <= NATErr_Opcode) n->NewResult = mStatus_NATPortMappingUnsupported;
}
else
{
if (lease > 999999999UL / mDNSPlatformOneSecond)
lease = 999999999UL / mDNSPlatformOneSecond;
n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond);
if (!mDNSSameIPPort(n->RequestedPort, extport))
LogOperation("natTraversalHandlePortMapReply: public port changed from %d to %d", mDNSVal16(n->RequestedPort), mDNSVal16(extport));
n->InterfaceID = InterfaceID;
n->RequestedPort = extport;
LogOperation("natTraversalHandlePortMapReply %p %s Internal Port %d External Port %d", n,
n->Protocol == NATOp_MapUDP ? "UDP Response" :
n->Protocol == NATOp_MapTCP ? "TCP Response" : "?", mDNSVal16(n->IntPort), mDNSVal16(n->RequestedPort));
NATSetNextRenewalTime(m, n); m->NextScheduledNATOp = m->timenow; }
}
mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal)
{
NATTraversalInfo **n;
LogOperation("mDNS_StartNATOperation_internal %d %d %d %d",
traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease);
n = &m->NATTraversals;
while (*n && *n != traversal) n=&(*n)->next;
if (*n) { LogMsg("Error! Tried to add a NAT traversal that's already in the active list"); return(mStatus_AlreadyRegistered); }
traversal->next = mDNSNULL;
traversal->ExpiryTime = 0;
traversal->retryInterval = NATMAP_INIT_RETRY;
traversal->retryPortMap = m->timenow;
traversal->NewResult = mStatus_NoError;
traversal->ExternalAddress = onesIPv4Addr;
traversal->ExternalPort = zeroIPPort;
traversal->Lifetime = 0;
traversal->Result = mStatus_NoError;
if (!traversal->NATLease) traversal->NATLease = NATMAP_DEFAULT_LEASE;
#ifdef _LEGACY_NAT_TRAVERSAL_
mDNSPlatformMemZero(&traversal->tcpInfo, sizeof(traversal->tcpInfo));
#endif _LEGACY_NAT_TRAVERSAL_
if (!m->NATTraversals) {
m->retryGetAddr = m->timenow;
m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
}
m->NextScheduledNATOp = m->timenow;
*n = traversal;
return(mStatus_NoError);
}
mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal)
{
NATTraversalInfo **ptr = &m->NATTraversals;
while (*ptr && *ptr != traversal) ptr=&(*ptr)->next;
if (*ptr) *ptr = (*ptr)->next; else
{
LogMsg("mDNS_StopNATOperation: NATTraversalInfo %p not found in list", traversal);
return(mStatus_BadReferenceErr);
}
LogOperation("mDNS_StopNATOperation_internal %d %d %d %d",
traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease);
if (m->CurrentNATTraversal == traversal)
m->CurrentNATTraversal = m->CurrentNATTraversal->next;
if (traversal->ExpiryTime)
{
traversal->NATLease = 0;
traversal->retryInterval = 0;
uDNS_SendNATMsg(m, traversal);
}
#ifdef _LEGACY_NAT_TRAVERSAL_
{
mStatus err = LNT_UnmapPort(m, traversal);
if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %ld", err);
}
#endif return(mStatus_NoError);
}
mDNSexport mStatus mDNS_StartNATOperation(mDNS *m, NATTraversalInfo *traversal)
{
mStatus status;
mDNS_Lock(m);
status = mDNS_StartNATOperation_internal(m, traversal);
mDNS_Unlock(m);
return(status);
}
mDNSexport mStatus mDNS_StopNATOperation(mDNS *m, NATTraversalInfo *traversal)
{
mStatus status;
mDNS_Lock(m);
status = mDNS_StopNATOperation_internal(m, traversal);
mDNS_Unlock(m);
return(status);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Long-Lived Queries
#endif
mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q)
{
LogOperation("StartLLQPolling: %##s", q->qname.c);
q->state = LLQ_Poll;
q->ThisQInterval = LLQ_POLL_INTERVAL;
q->LastQTime = m->timenow - q->ThisQInterval; SetNextQueryTime(m, q);
}
mDNSlocal void LLQNatMapComplete(mDNS *m, NATTraversalInfo *n);
mDNSlocal void StartLLQNatMap(mDNS *m, DNSQuestion *q)
{
LogOperation("StartLLQNatMap: LLQ_NatMapWaitUDP");
mDNSPlatformMemZero(&q->NATInfoUDP, sizeof(NATTraversalInfo));
q->NATInfoUDP.IntPort = q->NATInfoUDP.RequestedPort = m->UnicastPort4;
q->NATInfoUDP.Protocol = NATOp_MapUDP;
q->NATInfoUDP.clientCallback = LLQNatMapComplete;
q->NATInfoUDP.clientContext = q; mDNS_StartNATOperation_internal(m, &q->NATInfoUDP);
}
mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const 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; }
}
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;
optRD->OptData.llq = *data;
ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0);
if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; }
return ptr;
}
mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question)
{
InitializeDNSMessage(&msg->h, question->TargetQID, uQueryFlags);
*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 void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const LLQOptData *llq)
{
mDNSu8 *responsePtr = m->omsg.data;
mStatus err;
LLQOptData llqBuf;
if (q->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);
q->state = LLQ_Retry;
q->LastQTime = m->timenow;
q->ThisQInterval = (kLLQ_DEF_RETRY * mDNSPlatformOneSecond);
SetNextQueryTime(m, q);
return;
}
if (!llq) {
llqBuf.vers = kLLQ_Vers;
llqBuf.llqOp = kLLQOp_Setup;
llqBuf.err = LLQErr_NoError;
llqBuf.id = q->id;
llqBuf.llqlease = q->origLease;
llq = &llqBuf;
}
q->LastQTime = m->timenow;
q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond); SetNextQueryTime(m, q);
if (constructQueryMsg(&m->omsg, &responsePtr, q)) goto error;
responsePtr = putLLQ(&m->omsg, responsePtr, q, llq, mDNSfalse);
if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; }
err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
if (err) debugf("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err);
return;
error:
q->state = LLQ_Error;
}
mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const rdataOPT *opt)
{
mStatus err = mStatus_NoError;
if (rcode && rcode != kDNSFlag1_RC_NXDomain)
{ LogMsg("ERROR: recvSetupResponse %##s - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c); goto fail; }
if (opt->OptData.llq.llqOp != kLLQOp_Setup)
{ LogMsg("ERROR: recvSetupResponse %##s - bad op %d", q->qname.c, opt->OptData.llq.llqOp); goto fail; }
if (opt->OptData.llq.vers != kLLQ_Vers)
{ LogMsg("ERROR: recvSetupResponse %##s - bad vers %d", q->qname.c, opt->OptData.llq.vers); goto fail; }
if (q->state == LLQ_InitialRequest)
{
const LLQOptData *const llq = &opt->OptData.llq;
switch(llq->err)
{
case LLQErr_NoError: break;
case LLQErr_ServFull:
LogMsg("recvSetupResponse - received ServFull from server for LLQ %##s Retry in %lu sec", q->qname.c, llq->llqlease);
q->LastQTime = m->timenow;
q->ThisQInterval = ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond);
q->state = LLQ_Retry;
SetNextQueryTime(m, q);
case LLQErr_Static:
q->state = LLQ_Static;
q->ThisQInterval = 0;
LogMsg("LLQ %##s: static", q->qname.c);
goto exit;
case LLQErr_FormErr:
LogMsg("ERROR: recvSetupResponse - received FormErr from server for LLQ %##s", q->qname.c);
goto error;
case LLQErr_BadVers:
LogMsg("ERROR: recvSetupResponse - received BadVers from server");
goto error;
case LLQErr_UnknownErr:
LogMsg("ERROR: recvSetupResponse - received UnknownErr from server for LLQ %##s", q->qname.c);
goto error;
default:
LogMsg("ERROR: recvSetupResponse - received invalid error %d for LLQ %##s", llq->err, q->qname.c);
goto error;
}
if (q->origLease != llq->llqlease)
debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->origLease, llq->llqlease);
q->origLease = llq->llqlease;
q->expire = m->timenow + ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond);
q->state = LLQ_SecondaryRequest;
q->id = llq->id;
q->ntries = 0;
sendChallengeResponse(m, q, llq);
goto exit;
error:
q->state = LLQ_Error;
goto exit;
}
if (q->state == LLQ_SecondaryRequest)
{
if (q->AuthInfo)
{
LogOperation("Private LLQ_SecondaryRequest; copying id %08X%08X", opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1]);
q->id = opt->OptData.llq.id;
}
if (opt->OptData.llq.err) { LogMsg("ERROR: recvSetupResponse %##s code %d from server", q->qname.c, opt->OptData.llq.err); q->state = LLQ_Error; goto fail; }
if (!mDNSSameOpaque64(&q->id, &opt->OptData.llq.id))
{ LogMsg("recvSetupResponse - ID changed. discarding"); goto exit; } q->expire = m->timenow + ((mDNSs32)opt->OptData.llq.llqlease * mDNSPlatformOneSecond );
q->LastQTime = m->timenow;
q->ThisQInterval = ((mDNSs32)opt->OptData.llq.llqlease * (mDNSPlatformOneSecond / 2));
q->origLease = opt->OptData.llq.llqlease;
q->state = LLQ_Established;
SetNextQueryTime(m, q);
goto exit;
}
if (q->state == LLQ_Established) goto exit;
LogMsg("ERROR: recvSetupResponse %##s - bad state %d", q->qname.c, q->state);
fail:
err = mStatus_UnknownErr;
exit:
if (err)
{
LogOperation("recvSetupResponse error %d ", err);
StartLLQPolling(m, q);
}
}
mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport)
{
DNSQuestion pktQ, *q;
if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ))
{
const rdataOPT *opt = GetLLQOptData(m, msg, end);
if (opt)
{
for (q = m->Questions; q; q = q->next)
{
if (q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname))
{
debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d",
q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr,
opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1], q->id.l[0], q->id.l[1], opt->OptData.llq.llqOp);
if (q->state == LLQ_Poll)
{
m->rec.r.resrec.RecordType = 0; return uDNS_LLQ_Poll;
}
else if (q->state == LLQ_Established || (q->state == LLQ_Refresh && msg->h.numAnswers))
{
if (opt->OptData.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
{
mDNSu8 *ackEnd;
if (q->LongLived && q->state == LLQ_Poll && !mDNSIPPortIsZero(q->servPort)) q->ThisQInterval = LLQ_POLL_INTERVAL;
else if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags);
ackEnd = putLLQ(&m->omsg, m->omsg.data, mDNSNULL, &opt->OptData.llq, mDNSfalse);
if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, srcaddr, srcport, mDNSNULL, mDNSNULL);
m->rec.r.resrec.RecordType = 0; return uDNS_LLQ_Events;
}
}
if (mDNSSameOpaque16(msg->h.id, q->TargetQID))
{
if (opt->OptData.llq.llqOp == kLLQOp_Refresh && q->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
{
if (opt->OptData.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->OptData.llq.err);
else
{
GrantCacheExtensions(m, q, opt->OptData.llq.llqlease);
q->expire = q->LastQTime + ((mDNSs32)opt->OptData.llq.llqlease * mDNSPlatformOneSecond );
q->ThisQInterval = ((mDNSs32)opt->OptData.llq.llqlease * (mDNSPlatformOneSecond/2));
q->origLease = opt->OptData.llq.llqlease;
q->state = LLQ_Established;
}
m->rec.r.resrec.RecordType = 0; return uDNS_LLQ_Events;
}
if (q->state < LLQ_Static)
{
if (mDNSSameAddress(srcaddr, &q->servAddr))
{
LLQ_State oldstate = q->state;
recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, opt);
m->rec.r.resrec.RecordType = 0; return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Setup : uDNS_LLQ_Events);
}
}
}
}
}
m->rec.r.resrec.RecordType = 0; }
}
return uDNS_LLQ_Not;
}
mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease);
struct TCPSocket_struct { TCPSocketFlags flags; };
mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
{
tcpInfo_t *tcpInfo = (tcpInfo_t *)context;
mDNSBool closed = mDNSfalse;
mDNSu8 *end;
mDNS *m = tcpInfo->m;
DomainAuthInfo *AuthInfo =
tcpInfo->question ? tcpInfo->question->AuthInfo :
tcpInfo->srs ? GetAuthInfoForName(m, tcpInfo->srs->RR_SRV.resrec.name) :
tcpInfo->rr ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL;
tcpInfo_t **backpointer =
tcpInfo->question ? &tcpInfo->question->tcp :
tcpInfo->srs ? &tcpInfo->srs->tcp :
tcpInfo->rr ? &tcpInfo->rr->tcp : mDNSNULL;
if (!backpointer) LogMsg("tcpCallback: Purpose of connection unidentified");
else if (*backpointer != tcpInfo)
LogMsg("tcpCallback: %d backpointer %p incorrect tcpInfo %p question %p srs %p rr %p",
mDNSPlatformTCPGetFD(tcpInfo->sock), *backpointer, tcpInfo, tcpInfo->question, tcpInfo->srs, tcpInfo->rr);
if (err) goto exit;
if (ConnectionEstablished)
{
if (tcpInfo->question && tcpInfo->question->LongLived && (tcpInfo->question->state == LLQ_Established || tcpInfo->question->state == LLQ_Refresh))
{
mDNS_Lock(m);
sendLLQRefresh(m, tcpInfo->question, tcpInfo->question->origLease);
mDNS_Unlock(m);
return;
}
else if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state != LLQ_Poll)
{
LLQOptData llqData; llqData.vers = kLLQ_Vers;
llqData.llqOp = kLLQOp_Setup;
llqData.err = LLQErr_NoError;
llqData.err = mDNSVal16(tcpInfo->question->eventPort); if (llqData.err == 0) llqData.err = 5353; LogOperation("tcpCallback: eventPort %d", llqData.err);
llqData.id = zeroOpaque64;
llqData.llqlease = kLLQ_DefLease;
InitializeDNSMessage(&tcpInfo->request.h, tcpInfo->question->TargetQID, uQueryFlags);
end = putLLQ(&tcpInfo->request, tcpInfo->request.data, tcpInfo->question, &llqData, mDNStrue);
if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; }
}
else if (tcpInfo->question)
{
err = constructQueryMsg(&tcpInfo->request, &end, tcpInfo->question);
if (err) { LogMsg("ERROR: tcpCallback: constructQueryMsg - %ld", err); err = mStatus_UnknownErr; goto exit; }
}
else
end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen;
err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo);
if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %ld", err); err = mStatus_UnknownErr; goto exit; }
if (tcpInfo->question)
{
mDNS_Lock(m);
tcpInfo->question->LastQTime = m->timenow;
tcpInfo->question->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
SetNextQueryTime(m, tcpInfo->question);
mDNS_Unlock(m);
}
}
else
{
long n;
if (tcpInfo->nread < 2) {
mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replylen;
n = mDNSPlatformReadTCP(sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed);
if (n < 0) { LogMsg("ERROR:tcpCallback - attempt to read message length failed (%d)", n); err = mStatus_ConnFailed; goto exit; }
else if (closed)
{
if (tcpInfo->numReplies == 0) LogMsg("ERROR: socket closed prematurely %d", tcpInfo->nread);
err = mStatus_ConnFailed;
goto exit;
}
tcpInfo->nread += n;
if (tcpInfo->nread < 2) goto exit;
tcpInfo->replylen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]);
if (tcpInfo->replylen < sizeof(DNSMessageHeader))
{ LogMsg("ERROR: tcpCallback - length too short (%d bytes)", tcpInfo->replylen); err = mStatus_UnknownErr; goto exit; }
tcpInfo->reply = mDNSPlatformMemAllocate(tcpInfo->replylen);
if (!tcpInfo->reply) { LogMsg("ERROR: tcpCallback - malloc failed"); err = mStatus_NoMemoryErr; goto exit; }
}
n = mDNSPlatformReadTCP(sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replylen - (tcpInfo->nread - 2), &closed);
if (n < 0) { LogMsg("ERROR: tcpCallback - read returned %d", n); err = mStatus_ConnFailed; goto exit; }
else if (closed) { LogMsg("ERROR: socket closed prematurely %d", tcpInfo->nread); err = mStatus_ConnFailed; goto exit; }
tcpInfo->nread += n;
if ((tcpInfo->nread - 2) == tcpInfo->replylen)
{
AuthRecord *rr = tcpInfo->rr;
DNSMessage *reply = tcpInfo->reply;
mDNSu8 *end = (mDNSu8 *)tcpInfo->reply + tcpInfo->replylen;
mDNSAddr Addr = tcpInfo->Addr;
mDNSIPPort Port = tcpInfo->Port;
tcpInfo->numReplies++;
tcpInfo->reply = mDNSNULL; tcpInfo->nread = 0;
tcpInfo->replylen = 0;
if (!tcpInfo->question || !tcpInfo->question->LongLived) { *backpointer = mDNSNULL; DisposeTCPConn(tcpInfo); }
if (rr && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
{
mDNS_Lock(m);
LogOperation("tcpCallback: CompleteDeregistration %s", ARDisplayString(m, rr));
CompleteDeregistration(m, rr); mDNS_Unlock(m);
}
else
mDNSCoreReceive(m, reply, end, &Addr, Port, (sock->flags & kTCPSocketFlags_UseTLS) ? (mDNSAddr *)1 : mDNSNULL, zeroIPPort, 0);
mDNSPlatformMemFree(reply);
return;
}
}
exit:
if (err)
{
*backpointer = mDNSNULL;
if (tcpInfo->question)
{
DNSQuestion *q = tcpInfo->question;
mDNS_Lock(m); if (q->ThisQInterval == 0 || q->LastQTime + q->ThisQInterval - m->timenow > MAX_UCAST_POLL_INTERVAL)
{
q->LastQTime = m->timenow;
q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
SetNextQueryTime(m, q);
}
mDNS_Unlock(m);
if (err != mStatus_ConnFailed) tcpInfo->question->state = LLQ_Error;
}
if (tcpInfo->rr)
{
mDNSBool deregPending = (tcpInfo->rr->state == regState_DeregPending) ? mDNStrue : mDNSfalse;
UnlinkAuthRecord(m, tcpInfo->rr);
tcpInfo->rr->state = regState_Unregistered;
if (!deregPending)
{
if (tcpInfo->rr->RecordCallback)
tcpInfo->rr->RecordCallback(m, tcpInfo->rr, err);
}
}
if (tcpInfo->srs)
{
mDNSBool deregPending = (tcpInfo->srs->state == regState_DeregPending) ? mDNStrue : mDNSfalse;
unlinkSRS(m, tcpInfo->srs);
tcpInfo->srs->state = regState_Unregistered;
if (!deregPending)
{
tcpInfo->srs->ServiceCallback(m, tcpInfo->srs, err);
}
}
DisposeTCPConn(tcpInfo);
}
}
mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
TCPSocketFlags flags, const mDNSAddr *const Addr, const mDNSIPPort Port,
DNSQuestion *const question, ServiceRecordSet *const srs, AuthRecord *const rr)
{
mStatus err;
mDNSIPPort srcport = zeroIPPort;
tcpInfo_t *info = (tcpInfo_t *)mDNSPlatformMemAllocate(sizeof(tcpInfo_t));
if (!info) { LogMsg("ERROR: MakeTCP - memallocate failed"); return(mDNSNULL); }
mDNSPlatformMemZero(info, sizeof(tcpInfo_t));
info->m = m;
if (msg)
{
info->request = *msg;
info->requestLen = (int) (end - ((mDNSu8*)msg));
}
info->question = question;
info->srs = srs;
info->rr = rr;
info->Addr = *Addr;
info->Port = Port;
info->sock = mDNSPlatformTCPSocket(m, flags, &srcport);
if (!info->sock) { LogMsg("SendServiceRegistration: uanble to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); }
err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpCallback, info);
if (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); }
else if (err != mStatus_ConnPending ) { LogMsg("MakeTCPConnection: connection failed"); mDNSPlatformMemFree(info); return(mDNSNULL); }
return(info);
}
mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp)
{
mDNSPlatformTCPCloseConnection(tcp->sock);
if (tcp->reply) mDNSPlatformMemFree(tcp->reply);
mDNSPlatformMemFree(tcp);
}
mDNSlocal void RemoveLLQNatMappings(mDNS *m, DNSQuestion *q)
{
if (q->NATInfoUDP.clientContext)
{
mDNS_StopNATOperation_internal(m, &q->NATInfoUDP);
q->NATInfoUDP.clientContext = mDNSNULL;
}
}
mDNSlocal void startLLQHandshake(mDNS *m, DNSQuestion *q)
{
mStatus err = mStatus_NoError;
if (q->AuthInfo)
{
LogOperation("startLLQHandshakePrivate Addr %#a%s Server %#a:%d%s %##s (%s) eventport %d",
&m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
&q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "",
q->qname.c, DNSTypeName(q->qtype), mDNSVal16(q->eventPort));
if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&q->servAddr))
if (q->state == LLQ_InitialRequest)
{
if (!q->NATInfoUDP.clientContext) { q->state = LLQ_NatMapWaitUDP; StartLLQNatMap(m, q); goto exit; }
else
{
LogMsg("startLLQHandshake state == LLQ_InitialRequest but already have NATInfoUDP.clientContext %##s (%s)",
q->qname.c, DNSTypeName(q->qtype));
err = mStatus_UnknownErr;
goto exit;
}
}
LogOperation("startLLQHandshake TCP %p %##s (%s)", q->tcp, q->qname.c, DNSTypeName(q->qtype));
if (q->tcp) LogMsg("startLLQHandshake: Already have TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
else q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, q, mDNSNULL, mDNSNULL);
q->state = LLQ_SecondaryRequest; q->origLease = kLLQ_DefLease;
q->ThisQInterval = 0;
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
err = mStatus_NoError;
}
else
{
mDNSu8 *end;
LLQOptData llqData;
LogOperation("startLLQHandshake Addr %#a%s Server %#a:%d%s %##s (%s) RequestedPort %d",
&m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
&q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "",
q->qname.c, DNSTypeName(q->qtype), mDNSVal16(q->NATInfoUDP.RequestedPort));
if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&q->servAddr))
{
if (!q->NATInfoUDP.clientContext) { q->state = LLQ_NatMapWaitUDP; StartLLQNatMap(m, q); }
else { err = mStatus_UnknownErr; goto exit; }
}
if (q->ntries++ >= kLLQ_MAX_TRIES)
{ LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); err = mStatus_UnknownErr; goto exit; }
llqData.vers = kLLQ_Vers;
llqData.llqOp = kLLQOp_Setup;
llqData.err = LLQErr_NoError;
llqData.id = zeroOpaque64;
llqData.llqlease = kLLQ_DefLease;
InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
end = putLLQ(&m->omsg, m->omsg.data, q, &llqData, mDNStrue);
if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); q->state = LLQ_Error; return; }
err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
if (err) { debugf("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err); err = mStatus_NoError; }
q->state = LLQ_InitialRequest;
q->origLease = kLLQ_DefLease;
q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
q->LastQTime = m->timenow - q->ThisQInterval;
SetNextQueryTime(m, q);
err = mStatus_NoError;
}
exit:
if (err)
{
LogOperation("startLLQHandshake error %d ", err);
StartLLQPolling(m, q);
}
}
mDNSlocal void LLQNatMapComplete(mDNS *m, NATTraversalInfo *n)
{
if (m->CurrentQuestion)
LogMsg("LLQNatMapComplete: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
m->CurrentQuestion = m->Questions;
while (m->CurrentQuestion)
{
DNSQuestion *q = m->CurrentQuestion;
m->CurrentQuestion = m->CurrentQuestion->next;
if (q->LongLived)
{
if (q->state == LLQ_NatMapWaitUDP)
{
if (!n->Result)
{
q->state = LLQ_GetZoneInfo;
q->eventPort = n->ExternalPort;
startLLQHandshake(m, q);
}
else
{
LogMsg("LLQNatMapComplete error %d Internal %d Requested %d External %d", n->Result,
mDNSVal16(q->NATInfoUDP.IntPort), mDNSVal16(q->NATInfoUDP.RequestedPort), mDNSVal16(q->NATInfoUDP.ExternalPort));
RemoveLLQNatMappings(m, q);
StartLLQPolling(m, q);
}
}
}
}
m->CurrentQuestion = mDNSNULL;
}
mDNSexport const domainname *GetServiceTarget(mDNS *m, ServiceRecordSet *srs)
{
LogOperation("GetServiceTarget %##s", srs->RR_SRV.resrec.name->c);
if (!srs->RR_SRV.AutoTarget) return(&srs->RR_SRV.resrec.rdata->u.srv.target);
else
{
HostnameInfo *hi = m->Hostnames;
#if APPLE_OSX_mDNSResponder
DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name);
if (AuthInfo && AuthInfo->AutoTunnel)
{
if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0)
{
if (m->AutoTunnelHostAddr.b[0]) SetupLocalAutoTunnelInterface_internal(m);
return(mDNSNULL);
}
return(&AuthInfo->AutoTunnelHostRecord.namestorage);
}
#endif APPLE_OSX_mDNSResponder
while (hi)
{
if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh) return(hi->arv4.resrec.name);
if (hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) return(hi->arv6.resrec.name);
hi = hi->next;
}
if (m->StaticHostname.c[0]) return(&m->StaticHostname);
return(mDNSNULL);
}
}
mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs)
{
mDNSu8 *ptr = m->omsg.data;
mDNSu8 *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
mDNSOpaque16 id;
mStatus err = mStatus_NoError;
const domainname *target;
mDNSu32 i;
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("SendServiceRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
if (mDNSIPv4AddressIsZero(srs->ns.ip.v4)) { LogMsg("SendServiceRegistration - NS not set!"); return; }
id = mDNS_NewMessageID(m);
InitializeDNSMessage(&m->omsg.h, id, UpdateReqFlags);
SetNewRData(&srs->RR_PTR.resrec, mDNSNULL, 0); SetNewRData(&srs->RR_TXT.resrec, mDNSNULL, 0);
if (srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(srs->NATinfo.ExternalPort))
srs->RR_SRV.resrec.rdata->u.srv.port = srs->NATinfo.ExternalPort;
ptr = putZone(&m->omsg, ptr, end, &srs->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass));
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
if (srs->TestForSelfConflict)
{
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0))) { err = mStatus_UnknownErr; goto exit; }
if (!(ptr = putDeleteRRSet(&m->omsg, ptr, srs->RR_TXT.resrec.name, srs->RR_TXT.resrec.rrtype))) { err = mStatus_UnknownErr; goto exit; }
}
else if (srs->state != regState_Refresh && srs->state != regState_UpdatePending)
{
ptr = putDeleteRRSet(&m->omsg, ptr, srs->RR_SRV.resrec.name, kDNSQType_ANY);
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
}
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->RR_PTR.resrec, srs->RR_PTR.resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
for (i = 0; i < srs->NumSubTypes; i++)
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->SubTypes[i].resrec, srs->SubTypes[i].resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
if (srs->state == regState_UpdatePending) {
AuthRecord *txt = &srs->RR_TXT;
SetNewRData(&txt->resrec, txt->OrigRData, txt->OrigRDLen);
if (!(ptr = putDeletionRecord(&m->omsg, ptr, &srs->RR_TXT.resrec))) { err = mStatus_UnknownErr; goto exit; }
SetNewRData(&txt->resrec, txt->InFlightRData, txt->InFlightRDLen);
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
}
else
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
target = GetServiceTarget(m, srs);
if (!target)
{
debugf("SendServiceRegistration - no target for %##s", srs->RR_SRV.resrec.name->c);
srs->state = regState_NoTarget;
return;
}
if (!SameDomainName(target, &srs->RR_SRV.resrec.rdata->u.srv.target))
{
AssignDomainName(&srs->RR_SRV.resrec.rdata->u.srv.target, target);
SetNewRData(&srs->RR_SRV.resrec, mDNSNULL, 0); }
ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->RR_SRV.resrec, srs->RR_SRV.resrec.rroriginalttl);
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
if (srs->srs_uselease)
{ ptr = putUpdateLease(&m->omsg, ptr, DEFAULT_UPDATE_LEASE); if (!ptr) { err = mStatus_UnknownErr; goto exit; } }
if (srs->state != regState_Refresh && srs->state != regState_DeregDeferred && srs->state != regState_UpdatePending)
srs->state = regState_Pending;
srs->id = id;
if (srs->Private)
{
LogOperation("SendServiceRegistration TCP %p %s", srs->tcp, ARDisplayString(m, &srs->RR_SRV));
if (srs->tcp) LogMsg("SendServiceRegistration: Already have TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
else srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->ns, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
srs->RR_SRV.LastAPTime = m->timenow;
srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; }
else
{
err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->ns, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
if (err) debugf("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, &srs->RR_SRV, err);
}
err = mStatus_NoError;
exit:
if (err)
{
LogMsg("SendServiceRegistration - Error formatting message %d", err);
unlinkSRS(m, srs);
srs->state = regState_Unregistered;
mDNS_DropLockBeforeCallback();
srs->ServiceCallback(m, srs, err);
mDNS_ReclaimLockAfterCallback();
}
}
mDNSlocal const domainname *PUBLIC_UPDATE_SERVICE_TYPE = (const domainname*)"\x0B_dns-update" "\x04_udp";
mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x08_dns-llq" "\x04_udp";
mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp";
mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp";
mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp";
#define ZoneDataSRV(X) (\
(X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \
(X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \
(X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : (const domainname*)"")
mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype);
mDNSexport void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
ZoneData *zd = (ZoneData*)question->QuestionContext;
debugf("GetZoneData_QuestionCallback: %s %s", AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
if (!AddRecord) return; if (AddRecord == QC_addnocache && answer->rdlength == 0) return; if (answer->rrtype != question->qtype) return;
if (answer->rrtype == kDNSType_SOA)
{
debugf("GetZoneData GOT SOA %s", RRDisplayString(m, answer));
mDNS_StopQuery(m, question);
if (answer->rdlength)
{
AssignDomainName(&zd->ZoneName, answer->name);
zd->ZoneClass = answer->rrclass;
AssignDomainName(&zd->question.qname, &zd->ZoneName);
GetZoneData_StartQuery(m, zd, kDNSType_SRV);
}
else if (zd->CurrentSOA->c[0])
{
zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1);
AssignDomainName(&zd->question.qname, zd->CurrentSOA);
GetZoneData_StartQuery(m, zd, kDNSType_SOA);
}
else
{
LogMsg("ERROR: GetZoneData_QuestionCallback - recursed to root label of %##s without finding SOA", zd->ChildName.c);
zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd);
mDNSPlatformMemFree(zd);
}
}
else if (answer->rrtype == kDNSType_SRV)
{
debugf("GetZoneData GOT SRV %s", RRDisplayString(m, answer));
mDNS_StopQuery(m, question);
if (!answer->rdlength && zd->ZonePrivate && zd->ZoneService != ZoneServiceQuery)
{
zd->ZonePrivate = mDNSfalse; GetZoneData_StartQuery(m, zd, kDNSType_SRV); }
else
{
if (answer->rdlength)
{
AssignDomainName(&zd->Host, &answer->rdata->u.srv.target);
zd->Port = answer->rdata->u.srv.port;
AssignDomainName(&zd->question.qname, &zd->Host);
GetZoneData_StartQuery(m, zd, kDNSType_A);
}
else
{
zd->ZonePrivate = mDNSfalse;
zd->Host.c[0] = 0;
zd->Port = zeroIPPort;
zd->Addr = zeroAddr;
zd->ZoneDataCallback(m, mStatus_NoError, zd);
mDNSPlatformMemFree(zd);
}
}
}
else if (answer->rrtype == kDNSType_A)
{
debugf("GetZoneData GOT A %s", RRDisplayString(m, answer));
mDNS_StopQuery(m, question);
zd->Addr.type = mDNSAddrType_IPv4;
zd->Addr.ip.v4 = (answer->rdlength == 4) ? answer->rdata->u.ipv4 : zerov4Addr;
zd->ZoneDataCallback(m, mStatus_NoError, zd);
mDNSPlatformMemFree(zd);
}
}
mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype)
{
if (qtype == kDNSType_SRV)
{
LogOperation("lookupDNSPort %##s", ZoneDataSRV(zd));
AssignDomainName(&zd->question.qname, ZoneDataSRV(zd));
AppendDomainName(&zd->question.qname, &zd->ZoneName);
}
zd->question.ThisQInterval = -1; zd->question.InterfaceID = mDNSInterface_Any;
zd->question.Target = zeroAddr;
zd->question.qtype = qtype;
zd->question.qclass = kDNSClass_IN;
zd->question.LongLived = mDNSfalse;
zd->question.ExpectUnique = mDNStrue;
zd->question.ForceMCast = mDNSfalse;
zd->question.ReturnIntermed = mDNStrue;
zd->question.QuestionCallback = GetZoneData_QuestionCallback;
zd->question.QuestionContext = zd;
return(mDNS_StartQuery(m, &zd->question));
}
mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext)
{
DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, name);
int initialskip = (AuthInfo && AuthInfo->AutoTunnel) ? DomainNameLength(name) - DomainNameLength(&AuthInfo->domain) : 0;
ZoneData *zd = (ZoneData*)mDNSPlatformMemAllocate(sizeof(ZoneData));
if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocate failed"); return mDNSNULL; }
mDNSPlatformMemZero(zd, sizeof(ZoneData));
AssignDomainName(&zd->ChildName, name);
zd->ZoneService = target;
zd->CurrentSOA = (domainname *)(&zd->ChildName.c[initialskip]);
zd->ZoneName.c[0] = 0;
zd->ZoneClass = 0;
zd->Host.c[0] = 0;
zd->Port = zeroIPPort;
zd->Addr = zeroAddr;
zd->ZonePrivate = AuthInfo ? mDNStrue : mDNSfalse;
zd->ZoneDataCallback = callback;
zd->ZoneDataContext = ZoneDataContext;
zd->question.QuestionContext = zd;
AssignDomainName(&zd->question.qname, zd->CurrentSOA);
mDNS_DropLockBeforeCallback(); GetZoneData_StartQuery(m, zd, kDNSType_SOA);
mDNS_ReclaimLockAfterCallback();
return zd;
}
mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m)
{
NATTraversalInfo *n = m->NATTraversals;
DNSQuestion *q;
while (n)
{
NATTraversalInfo *current = n;
n = n->next;
if (current->clientCallback == LLQNatMapComplete)
{
for (q = m->Questions; q; q = q->next) if (&q->NATInfoUDP == current) break;
if (!q) mDNS_StopNATOperation_internal(m, current);
}
}
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - host name and interface management
#endif
mDNSlocal void CompleteSRVNatMap(mDNS *m, NATTraversalInfo *n)
{
ServiceRecordSet *srs = (ServiceRecordSet *)n->clientContext;
LogOperation("SRVNatMap complete %.4a %u %u TTL %u", &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), n->NATLease);
if (!srs) { LogMsg("CompleteSRVNatMap called with unknown ServiceRecordSet object"); return; }
if (!n->NATLease) return;
if (n->Result)
{
HostnameInfo *hi = m->Hostnames;
while (hi)
{
if (hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) break;
else hi = hi->next;
}
if (hi)
{
debugf("Port map failed for service %##s - using IPv6 service target", srs->RR_SRV.resrec.name->c);
mDNS_StopNATOperation(m, &srs->NATinfo);
goto register_service;
}
else srs->state = regState_NATError;
}
register_service:
mDNS_Lock(m);
if (!mDNSIPv4AddressIsZero(srs->ns.ip.v4))
SendServiceRegistration(m, srs); else
{
LogOperation("ERROR: CompleteSRVNatMap called but srs->ns.ip.v4 is zero!");
srs->state = regState_FetchingZoneData;
if (srs->nta) CancelGetZoneData(m, srs->nta); srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
}
mDNS_Unlock(m);
}
mDNSlocal void StartSRVNatMap(mDNS *m, ServiceRecordSet *srs)
{
mDNSu8 *p = srs->RR_PTR.resrec.name->c;
if (p[0]) p += 1 + p[0];
if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) srs->NATinfo.Protocol = NATOp_MapTCP;
else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) srs->NATinfo.Protocol = NATOp_MapUDP;
else { LogMsg("StartSRVNatMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); goto error; }
srs->NATinfo.IntPort = srs->RR_SRV.resrec.rdata->u.srv.port;
srs->NATinfo.RequestedPort = srs->RR_SRV.resrec.rdata->u.srv.port;
srs->NATinfo.NATLease = 0; srs->NATinfo.clientCallback = CompleteSRVNatMap;
srs->NATinfo.clientContext = srs;
mDNS_StartNATOperation_internal(m, &srs->NATinfo);
return;
error:
if (srs->nta) CancelGetZoneData(m, srs->nta); srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
}
mDNSexport void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err, const ZoneData *zoneData)
{
ServiceRecordSet *srs = (ServiceRecordSet *)zoneData->ZoneDataContext;
if (m->mDNS_busy != m->mDNS_reentrancy)
LogMsg("ServiceRegistrationZoneDataComplete: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
srs->nta = mDNSNULL;
if (err) goto error;
if (!zoneData) { LogMsg("ERROR: ServiceRegistrationZoneDataComplete invoked with NULL result and no error"); goto error; }
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->zone, &zoneData->ZoneName);
srs->ns.type = mDNSAddrType_IPv4;
srs->ns = zoneData->Addr;
if (!mDNSIPPortIsZero(zoneData->Port))
{
srs->SRSUpdatePort = zoneData->Port;
srs->Private = zoneData->ZonePrivate;
}
else
{
debugf("Update port not advertised via SRV - guessing port 53, no lease option");
srs->SRSUpdatePort = UnicastDNSPort;
srs->srs_uselease = mDNSfalse;
}
LogOperation("ServiceRegistrationZoneDataComplete %#a %d %#a %d", &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4), &srs->ns, mDNSAddrIsRFC1918(&srs->ns));
if (!mDNSIPPortIsZero(srs->RR_SRV.resrec.rdata->u.srv.port) &&
mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->ns) &&
srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
{
srs->state = regState_NATMap;
LogOperation("ServiceRegistrationZoneDataComplete StartSRVNatMap");
StartSRVNatMap(m, srs);
}
else
{
mDNS_Lock(m);
SendServiceRegistration(m, srs);
mDNS_Unlock(m);
}
return;
error:
unlinkSRS(m, srs);
srs->state = regState_Unregistered;
srs->ServiceCallback(m, srs, err);
}
mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
{
mDNSOpaque16 id;
mDNSu8 *ptr = m->omsg.data;
mDNSu8 *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
mStatus err = mStatus_UnknownErr;
mDNSu32 i;
id = mDNS_NewMessageID(m);
InitializeDNSMessage(&m->omsg.h, id, UpdateReqFlags);
ptr = putZone(&m->omsg, ptr, end, &srs->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass));
if (!ptr) { LogMsg("ERROR: SendServiceDeregistration - putZone"); err = mStatus_UnknownErr; goto exit; }
if (!(ptr = putDeleteAllRRSets(&m->omsg, ptr, srs->RR_SRV.resrec.name))) { err = mStatus_UnknownErr; goto exit; } if (!(ptr = putDeletionRecord(&m->omsg, ptr, &srs->RR_PTR.resrec))) { err = mStatus_UnknownErr; goto exit; }
for (i = 0; i < srs->NumSubTypes; i++)
if (!(ptr = putDeletionRecord(&m->omsg, ptr, &srs->SubTypes[i].resrec))) { err = mStatus_UnknownErr; goto exit; }
srs->id = id;
srs->state = regState_DeregPending;
if (srs->Private)
{
LogOperation("SendServiceDeregistration TCP %p %s", srs->tcp, ARDisplayString(m, &srs->RR_SRV));
if (srs->tcp) LogMsg("SendServiceDeregistration: Already have TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
else srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->ns, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
srs->RR_SRV.LastAPTime = m->timenow;
srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; }
else
{
err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->ns, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
if (err && err != mStatus_TransientErr) { debugf("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto exit; }
SetRecordRetry(m, &srs->RR_SRV, err);
}
err = mStatus_NoError;
exit:
if (err)
{
unlinkSRS(m, srs);
srs->state = regState_Unregistered;
}
}
mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs)
{
ExtraResourceRecord *e;
domainname *curtarget = &srs->RR_SRV.resrec.rdata->u.srv.target;
const domainname *const nt = GetServiceTarget(m, srs);
const domainname *const newtarget = nt ? nt : (domainname*)"";
mDNSBool TargetChanged = (newtarget->c[0] && srs->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget);
mDNSBool HaveZoneData = !mDNSIPv4AddressIsZero(srs->ns.ip.v4);
mDNSIPPort port = srs->RR_SRV.resrec.rdata->u.srv.port;
mDNSBool NowBehindNAT = (!mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->ns));
mDNSBool WereBehindNAT = (srs->NATinfo.clientContext != mDNSNULL);
mDNSBool PortWasMapped = (srs->NATinfo.clientContext && !mDNSSameIPPort(srs->NATinfo.RequestedPort, port)); mDNSBool NATChanged = (!WereBehindNAT && NowBehindNAT) || (!NowBehindNAT && PortWasMapped);
LogOperation("UpdateSRV %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowBehindNAT %d WereBehindNAT %d PortWasMapped %d NATChanged %d",
srs->RR_SRV.resrec.name->c, newtarget,
TargetChanged, HaveZoneData, mDNSVal16(port), NowBehindNAT, WereBehindNAT, PortWasMapped, NATChanged);
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("UpdateSRV: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
if (!TargetChanged && !NATChanged) return;
switch(srs->state)
{
case regState_FetchingZoneData:
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->SRVUpdateDeferred = mDNStrue;
return;
case regState_NATError:
if (!NATChanged) return;
case regState_NoTarget:
if (newtarget->c[0])
{
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->state = regState_FetchingZoneData;
if (srs->nta) CancelGetZoneData(m, srs->nta); srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
}
else
{
if (srs->NATinfo.clientContext && (NATChanged || !NowBehindNAT))
{
mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.clientContext = mDNSNULL;
}
if (NATChanged && NowBehindNAT && srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
{ srs->state = regState_NATMap; StartSRVNatMap(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.state = regState_ExtraQueued; srs->SRVChanged = mDNStrue;
SendServiceDeregistration(m, srs);
return;
default: LogMsg("UpdateSRV: Unknown state %d for %##s", srs->state, srs->RR_SRV.resrec.name->c);
}
}
mDNSlocal void UpdateSRVRecords(mDNS *m)
{
if (CurrentServiceRecordSet)
LogMsg("UpdateSRVRecords ERROR CurrentServiceRecordSet already set");
CurrentServiceRecordSet = m->ServiceRegistrations;
while (CurrentServiceRecordSet)
{
ServiceRecordSet *s = CurrentServiceRecordSet;
CurrentServiceRecordSet = CurrentServiceRecordSet->uDNS_next;
UpdateSRV(m, s);
}
}
mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
mDNSlocal void hostnameGetPublicAddressCallback(mDNS *m, NATTraversalInfo *n)
{
HostnameInfo *h = (HostnameInfo *)n->clientContext;
if (!h) { LogMsg("RegisterHostnameRecord: registration cancelled"); return; }
if (!n->Result)
{
if (mDNSIPv4AddressIsZero(n->ExternalAddress) || mDNSv4AddrIsRFC1918(&n->ExternalAddress)) return;
if (h->arv4.resrec.RecordType)
{
if (mDNSSameIPv4Address(h->arv4.resrec.rdata->u.ipv4, n->ExternalAddress)) return; LogMsg("Updating hostname %##s IPv4 from %.4a to %.4a (NAT gateway's external address)", h->arv4.resrec.name->c, &h->arv4.resrec.rdata->u.ipv4, &n->ExternalAddress);
mDNS_Deregister(m, &h->arv4); }
else
{
LogMsg("Advertising hostname %##s IPv4 %.4a (NAT gateway's external address)", h->arv4.resrec.name->c, &n->ExternalAddress);
h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique;
h->arv4.resrec.rdata->u.ipv4 = n->ExternalAddress;
mDNS_Register(m, &h->arv4);
}
}
}
mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h)
{
if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered)
{
mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, HostnameCallback, h);
AssignDomainName(&h->arv4.namestorage, &h->fqdn);
h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4;
h->arv4.state = regState_Unregistered;
if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4))
{
if (h->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &h->natinfo);
h->natinfo.Protocol = 0;
h->natinfo.IntPort = zeroIPPort;
h->natinfo.RequestedPort = zeroIPPort;
h->natinfo.NATLease = 0;
h->natinfo.clientCallback = hostnameGetPublicAddressCallback;
h->natinfo.clientContext = h;
mDNS_StartNATOperation_internal(m, &h->natinfo);
}
else
{
LogMsg("Advertising hostname %##s IPv4 %.4a", h->arv4.resrec.name->c, &m->AdvertisedV4.ip.v4);
h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique;
mDNS_Register_internal(m, &h->arv4);
}
}
if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered)
{
mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, HostnameCallback, h);
AssignDomainName(&h->arv6.namestorage, &h->fqdn);
h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6;
h->arv6.state = regState_Unregistered;
LogMsg("Advertising hostname %##s IPv6 %.16a", h->arv6.resrec.name->c, &m->AdvertisedV6.ip.v6);
mDNS_Register_internal(m, &h->arv6);
}
}
mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
HostnameInfo *hi = (HostnameInfo *)rr->RecordContext;
if (result == mStatus_MemFree)
{
if (hi)
{
HostnameInfo *i;
LogOperation("HostnameCallback: Got mStatus_MemFree for %p %p %s", hi, rr, ARDisplayString(m, rr));
for (i = m->Hostnames; i; i = i->next)
if (rr == &i->arv4 || rr == &i->arv6)
{ mDNS_Lock(m); AdvertiseHostname(m, i); mDNS_Unlock(m); return; }
if (hi->arv4.resrec.RecordType == kDNSRecordTypeUnregistered &&
hi->arv6.resrec.RecordType == kDNSRecordTypeUnregistered)
{
if (hi->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &hi->natinfo);
hi->natinfo.clientContext = mDNSNULL;
mDNSPlatformMemFree(hi); }
}
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) { mDNSPlatformMemFree(rr); return; }
if (rr->state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!");
if (hi->arv4.state == regState_Unregistered &&
hi->arv6.state == regState_Unregistered)
{
rr->RecordContext = (void *)hi->StatusContext;
if (hi->StatusCallback)
hi->StatusCallback(m, rr, result); rr->RecordContext = (void *)hi;
}
return;
}
mDNS_Lock(m);
UpdateSRVRecords(m);
mDNS_Unlock(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 FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
const domainname *pktname = &answer->rdata->u.name;
domainname *storedname = &m->StaticHostname;
HostnameInfo *h = m->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.state == regState_FetchingZoneData || h->arv4.state == regState_Pending || h->arv4.state == regState_NATMap ||
h->arv6.state == regState_FetchingZoneData || h->arv6.state == regState_Pending)
{
m->NextSRVUpdate = NonZeroTime(m->timenow + (5 * mDNSPlatformOneSecond));
return;
}
h = h->next;
}
mDNS_Lock(m);
UpdateSRVRecords(m);
mDNS_Unlock(m);
}
else if (!AddRecord && SameDomainName(pktname, storedname))
{
mDNS_Lock(m);
storedname->c[0] = 0;
UpdateSRVRecords(m);
mDNS_Unlock(m);
}
}
mDNSlocal void GetStaticHostname(mDNS *m)
{
char buf[MAX_REVERSE_MAPPING_NAME_V4];
DNSQuestion *q = &m->ReverseMap;
mDNSu8 *ip = m->AdvertisedV4.ip.v4.b;
mStatus err;
if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, q);
m->StaticHostname.c[0] = 0;
if (mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4)) return;
mDNSPlatformMemZero(q, sizeof(*q));
mDNS_snprintf(buf, sizeof(buf), "%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->ReturnIntermed = mDNStrue;
q->QuestionCallback = FoundStaticHostname;
q->QuestionContext = mDNSNULL;
err = mDNS_StartQuery_internal(m, q);
if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err);
}
mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
{
HostnameInfo **ptr = &m->Hostnames;
LogOperation("mDNS_AddDynDNSHostName %##s", fqdn);
while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next;
if (*ptr) { LogMsg("DynDNSHostName %##s already in list", fqdn->c); return; }
*ptr = mDNSPlatformMemAllocate(sizeof(**ptr));
if (!*ptr) { LogMsg("ERROR: mDNS_AddDynDNSHostName - malloc"); return; }
mDNSPlatformMemZero(*ptr, sizeof(**ptr));
AssignDomainName(&(*ptr)->fqdn, fqdn);
(*ptr)->arv4.state = regState_Unregistered;
(*ptr)->arv6.state = regState_Unregistered;
(*ptr)->StatusCallback = StatusCallback;
(*ptr)->StatusContext = StatusContext;
AdvertiseHostname(m, *ptr);
}
mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
{
HostnameInfo **ptr = &m->Hostnames;
LogOperation("mDNS_RemoveDynDNSHostName %##s", fqdn);
while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next;
if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c);
else
{
HostnameInfo *hi = *ptr;
mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered;
mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered;
if (f4) LogOperation("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn);
if (f6) LogOperation("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn);
*ptr = (*ptr)->next; if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal);
if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal);
}
UpdateSRVRecords(m);
}
mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
{
mDNSBool v4Changed, v6Changed, RouterChanged;
if (m->mDNS_busy != m->mDNS_reentrancy)
LogMsg("mDNS_SetPrimaryInterfaceInfo: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo v4 address - incorrect type. Discarding. %#a", v4addr); return; }
if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo v6 address - incorrect type. Discarding. %#a", v6addr); return; }
if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-v4 router. Discarding. %#a", router); return; }
mDNS_Lock(m);
if (v4addr && !mDNSv4AddressIsLinkLocal(&v4addr->ip.v4)) v6addr = mDNSNULL;
v4Changed = !mDNSSameIPv4Address(m->AdvertisedV4.ip.v4, v4addr ? v4addr->ip.v4 : zerov4Addr);
v6Changed = !mDNSSameIPv6Address(m->AdvertisedV6.ip.v6, v6addr ? v6addr->ip.v6 : zerov6Addr);
RouterChanged = !mDNSSameIPv4Address(m->Router.ip.v4, router ? router->ip.v4 : zerov4Addr);
if (v4addr && (v4Changed || RouterChanged))
debugf("mDNS_SetPrimaryInterfaceInfo: address changed from %#a to %#a", &m->AdvertisedV4, v4addr);
if (v4addr) m->AdvertisedV4 = *v4addr; else m->AdvertisedV4.ip.v4 = zerov4Addr;
if (v6addr) m->AdvertisedV6 = *v6addr; else m->AdvertisedV6.ip.v6 = zerov6Addr;
if (router) m->Router = *router; else m->Router .ip.v4 = zerov4Addr;
if (v4Changed || RouterChanged || v6Changed)
{
HostnameInfo *i;
LogOperation("mDNS_SetPrimaryInterfaceInfo: %s%s%s%#a %#a %#a",
v4Changed ? "v4Changed " : "",
RouterChanged ? "RouterChanged " : "",
v6Changed ? "v6Changed " : "", v4addr, v6addr, router);
for (i = m->Hostnames; i; i = i->next)
{
LogOperation("mDNS_SetPrimaryInterfaceInfo updating host name registrations for %##s", i->fqdn.c);
if (i->arv4.resrec.RecordType > kDNSRecordTypeDeregistering &&
!mDNSSameIPv4Address(i->arv4.resrec.rdata->u.ipv4, m->AdvertisedV4.ip.v4))
{
LogOperation("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv4));
mDNS_Deregister_internal(m, &i->arv4, mDNS_Dereg_normal);
}
if (i->arv6.resrec.RecordType > kDNSRecordTypeDeregistering &&
!mDNSSameIPv6Address(i->arv6.resrec.rdata->u.ipv6, m->AdvertisedV6.ip.v6))
{
LogOperation("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv6));
mDNS_Deregister_internal(m, &i->arv6, mDNS_Dereg_normal);
}
AdvertiseHostname(m, i);
}
if (v4Changed || RouterChanged)
{
m->ExternalAddress = zerov4Addr;
m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
m->retryGetAddr = m->timenow;
m->NextScheduledNATOp = m->timenow;
ClearUPnPState(m);
}
UpdateSRVRecords(m);
GetStaticHostname(m); }
mDNS_Unlock(m);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Incoming Message Processing
#endif
mDNSlocal mStatus ParseTSIGError(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const domainname *const displayname)
{
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, &m->rec);
if (!ptr) goto finish;
if (m->rec.r.resrec.rrtype == kDNSType_TSIG)
{
mDNSu32 macsize;
mDNSu8 *rd = m->rec.r.resrec.rdata->u.data;
mDNSu8 *rdend = rd + m->rec.r.resrec.rdlength;
int alglen = DomainNameLengthLimit(&m->rec.r.resrec.rdata->u.name, rdend);
if (alglen > MAX_DOMAIN_NAME) 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;
}
m->rec.r.resrec.RecordType = 0; }
finish:
m->rec.r.resrec.RecordType = 0; return err;
}
mDNSlocal mStatus checkUpdateResult(mDNS *const m, const domainname *const displayname, const mDNSu8 rcode, const DNSMessage *const msg, const mDNSu8 *const 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 SendRecordRegistration(mDNS *const m, AuthRecord *rr)
{
mDNSu8 *ptr = m->omsg.data;
mDNSu8 *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
mStatus err = mStatus_UnknownErr;
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
rr->RequireGoodbye = mDNStrue;
rr->id = mDNS_NewMessageID(m);
InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
if (rr->state == regState_UpdatePending)
{
SetNewRData(&rr->resrec, rr->OrigRData, rr->OrigRDLen);
if (!(ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec))) { err = mStatus_UnknownErr; goto exit; }
SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
}
else
{
if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
{
ptr = putDeleteRRSet(&m->omsg, ptr, rr->resrec.name, rr->resrec.rrtype);
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
}
else if (rr->resrec.RecordType != kDNSRecordTypeShared)
{
ptr = putPrereqNameNotInUse(rr->resrec.name, &m->omsg, ptr, end);
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
}
ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl);
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
}
if (rr->uselease)
{
ptr = putUpdateLease(&m->omsg, ptr, DEFAULT_UPDATE_LEASE); if (!ptr) { err = mStatus_UnknownErr; goto exit; }
}
if (rr->Private)
{
LogOperation("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
if (rr->tcp) LogMsg("SendRecordRegistration: Already have TCP connection for %s", ARDisplayString(m, rr));
else rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
rr->LastAPTime = m->timenow;
rr->ThisAPInterval = 0x3FFFFFFF; }
else
{
err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr, err);
}
if (rr->state != regState_Refresh && rr->state != regState_DeregDeferred && rr->state != regState_UpdatePending)
rr->state = regState_Pending;
err = mStatus_NoError;
exit:
if (err)
{
LogMsg("SendRecordRegistration: Error formatting message %d", err);
if (rr->state != regState_Unregistered)
{
UnlinkAuthRecord(m, rr);
rr->state = regState_Unregistered;
}
mDNS_DropLockBeforeCallback();
if (rr->RecordCallback)
rr->RecordCallback(m, rr, err);
mDNS_ReclaimLockAfterCallback();
}
}
mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs, mStatus err)
{
mDNSBool InvokeCallback = mDNSfalse;
ExtraResourceRecord **e = &srs->Extras;
AuthRecord *txt = &srs->RR_TXT;
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("hndlServiceUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
switch (srs->state)
{
case regState_Pending:
if (err == mStatus_NameConflict && !srs->TestForSelfConflict)
{
srs->TestForSelfConflict = mDNStrue;
debugf("checking for self-conflict of service %##s", srs->RR_SRV.resrec.name->c);
SendServiceRegistration(m, srs);
return;
}
else if (srs->TestForSelfConflict)
{
srs->TestForSelfConflict = mDNSfalse;
if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; if (err) srs->state = regState_Unregistered;
else srs->state = regState_Registered;
InvokeCallback = mDNStrue;
break;
}
else if (srs->srs_uselease && err == mStatus_UnknownErr && mDNSSameIPPort(srs->SRSUpdatePort, UnicastDNSPort))
{
LogMsg("Re-trying update of service %##s without lease option", srs->RR_SRV.resrec.name->c);
srs->srs_uselease = mDNSfalse;
SendServiceRegistration(m, srs);
return;
}
else
{
if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c); srs->state = regState_Unregistered; }
else srs->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;
srs->state = regState_Unregistered;
}
else srs->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 (srs->SRVChanged)
{
srs->state = regState_NoTarget; break;
}
err = mStatus_MemFree;
InvokeCallback = mDNStrue;
if (srs->NATinfo.clientContext)
{
mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.clientContext = mDNSNULL;
}
srs->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;
srs->state = regState_Unregistered;
break;
}
else
{
debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name->c);
srs->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);
srs->state = regState_Unregistered;
InvokeCallback = mDNStrue;
}
else
{
srs->state = regState_Registered;
if (txt->UpdateCallback) txt->UpdateCallback(m, txt, txt->OrigRData);
SetNewRData(&txt->resrec, txt->InFlightRData, txt->InFlightRDLen);
txt->OrigRData = mDNSNULL;
txt->InFlightRData = mDNSNULL;
}
break;
case regState_FetchingZoneData:
case regState_Registered:
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, srs->state, err);
err = mStatus_UnknownErr;
default: LogMsg("hndlServiceUpdateReply: Unknown state %d for %##s", srs->state, srs->RR_SRV.resrec.name->c);
}
if ((srs->SRVChanged || srs->SRVUpdateDeferred) && (srs->state == regState_NoTarget || srs->state == regState_Registered))
{
LogOperation("hndlServiceUpdateReply: SRVChanged %d SRVUpdateDeferred %d state %d", srs->SRVChanged, srs->SRVUpdateDeferred, srs->state);
if (InvokeCallback)
{
srs->ClientCallbackDeferred = mDNStrue;
srs->DeferredStatus = err;
}
srs->SRVChanged = srs->SRVUpdateDeferred = mDNSfalse;
UpdateSRV(m, srs);
return;
}
while (*e)
{
if ((*e)->r.state == regState_ExtraQueued)
{
if (srs->state == regState_Registered && !err)
{
AssignDomainName(&(*e)->r.zone, &srs->zone);
(*e)->r.UpdateServer = srs->ns;
(*e)->r.UpdatePort = srs->SRSUpdatePort;
(*e)->r.uselease = srs->srs_uselease;
SendRecordRegistration(m, &(*e)->r);
e = &(*e)->next;
}
else if (err && (*e)->r.state != regState_Unregistered)
{
(*e)->r.state = regState_Unregistered;
*e = (*e)->next;
}
else e = &(*e)->next;
}
else e = &(*e)->next;
}
srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; if (srs->state == regState_Unregistered) unlinkSRS(m, srs);
else if (txt->QueuedRData && srs->state == regState_Registered)
{
if (InvokeCallback)
{
srs->ClientCallbackDeferred = mDNStrue;
srs->DeferredStatus = err;
}
srs->state = regState_UpdatePending;
txt->InFlightRData = txt->QueuedRData;
txt->InFlightRDLen = txt->QueuedRDLen;
txt->OrigRData = txt->resrec.rdata;
txt->OrigRDLen = txt->resrec.rdlength;
txt->QueuedRData = mDNSNULL;
SendServiceRegistration(m, srs);
return;
}
mDNS_DropLockBeforeCallback();
if (InvokeCallback)
srs->ServiceCallback(m, srs, err);
else if (srs->ClientCallbackDeferred)
{
srs->ClientCallbackDeferred = mDNSfalse;
srs->ServiceCallback(m, srs, srs->DeferredStatus);
}
mDNS_ReclaimLockAfterCallback();
}
mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err)
{
mDNSBool InvokeCallback = mDNStrue;
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
if (rr->state == regState_UpdatePending)
{
if (err)
{
LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
rr->state = regState_Unregistered;
}
else
{
debugf("Update record %##s - success", rr->resrec.name->c);
rr->state = regState_Registered;
if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData);
SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
rr->OrigRData = mDNSNULL;
rr->InFlightRData = mDNSNULL;
}
}
if (rr->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;
rr->state = regState_Unregistered;
}
if (rr->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);
rr->state = regState_Unregistered;
}
debugf("Calling deferred deregistration of record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype);
rr->state = regState_Registered;
mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
return;
}
if (rr->state == regState_Pending || rr->state == regState_Refresh)
{
if (!err)
{
rr->state = regState_Registered;
if (rr->state == regState_Refresh) InvokeCallback = mDNSfalse;
}
else
{
if (rr->uselease && err == mStatus_UnknownErr && mDNSSameIPPort(rr->UpdatePort, UnicastDNSPort))
{
LogMsg("Re-trying update of record %##s without lease option", rr->resrec.name->c);
rr->uselease = mDNSfalse;
SendRecordRegistration(m, rr);
return;
}
LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %ld", rr->resrec.name->c, rr->resrec.rrtype, err);
rr->state = regState_Unregistered;
}
}
if (rr->state == regState_Unregistered) UnlinkAuthRecord(m, rr);
else rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1;
if (rr->QueuedRData && rr->state == regState_Registered)
{
rr->state = regState_UpdatePending;
rr->InFlightRData = rr->QueuedRData;
rr->InFlightRDLen = rr->QueuedRDLen;
rr->OrigRData = rr->resrec.rdata;
rr->OrigRDLen = rr->resrec.rdlength;
rr->QueuedRData = mDNSNULL;
SendRecordRegistration(m, rr);
return;
}
if (InvokeCallback && rr->RecordCallback)
{
mDNS_DropLockBeforeCallback();
rr->RecordCallback(m, rr, err);
mDNS_ReclaimLockAfterCallback();
}
}
mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len)
{
NATTraversalInfo *ptr;
NATAddrReply *AddrReply = (NATAddrReply *)pkt;
NATPortMapReply *PortMapReply = (NATPortMapReply *)pkt;
mDNSu32 nat_elapsed, our_elapsed;
if (!AddrReply->err && len < 8) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; }
if (AddrReply->vers != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expected %d)", pkt[0], NATMAP_VERS); return; }
AddrReply->err = (mDNSu16) ( (mDNSu16)pkt[2] << 8 | pkt[3]);
AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]);
nat_elapsed = AddrReply->upseconds - m->LastNATupseconds;
our_elapsed = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond;
LogOperation("uDNS_ReceiveNATPMPPacket %X upseconds %u nat_elapsed %d our_elapsed %d", AddrReply->opcode, AddrReply->upseconds, nat_elapsed, our_elapsed);
if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8)
{ LogMsg("NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m); }
m->LastNATupseconds = AddrReply->upseconds;
m->LastNATReplyLocalTime = m->timenow;
ClearUPnPState(m);
if (AddrReply->opcode == NATOp_AddrResponse)
{
if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal AddrResponse message too short (%d bytes)", len); return; }
natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr);
}
else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse)
{
mDNSu8 Protocol = AddrReply->opcode & 0x7F;
if (!PortMapReply->err)
{
if (len < sizeof(NATPortMapReply)) { LogMsg("NAT Traversal PortMapReply message too short (%d bytes)", len); return; }
PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]);
}
for (ptr = m->NATTraversals; ptr; ptr=ptr->next)
if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport))
natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease);
}
else { LogMsg("Received NAT Traversal response with version unknown opcode 0x%X", AddrReply->opcode); return; }
}
mDNSlocal const domainname *DNSRelayTestQuestion = (const 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 mDNSIPPort srcport)
{
const mDNSu8 *ptr = msg->data;
DNSQuestion q;
DNSServer *s;
mDNSu32 result = 0;
if (msg->h.numQuestions != 1) return(mDNSfalse);
ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &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_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0)
result = DNSServer_Failed;
else
result = DNSServer_Passed;
for (s = m->DNSServers; s; s = s->next)
if (mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port) && s->teststate != result)
{
DNSQuestion *q;
if (s->teststate != result)
{
s->teststate = result;
if (result == DNSServer_Passed) LogOperation("DNS Server %#a:%d passed", srcaddr, mDNSVal16(srcport));
else LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d", srcaddr, mDNSVal16(srcport));
}
if (result == DNSServer_Passed) for (q = m->Questions; q; q=q->next)
if (q->qDNSServer == s)
{ q->LastQTime = m->timenow - q->ThisQInterval; m->NextScheduledQuery = m->timenow; }
}
return(mDNStrue); }
mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport)
{
DNSQuestion *qptr;
mStatus err = mStatus_NoError;
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_Mask);
(void)srcport;
debugf("uDNS_ReceiveMsg from %#-15a with "
"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
srcaddr,
msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,",
msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,",
msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,",
msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
if (QR_OP == StdR)
{
if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return;
if (!mDNSOpaque16IsZero(msg->h.id))
for (qptr = m->Questions; qptr; qptr = qptr->next)
if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW)
{
if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring");
else if (qptr->tcp)
{
LogOperation("uDNS_ReceiveMsg: Using existing TCP connection for %##s (%s)", qptr->qname.c, DNSTypeName(qptr->qtype));
mDNS_DropLockBeforeCallback();
tcpCallback(qptr->tcp->sock, qptr->tcp, mDNStrue, mStatus_NoError);
mDNS_ReclaimLockAfterCallback();
}
else qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, qptr, mDNSNULL, mDNSNULL);
}
}
if (QR_OP == UpdateR && !mDNSOpaque16IsZero(msg->h.id))
{
mDNSu32 lease = GetPktLease(m, msg, end);
mDNSs32 expire = (m->timenow + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4);
if (CurrentServiceRecordSet)
LogMsg("uDNS_ReceiveMsg ERROR CurrentServiceRecordSet already set");
CurrentServiceRecordSet = m->ServiceRegistrations;
while (CurrentServiceRecordSet)
{
ServiceRecordSet *sptr = CurrentServiceRecordSet;
CurrentServiceRecordSet = CurrentServiceRecordSet->uDNS_next;
if (mDNSSameOpaque16(sptr->id, msg->h.id))
{
err = checkUpdateResult(m, sptr->RR_SRV.resrec.name, rcode, msg, end);
if (!err && sptr->srs_uselease && lease)
if (sptr->expire - expire >= 0 || sptr->state != regState_UpdatePending)
sptr->expire = expire;
hndlServiceUpdateReply(m, sptr, err);
CurrentServiceRecordSet = mDNSNULL;
return;
}
}
if (m->CurrentRecord)
LogMsg("uDNS_ReceiveMsg ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
m->CurrentRecord = m->ResourceRecords;
while (m->CurrentRecord)
{
AuthRecord *rptr = m->CurrentRecord;
m->CurrentRecord = m->CurrentRecord->next;
if (mDNSSameOpaque16(rptr->id, msg->h.id))
{
err = checkUpdateResult(m, rptr->resrec.name, rcode, msg, end);
if (!err && rptr->uselease && lease)
if (rptr->expire - expire >= 0 || rptr->state != regState_UpdatePending)
rptr->expire = expire;
hndlRecordUpdateReply(m, rptr, err);
m->CurrentRecord = mDNSNULL;
return;
}
}
}
debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id));
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Query Routines
#endif
mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
{
mDNSu8 *end;
LLQOptData llq;
mStatus err;
if (lease == 0 && q->AuthInfo && !q->tcp) return;
if (q->AuthInfo && !q->tcp)
{
q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, q, mDNSNULL, mDNSNULL);
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
return;
}
if ((q->state == LLQ_Refresh && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0)
{
LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d minutes", q->qname.c, DNSTypeName(q->qtype), kLLQ_DEF_RETRY/60);
q->state = LLQ_Retry;
q->LastQTime = m->timenow;
q->ThisQInterval = kLLQ_DEF_RETRY * mDNSPlatformOneSecond;
SetNextQueryTime(m, q);
return;
}
llq.vers = kLLQ_Vers;
llq.llqOp = kLLQOp_Refresh;
llq.err = LLQErr_NoError;
llq.id = q->id;
llq.llqlease = lease;
InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
end = putLLQ(&m->omsg, m->omsg.data, q, &llq, mDNStrue);
if (!end) { LogMsg("ERROR: sendLLQRefresh - putLLQ"); return; }
err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
if (err) debugf("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err);
if (q->state == LLQ_Established) q->ntries = 1;
else q->ntries++;
debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
q->state = LLQ_Refresh;
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
}
mDNSexport void startLLQHandshakeCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
{
DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext;
q->nta = mDNSNULL;
if (q->state == LLQ_Cancelled)
{
debugf("startLLQHandshakeCallback - LLQ Cancelled.");
return;
}
mDNS_Lock(m);
if (q->state != LLQ_GetZoneInfo)
{
LogMsg("ERROR: startLLQHandshakeCallback - bad state %d", q->state);
err = mStatus_UnknownErr;
goto exit;
}
if (err)
{
LogMsg("ERROR: startLLQHandshakeCallback %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
StartLLQPolling(m, q);
err = mStatus_NoError;
goto exit;
}
if (!zoneInfo)
{
LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code");
err = mStatus_UnknownErr;
goto exit;
}
if (mDNSIPPortIsZero(zoneInfo->Port))
{
LogOperation("LLQ port lookup failed - reverting to polling for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
q->servPort = zeroIPPort;
StartLLQPolling(m, q);
goto exit;
}
q->servAddr = zoneInfo->Addr;
q->servPort = zoneInfo->Port;
if (!zoneInfo->ZonePrivate) q->AuthInfo = mDNSNULL;
q->ntries = 0;
if (q->state == LLQ_SuspendDeferred) q->state = LLQ_Suspended;
else startLLQHandshake(m, q);
exit:
if (err && q) q->state = LLQ_Error;
mDNS_Unlock(m);
}
mDNSlocal void startPrivateQueryCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
{
DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext;
LogOperation("startPrivateQueryCallback %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
q->nta = mDNSNULL;
if (err)
{
LogMsg("ERROR: startPrivateQueryCallback %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
goto exit;
}
if (!zoneInfo)
{
LogMsg("ERROR: startPrivateQueryCallback invoked with NULL result and no error code");
err = mStatus_UnknownErr;
goto exit;
}
if (!zoneInfo->ZonePrivate)
{
debugf("Private port lookup failed -- retrying without TLS -- %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
q->AuthInfo = mDNSNULL; q->ThisQInterval = InitialQuestionInterval;
q->LastQTime = m->timenow - q->ThisQInterval;
mDNS_Lock(m);
SetNextQueryTime(m, q);
mDNS_Unlock(m);
goto exit;
}
if (!q->AuthInfo)
{
LogMsg("ERROR: startPrivateQueryCallback: cannot find credentials for q %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
err = mStatus_UnknownErr;
goto exit;
}
q->TargetQID = mDNS_NewMessageID(m);
if (q->tcp)
{
LogOperation("startPrivateQueryCallback: Already have TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
tcpCallback(q->tcp->sock, q->tcp, mDNStrue, mStatus_NoError);
}
else q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, q, mDNSNULL, mDNSNULL);
exit:
if (err) mDNS_StopQuery(m, q);
}
mDNSexport void uDNS_StopLongLivedQuery(mDNS *const m, DNSQuestion *const question)
{
LogOperation("uDNS_StopLongLivedQuery %##s (%s) state %d", question->qname.c, DNSTypeName(question->qtype), question->state);
switch (question->state)
{
case LLQ_UnInit: LogMsg("ERROR: uDNS_StopLongLivedQuery - state LLQ_UnInit"); return;
case LLQ_GetZoneInfo:
case LLQ_SuspendDeferred: question->state = LLQ_Cancelled; return;
case LLQ_Established:
case LLQ_Refresh: sendLLQRefresh(m, question, 0); break;
default: debugf("uDNS_StopLongLivedQuery - silently discarding LLQ in state %d", question->state); break;
}
RemoveLLQNatMappings(m, question);
CheckForUnreferencedLLQMapping(m);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Dynamic Updates
#endif
mDNSexport void RecordRegistrationCallback(mDNS *const m, mStatus err, const ZoneData *zoneData)
{
AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext;
AuthRecord *ptr;
if (m->mDNS_busy != m->mDNS_reentrancy)
LogMsg("RecordRegistrationCallback: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
newRR->nta = mDNSNULL;
for (ptr = m->ResourceRecords; 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 (!zoneData) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); 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("RecordRegistrationCallback: Only name server claiming responsibility for \"%##s\" is \"%##s\"!", newRR->resrec.name->c, zoneData->ZoneName.c);
err = mStatus_NoSuchNameErr;
goto error;
}
AssignDomainName(&newRR->zone, &zoneData->ZoneName);
newRR->UpdateServer = zoneData->Addr;
newRR->UpdatePort = zoneData->Port;
newRR->Private = zoneData->ZonePrivate;
debugf("RecordRegistrationCallback: Set newRR->UpdateServer %##s %##s to %#a:%d",
newRR->resrec.name->c, zoneData->ZoneName.c, &newRR->UpdateServer, mDNSVal16(newRR->UpdatePort));
if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr))
{
LogMsg("RecordRegistrationCallback: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c);
err = mStatus_NoSuchNameErr;
goto error;
}
mDNS_Lock(m); SendRecordRegistration(m, newRR);
mDNS_Unlock(m);
return;
error:
if (newRR->state != regState_Unregistered)
{
mDNS_Lock(m);
UnlinkAuthRecord(m, newRR);
newRR->state = regState_Unregistered;
mDNS_Unlock(m);
}
if (newRR->RecordCallback)
newRR->RecordCallback(m, newRR, err);
}
mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
{
mDNSu8 *ptr = m->omsg.data;
mDNSu8 *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
mStatus err = mStatus_NoError;
InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) { err = mStatus_UnknownErr; goto exit; }
if (!(ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec))) { err = mStatus_UnknownErr; goto exit; }
rr->state = regState_DeregPending;
if (rr->Private)
{
LogOperation("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
if (rr->tcp) LogMsg("SendRecordDeregistration: ERROR: Already have TCP connection for %s", ARDisplayString(m, rr));
else rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
rr->LastAPTime = m->timenow;
rr->ThisAPInterval = 0x3FFFFFFF; }
else
{
err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
SetRecordRetry(m, rr, err);
CompleteDeregistration(m, rr); return;
}
err = mStatus_NoError;
exit:
if (err)
{
LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet");
UnlinkAuthRecord(m, rr);
rr->state = regState_Unregistered;
}
}
mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
{
switch (rr->state)
{
case regState_NATMap: LogMsg("regState_NATMap %##s type %s", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
case regState_ExtraQueued: rr->state = regState_Unregistered; break;
case regState_Refresh:
case regState_Pending:
case regState_UpdatePending:
rr->state = regState_DeregDeferred;
LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name->c);
return mStatus_NoError;
case regState_FetchingZoneData:
case regState_Registered: break;
case regState_DeregPending: break;
case regState_DeregDeferred: LogMsg("regState_DeregDeferred %##s type %s", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
case regState_Unregistered: LogMsg("regState_Unregistered %##s type %s", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
case regState_NATError: LogMsg("regState_NATError %##s type %s", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
case regState_NoTarget: LogMsg("regState_NoTarget %##s type %s", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
default: LogMsg("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
}
if (rr->state != regState_Unregistered) SendRecordDeregistration(m, rr);
return mStatus_NoError;
}
mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
{
char *errmsg = "Unknown State";
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("uDNS_DeregisterService: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
srs->SRVChanged = srs->SRVUpdateDeferred = mDNSfalse;
if (srs->nta) { CancelGetZoneData(m, srs->nta); srs->nta = mDNSNULL; }
if (srs->NATinfo.clientContext)
{
mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.clientContext = mDNSNULL;
}
switch (srs->state)
{
case regState_Unregistered:
debugf("uDNS_DeregisterService - service %##s not registered", srs->RR_SRV.resrec.name->c);
return mStatus_BadReferenceErr;
case regState_Pending:
case regState_Refresh:
case regState_UpdatePending:
srs->state = regState_DeregDeferred;
return mStatus_NoError;
case regState_DeregPending:
case regState_DeregDeferred:
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->state = regState_Unregistered;
mDNS_DropLockBeforeCallback();
srs->ServiceCallback(m, srs, mStatus_MemFree);
mDNS_ReclaimLockAfterCallback();
return mStatus_NoError;
case regState_FetchingZoneData:
case regState_Registered:
srs->state = regState_DeregPending;
SendServiceDeregistration(m, srs);
return mStatus_NoError;
case regState_ExtraQueued: errmsg = "bad state (regState_ExtraQueued)";
goto error;
default: LogMsg("uDNS_DeregisterService: Unknown state %d for %##s", srs->state, srs->RR_SRV.resrec.name->c);
}
error:
LogMsg("Error, uDNS_DeregisterService: %s", errmsg);
return mStatus_BadReferenceErr;
}
mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr)
{
ServiceRecordSet *parent = mDNSNULL;
AuthRecord *rptr;
regState_t *stateptr = mDNSNULL;
for (parent = m->ServiceRegistrations; parent; parent = parent->uDNS_next)
if (&parent->RR_TXT == rr) { stateptr = &parent->state; break; }
if (!parent)
{
for (rptr = m->ResourceRecords; rptr; rptr = rptr->next)
if (rptr == rr) { stateptr = &rr->state; break; }
if (!rptr) goto unreg_error;
}
switch(*stateptr)
{
case regState_DeregPending:
case regState_DeregDeferred:
case regState_Unregistered:
goto unreg_error;
case regState_FetchingZoneData:
case regState_NATMap:
case regState_ExtraQueued:
case regState_NoTarget:
if (rr->UpdateCallback) rr->UpdateCallback(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 (rr->QueuedRData && rr->UpdateCallback)
rr->UpdateCallback(m, rr, rr->QueuedRData);
rr->QueuedRData = rr->NewRData;
rr->QueuedRDLen = rr->newrdlength;
rr->NewRData = mDNSNULL;
return mStatus_NoError;
case regState_Registered:
rr->OrigRData = rr->resrec.rdata;
rr->OrigRDLen = rr->resrec.rdlength;
rr->InFlightRData = rr->NewRData;
rr->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;
default: LogMsg("uDNS_UpdateRecord: Unknown state %d for %##s", *stateptr, rr->resrec.name->c);
}
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 mDNSBool NoTestQuery(DNSQuestion *q)
{
int i;
mDNSu8 *p = q->qname.c;
if (q->AuthInfo) return(mDNStrue); if (q->qtype != kDNSType_PTR) return(mDNStrue); for (i=0; i<4; i++) {
if (p[0] < 1 || p[0] > 3) return(mDNSfalse);
if ( p[1] < '0' || p[1] > '9' ) return(mDNSfalse);
if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse);
if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse);
p += 1 + p[0];
}
return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa"));
}
mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
{
DNSQuestion *q = m->CurrentQuestion;
mDNSs32 sendtime = q->LastQTime + q->ThisQInterval;
if (!q->LongLived && m->SuppressStdPort53Queries && sendtime - m->SuppressStdPort53Queries < 0)
sendtime = m->SuppressStdPort53Queries;
if (m->timenow - sendtime < 0) return;
if (q->LongLived && q->state != LLQ_Poll)
{
if (q->state >= LLQ_InitialRequest && q->state <= LLQ_Established)
{
if (q->state == LLQ_Established || q->state == LLQ_Refresh) sendLLQRefresh(m, q, q->origLease);
else if (q->state == LLQ_InitialRequest ) startLLQHandshake(m, q);
else if (q->state == LLQ_SecondaryRequest ) sendChallengeResponse(m, q, mDNSNULL);
else if (q->state == LLQ_Retry ) { q->ntries = 0; startLLQHandshake(m, q); }
}
else
{
LogMsg("uDNS_CheckCurrentQuestion: %##s (%s) state %d sendtime %d ThisQInterval %d",
q->qname.c, DNSTypeName(q->qtype), q->state, sendtime - m->timenow, q->ThisQInterval);
q->LastQTime = m->timenow;
q->ThisQInterval *= 2;
SetNextQueryTime(m, q);
}
}
if (!(q->LongLived && q->state != LLQ_Poll))
{
if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled)
{
mDNSu8 *end = m->omsg.data;
mStatus err = mStatus_NoError;
DomainAuthInfo *private = mDNSNULL;
if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q))
{
err = constructQueryMsg(&m->omsg, &end, q);
private = q->AuthInfo;
}
else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) {
LogOperation("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port));
q->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
q->qDNSServer->lasttest = m->timenow;
InitializeDNSMessage(&m->omsg.h, mDNS_NewMessageID(m), uQueryFlags);
end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN);
}
if (err) LogMsg("Error: uDNS_CheckCurrentQuestion - constructQueryMsg. Skipping question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
else
{
if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
{
if (private)
{
if (q->nta) LogMsg("uDNS_CheckCurrentQuestion Error: GetZoneData already started for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
else q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, startPrivateQueryCallback, q);
q->ThisQInterval = 0; }
else
{
err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
}
}
if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); else if (!q->LongLived && q->ThisQInterval < MAX_UCAST_POLL_INTERVAL)
{
q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; LogOperation("Adjusted ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype));
}
else if (q->LongLived && q->state == LLQ_Poll)
{
q->ThisQInterval = LLQ_POLL_INTERVAL;
}
}
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
}
else
{
CacheRecord *rr;
const mDNSu32 slot = HashSlot(&q->qname);
CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
if (cg)
for (rr = cg->members; rr; rr=rr->next)
if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr);
if (!q->qDNSServer) LogMsg("uDNS_CheckCurrentQuestion no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
else LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
MakeNegativeCacheRecord(m, &q->qname, q->qnamehash, q->qtype, q->qclass, 60);
q->ThisQInterval = 0;
CreateNewCacheEntry(m, slot, cg);
m->rec.r.resrec.RecordType = 0; }
}
}
mDNSlocal void CheckNATMappings(mDNS *m)
{
mStatus err = mStatus_NoError;
mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4);
mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4);
m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF;
if (HaveRoutable) m->ExternalAddress = m->AdvertisedV4.ip.v4;
if (m->NATTraversals && rfc1918) {
if (m->NATMcastRecvskt == mDNSNULL) {
m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort);
m->NATMcastRecvsk2 = mDNSPlatformUDPSocket(m, NATPMPPort); if (!m->NATMcastRecvskt) LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for NAT-PMP announcements");
if (!m->NATMcastRecvsk2) LogOperation("CheckNATMappings: Failed to allocate port 5351 UDP multicast socket for NAT-PMP announcements");
}
}
else {
if (m->NATMcastRecvskt) { mDNSPlatformUDPClose(m->NATMcastRecvskt); m->NATMcastRecvskt = mDNSNULL; }
if (m->NATMcastRecvsk2) { mDNSPlatformUDPClose(m->NATMcastRecvsk2); m->NATMcastRecvsk2 = mDNSNULL; }
}
if (m->NATTraversals)
{
if (m->timenow - m->retryGetAddr >= 0)
{
err = uDNS_SendNATMsg(m, mDNSNULL); if (!err)
{
if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2;
else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
}
m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
}
if (m->NextScheduledNATOp - m->retryGetAddr > 0)
m->NextScheduledNATOp = m->retryGetAddr;
}
if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use");
m->CurrentNATTraversal = m->NATTraversals;
while (m->CurrentNATTraversal)
{
NATTraversalInfo *cur = m->CurrentNATTraversal;
m->CurrentNATTraversal = m->CurrentNATTraversal->next;
if (HaveRoutable) {
cur->ExpiryTime = 0;
cur->NewResult = mStatus_NoError;
}
else if (cur->Protocol) {
if (m->timenow - cur->retryPortMap >= 0) {
if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0) {
cur->ExpiryTime = 0;
cur->retryInterval = NATMAP_INIT_RETRY;
}
err = uDNS_SendNATMsg(m, cur);
if (cur->ExpiryTime) NATSetNextRenewalTime(m, cur);
else {
if (cur->retryInterval < NATMAP_INIT_RETRY ) cur->retryInterval = NATMAP_INIT_RETRY;
else if (cur->retryInterval < NATMAP_MAX_RETRY_INTERVAL / 2) cur->retryInterval *= 2;
else cur->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
cur->retryPortMap = m->timenow + cur->retryInterval;
}
}
if (m->NextScheduledNATOp - cur->retryPortMap > 0)
m->NextScheduledNATOp = cur->retryPortMap;
}
if (!mDNSIPv4AddressIsZero(m->ExternalAddress) || m->retryIntervalGetAddr > NATMAP_INIT_RETRY * 8)
{
const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort :
!mDNSIPv4AddressIsZero(m->ExternalAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort;
if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8)
if (!mDNSSameIPv4Address(cur->ExternalAddress, m->ExternalAddress) ||
!mDNSSameIPPort (cur->ExternalPort, ExternalPort) ||
cur->Result != cur->NewResult)
{
if (cur->Protocol && mDNSIPPortIsZero(ExternalPort))
LogMsg("Failed to obtain NAT port mapping from router %#a external address %.4a internal port %d",
&m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort));
cur->ExternalAddress = m->ExternalAddress;
cur->ExternalPort = ExternalPort;
cur->Lifetime = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ?
(cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0;
cur->Result = cur->NewResult;
mDNS_DropLockBeforeCallback(); if (cur->clientCallback)
cur->clientCallback(m, cur);
mDNS_ReclaimLockAfterCallback(); }
}
}
}
mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m)
{
AuthRecord *rr;
mDNSs32 nextevent = m->timenow + 0x3FFFFFFF;
for (rr = m->ResourceRecords; rr; rr = rr->next)
{
if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending || rr->state == regState_DeregDeferred || rr->state == regState_Refresh)
{
if (rr->LastAPTime + rr->ThisAPInterval - m->timenow < 0)
{
if (rr->tcp) { rr->LastAPTime = m->timenow; rr->ThisAPInterval = 0x3FFFFFFF; }
else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
else SendRecordRegistration(m, rr);
}
if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval;
}
if (rr->uselease && rr->state == regState_Registered)
{
if (rr->expire - m->timenow < 0)
{
debugf("refreshing record %##s", rr->resrec.name->c);
rr->state = regState_Refresh;
SendRecordRegistration(m, rr);
}
if (rr->expire - nextevent < 0) nextevent = rr->expire;
}
}
return nextevent;
}
mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m)
{
mDNSs32 nextevent = m->timenow + 0x3FFFFFFF;
if (CurrentServiceRecordSet)
LogMsg("CheckServiceRegistrations ERROR CurrentServiceRecordSet already set");
CurrentServiceRecordSet = m->ServiceRegistrations;
while (CurrentServiceRecordSet)
{
ServiceRecordSet *srs = CurrentServiceRecordSet;
CurrentServiceRecordSet = CurrentServiceRecordSet->uDNS_next;
if (srs->state == regState_Pending || srs->state == regState_DeregPending || srs->state == regState_DeregDeferred || srs->state == regState_Refresh || srs->state == regState_UpdatePending)
{
if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - m->timenow < 0)
{
if (srs->tcp) { srs->RR_SRV.LastAPTime = m->timenow; srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; }
else if (srs->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 (srs->srs_uselease && srs->state == regState_Registered)
{
if (srs->expire - m->timenow < 0)
{
debugf("refreshing service %##s", srs->RR_SRV.resrec.name->c);
srs->state = regState_Refresh;
SendServiceRegistration(m, srs);
}
if (srs->expire - nextevent < 0) nextevent = srs->expire;
}
}
return nextevent;
}
mDNSexport void uDNS_Execute(mDNS *const m)
{
mDNSs32 nexte;
m->NextuDNSEvent = m->timenow + 0x3FFFFFFF;
if (m->NextSRVUpdate && m->NextSRVUpdate - m->timenow < 0)
{ m->NextSRVUpdate = 0; UpdateSRVRecords(m); }
CheckNATMappings(m);
if (m->SuppressStdPort53Queries && m->timenow - m->SuppressStdPort53Queries >= 0)
m->SuppressStdPort53Queries = 0;
nexte = CheckRecordRegistrations(m);
if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte;
nexte = CheckServiceRegistrations(m);
if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte;
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Startup, Shutdown, and Sleep
#endif
mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive)
{
DNSQuestion *q;
for (q = m->Questions; q; q = q->next)
{
if (q->LongLived)
{
if (q->state == LLQ_GetZoneInfo)
{
debugf("Marking %##s suspend-deferred", q->qname.c);
q->state = LLQ_SuspendDeferred; }
else if (q->state < LLQ_Suspended)
{
if (DeregisterActive && (q->state == LLQ_Established || q->state == LLQ_Refresh))
{
debugf("Deleting LLQ %##s", q->qname.c);
sendLLQRefresh(m, q, 0);
}
debugf("Marking %##s suspended", q->qname.c);
q->state = LLQ_Suspended;
if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
q->id = zeroOpaque64;
}
else if (q->state == LLQ_Poll)
{
debugf("Marking %##s suspended-poll", q->qname.c);
q->state = LLQ_SuspendedPoll;
}
RemoveLLQNatMappings(m, q); }
}
CheckForUnreferencedLLQMapping(m);
}
mDNSlocal void RestartQueries(mDNS *m)
{
if (m->CurrentQuestion)
LogMsg("RestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
m->CurrentQuestion = m->Questions;
while (m->CurrentQuestion)
{
DNSQuestion *q = m->CurrentQuestion;
m->CurrentQuestion = m->CurrentQuestion->next;
if (!mDNSOpaque16IsZero(q->TargetQID) && !q->DuplicateOf)
{
if (q->LongLived)
{
if (q->state == LLQ_Suspended || q->state == LLQ_NatMapWaitUDP)
{
q->ntries = -1;
startLLQHandshake(m, q);
}
else if (q->state == LLQ_SuspendDeferred)
q->state = LLQ_GetZoneInfo; else if (q->state == LLQ_SuspendedPoll)
{
q->ntries = 0;
q->state = LLQ_GetZoneInfo;
if (q->nta) CancelGetZoneData(m, q->nta); q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, startLLQHandshakeCallback, q);
}
}
else
{
q->LastQTime = m->timenow;
q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; SetNextQueryTime(m, q);
}
}
}
m->CurrentQuestion = mDNSNULL;
}
mDNSexport void mDNS_UpdateLLQs(mDNS *m)
{
mDNS_Lock(m);
SuspendLLQs(m, mDNStrue);
RestartQueries(m);
mDNS_Unlock(m);
}
mDNSlocal void SleepRecordRegistrations(mDNS *m)
{
AuthRecord *rr = m->ResourceRecords;
while (rr)
{
if (rr->state == regState_Registered ||
rr->state == regState_Refresh)
{
mDNSu8 *ptr = m->omsg.data, *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
InitializeDNSMessage(&m->omsg.h, mDNS_NewMessageID(m), UpdateReqFlags);
ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; }
ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec);
if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; }
mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
rr->state = regState_Refresh;
rr->LastAPTime = m->timenow;
rr->ThisAPInterval = 300 * mDNSPlatformOneSecond;
}
rr = rr->next;
}
}
mDNSlocal void WakeRecordRegistrations(mDNS *m)
{
AuthRecord *rr = m->ResourceRecords;
while (rr)
{
if (rr->state == regState_Refresh)
{
rr->LastAPTime = m->timenow;
rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
}
rr = rr->next;
}
}
mDNSlocal void SleepServiceRegistrations(mDNS *m)
{
ServiceRecordSet *srs = m->ServiceRegistrations;
while (srs)
{
if (srs->nta) { CancelGetZoneData(m, srs->nta); srs->nta = mDNSNULL; }
if (srs->NATinfo.clientContext)
{
mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.clientContext = mDNSNULL;
}
if (srs->state == regState_UpdatePending)
{
AuthRecord *txt = &srs->RR_TXT;
srs->state = regState_Registered;
if (txt->UpdateCallback) txt->UpdateCallback(m, txt, txt->OrigRData);
SetNewRData(&txt->resrec, txt->InFlightRData, txt->InFlightRDLen);
txt->OrigRData = mDNSNULL;
txt->InFlightRData = mDNSNULL;
}
if (srs->state == regState_Registered || srs->state == regState_Refresh)
{
mDNSOpaque16 origid = srs->id;
srs->state = regState_DeregPending; SendServiceDeregistration(m, srs);
srs->id = origid;
srs->state = regState_NoTarget; srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0;
}
srs = srs->uDNS_next;
}
}
mDNSlocal void WakeServiceRegistrations(mDNS *m)
{
ServiceRecordSet *srs = m->ServiceRegistrations;
while (srs)
{
if (srs->state == regState_Refresh)
{
srs->RR_SRV.LastAPTime = m->timenow;
srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
}
srs = srs->uDNS_next;
}
}
mDNSexport void mDNS_AddSearchDomain(const domainname *const domain)
{
SearchListElem **p;
for (p = &SearchList; *p; p = &(*p)->next)
if (SameDomainName(&(*p)->domain, domain))
{
if ((*p)->flag == -1) (*p)->flag = 0;
LogOperation("mDNS_AddSearchDomain already in list %##s", domain->c);
return;
}
*p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
mDNSPlatformMemZero(*p, sizeof(SearchListElem));
AssignDomainName(&(*p)->domain, domain);
(*p)->flag = 1; (*p)->next = mDNSNULL;
LogOperation("mDNS_AddSearchDomain created new %##s", domain->c);
}
mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
(void)m; if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
}
mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
SearchListElem *slElem = question->QuestionContext;
mStatus err;
if (answer->rrtype != kDNSType_PTR) return;
if (answer->RecordType == kDNSRecordTypePacketNegative) return;
if (AddRecord)
{
const char *name;
ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem));
if (!arElem) { LogMsg("ERROR: malloc"); return; }
mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem);
if (question == &slElem->BrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse];
else if (question == &slElem->DefBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault];
else if (question == &slElem->AutomaticBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic];
else if (question == &slElem->RegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration];
else if (question == &slElem->DefRegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault];
else { LogMsg("FoundDomain - unknown question"); mDNSPlatformMemFree(arElem); return; }
MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name);
AppendDNSNameString (&arElem->ar.namestorage, "local");
AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name);
err = mDNS_Register(m, &arElem->ar);
if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); mDNSPlatformMemFree(arElem); return; }
arElem->next = slElem->AuthRecs;
slElem->AuthRecs = arElem;
}
else
{
ARListElem **ptr = &slElem->AuthRecs;
while (*ptr)
{
if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, &answer->rdata->u.name))
{
ARListElem *dereg = *ptr;
*ptr = (*ptr)->next;
debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
err = mDNS_Deregister(m, &dereg->ar);
if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err);
}
else
ptr = &(*ptr)->next;
}
}
}
#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
mDNSexport void udns_validatelists(void *const v)
{
mDNS *const m = v;
ServiceRecordSet *s;
for (s = m->ServiceRegistrations; s; s=s->uDNS_next)
if (s->uDNS_next == (ServiceRecordSet*)~0)
LogMemCorruption("m->ServiceRegistrations: %p is garbage (%lX)", s, s->uDNS_next);
NATTraversalInfo *n;
for (n = m->NATTraversals; n; n=n->next)
if (n->next == (NATTraversalInfo *)~0 || n->clientCallback == (NATTraversalClientCallback)~0)
LogMemCorruption("m->NATTraversals: %p is garbage", n);
DNSServer *d;
for (d = m->DNSServers; d; d=d->next)
if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled)
LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate);
DomainAuthInfo *info;
for (info = m->AuthInfoList; info; info = info->next)
if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (mDNSBool)~0)
LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel);
HostnameInfo *hi;
for (hi = m->Hostnames; hi; hi = hi->next)
if (hi->next == (HostnameInfo *)~0 || hi->StatusCallback == (mDNSRecordCallback*)~0)
LogMemCorruption("m->Hostnames: %p is garbage", n);
SearchListElem *ptr;
for (ptr = SearchList; ptr; ptr = ptr->next)
if (ptr->next == (SearchListElem *)~0 || ptr->AuthRecs == (void*)~0)
LogMemCorruption("SearchList: %p is garbage (%X)", ptr, ptr->AuthRecs);
}
#endif
mDNSexport mStatus uDNS_RegisterSearchDomains(mDNS *const m)
{
SearchListElem **p = &SearchList, *ptr;
mStatus err;
for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = -1;
mDNS_Lock(m);
m->RegisterSearchDomains = mDNStrue;
mDNSPlatformSetDNSConfig(m, mDNSfalse, m->RegisterSearchDomains, mDNSNULL, mDNSNULL, mDNSNULL);
mDNS_Unlock(m);
while (*p)
{
ptr = *p;
debugf("RegisterSearchDomains %d %p %##s", ptr->flag, ptr->AuthRecs, ptr->domain.c);
if (ptr->flag == -1) {
ARListElem *arList = ptr->AuthRecs;
ptr->AuthRecs = mDNSNULL;
*p = ptr->next;
mDNS_StopGetDomains(m, &ptr->BrowseQ);
mDNS_StopGetDomains(m, &ptr->RegisterQ);
mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
mDNSPlatformMemFree(ptr);
while (arList)
{
ARListElem *dereg = arList;
arList = arList->next;
debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
err = mDNS_Deregister(m, &dereg->ar);
if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err);
}
continue;
}
if (ptr->flag == 1) {
mStatus err1, err2, err3, err4, err5;
err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
if (err1 || err2 || err3 || err4 || err5)
LogMsg("GetDomains for domain %##s returned error(s):\n"
"%d (mDNS_DomainTypeBrowse)\n"
"%d (mDNS_DomainTypeBrowseDefault)\n"
"%d (mDNS_DomainTypeRegistration)\n"
"%d (mDNS_DomainTypeRegistrationDefault)"
"%d (mDNS_DomainTypeBrowseAutomatic)\n",
ptr->domain.c, err1, err2, err3, err4, err5);
ptr->flag = 0;
}
if (ptr->flag) { LogMsg("RegisterSearchDomains - unknown flag %d. Skipping.", ptr->flag); }
p = &ptr->next;
}
return mStatus_NoError;
}
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);
}
struct CompileTimeAssertionChecks_uDNS
{
char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9100) ? 1 : -1];
char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 3800) ? 1 : -1];
};