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