115177Snate/* -*- Mode: C; tab-width: 4 -*- 215284Snate * 315284Snate * Copyright (c) 2011-2013 Apple Inc. All rights reserved. 415284Snate * 515284Snate * Licensed under the Apache License, Version 2.0 (the "License"); 615284Snate * you may not use this file except in compliance with the License. 715284Snate * You may obtain a copy of the License at 815284Snate * 915284Snate * http://www.apache.org/licenses/LICENSE-2.0 1015284Snate * 1115284Snate * Unless required by applicable law or agreed to in writing, software 1215284Snate * distributed under the License is distributed on an "AS IS" BASIS, 1315284Snate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1415284Snate * See the License for the specific language governing permissions and 1515284Snate * limitations under the License. 1615284Snate */ 1715284Snate 1815284Snate#include "dnsproxy.h" 1915284Snate 2015284Snate#ifndef UNICAST_DISABLED 2115284Snate 2215284SnatemDNSexport mDNS mDNSStorage; 2315284Snate 2415284Snate// Implementation Notes 2510217Sphk// 2630171Scharnier// DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only 2730171Scharnier// the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns 2830171Scharnier// "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts 2930171Scharnier// sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback 3030171Scharnier// defined here. For TCP socket, the platform does the "accept" and only sends the received packets 3130171Scharnier// on the newly accepted socket. A single UDP socket (per address family) is used to send/recv 3230171Scharnier// requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some 3310217Sphk// extra state that needs to be disposed at the end. 3410217Sphk// 3510217Sphk// When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks 3630171Scharnier// for duplicates, before creating DNSProxyClient state and starting a question with the "core" 3710217Sphk// (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary 3810217Sphk// resource records, constructs a response and sends it back to the client. 3910217Sphk// 4010217Sphk// - Question callback is called with only one resource record at a time. We need all the resource 4110217Sphk// records to construct the response. Hence, we lookup all the records ourselves. 4210217Sphk// 4310217Sphk// - The response may not fit the client's buffer size. In that case, we need to set the truncate bit 4416487Snate// and the client would retry using TCP. 4516487Snate// 4616487Snate// - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to 4716487Snate// return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to 4816487Snate// ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set. 4916487Snate// 5016487Snate// Once the response is sent to the client, the client state is disposed. When there is no response 5116487Snate// from the "core", it eventually times out and we will not find any answers in the cache and we send a 5216487Snate// "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case 5316487Snate// of errors. 5410217Sphk 5516487Snatetypedef struct DNSProxyClient_struct DNSProxyClient; 5615177Snate 5715177Snatestruct DNSProxyClient_struct { 5815177Snate 5915177Snate DNSProxyClient *next; 6015177Snate mDNSAddr addr; // Client's IP address 6115177Snate mDNSIPPort port; // Client's port number 6215177Snate mDNSOpaque16 msgid; // DNS msg id 6315177Snate mDNSInterfaceID interfaceID; // Interface on which we received the request 6415177Snate void *socket; // Return socket 6515177Snate mDNSBool tcp; // TCP or UDP ? 6615177Snate mDNSOpaque16 requestFlags; // second 16 bit word in the DNSMessageHeader of the request 6715177Snate mDNSu8 *optRR; // EDNS0 option 6815177Snate mDNSu16 optLen; // Total Length of the EDNS0 option 6915177Snate mDNSu16 rcvBufSize; // How much can the client receive ? 7015177Snate mDNSBool DNSSECOK; // DNSSEC OK ? 7115177Snate void *context; // Platform context to be disposed if non-NULL 7215177Snate domainname qname; // q->qname can't be used for duplicate check 7315177Snate DNSQuestion q; // as it can change underneath us for CNAMEs 7415177Snate}; 7515177Snate 7615177Snate#define MIN_DNS_MESSAGE_SIZE 512 7715177Snatestatic DNSProxyClient *DNSProxyClients; 7815177Snate 7915177SnatemDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc) 8015177Snate{ 8115177Snate if (pc->optRR) 8215177Snate mDNSPlatformMemFree(pc->optRR); 8315177Snate mDNSPlatformMemFree(pc); 8415177Snate} 8515177Snate 8615177SnatemDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit) 8715177Snate{ 8810217Sphk if (ptr + length > limit) 8910217Sphk { 9010217Sphk LogInfo("ParseEDNS0: Not enough space in the packet"); 9110217Sphk return mDNSfalse; 9210217Sphk } 9310217Sphk // Skip the root label 9410217Sphk ptr++; 9515177Snate mDNSu16 rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); 9615177Snate if (rrtype != kDNSType_OPT) 9715177Snate { 9810217Sphk LogInfo("ParseEDNS0: Not the right type %d", rrtype); 9910217Sphk return mDNSfalse; 10010217Sphk } 10110217Sphk mDNSu16 rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]); 10215177Snate#if MDNS_DEBUGMSGS 10310217Sphk mDNSu8 rcode = ptr[4]; 10410217Sphk mDNSu8 version = ptr[5]; 10515177Snate mDNSu16 flag = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]); 10610217Sphk debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag); 10715177Snate#endif 10815177Snate pc->rcvBufSize = rrclass; 10910217Sphk pc->DNSSECOK = ptr[6] & 0x80; 11015177Snate 11115177Snate return mDNStrue; 11215177Snate} 11315177Snate 11415177SnatemDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) 11515177Snate{ 11615177Snate DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext; 11715177Snate 11815177Snate (void) msg; 11915177Snate 12015177Snate h->flags = pc->requestFlags; 12115177Snate if (pc->optRR) 12215177Snate { 12315177Snate if (ptr + pc->optLen > limit) 12415177Snate { 12515177Snate LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit); 12610217Sphk return ptr; 12710217Sphk } 12815177Snate h->numAdditionals++; 12910217Sphk mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen); 13015177Snate ptr += pc->optLen; 13110217Sphk } 13210217Sphk return ptr; 13310217Sphk} 13410217Sphk 13510217SphkmDNSlocal mDNSu8 *AddEDNS0Option(mDNSu8 *ptr, mDNSu8 *limit) 13610217Sphk{ 13715177Snate int len = 4096; 13815177Snate 13915177Snate if (ptr + 11 > limit) 14015177Snate { 14115177Snate LogInfo("AddEDNS0Option: not enough space"); 14210217Sphk return mDNSNULL; 14315177Snate } 14410217Sphk mDNSStorage.omsg.h.numAdditionals++; 14515177Snate ptr[0] = 0; 14610217Sphk ptr[1] = (mDNSu8) (kDNSType_OPT >> 8); 14710217Sphk ptr[2] = (mDNSu8) (kDNSType_OPT & 0xFF); 14810217Sphk ptr[3] = (mDNSu8) (len >> 8); 14910217Sphk ptr[4] = (mDNSu8) (len & 0xFF); 15015177Snate ptr[5] = 0; // rcode 15110217Sphk ptr[6] = 0; // version 15215177Snate ptr[7] = 0; 15310217Sphk ptr[8] = 0; // flags 15415177Snate ptr[9] = 0; // rdlength 15510217Sphk ptr[10] = 0; // rdlength 15610217Sphk 15715177Snate debugf("AddEDNS0 option"); 15815177Snate 15910217Sphk return (ptr + 11); 16010217Sphk} 16115177Snate 16210217Sphk// Currently RD and CD bit should be copied if present in the request or cleared if 16315177Snate// not present in the request. RD bit is normally set in the response and hence the 16410217Sphk// cache reflects the right value. CD bit behaves differently. If the CD bit is set 16510217Sphk// the first time, the cache retains it, if it is present in response (assuming the 16615177Snate// upstream server does it right). Next time through we should not use the cached 16710217Sphk// value of the CD bit blindly. It depends on whether it was in the request or not. 16810217SphkmDNSlocal mDNSOpaque16 SetResponseFlags(DNSProxyClient *pc, const mDNSOpaque16 responseFlags) 16910217Sphk{ 17016487Snate mDNSOpaque16 rFlags = responseFlags; 17110217Sphk 17210217Sphk if (pc->requestFlags.b[0] & kDNSFlag0_RD) 17310217Sphk rFlags.b[0] |= kDNSFlag0_RD; 17410217Sphk else 17516466Snate rFlags.b[0] &= ~kDNSFlag0_RD; 17615177Snate 17716466Snate if (pc->requestFlags.b[1] & kDNSFlag1_CD) 17815177Snate rFlags.b[1] |= kDNSFlag1_CD; 17916466Snate else 18015177Snate rFlags.b[1] &= ~kDNSFlag1_CD; 18116466Snate 18210217Sphk return rFlags; 18315177Snate} 18410217Sphk 18510217SphkmDNSlocal mDNSu8 *AddResourceRecords(DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error) 18610217Sphk{ 18716487Snate mDNS *const m = &mDNSStorage; 18810255Sphk CacheGroup *cg; 18910217Sphk CacheRecord *cr; 19015177Snate int len = sizeof(DNSMessageHeader); 19110217Sphk mDNSu8 *orig = m->omsg.data; 19210217Sphk mDNSBool first = mDNStrue; 19310217Sphk mDNSu8 *ptr = mDNSNULL; 19410217Sphk mDNSs32 now; 19510217Sphk mDNSs32 ttl; 19615177Snate CacheRecord *nsec = mDNSNULL; 19710217Sphk CacheRecord *soa = mDNSNULL; 19810217Sphk CacheRecord *cname = mDNSNULL; 19915177Snate mDNSu8 *limit; 20010217Sphk domainname tempQName; 20110217Sphk mDNSu32 tempQNameHash; 20215177Snate 20310217Sphk *error = mStatus_NoError; 20415177Snate *prevptr = mDNSNULL; 20510217Sphk 20610217Sphk mDNS_Lock(m); 20710217Sphk now = m->timenow; 20816487Snate mDNS_Unlock(m); 20910255Sphk 21010217Sphk if (!pc->tcp) 21115177Snate { 21215177Snate if (!pc->rcvBufSize) 21315177Snate { 21415177Snate limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE; 21515177Snate } 21615177Snate else 21710217Sphk { 21810217Sphk limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize); 21910217Sphk } 22010217Sphk } 22115177Snate else 22210217Sphk { 22310217Sphk // For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and 22410217Sphk // AbsoluteMaxDNSMessageData is smaller than 64k. 22510217Sphk limit = m->omsg.data + AbsoluteMaxDNSMessageData; 22615177Snate } 22710217Sphk LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data); 22810217Sphk 22910217Sphk AssignDomainName(&tempQName, &pc->qname); 23016487Snate tempQNameHash = DomainNameHashValue(&tempQName); 23110255Sphk 23210217Sphkagain: 23315177Snate nsec = soa = cname = mDNSNULL; 23415177Snate 23515177Snate cg = CacheGroupForName(m, tempQNameHash, &tempQName); 23615177Snate if (!cg) 23715177Snate { 23815177Snate LogInfo("AddResourceRecords: CacheGroup not found for %##s", tempQName.c); 23915177Snate *error = mStatus_NoSuchRecord; 24015177Snate return mDNSNULL; 24115177Snate } 24215177Snate // Set ValidatingResponse so that you can get RRSIGs also matching 24310217Sphk // the question 24410217Sphk if (pc->DNSSECOK) 24515177Snate pc->q.ValidatingResponse = 1; 24610217Sphk for (cr = cg->members; cr; cr = cr->next) 24710217Sphk { 24810217Sphk if (SameNameRecordAnswersQuestion(&cr->resrec, &pc->q)) 24915177Snate { 25010217Sphk if (first) 25110217Sphk { 25210217Sphk // If this is the first time, initialize the header and the question. 25310217Sphk // This code needs to be here so that we can use the responseFlags from the 25410217Sphk // cache record 25510217Sphk mDNSOpaque16 responseFlags = SetResponseFlags(pc, cr->responseFlags); 25610217Sphk InitializeDNSMessage(&m->omsg.h, pc->msgid, responseFlags); 25715177Snate ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass); 25810217Sphk if (!ptr) 25910217Sphk { 26010217Sphk LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); 26110217Sphk return mDNSNULL; 26210217Sphk } 26315177Snate first = mDNSfalse; 26415177Snate } 26515177Snate // - For NegativeAnswers there is nothing to add 26610217Sphk // - If DNSSECOK is set, we also automatically lookup the RRSIGs which 26710217Sphk // will also be returned. If the client is explicitly looking up 26810217Sphk // a DNSSEC record (e.g., DNSKEY, DS) we should return the response. 26910217Sphk // DNSSECOK bit only influences whether we add the RRSIG or not. 27010217Sphk if (cr->resrec.RecordType != kDNSRecordTypePacketNegative) 27110217Sphk { 27210217Sphk LogInfo("AddResourceRecords: Answering question with %s", CRDisplayString(m, cr)); 27310217Sphk ttl = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; 27415177Snate ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAnswers, &cr->resrec, ttl, limit); 27515177Snate if (!ptr) 27610217Sphk { 27710217Sphk *prevptr = orig; 27815177Snate return mDNSNULL; 27910217Sphk } 28010217Sphk len += (ptr - orig); 28115177Snate orig = ptr; 28210217Sphk } 28310217Sphk // If we have nsecs (wildcard expanded answer or negative response), add them 28410217Sphk // in the additional section below if the DNSSECOK bit is set 28515177Snate if (pc->DNSSECOK && cr->nsec) 28610217Sphk { 28710217Sphk LogInfo("AddResourceRecords: nsec set for %s", CRDisplayString(m ,cr)); 28810217Sphk nsec = cr->nsec; 28910217Sphk } 29010217Sphk if (cr->soa) 29110217Sphk { 29210217Sphk LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr)); 29315177Snate soa = cr->soa; 29410217Sphk } 29510217Sphk // If we are using CNAME to answer a question and CNAME is not the type we 29610217Sphk // are looking for, note down the CNAME record so that we can follow them 29710217Sphk // later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard 29810217Sphk // expanded) if any. 29910217Sphk if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME) 30010217Sphk { 30110217Sphk LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr)); 30210217Sphk cname = cr; 30310217Sphk } 30410217Sphk } 30510217Sphk } 30610217Sphk // Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need 30710217Sphk // to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed 30810217Sphk // to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to 30915177Snate // by "cr->nsec". Two cases: 31010217Sphk // 31110217Sphk // - if we issue a DNSSEC question followed by non-DNSSEC question for the same name, 31215177Snate // we only have the nsec records and we need to filter the SOA record alone for the 31310217Sphk // non-DNSSEC questions. 31410217Sphk // 31510217Sphk // - if we issue a non-DNSSEC question followed by DNSSEC question for the same name, 31610217Sphk // the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and 31710217Sphk // in this case we return all the DNSSEC records we have. 31810217Sphk for (; nsec; nsec = nsec->next) 31910217Sphk { 32010217Sphk if (!pc->DNSSECOK && DNSSECRecordType(nsec->resrec.rrtype)) 32110217Sphk continue; 32210217Sphk LogInfo("AddResourceRecords:NSEC Answering question with %s", CRDisplayString(m, nsec)); 32310217Sphk ttl = nsec->resrec.rroriginalttl - (now - nsec->TimeRcvd) / mDNSPlatformOneSecond; 32410217Sphk ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &nsec->resrec, ttl, limit); 32510217Sphk if (!ptr) 32610217Sphk { 32710217Sphk *prevptr = orig; 32810217Sphk return mDNSNULL; 32910217Sphk } 33010217Sphk len += (ptr - orig); 33115177Snate orig = ptr; 33210217Sphk } 33310217Sphk if (soa) 33410217Sphk { 33515177Snate LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m, soa)); 33615177Snate ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &soa->resrec, soa->resrec.rroriginalttl, limit); 33710217Sphk if (!ptr) 33810217Sphk { 33910217Sphk *prevptr = orig; 34015177Snate return mDNSNULL; 34110217Sphk } 34210217Sphk len += (ptr - orig); 34310217Sphk orig = ptr; 34415177Snate } 34515177Snate if (cname) 34610217Sphk { 34710217Sphk AssignDomainName(&tempQName, &cname->resrec.rdata->u.name); 34810217Sphk tempQNameHash = DomainNameHashValue(&tempQName); 34910217Sphk goto again; 35010217Sphk } 35115177Snate if (!ptr) 35210217Sphk { 35310217Sphk LogInfo("AddResourceRecords: Did not find any valid ResourceRecords"); 35410217Sphk *error = mStatus_NoSuchRecord; 35510217Sphk return mDNSNULL; 35610217Sphk } 35710217Sphk if (pc->rcvBufSize) 35810217Sphk { 35910217Sphk ptr = AddEDNS0Option(ptr, limit); 36010217Sphk if (!ptr) 36110217Sphk { 36210217Sphk *prevptr = orig; 36315177Snate return mDNSNULL; 36410217Sphk } 36510217Sphk len += (ptr - orig); 36610217Sphk // orig = ptr; Commented out to avoid ���value never read��� error message 36710217Sphk } 36810255Sphk LogInfo("AddResourceRecord: Added %d bytes to the packet", len); 36910255Sphk return ptr; 37010217Sphk} 37115177Snate 37210217SphkmDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) 37310217Sphk{ 37410217Sphk DNSProxyClient *pc = question->QuestionContext; 37515177Snate DNSProxyClient **ppc = &DNSProxyClients; 37610217Sphk mDNSu8 *ptr; 37710217Sphk mDNSu8 *prevptr; 37815177Snate mStatus error; 37910217Sphk 38015177Snate if (!AddRecord) 38110217Sphk return; 38210217Sphk 38310217Sphk LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer)); 38415177Snate 38510217Sphk // We asked for validation and not timed out yet, then wait for the DNSSEC result. 38615177Snate // We have to set the AD bit in the response if it is secure which can't be done 38715177Snate // till we get the DNSSEC result back (indicated by QC_dnssec). 38810217Sphk if (question->ValidationRequired) 38910217Sphk { 39015177Snate mDNSs32 now; 39110217Sphk 39215177Snate mDNS_Lock(m); 39310217Sphk now = m->timenow; 39410217Sphk mDNS_Unlock(m); 39510217Sphk if (((now - question->StopTime) < 0) && AddRecord != QC_dnssec) 39610217Sphk { 39710217Sphk LogInfo("ProxyClientCallback: No DNSSEC answer yet for Question %##s (%s), AddRecord %d, answer %s", question->qname.c, 39810217Sphk DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer)); 39910217Sphk return; 40010217Sphk } 40110217Sphk } 40210217Sphk 40310217Sphk if (answer->RecordType != kDNSRecordTypePacketNegative) 40410217Sphk { 40510217Sphk if (answer->rrtype != question->qtype) 40610217Sphk { 40710217Sphk // Wait till we get called for the real response 40810217Sphk LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer)); 40910217Sphk return; 41010217Sphk } 41116487Snate } 41210217Sphk ptr = AddResourceRecords(pc, &prevptr, &error); 41310217Sphk if (!ptr) 41415177Snate { 41515177Snate LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); 41615177Snate if (error == mStatus_NoError && prevptr) 41715177Snate { 41810217Sphk // No space to add the record. Set the Truncate bit for UDP. 41910217Sphk // 42015177Snate // TBD: For TCP, we need to send the rest of the data. But finding out what is left 42115177Snate // is harder. We should allocate enough buffer in the first place to send all 42215177Snate // of the data. 42315177Snate if (!pc->tcp) 42410217Sphk { 42510217Sphk m->omsg.h.flags.b[0] |= kDNSFlag0_TC; 42615177Snate ptr = prevptr; 42710217Sphk } 42810217Sphk else 42915177Snate { 43015177Snate LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); 43110217Sphk ptr = prevptr; 43215177Snate } 43315177Snate } 43415177Snate else 43510217Sphk { 43610217Sphk mDNSOpaque16 flags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery, kDNSFlag1_RC_ServFail } }; 43715177Snate // We could not find the record for some reason. Return a response, so that the client 43810217Sphk // is not waiting forever. 43915177Snate LogInfo("ProxyClientCallback: No response"); 44015177Snate if (!mDNSOpaque16IsZero(pc->q.responseFlags)) 44110217Sphk flags = pc->q.responseFlags; 44210217Sphk InitializeDNSMessage(&m->omsg.h, pc->msgid, flags); 44310217Sphk ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass); 44410217Sphk if (!ptr) 44515177Snate { 44615177Snate LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); 44715177Snate goto done; 44815177Snate } 44915177Snate } 45015177Snate } 45115177Snate if (question->ValidationRequired) 45215177Snate { 45315177Snate if (question->ValidationState == DNSSECValDone && question->ValidationStatus == DNSSEC_Secure) 45410217Sphk { 45510217Sphk LogInfo("ProxyClientCallback: Setting AD bit for Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); 45615177Snate m->omsg.h.flags.b[1] |= kDNSFlag1_AD; 45710217Sphk } 45815177Snate else 45915177Snate { 46015177Snate // If some external resolver sets the AD bit and we did not validate the response securely, don't set 46110217Sphk // the AD bit. It is possible that we did not see all the records that the upstream resolver saw or 46215177Snate // a buggy implementation somewhere. 46310217Sphk if (m->omsg.h.flags.b[1] & kDNSFlag1_AD) 46410217Sphk { 46510217Sphk LogInfo("ProxyClientCallback: AD bit set in the response for response that was not validated locally %##s (%s)", 46616487Snate question->qname.c, DNSTypeName(question->qtype)); 46710217Sphk m->omsg.h.flags.b[1] &= ~kDNSFlag1_AD; 46810217Sphk } 46915177Snate } 47015177Snate } 47115177Snate 47215177Snate debugf("ProxyClientCallback: InterfaceID is %p for response to client", pc->interfaceID); 47315177Snate 47410217Sphk if (!pc->tcp) 47515177Snate { 47610217Sphk mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse); 47710217Sphk } 47815177Snate else 47910217Sphk { 48010217Sphk mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse); 48110217Sphk } 48210217Sphk 48310217Sphkdone: 48415177Snate mDNS_StopQuery(m, question); 48515177Snate 48630171Scharnier while (*ppc && *ppc != pc) 48710217Sphk ppc=&(*ppc)->next; 48815177Snate if (!*ppc) 48910217Sphk { 49010217Sphk LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype)); 49110217Sphk return; 49210217Sphk } 49310217Sphk *ppc = pc->next; 49415177Snate mDNSPlatformDisposeProxyContext(pc->context); 49530171Scharnier FreeDNSProxyClient(pc); 49610217Sphk} 49715177Snate 49810217SphkmDNSlocal void SendError(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *dstaddr, 49910217Sphk const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode) 50010217Sphk{ 50121371Snate mDNS *const m = &mDNSStorage; 50210217Sphk int pktlen = (int)(end - (mDNSu8 *)msg); 50315177Snate 50410217Sphk // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing 50510217Sphk // in the body or send back whatever we get for updates. It is easy to return whatever we get 50615177Snate // in the question back to the responder. We return as much as we can fit in our standard 50715177Snate // output packet. 50810217Sphk if (pktlen > AbsoluteMaxDNSMessageData) 50910217Sphk pktlen = AbsoluteMaxDNSMessageData; 51015177Snate 51130171Scharnier mDNSPlatformMemCopy(&m->omsg.h, &msg->h, sizeof(DNSMessageHeader)); 51210217Sphk m->omsg.h.flags.b[0] |= kDNSFlag0_QR_Response; 51310217Sphk m->omsg.h.flags.b[1] = rcode; 51415177Snate mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->data, (pktlen - sizeof(DNSMessageHeader))); 51515177Snate 51615177Snate if (!tcp) 51715177Snate { 51815177Snate mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, socket, dstaddr, dstport, mDNSNULL, mDNSNULL, 51915177Snate mDNSfalse); 52010217Sphk } 52115177Snate else 52210217Sphk { 52310217Sphk mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket, 52415177Snate mDNSNULL, mDNSfalse); 52515177Snate } 52610217Sphk mDNSPlatformDisposeProxyContext(context); 52710217Sphk} 52810217Sphk 52910217SphkmDNSlocal DNSQuestion *IsDuplicateClient(const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id, 53015177Snate const DNSQuestion *const question) 53115177Snate{ 53210217Sphk DNSProxyClient *pc; 53315177Snate 53410217Sphk for (pc = DNSProxyClients; pc; pc = pc->next) 53510217Sphk { 53610217Sphk if (mDNSSameAddress(&pc->addr, addr) && 53716487Snate mDNSSameIPPort(pc->port, port) && 53810217Sphk mDNSSameOpaque16(pc->msgid, id) && 53910217Sphk pc->q.qtype == question->qtype && 54015177Snate pc->q.qclass == question->qclass && 54110217Sphk SameDomainName(&pc->qname, &question->qname)) 54210217Sphk { 54310217Sphk LogInfo("IsDuplicateClient: Found a duplicate client in the list"); 54410217Sphk return(&pc->q); 54515177Snate } 54610217Sphk } 54710217Sphk return(mDNSNULL); 54810217Sphk} 54910217Sphk 55010217SphkmDNSlocal mDNSBool CheckDNSProxyIpIntf(mDNSInterfaceID InterfaceID) 55115177Snate{ 55215177Snate mDNS *const m = &mDNSStorage; 55310217Sphk int i; 55415177Snate mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID; 55510217Sphk 55610217Sphk LogInfo("CheckDNSProxyIpIntf: Check for ifindex[%d] in stored input interface list: [%d] [%d] [%d] [%d] [%d]", 55710217Sphk ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]); 55810217Sphk 55916487Snate if (ip_ifindex > 0) 56010217Sphk { 56110217Sphk for (i = 0; i < MaxIp; i++) 56215177Snate { 56310217Sphk if (ip_ifindex == m->dp_ipintf[i]) 56410217Sphk return mDNStrue; 56510217Sphk } 56610217Sphk } 56715177Snate 56810217Sphk LogMsg("CheckDNSProxyIpIntf: ifindex[%d] not in stored input interface list: [%d] [%d] [%d] [%d] [%d]", 56915177Snate ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]); 57010217Sphk 57110217Sphk return mDNSfalse; 57210217Sphk 57315177Snate} 57415177Snate 57510217SphkmDNSlocal void ProxyCallbackCommon(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, 57615177Snate const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context) 57715177Snate{ 57815177Snate mDNS *const m = &mDNSStorage; 57915177Snate mDNSu8 QR_OP; 58015177Snate const mDNSu8 *ptr; 58115177Snate DNSQuestion q, *qptr; 58215177Snate DNSProxyClient *pc; 58315177Snate const mDNSu8 *optRR = mDNSNULL; 58410217Sphk int optLen = 0; 58510217Sphk DNSProxyClient **ppc = &DNSProxyClients; 58610217Sphk 58715177Snate (void) dstaddr; 58815177Snate (void) dstport; 58910217Sphk 59015177Snate debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID); 59110217Sphk // Ignore if the DNS Query is not from a Valid Input InterfaceID 59210217Sphk if (!CheckDNSProxyIpIntf(InterfaceID)) 59310217Sphk { 59416487Snate LogMsg("ProxyCallbackCommon: Rejecting DNS Query coming from InterfaceID %p", InterfaceID); 59510217Sphk return; 59610217Sphk } 59715177Snate 59810217Sphk if ((unsigned)(end - (mDNSu8 *)msg) < sizeof(DNSMessageHeader)) 59910217Sphk { 60010217Sphk debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg)); 60115177Snate return; 60210217Sphk } 60315177Snate 60410217Sphk // Read the integer parts which are in IETF byte-order (MSB first, LSB second) 60515177Snate ptr = (mDNSu8 *)&msg->h.numQuestions; 60616487Snate msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); 60710217Sphk msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); 60810217Sphk msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); 60915177Snate msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); 61010217Sphk 61110217Sphk QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); 61210217Sphk if (QR_OP != kDNSFlag0_QR_Query) 61315177Snate { 61415177Snate LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport)); 61510217Sphk SendError(socket, msg, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl); 616 return; 617 } 618 619 if (msg->h.numQuestions != 1 || msg->h.numAnswers || msg->h.numAuthorities) 620 { 621 LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr, mDNSVal16(srcport), 622 msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities); 623 SendError(socket, msg, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); 624 return; 625 } 626 ptr = msg->data; 627 ptr = getQuestion(msg, ptr, end, InterfaceID, &q); 628 if (!ptr) 629 { 630 LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr, mDNSVal16(srcport)); 631 SendError(socket, msg, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); 632 return; 633 } 634 else 635 { 636 LogInfo("ProxyCallbackCommon: Question %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); 637 } 638 ptr = LocateOptRR(msg, end, 0); 639 if (ptr) 640 { 641 optRR = ptr; 642 ptr = skipResourceRecord(msg, ptr, end); 643 // Be liberal and ignore the EDNS0 option if we can't parse it properly 644 if (!ptr) 645 { 646 LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr, mDNSVal16(srcport)); 647 } 648 else 649 { 650 optLen = ptr - optRR; 651 LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen, q.qname.c, DNSTypeName(q.qtype)); 652 } 653 } 654 else 655 { 656 LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q.qname.c, DNSTypeName(q.qtype), ptr); 657 } 658 659 qptr = IsDuplicateClient(srcaddr, srcport, msg->h.id, &q); 660 if (qptr) 661 { 662 LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); 663 return; 664 } 665 pc = mDNSPlatformMemAllocate(sizeof(DNSProxyClient)); 666 if (!pc) 667 { 668 LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); 669 return; 670 } 671 mDNSPlatformMemZero(pc, sizeof(DNSProxyClient)); 672 pc->addr = *srcaddr; 673 pc->port = srcport; 674 pc->msgid = msg->h.id; 675 pc->interfaceID = InterfaceID; // input interface 676 pc->socket = socket; 677 pc->tcp = tcp; 678 pc->requestFlags = msg->h.flags; 679 pc->context = context; 680 AssignDomainName(&pc->qname, &q.qname); 681 if (optRR) 682 { 683 if (!ParseEDNS0(pc, optRR, optLen, end)) 684 { 685 LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); 686 } 687 else 688 { 689 pc->optRR = mDNSPlatformMemAllocate(optLen); 690 if (!pc->optRR) 691 { 692 LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); 693 FreeDNSProxyClient(pc); 694 return; 695 } 696 mDNSPlatformMemCopy(pc->optRR, optRR, optLen); 697 pc->optLen = optLen; 698 } 699 } 700 701 debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m->dp_opintf); 702 mDNS_SetupQuestion(&pc->q, (mDNSInterfaceID)(unsigned long)m->dp_opintf, &q.qname, q.qtype, ProxyClientCallback, pc); 703 pc->q.TimeoutQuestion = 1; 704 // Set ReturnIntermed so that we get the negative responses 705 pc->q.ReturnIntermed = mDNStrue; 706 pc->q.ProxyQuestion = mDNStrue; 707 pc->q.ProxyDNSSECOK = pc->DNSSECOK; 708 pc->q.responseFlags = zeroID; 709 if (pc->DNSSECOK) 710 { 711 if (!(msg->h.flags.b[1] & kDNSFlag1_CD) && pc->q.qtype != kDNSType_RRSIG && pc->q.qtype != kDNSQType_ANY) 712 { 713 LogInfo("ProxyCallbackCommon: Setting Validation required bit for %#a:%d, validating %##s (%s)", srcaddr, mDNSVal16(srcport), 714 q.qname.c, DNSTypeName(q.qtype)); 715 pc->q.ValidationRequired = DNSSEC_VALIDATION_SECURE; 716 } 717 else 718 { 719 LogInfo("ProxyCallbackCommon: CD bit not set OR not a valid type for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport), 720 q.qname.c, DNSTypeName(q.qtype)); 721 } 722 } 723 else 724 { 725 LogInfo("ProxyCallbackCommon: DNSSEC OK bit not set for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport), 726 q.qname.c, DNSTypeName(q.qtype)); 727 } 728 729 while (*ppc) 730 ppc = &((*ppc)->next); 731 *ppc = pc; 732 733 mDNS_StartQuery(m, &pc->q); 734} 735 736mDNSexport void ProxyUDPCallback(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, 737 const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) 738{ 739 LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg)); 740 ProxyCallbackCommon(socket, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context); 741} 742 743mDNSexport void ProxyTCPCallback(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, 744 const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) 745{ 746 LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg)); 747 748 // If the connection was closed from the other side or incoming packet does not match stored input interface list, locate the client 749 // state and free it. 750 if (((end - (mDNSu8 *)msg) == 0) || (!CheckDNSProxyIpIntf(InterfaceID))) 751 { 752 DNSProxyClient **ppc = &DNSProxyClients; 753 DNSProxyClient **prevpc; 754 755 prevpc = ppc; 756 while (*ppc && (*ppc)->socket != socket) 757 { 758 prevpc = ppc; 759 ppc=&(*ppc)->next; 760 } 761 if (!*ppc) 762 { 763 mDNSPlatformDisposeProxyContext(socket); 764 LogMsg("ProxyTCPCallback: socket cannot be found"); 765 return; 766 } 767 *prevpc = (*ppc)->next; 768 LogInfo("ProxyTCPCallback: free"); 769 mDNSPlatformDisposeProxyContext(socket); 770 FreeDNSProxyClient(*ppc); 771 return; 772 } 773 ProxyCallbackCommon(socket, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context); 774} 775 776mDNSexport void DNSProxyInit(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) 777{ 778 mDNS *const m = &mDNSStorage; 779 int i; 780 781 // Store DNSProxy Interface fields in mDNS struct 782 for (i = 0; i < MaxIp; i++) 783 m->dp_ipintf[i] = IpIfArr[i]; 784 m->dp_opintf = OpIf; 785 786 LogInfo("DNSProxyInit Storing interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0], 787 m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); 788} 789 790mDNSexport void DNSProxyTerminate(void) 791{ 792 mDNS *const m = &mDNSStorage; 793 int i; 794 795 // Clear DNSProxy Interface fields from mDNS struct 796 for (i = 0; i < MaxIp; i++) 797 m->dp_ipintf[i] = 0; 798 m->dp_opintf = 0; 799 800 LogInfo("DNSProxyTerminate Cleared interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0], 801 m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); 802} 803#else // UNICAST_DISABLED 804 805mDNSexport void ProxyUDPCallback(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) 806{ 807 (void) socket; 808 (void) msg; 809 (void) end; 810 (void) srcaddr; 811 (void) srcport; 812 (void) dstaddr; 813 (void) dstport; 814 (void) InterfaceID; 815 (void) context; 816} 817 818mDNSexport void ProxyTCPCallback(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) 819{ 820 (void) socket; 821 (void) msg; 822 (void) end; 823 (void) srcaddr; 824 (void) srcport; 825 (void) dstaddr; 826 (void) dstport; 827 (void) InterfaceID; 828 (void) context; 829} 830 831mDNSexport void DNSProxyInit(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) 832{ 833 (void) IpIfArr; 834 (void) OpIf; 835} 836extern void DNSProxyTerminate(void) 837{ 838} 839 840 841#endif // UNICAST_DISABLED 842