diff options
Diffstat (limited to 'mDNSResponder/mDNSCore/dnsproxy.c')
-rw-r--r-- | mDNSResponder/mDNSCore/dnsproxy.c | 836 |
1 files changed, 836 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSCore/dnsproxy.c b/mDNSResponder/mDNSCore/dnsproxy.c new file mode 100644 index 00000000..11bacc10 --- /dev/null +++ b/mDNSResponder/mDNSCore/dnsproxy.c @@ -0,0 +1,836 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dnsproxy.h" + +#ifndef UNICAST_DISABLED + +// Implementation Notes +// +// DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only +// the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns +// "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts +// sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback +// defined here. For TCP socket, the platform does the "accept" and only sends the received packets +// on the newly accepted socket. A single UDP socket (per address family) is used to send/recv +// requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some +// extra state that needs to be disposed at the end. +// +// When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks +// for duplicates, before creating DNSProxyClient state and starting a question with the "core" +// (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary +// resource records, constructs a response and sends it back to the client. +// +// - Question callback is called with only one resource record at a time. We need all the resource +// records to construct the response. Hence, we lookup all the records ourselves. +// +// - The response may not fit the client's buffer size. In that case, we need to set the truncate bit +// and the client would retry using TCP. +// +// - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to +// return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to +// ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set. +// +// Once the response is sent to the client, the client state is disposed. When there is no response +// from the "core", it eventually times out and we will not find any answers in the cache and we send a +// "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case +// of errors. + +typedef struct DNSProxyClient_struct DNSProxyClient; + +struct DNSProxyClient_struct { + + DNSProxyClient *next; + mDNSAddr addr; // Client's IP address + mDNSIPPort port; // Client's port number + mDNSOpaque16 msgid; // DNS msg id + mDNSInterfaceID interfaceID; // Interface on which we received the request + void *socket; // Return socket + mDNSBool tcp; // TCP or UDP ? + mDNSOpaque16 requestFlags; // second 16 bit word in the DNSMessageHeader of the request + mDNSu8 *optRR; // EDNS0 option + mDNSu16 optLen; // Total Length of the EDNS0 option + mDNSu16 rcvBufSize; // How much can the client receive ? + mDNSBool DNSSECOK; // DNSSEC OK ? + void *context; // Platform context to be disposed if non-NULL + domainname qname; // q->qname can't be used for duplicate check + DNSQuestion q; // as it can change underneath us for CNAMEs +}; + +#define MIN_DNS_MESSAGE_SIZE 512 +DNSProxyClient *DNSProxyClients; + +mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc) +{ + if (pc->optRR) + mDNSPlatformMemFree(pc->optRR); + mDNSPlatformMemFree(pc); +} + +mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit) +{ + mDNSu16 rrtype, rrclass; + mDNSu8 rcode, version; + mDNSu16 flag; + + if (ptr + length > limit) + { + LogInfo("ParseEDNS0: Not enough space in the packet"); + return mDNSfalse; + } + // Skip the root label + ptr++; + rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + if (rrtype != kDNSType_OPT) + { + LogInfo("ParseEDNS0: Not the right type %d", rrtype); + return mDNSfalse; + } + rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]); + rcode = ptr[4]; + version = ptr[5]; + flag = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]); + + debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag); + pc->rcvBufSize = rrclass; + pc->DNSSECOK = ptr[6] & 0x80; + + return mDNStrue; +} + +mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) +{ + DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext; + + (void) msg; + + h->flags = pc->requestFlags; + if (pc->optRR) + { + if (ptr + pc->optLen > limit) + { + LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit); + return ptr; + } + h->numAdditionals++; + mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen); + ptr += pc->optLen; + } + return ptr; +} + +mDNSlocal mDNSu8 *AddEDNS0Option(mDNS *const m, mDNSu8 *ptr, mDNSu8 *limit) +{ + int len = 4096; + + if (ptr + 11 > limit) + { + LogInfo("AddEDNS0Option: not enough space"); + return mDNSNULL; + } + m->omsg.h.numAdditionals++; + ptr[0] = 0; + ptr[1] = (mDNSu8) (kDNSType_OPT >> 8); + ptr[2] = (mDNSu8) (kDNSType_OPT & 0xFF); + ptr[3] = (mDNSu8) (len >> 8); + ptr[4] = (mDNSu8) (len & 0xFF); + ptr[5] = 0; // rcode + ptr[6] = 0; // version + ptr[7] = 0; + ptr[8] = 0; // flags + ptr[9] = 0; // rdlength + ptr[10] = 0; // rdlength + + debugf("AddEDNS0 option"); + + return (ptr + 11); +} + +// Currently RD and CD bit should be copied if present in the request or cleared if +// not present in the request. RD bit is normally set in the response and hence the +// cache reflects the right value. CD bit behaves differently. If the CD bit is set +// the first time, the cache retains it, if it is present in response (assuming the +// upstream server does it right). Next time through we should not use the cached +// value of the CD bit blindly. It depends on whether it was in the request or not. +mDNSlocal mDNSOpaque16 SetResponseFlags(DNSProxyClient *pc, const mDNSOpaque16 responseFlags) +{ + mDNSOpaque16 rFlags = responseFlags; + + if (pc->requestFlags.b[0] & kDNSFlag0_RD) + rFlags.b[0] |= kDNSFlag0_RD; + else + rFlags.b[0] &= ~kDNSFlag0_RD; + + if (pc->requestFlags.b[1] & kDNSFlag1_CD) + rFlags.b[1] |= kDNSFlag1_CD; + else + rFlags.b[1] &= ~kDNSFlag1_CD; + + return rFlags; +} + +mDNSlocal mDNSu8 *AddResourceRecords(mDNS *const m, DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + int len = sizeof(DNSMessageHeader); + mDNSu8 *orig = m->omsg.data; + mDNSBool first = mDNStrue; + mDNSu8 *ptr = mDNSNULL; + mDNSs32 now; + mDNSs32 ttl; + CacheRecord *nsec = mDNSNULL; + CacheRecord *soa = mDNSNULL; + CacheRecord *cname = mDNSNULL; + mDNSu8 *limit; + + *error = mStatus_NoError; + *prevptr = mDNSNULL; + + mDNS_Lock(m); + now = m->timenow; + mDNS_Unlock(m); + + if (!pc->tcp) + { + if (!pc->rcvBufSize) + { + limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE; + } + else + { + limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize); + } + } + else + { + // For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and + // AbsoluteMaxDNSMessageData is smaller than 64k. + limit = m->omsg.data + AbsoluteMaxDNSMessageData; + } + LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data); + + if (!SameDomainName(&pc->qname, &pc->q.qname)) + { + AssignDomainName(&pc->q.qname, &pc->qname); + pc->q.qnamehash = DomainNameHashValue(&pc->q.qname); + } + +again: + nsec = soa = cname = mDNSNULL; + slot = HashSlot(&pc->q.qname); + + cg = CacheGroupForName(m, slot, pc->q.qnamehash, &pc->q.qname); + if (!cg) + { + LogInfo("AddResourceRecords: CacheGroup not found"); + *error = mStatus_NoSuchRecord; + return mDNSNULL; + } + // Set ValidatingResponse so that you can get RRSIGs also matching + // the question + if (pc->DNSSECOK) + pc->q.ValidatingResponse = 1; + for (cr = cg->members; cr; cr = cr->next) + { + if (SameNameRecordAnswersQuestion(&cr->resrec, &pc->q)) + { + if (first) + { + // If this is the first time, initialize the header and the question. + // This code needs to be here so that we can use the responseFlags from the + // cache record + mDNSOpaque16 responseFlags = SetResponseFlags(pc, cr->responseFlags); + InitializeDNSMessage(&m->omsg.h, pc->msgid, responseFlags); + ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass); + if (!ptr) + { + LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + return mDNSNULL; + } + first = mDNSfalse; + } + // - For NegativeAnswers there is nothing to add + // - If DNSSECOK is set, we also automatically lookup the RRSIGs which + // will also be returned. If the client is explicitly looking up + // a DNSSEC record (e.g., DNSKEY, DS) we should return the response. + // DNSSECOK bit only influences whether we add the RRSIG or not. + if (cr->resrec.RecordType != kDNSRecordTypePacketNegative) + { + LogInfo("AddResourceRecords: Answering question with %s", CRDisplayString(m, cr)); + ttl = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAnswers, &cr->resrec, ttl, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + // If we have nsecs (wildcard expanded answer or negative response), add them + // in the additional section below if the DNSSECOK bit is set + if (pc->DNSSECOK && cr->nsec) + { + LogInfo("AddResourceRecords: nsec set for %s", CRDisplayString(m ,cr)); + nsec = cr->nsec; + } + if (cr->soa) + { + LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr)); + soa = cr->soa; + } + // If we are using CNAME to answer a question and CNAME is not the type we + // are looking for, note down the CNAME record so that we can follow them + // later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard + // expanded) if any. + if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME) + { + LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr)); + cname = cr; + } + } + } + // Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need + // to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed + // to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to + // by "cr->nsec". Two cases: + // + // - if we issue a DNSSEC question followed by non-DNSSEC question for the same name, + // we only have the nsec records and we need to filter the SOA record alone for the + // non-DNSSEC questions. + // + // - if we issue a non-DNSSEC question followed by DNSSEC question for the same name, + // the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and + // in this case we return all the DNSSEC records we have. + for (; nsec; nsec = nsec->next) + { + if (!pc->DNSSECOK && DNSSECRecordType(nsec->resrec.rrtype)) + continue; + LogInfo("AddResourceRecords:NSEC Answering question with %s", CRDisplayString(m, nsec)); + ttl = nsec->resrec.rroriginalttl - (now - nsec->TimeRcvd) / mDNSPlatformOneSecond; + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &nsec->resrec, ttl, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + if (soa) + { + LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m, soa)); + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &soa->resrec, soa->resrec.rroriginalttl, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + if (cname) + { + AssignDomainName(&pc->q.qname, &cname->resrec.rdata->u.name); + pc->q.qnamehash = DomainNameHashValue(&pc->q.qname); + goto again; + } + if (!ptr) + { + LogInfo("AddResourceRecords: Did not find any valid ResourceRecords"); + *error = mStatus_NoSuchRecord; + return mDNSNULL; + } + if (pc->rcvBufSize) + { + ptr = AddEDNS0Option(m, ptr, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + LogInfo("AddResourceRecord: Added %d bytes to the packet", len); + return ptr; +} + +mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + DNSProxyClient *pc = question->QuestionContext; + DNSProxyClient **ppc = &DNSProxyClients; + mDNSu8 *ptr; + mDNSu8 *prevptr; + mStatus error; + + if (!AddRecord) + return; + + LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer)); + + // We asked for validation and not timed out yet, then wait for the DNSSEC result. + // We have to set the AD bit in the response if it is secure which can't be done + // till we get the DNSSEC result back (indicated by QC_dnssec). + if (question->ValidationRequired) + { + mDNSs32 now; + + mDNS_Lock(m); + now = m->timenow; + mDNS_Unlock(m); + if (((now - question->StopTime) < 0) && AddRecord != QC_dnssec) + { + LogInfo("ProxyClientCallback: No DNSSEC answer yet for Question %##s (%s), AddRecord %d, answer %s", question->qname.c, + DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer)); + return; + } + } + + if (answer->RecordType != kDNSRecordTypePacketNegative) + { + if (answer->rrtype != question->qtype) + { + // Wait till we get called for the real response + LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer)); + return; + } + } + ptr = AddResourceRecords(m, pc, &prevptr, &error); + if (!ptr) + { + LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + if (error == mStatus_NoError && prevptr) + { + // No space to add the record. Set the Truncate bit for UDP. + // + // TBD: For TCP, we need to send the rest of the data. But finding out what is left + // is harder. We should allocate enough buffer in the first place to send all + // of the data. + if (!pc->tcp) + { + m->omsg.h.flags.b[0] |= kDNSFlag0_TC; + ptr = prevptr; + } + else + { + LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + ptr = prevptr; + } + } + else + { + mDNSOpaque16 flags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery, kDNSFlag1_RC_ServFail } }; + // We could not find the record for some reason. Return a response, so that the client + // is not waiting forever. + LogInfo("ProxyClientCallback: No response"); + if (!mDNSOpaque16IsZero(pc->q.responseFlags)) + flags = pc->q.responseFlags; + InitializeDNSMessage(&m->omsg.h, pc->msgid, flags); + ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass); + if (!ptr) + { + LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + goto done; + } + } + } + if (question->ValidationRequired) + { + if (question->ValidationState == DNSSECValDone && question->ValidationStatus == DNSSEC_Secure) + { + LogInfo("ProxyClientCallback: Setting AD bit for Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + m->omsg.h.flags.b[1] |= kDNSFlag1_AD; + } + else + { + // If some external resolver sets the AD bit and we did not validate the response securely, don't set + // the AD bit. It is possible that we did not see all the records that the upstream resolver saw or + // a buggy implementation somewhere. + if (m->omsg.h.flags.b[1] & kDNSFlag1_AD) + { + LogInfo("ProxyClientCallback: AD bit set in the response for response that was not validated locally %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->omsg.h.flags.b[1] &= ~kDNSFlag1_AD; + } + } + } + + if (!pc->tcp) + { + mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse); + } + else + { + mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse); + } + +done: + mDNS_StopQuery(m, question); + + while (*ppc && *ppc != pc) + ppc=&(*ppc)->next; + if (!*ppc) + { + LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype)); + return; + } + *ppc = pc->next; + mDNSPlatformDisposeProxyContext(pc->context); + FreeDNSProxyClient(pc); +} + +mDNSlocal void SendError(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *dstaddr, + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode) +{ + int pktlen = (int)(end - (mDNSu8 *)pkt); + DNSMessage *msg = (DNSMessage *)pkt; + + (void) InterfaceID; + + // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing + // in the body or send back whatever we get for updates. It is easy to return whatever we get + // in the question back to the responder. We return as much as we can fit in our standard + // output packet. + if (pktlen > AbsoluteMaxDNSMessageData) + pktlen = AbsoluteMaxDNSMessageData; + + mDNSPlatformMemCopy(&m->omsg.h, &msg->h, sizeof(DNSMessageHeader)); + m->omsg.h.flags.b[0] |= kDNSFlag0_QR_Response; + m->omsg.h.flags.b[1] = rcode; + mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->h.numQuestions, pktlen); + if (!tcp) + { + mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, socket, dstaddr, dstport, mDNSNULL, mDNSNULL, + mDNSfalse); + } + else + { + mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket, + mDNSNULL, mDNSfalse); + } + mDNSPlatformDisposeProxyContext(context); +} + +mDNSlocal DNSQuestion *IsDuplicateClient(const mDNS *const m, const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id, + const DNSQuestion *const question) +{ + DNSProxyClient *pc; + + (void) m; // unused + + for (pc = DNSProxyClients; pc; pc = pc->next) + { + if (mDNSSameAddress(&pc->addr, addr) && + mDNSSameIPPort(pc->port, port) && + mDNSSameOpaque16(pc->msgid, id) && + pc->q.qtype == question->qtype && + pc->q.qclass == question->qclass && + SameDomainName(&pc->qname, &question->qname)) + { + LogInfo("IsDuplicateClient: Found a duplicate client in the list"); + return(&pc->q); + } + } + return(mDNSNULL); +} + +mDNSlocal mDNSBool CheckDNSProxyIpIntf(const mDNS *const m, mDNSInterfaceID InterfaceID) +{ + int i; + mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID; + + LogInfo("CheckDNSProxyIpIntf: Stored Input Interface List: [%d] [%d] [%d] [%d] [%d]", m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], + m->dp_ipintf[3], m->dp_ipintf[4]); + + for (i = 0; i < MaxIp; i++) + { + if (ip_ifindex == m->dp_ipintf[i]) + return mDNStrue; + } + return mDNSfalse; + +} + +mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context) +{ + DNSMessage *msg = (DNSMessage *)pkt; + mDNSu8 QR_OP; + const mDNSu8 *ptr; + DNSQuestion q, *qptr; + DNSProxyClient *pc; + const mDNSu8 *optRR; + int optLen = 0; + DNSProxyClient **ppc = &DNSProxyClients; + + (void) dstaddr; + (void) dstport; + + debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID); + // Ignore if the DNS Query is not from a Valid Input InterfaceID + if (!CheckDNSProxyIpIntf(m, InterfaceID)) + return; + + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + { + debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + return; + } + + QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + if (QR_OP != kDNSFlag0_QR_Query) + { + LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport)); + SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl); + return; + } + + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + if (msg->h.numQuestions != 1 || msg->h.numAnswers || msg->h.numAuthorities) + { + LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr, mDNSVal16(srcport), + msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities); + SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); + return; + } + ptr = msg->data; + ptr = getQuestion(msg, ptr, end, InterfaceID, &q); + if (!ptr) + { + LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr, mDNSVal16(srcport)); + SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); + return; + } + else + { + LogInfo("ProxyCallbackCommon: Question %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + } + ptr = LocateOptRR(msg, end, 0); + if (ptr) + { + optRR = ptr; + ptr = skipResourceRecord(msg, ptr, end); + // Be liberal and ignore the EDNS0 option if we can't parse it properly + if (!ptr) + { + LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr, mDNSVal16(srcport)); + } + else + { + optLen = ptr - optRR; + LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen, q.qname.c, DNSTypeName(q.qtype)); + } + } + else + { + LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q.qname.c, DNSTypeName(q.qtype), ptr); + } + + qptr = IsDuplicateClient(m, srcaddr, srcport, msg->h.id, &q); + if (qptr) + { + LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + return; + } + pc = mDNSPlatformMemAllocate(sizeof(DNSProxyClient)); + if (!pc) + { + LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + return; + } + mDNSPlatformMemZero(pc, sizeof(DNSProxyClient)); + pc->addr = *srcaddr; + pc->port = srcport; + pc->msgid = msg->h.id; + pc->interfaceID = InterfaceID; // input interface + pc->socket = socket; + pc->tcp = tcp; + pc->requestFlags = msg->h.flags; + pc->context = context; + AssignDomainName(&pc->qname, &q.qname); + if (optRR) + { + if (!ParseEDNS0(pc, optRR, optLen, end)) + { + LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + } + else + { + pc->optRR = mDNSPlatformMemAllocate(optLen); + if (!pc->optRR) + { + LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + FreeDNSProxyClient(pc); + return; + } + mDNSPlatformMemCopy(pc->optRR, optRR, optLen); + pc->optLen = optLen; + } + } + + debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m->dp_opintf); + mDNS_SetupQuestion(&pc->q, (mDNSInterfaceID)(unsigned long)m->dp_opintf, &q.qname, q.qtype, ProxyClientCallback, pc); + pc->q.TimeoutQuestion = 1; + // Even though we don't care about intermediate responses, set ReturnIntermed so that + // we get the negative responses + pc->q.ReturnIntermed = mDNStrue; + pc->q.ProxyQuestion = mDNStrue; + pc->q.ProxyDNSSECOK = pc->DNSSECOK; + pc->q.responseFlags = zeroID; + if (pc->DNSSECOK) + { + if (!(msg->h.flags.b[1] & kDNSFlag1_CD) && pc->q.qtype != kDNSType_RRSIG && pc->q.qtype != kDNSQType_ANY) + { + LogInfo("ProxyCallbackCommon: Setting Validation required bit for %#a:%d, validating %##s (%s)", srcaddr, mDNSVal16(srcport), + q.qname.c, DNSTypeName(q.qtype)); + pc->q.ValidationRequired = DNSSEC_VALIDATION_SECURE; + } + else + { + LogInfo("ProxyCallbackCommon: CD bit not set OR not a valid type for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport), + q.qname.c, DNSTypeName(q.qtype)); + } + } + else + { + LogInfo("ProxyCallbackCommon: DNSSEC OK bit not set for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport), + q.qname.c, DNSTypeName(q.qtype)); + } + + while (*ppc) + ppc = &((*ppc)->next); + *ppc = pc; + + mDNS_StartQuery(m, &pc->q); +} + +mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context); +} + +mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + // If the connection was closed from the other side, locate the client + // state and free it. + if ((end - (mDNSu8 *)pkt) == 0) + { + DNSProxyClient **ppc = &DNSProxyClients; + DNSProxyClient **prevpc; + + prevpc = ppc; + while (*ppc && (*ppc)->socket != socket) + { + prevpc = ppc; + ppc=&(*ppc)->next; + } + if (!*ppc) + { + mDNSPlatformDisposeProxyContext(socket); + LogMsg("ProxyTCPCallback: socket cannot be found"); + return; + } + *prevpc = (*ppc)->next; + LogInfo("ProxyTCPCallback: free"); + mDNSPlatformDisposeProxyContext(socket); + FreeDNSProxyClient(*ppc); + return; + } + ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context); +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) +{ + int i; + + // Store DNSProxy Interface fields in mDNS struct + for (i = 0; i < MaxIp; i++) + m->dp_ipintf[i] = IpIfArr[i]; + m->dp_opintf = OpIf; + + LogInfo("DNSProxyInit Storing interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0], + m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); +} + +mDNSexport void DNSProxyTerminate(mDNS *const m) +{ + int i; + + // Clear DNSProxy Interface fields from mDNS struct + for (i = 0; i < MaxIp; i++) + m->dp_ipintf[i] = 0; + m->dp_opintf = 0; + + LogInfo("DNSProxyTerminate Cleared interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0], + m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); +} +#else // UNICAST_DISABLED + +mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + (void) m; + (void) socket; + (void) pkt; + (void) end; + (void) srcaddr; + (void) srcport; + (void) dstaddr; + (void) dstport; + (void) InterfaceID; + (void) context; +} + +mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + (void) m; + (void) socket; + (void) pkt; + (void) end; + (void) srcaddr; + (void) srcport; + (void) dstaddr; + (void) dstport; + (void) InterfaceID; + (void) context; +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) +{ + (void) m; + (void) IpIfArr; + (void) OpIf; +} +extern void DNSProxyTerminate(mDNS *const m) +{ + (void) m; +} + + +#endif // UNICAST_DISABLED |