1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011-2013 Apple Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "dnsproxy.h"
19
20#ifndef UNICAST_DISABLED
21
22mDNSexport mDNS mDNSStorage;
23
24// Implementation Notes
25//
26// DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only
27// the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns
28// "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts
29// sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback
30// defined here. For TCP socket, the platform does the "accept" and only sends the received packets
31// on the newly accepted socket. A single UDP socket (per address family) is used to send/recv
32// requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some
33// extra state that needs to be disposed at the end.
34//
35// When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks
36// for duplicates, before creating DNSProxyClient state and starting a question with the "core"
37// (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary
38// resource records, constructs a response and sends it back to the client.
39//
40//   - Question callback is called with only one resource record at a time. We need all the resource
41//     records to construct the response. Hence, we lookup all the records ourselves.
42//
43//   - The response may not fit the client's buffer size. In that case, we need to set the truncate bit
44//     and the client would retry using TCP.
45//
46//   - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to
47//     return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to
48//     ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set.
49//
50// Once the response is sent to the client, the client state is disposed. When there is no response
51// from the "core", it eventually times out and we will not find any answers in the cache and we send a
52// "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case
53// of errors.
54
55typedef struct DNSProxyClient_struct DNSProxyClient;
56
57struct DNSProxyClient_struct {
58
59    DNSProxyClient *next;
60    mDNSAddr    addr;               // Client's IP address
61    mDNSIPPort  port;               // Client's port number
62    mDNSOpaque16 msgid;             // DNS msg id
63    mDNSInterfaceID interfaceID;    // Interface on which we received the request
64    void *socket;                   // Return socket
65    mDNSBool tcp;                   // TCP or UDP ?
66    mDNSOpaque16 requestFlags;      // second 16 bit word in the DNSMessageHeader of the request
67    mDNSu8 *optRR;                  // EDNS0 option
68    mDNSu16 optLen;                 // Total Length of the EDNS0 option
69    mDNSu16 rcvBufSize;             // How much can the client receive ?
70    mDNSBool DNSSECOK;              // DNSSEC OK ?
71    void *context;                  // Platform context to be disposed if non-NULL
72    domainname qname;               // q->qname can't be used for duplicate check
73    DNSQuestion q;                  // as it can change underneath us for CNAMEs
74};
75
76#define MIN_DNS_MESSAGE_SIZE    512
77static DNSProxyClient *DNSProxyClients;
78
79mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc)
80{
81    if (pc->optRR)
82        mDNSPlatformMemFree(pc->optRR);
83    mDNSPlatformMemFree(pc);
84}
85
86mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit)
87{
88    if (ptr + length > limit)
89    {
90        LogInfo("ParseEDNS0: Not enough space in the packet");
91        return mDNSfalse;
92    }
93    // Skip the root label
94    ptr++;
95    mDNSu16 rrtype  = (mDNSu16) ((mDNSu16)ptr[0] <<  8 | ptr[1]);
96    if (rrtype != kDNSType_OPT)
97    {
98        LogInfo("ParseEDNS0: Not the right type %d", rrtype);
99        return mDNSfalse;
100    }
101    mDNSu16 rrclass = (mDNSu16) ((mDNSu16)ptr[2] <<  8 | ptr[3]);
102#if MDNS_DEBUGMSGS
103    mDNSu8  rcode   = ptr[4];
104    mDNSu8  version = ptr[5];
105    mDNSu16 flag    = (mDNSu16) ((mDNSu16)ptr[6] <<  8 | ptr[7]);
106    debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag);
107#endif
108    pc->rcvBufSize = rrclass;
109    pc->DNSSECOK = ptr[6] & 0x80;
110
111    return mDNStrue;
112}
113
114mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
115{
116    DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext;
117
118    (void) msg;
119
120    h->flags = pc->requestFlags;
121    if (pc->optRR)
122    {
123        if (ptr + pc->optLen > limit)
124        {
125            LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit);
126            return ptr;
127        }
128        h->numAdditionals++;
129        mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen);
130        ptr += pc->optLen;
131    }
132    return ptr;
133}
134
135mDNSlocal mDNSu8 *AddEDNS0Option(mDNSu8 *ptr, mDNSu8 *limit)
136{
137    int len = 4096;
138
139    if (ptr + 11 > limit)
140    {
141        LogInfo("AddEDNS0Option: not enough space");
142        return mDNSNULL;
143    }
144    mDNSStorage.omsg.h.numAdditionals++;
145    ptr[0] = 0;
146    ptr[1] = (mDNSu8) (kDNSType_OPT >> 8);
147    ptr[2] = (mDNSu8) (kDNSType_OPT & 0xFF);
148    ptr[3] = (mDNSu8) (len >> 8);
149    ptr[4] = (mDNSu8) (len & 0xFF);
150    ptr[5] = 0;     // rcode
151    ptr[6] = 0;     // version
152    ptr[7] = 0;
153    ptr[8] = 0;     // flags
154    ptr[9] = 0;     // rdlength
155    ptr[10] = 0;    // rdlength
156
157    debugf("AddEDNS0 option");
158
159    return (ptr + 11);
160}
161
162// Currently RD and CD bit should be copied if present in the request or cleared if
163// not present in the request. RD bit is normally set in the response and hence the
164// cache reflects the right value. CD bit behaves differently. If the CD bit is set
165// the first time, the cache retains it, if it is present in response (assuming the
166// upstream server does it right). Next time through we should not use the cached
167// value of the CD bit blindly. It depends on whether it was in the request or not.
168mDNSlocal mDNSOpaque16 SetResponseFlags(DNSProxyClient *pc, const mDNSOpaque16 responseFlags)
169{
170    mDNSOpaque16 rFlags = responseFlags;
171
172    if (pc->requestFlags.b[0] & kDNSFlag0_RD)
173        rFlags.b[0] |= kDNSFlag0_RD;
174    else
175        rFlags.b[0] &= ~kDNSFlag0_RD;
176
177    if (pc->requestFlags.b[1] & kDNSFlag1_CD)
178        rFlags.b[1] |= kDNSFlag1_CD;
179    else
180        rFlags.b[1] &= ~kDNSFlag1_CD;
181
182    return rFlags;
183}
184
185mDNSlocal mDNSu8 *AddResourceRecords(DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error)
186{
187    mDNS *const m = &mDNSStorage;
188    CacheGroup *cg;
189    CacheRecord *cr;
190    int len = sizeof(DNSMessageHeader);
191    mDNSu8 *orig = m->omsg.data;
192    mDNSBool first = mDNStrue;
193    mDNSu8 *ptr = mDNSNULL;
194    mDNSs32 now;
195    mDNSs32 ttl;
196    CacheRecord *nsec = mDNSNULL;
197    CacheRecord *soa = mDNSNULL;
198    CacheRecord *cname = mDNSNULL;
199    mDNSu8 *limit;
200    domainname tempQName;
201    mDNSu32 tempQNameHash;
202
203    *error = mStatus_NoError;
204    *prevptr = mDNSNULL;
205
206    mDNS_Lock(m);
207    now = m->timenow;
208    mDNS_Unlock(m);
209
210    if (!pc->tcp)
211    {
212        if (!pc->rcvBufSize)
213        {
214            limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE;
215        }
216        else
217        {
218            limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize);
219        }
220    }
221    else
222    {
223        // For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and
224        // AbsoluteMaxDNSMessageData is smaller than 64k.
225        limit = m->omsg.data + AbsoluteMaxDNSMessageData;
226    }
227    LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data);
228
229    AssignDomainName(&tempQName, &pc->qname);
230    tempQNameHash = DomainNameHashValue(&tempQName);
231
232again:
233    nsec = soa = cname = mDNSNULL;
234
235    cg = CacheGroupForName(m, tempQNameHash, &tempQName);
236    if (!cg)
237    {
238        LogInfo("AddResourceRecords: CacheGroup not found for %##s", tempQName.c);
239        *error = mStatus_NoSuchRecord;
240        return mDNSNULL;
241    }
242    // Set ValidatingResponse so that you can get RRSIGs also matching
243    // the question
244    if (pc->DNSSECOK)
245        pc->q.ValidatingResponse = 1;
246    for (cr = cg->members; cr; cr = cr->next)
247    {
248        if (SameNameRecordAnswersQuestion(&cr->resrec, &pc->q))
249        {
250            if (first)
251            {
252                // If this is the first time, initialize the header and the question.
253                // This code needs to be here so that we can use the responseFlags from the
254                // cache record
255                mDNSOpaque16 responseFlags = SetResponseFlags(pc, cr->responseFlags);
256                InitializeDNSMessage(&m->omsg.h, pc->msgid, responseFlags);
257                ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass);
258                if (!ptr)
259                {
260                    LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
261                    return mDNSNULL;
262                }
263                first = mDNSfalse;
264            }
265            // - For NegativeAnswers there is nothing to add
266            // - If DNSSECOK is set, we also automatically lookup the RRSIGs which
267            //   will also be returned. If the client is explicitly looking up
268            //   a DNSSEC record (e.g., DNSKEY, DS) we should return the response.
269            //   DNSSECOK bit only influences whether we add the RRSIG or not.
270            if (cr->resrec.RecordType != kDNSRecordTypePacketNegative)
271            {
272                LogInfo("AddResourceRecords: Answering question with %s", CRDisplayString(m, cr));
273                ttl = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
274                ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAnswers, &cr->resrec, ttl, limit);
275                if (!ptr)
276                {
277                    *prevptr = orig;
278                    return mDNSNULL;
279                }
280                len += (ptr - orig);
281                orig = ptr;
282            }
283            // If we have nsecs (wildcard expanded answer or negative response), add them
284            // in the additional section below if the DNSSECOK bit is set
285            if (pc->DNSSECOK && cr->nsec)
286            {
287                LogInfo("AddResourceRecords: nsec set for %s", CRDisplayString(m ,cr));
288                nsec = cr->nsec;
289            }
290            if (cr->soa)
291            {
292                LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr));
293                soa = cr->soa;
294            }
295            // If we are using CNAME to answer a question and CNAME is not the type we
296            // are looking for, note down the CNAME record so that we can follow them
297            // later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard
298            // expanded) if any.
299            if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME)
300            {
301                LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr));
302                cname = cr;
303            }
304        }
305    }
306    // Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need
307    // to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed
308    // to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to
309    // by "cr->nsec". Two cases:
310    //
311    // - if we issue a DNSSEC question followed by non-DNSSEC question for the same name,
312    //   we only have the nsec records and we need to filter the SOA record alone for the
313    //   non-DNSSEC questions.
314    //
315    // - if we issue a non-DNSSEC question followed by DNSSEC question for the same name,
316    //   the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and
317    //   in this case we return all the DNSSEC records we have.
318    for (; nsec; nsec = nsec->next)
319    {
320        if (!pc->DNSSECOK && DNSSECRecordType(nsec->resrec.rrtype))
321            continue;
322        LogInfo("AddResourceRecords:NSEC Answering question with %s", CRDisplayString(m, nsec));
323        ttl = nsec->resrec.rroriginalttl - (now - nsec->TimeRcvd) / mDNSPlatformOneSecond;
324        ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &nsec->resrec, ttl, limit);
325        if (!ptr)
326        {
327            *prevptr = orig;
328            return mDNSNULL;
329        }
330        len += (ptr - orig);
331        orig = ptr;
332    }
333    if (soa)
334    {
335        LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m, soa));
336        ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &soa->resrec, soa->resrec.rroriginalttl, limit);
337        if (!ptr)
338        {
339            *prevptr = orig;
340            return mDNSNULL;
341        }
342        len += (ptr - orig);
343        orig = ptr;
344    }
345    if (cname)
346    {
347        AssignDomainName(&tempQName, &cname->resrec.rdata->u.name);
348        tempQNameHash = DomainNameHashValue(&tempQName);
349        goto again;
350    }
351    if (!ptr)
352    {
353        LogInfo("AddResourceRecords: Did not find any valid ResourceRecords");
354        *error = mStatus_NoSuchRecord;
355        return mDNSNULL;
356    }
357    if (pc->rcvBufSize)
358    {
359        ptr = AddEDNS0Option(ptr, limit);
360        if (!ptr)
361        {
362            *prevptr = orig;
363            return mDNSNULL;
364        }
365        len += (ptr - orig);
366        // orig = ptr; Commented out to avoid ���value never read��� error message
367    }
368    LogInfo("AddResourceRecord: Added %d bytes to the packet", len);
369    return ptr;
370}
371
372mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
373{
374    DNSProxyClient *pc = question->QuestionContext;
375    DNSProxyClient **ppc = &DNSProxyClients;
376    mDNSu8 *ptr;
377    mDNSu8 *prevptr;
378    mStatus error;
379
380    if (!AddRecord)
381        return;
382
383    LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer));
384
385    // We asked for validation and not timed out yet, then wait for the DNSSEC result.
386    // We have to set the AD bit in the response if it is secure which can't be done
387    // till we get the DNSSEC result back (indicated by QC_dnssec).
388    if (question->ValidationRequired)
389    {
390        mDNSs32 now;
391
392        mDNS_Lock(m);
393        now = m->timenow;
394        mDNS_Unlock(m);
395        if (((now - question->StopTime) < 0) && AddRecord != QC_dnssec)
396        {
397            LogInfo("ProxyClientCallback: No DNSSEC answer yet for Question %##s (%s), AddRecord %d, answer %s", question->qname.c,
398                DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer));
399            return;
400        }
401    }
402
403    if (answer->RecordType != kDNSRecordTypePacketNegative)
404    {
405        if (answer->rrtype != question->qtype)
406        {
407            // Wait till we get called for the real response
408            LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer));
409            return;
410        }
411    }
412    ptr = AddResourceRecords(pc, &prevptr, &error);
413    if (!ptr)
414    {
415        LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
416        if (error == mStatus_NoError && prevptr)
417        {
418            // No space to add the record. Set the Truncate bit for UDP.
419            //
420            // TBD: For TCP, we need to send the rest of the data. But finding out what is left
421            // is harder. We should allocate enough buffer in the first place to send all
422            // of the data.
423            if (!pc->tcp)
424            {
425                m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
426                ptr = prevptr;
427            }
428            else
429            {
430                LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
431                ptr = prevptr;
432            }
433        }
434        else
435        {
436            mDNSOpaque16 flags   = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery, kDNSFlag1_RC_ServFail } };
437            // We could not find the record for some reason. Return a response, so that the client
438            // is not waiting forever.
439            LogInfo("ProxyClientCallback: No response");
440            if (!mDNSOpaque16IsZero(pc->q.responseFlags))
441                flags = pc->q.responseFlags;
442            InitializeDNSMessage(&m->omsg.h, pc->msgid, flags);
443            ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass);
444            if (!ptr)
445            {
446                LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
447                goto done;
448            }
449        }
450    }
451    if (question->ValidationRequired)
452    {
453        if (question->ValidationState == DNSSECValDone && question->ValidationStatus == DNSSEC_Secure)
454        {
455            LogInfo("ProxyClientCallback: Setting AD bit for Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
456            m->omsg.h.flags.b[1] |= kDNSFlag1_AD;
457        }
458        else
459        {
460            // If some external resolver sets the AD bit and we did not validate the response securely, don't set
461            // the AD bit. It is possible that we did not see all the records that the upstream resolver saw or
462            // a buggy implementation somewhere.
463            if (m->omsg.h.flags.b[1] & kDNSFlag1_AD)
464            {
465                LogInfo("ProxyClientCallback: AD bit set in the response for response that was not validated locally %##s (%s)",
466                    question->qname.c, DNSTypeName(question->qtype));
467                m->omsg.h.flags.b[1] &= ~kDNSFlag1_AD;
468            }
469        }
470    }
471
472    debugf("ProxyClientCallback: InterfaceID is %p for response to client", pc->interfaceID);
473
474    if (!pc->tcp)
475    {
476        mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse);
477    }
478    else
479    {
480        mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse);
481    }
482
483done:
484    mDNS_StopQuery(m, question);
485
486    while (*ppc && *ppc != pc)
487        ppc=&(*ppc)->next;
488    if (!*ppc)
489    {
490        LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype));
491        return;
492    }
493    *ppc = pc->next;
494    mDNSPlatformDisposeProxyContext(pc->context);
495    FreeDNSProxyClient(pc);
496}
497
498mDNSlocal void SendError(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *dstaddr,
499    const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode)
500{
501    mDNS *const m = &mDNSStorage;
502    int pktlen = (int)(end - (mDNSu8 *)msg);
503
504    // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing
505    // in the body or send back whatever we get for updates. It is easy to return whatever we get
506    // in the question back to the responder. We return as much as we can fit in our standard
507    // output packet.
508    if (pktlen > AbsoluteMaxDNSMessageData)
509        pktlen = AbsoluteMaxDNSMessageData;
510
511    mDNSPlatformMemCopy(&m->omsg.h, &msg->h, sizeof(DNSMessageHeader));
512    m->omsg.h.flags.b[0] |= kDNSFlag0_QR_Response;
513    m->omsg.h.flags.b[1] = rcode;
514    mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->data, (pktlen - sizeof(DNSMessageHeader)));
515
516    if (!tcp)
517    {
518        mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, socket, dstaddr, dstport, mDNSNULL, mDNSNULL,
519            mDNSfalse);
520    }
521    else
522    {
523        mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket,
524            mDNSNULL, mDNSfalse);
525    }
526    mDNSPlatformDisposeProxyContext(context);
527}
528
529mDNSlocal DNSQuestion *IsDuplicateClient(const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id,
530    const DNSQuestion *const question)
531{
532    DNSProxyClient *pc;
533
534	for (pc = DNSProxyClients; pc; pc = pc->next)
535    {
536        if (mDNSSameAddress(&pc->addr, addr)   &&
537            mDNSSameIPPort(pc->port, port)  &&
538            mDNSSameOpaque16(pc->msgid, id) &&
539            pc->q.qtype == question->qtype  &&
540            pc->q.qclass  == question->qclass &&
541            SameDomainName(&pc->qname, &question->qname))
542        {
543            LogInfo("IsDuplicateClient: Found a duplicate client in the list");
544            return(&pc->q);
545        }
546    }
547    return(mDNSNULL);
548}
549
550mDNSlocal mDNSBool CheckDNSProxyIpIntf(mDNSInterfaceID InterfaceID)
551{
552    mDNS *const m = &mDNSStorage;
553    int i;
554    mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID;
555
556    LogInfo("CheckDNSProxyIpIntf: Check for ifindex[%d] in stored input interface list: [%d] [%d] [%d] [%d] [%d]",
557            ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]);
558
559    if (ip_ifindex > 0)
560    {
561        for (i = 0; i < MaxIp; i++)
562        {
563            if (ip_ifindex == m->dp_ipintf[i])
564                return mDNStrue;
565        }
566    }
567
568    LogMsg("CheckDNSProxyIpIntf: ifindex[%d] not in stored input interface list: [%d] [%d] [%d] [%d] [%d]",
569            ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]);
570
571    return mDNSfalse;
572
573}
574
575mDNSlocal void ProxyCallbackCommon(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
576    const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context)
577{
578    mDNS *const m = &mDNSStorage;
579    mDNSu8 QR_OP;
580    const mDNSu8 *ptr;
581    DNSQuestion q, *qptr;
582    DNSProxyClient *pc;
583    const mDNSu8 *optRR = mDNSNULL;
584    int optLen = 0;
585    DNSProxyClient **ppc = &DNSProxyClients;
586
587    (void) dstaddr;
588    (void) dstport;
589
590    debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID);
591    // Ignore if the DNS Query is not from a Valid Input InterfaceID
592    if (!CheckDNSProxyIpIntf(InterfaceID))
593    {
594        LogMsg("ProxyCallbackCommon: Rejecting DNS Query coming from InterfaceID %p", InterfaceID);
595        return;
596    }
597
598    if ((unsigned)(end - (mDNSu8 *)msg) < sizeof(DNSMessageHeader))
599    {
600        debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg));
601        return;
602    }
603
604    // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
605    ptr = (mDNSu8 *)&msg->h.numQuestions;
606    msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
607    msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
608    msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
609    msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
610
611    QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
612    if (QR_OP != kDNSFlag0_QR_Query)
613    {
614        LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport));
615        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