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