1 /*
2 * Copyright (c) 2021, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the DNS-SD server.
32 */
33
34 #include "dnssd_server.hpp"
35
36 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
37
38 #include <openthread/platform/dns.h>
39
40 #include "common/array.hpp"
41 #include "common/as_core_type.hpp"
42 #include "common/code_utils.hpp"
43 #include "common/debug.hpp"
44 #include "common/locator_getters.hpp"
45 #include "common/log.hpp"
46 #include "common/string.hpp"
47 #include "instance/instance.hpp"
48 #include "net/srp_server.hpp"
49 #include "net/udp6.hpp"
50
51 namespace ot {
52 namespace Dns {
53 namespace ServiceDiscovery {
54
55 RegisterLogModule("DnssdServer");
56
57 const char Server::kDefaultDomainName[] = "default.service.arpa.";
58 const char Server::kSubLabel[] = "_sub";
59
60 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
61 const char *Server::kBlockedDomains[] = {"ipv4only.arpa."};
62 #endif
63
Server(Instance & aInstance)64 Server::Server(Instance &aInstance)
65 : InstanceLocator(aInstance)
66 , mSocket(aInstance)
67 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
68 , mEnableUpstreamQuery(false)
69 #endif
70 , mTimer(aInstance)
71 , mTestMode(kTestModeDisabled)
72 {
73 mCounters.Clear();
74 }
75
Start(void)76 Error Server::Start(void)
77 {
78 Error error = kErrorNone;
79
80 VerifyOrExit(!IsRunning());
81
82 SuccessOrExit(error = mSocket.Open(&Server::HandleUdpReceive, this));
83 SuccessOrExit(error = mSocket.Bind(kPort, kBindUnspecifiedNetif ? Ip6::kNetifUnspecified : Ip6::kNetifThread));
84
85 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
86 Get<Srp::Server>().HandleDnssdServerStateChange();
87 #endif
88
89 LogInfo("Started");
90
91 exit:
92 if (error != kErrorNone)
93 {
94 IgnoreError(mSocket.Close());
95 }
96
97 return error;
98 }
99
Stop(void)100 void Server::Stop(void)
101 {
102 for (ProxyQuery &query : mProxyQueries)
103 {
104 Finalize(query, Header::kResponseServerFailure);
105 }
106
107 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
108 for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions)
109 {
110 if (txn.IsValid())
111 {
112 ResetUpstreamQueryTransaction(txn, kErrorFailed);
113 }
114 }
115 #endif
116
117 mTimer.Stop();
118
119 IgnoreError(mSocket.Close());
120 LogInfo("Stopped");
121
122 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
123 Get<Srp::Server>().HandleDnssdServerStateChange();
124 #endif
125 }
126
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)127 void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
128 {
129 static_cast<Server *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
130 }
131
HandleUdpReceive(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)132 void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
133 {
134 Request request;
135
136 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
137 // We first let the `Srp::Server` process the received message.
138 // It returns `kErrorNone` to indicate that it successfully
139 // processed the message.
140
141 VerifyOrExit(Get<Srp::Server>().HandleDnssdServerUdpReceive(aMessage, aMessageInfo) != kErrorNone);
142 #endif
143
144 request.mMessage = &aMessage;
145 request.mMessageInfo = &aMessageInfo;
146 SuccessOrExit(aMessage.Read(aMessage.GetOffset(), request.mHeader));
147
148 VerifyOrExit(request.mHeader.GetType() == Header::kTypeQuery);
149
150 LogInfo("Received query from %s", aMessageInfo.GetPeerAddr().ToString().AsCString());
151
152 ProcessQuery(request);
153
154 exit:
155 return;
156 }
157
ProcessQuery(Request & aRequest)158 void Server::ProcessQuery(Request &aRequest)
159 {
160 ResponseCode rcode = Header::kResponseSuccess;
161 Response response(GetInstance());
162
163 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
164 if (mEnableUpstreamQuery && ShouldForwardToUpstream(aRequest))
165 {
166 Error error = ResolveByUpstream(aRequest);
167
168 if (error == kErrorNone)
169 {
170 ExitNow();
171 }
172
173 LogWarnOnError(error, "forwarding to upstream");
174
175 rcode = Header::kResponseServerFailure;
176
177 // Continue to allocate and prepare the response message
178 // to send the `kResponseServerFailure` response code.
179 }
180 #endif
181
182 SuccessOrExit(response.AllocateAndInitFrom(aRequest));
183
184 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
185 // Forwarding the query to the upstream may have already set the
186 // response error code.
187 SuccessOrExit(rcode);
188 #endif
189
190 SuccessOrExit(rcode = aRequest.ParseQuestions(mTestMode));
191 SuccessOrExit(rcode = response.AddQuestionsFrom(aRequest));
192
193 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
194 response.Log();
195 #endif
196
197 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
198 switch (response.ResolveBySrp())
199 {
200 case kErrorNone:
201 mCounters.mResolvedBySrp++;
202 ExitNow();
203
204 case kErrorNotFound:
205 rcode = Header::kResponseNameError;
206 break;
207
208 default:
209 rcode = Header::kResponseServerFailure;
210 ExitNow();
211 }
212 #endif
213
214 ResolveByProxy(response, *aRequest.mMessageInfo);
215
216 exit:
217 if (rcode != Header::kResponseSuccess)
218 {
219 response.SetResponseCode(rcode);
220 }
221
222 response.Send(*aRequest.mMessageInfo);
223 }
224
Response(Instance & aInstance)225 Server::Response::Response(Instance &aInstance)
226 : InstanceLocator(aInstance)
227 {
228 // `mHeader` constructors already clears it
229
230 mOffsets.Clear();
231 }
232
AllocateAndInitFrom(const Request & aRequest)233 Error Server::Response::AllocateAndInitFrom(const Request &aRequest)
234 {
235 Error error = kErrorNone;
236
237 mMessage.Reset(Get<Server>().mSocket.NewMessage());
238 VerifyOrExit(!mMessage.IsNull(), error = kErrorNoBufs);
239
240 mHeader.SetType(Header::kTypeResponse);
241 mHeader.SetMessageId(aRequest.mHeader.GetMessageId());
242 mHeader.SetQueryType(aRequest.mHeader.GetQueryType());
243
244 if (aRequest.mHeader.IsRecursionDesiredFlagSet())
245 {
246 mHeader.SetRecursionDesiredFlag();
247 }
248
249 // Append the empty header to reserve room for it in the message.
250 // Header will be updated in the message before sending it.
251 error = mMessage->Append(mHeader);
252
253 exit:
254 if (error != kErrorNone)
255 {
256 mMessage.Free();
257 }
258
259 return error;
260 }
261
Send(const Ip6::MessageInfo & aMessageInfo)262 void Server::Response::Send(const Ip6::MessageInfo &aMessageInfo)
263 {
264 ResponseCode rcode = mHeader.GetResponseCode();
265
266 VerifyOrExit(!mMessage.IsNull());
267
268 if (rcode == Header::kResponseServerFailure)
269 {
270 mHeader.SetQuestionCount(0);
271 mHeader.SetAnswerCount(0);
272 mHeader.SetAdditionalRecordCount(0);
273 IgnoreError(mMessage->SetLength(sizeof(Header)));
274 }
275
276 mMessage->Write(0, mHeader);
277
278 SuccessOrExit(Get<Server>().mSocket.SendTo(*mMessage, aMessageInfo));
279
280 // When `SendTo()` returns success it takes over ownership of
281 // the given message, so we release ownership of `mMessage`.
282
283 mMessage.Release();
284
285 LogInfo("Send response, rcode:%u", rcode);
286
287 Get<Server>().UpdateResponseCounters(rcode);
288
289 exit:
290 return;
291 }
292
ParseQuestions(uint8_t aTestMode)293 Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode)
294 {
295 // Parse header and questions from a `Request` query message and
296 // determine the `QueryType`.
297
298 ResponseCode rcode = Header::kResponseFormatError;
299 uint16_t offset = sizeof(Header);
300 uint16_t questionCount = mHeader.GetQuestionCount();
301 Question question;
302
303 VerifyOrExit(mHeader.GetQueryType() == Header::kQueryTypeStandard, rcode = Header::kResponseNotImplemented);
304 VerifyOrExit(!mHeader.IsTruncationFlagSet());
305
306 VerifyOrExit(questionCount > 0);
307
308 SuccessOrExit(Name::ParseName(*mMessage, offset));
309 SuccessOrExit(mMessage->Read(offset, question));
310 offset += sizeof(question);
311
312 switch (question.GetType())
313 {
314 case ResourceRecord::kTypePtr:
315 mType = kPtrQuery;
316 break;
317 case ResourceRecord::kTypeSrv:
318 mType = kSrvQuery;
319 break;
320 case ResourceRecord::kTypeTxt:
321 mType = kTxtQuery;
322 break;
323 case ResourceRecord::kTypeAaaa:
324 mType = kAaaaQuery;
325 break;
326 default:
327 ExitNow(rcode = Header::kResponseNotImplemented);
328 }
329
330 if (questionCount > 1)
331 {
332 VerifyOrExit(!(aTestMode & kTestModeSingleQuestionOnly));
333
334 VerifyOrExit(questionCount == 2);
335
336 SuccessOrExit(Name::CompareName(*mMessage, offset, *mMessage, sizeof(Header)));
337 SuccessOrExit(mMessage->Read(offset, question));
338
339 switch (question.GetType())
340 {
341 case ResourceRecord::kTypeSrv:
342 VerifyOrExit(mType == kTxtQuery);
343 break;
344
345 case ResourceRecord::kTypeTxt:
346 VerifyOrExit(mType == kSrvQuery);
347 break;
348
349 default:
350 ExitNow();
351 }
352
353 mType = kSrvTxtQuery;
354 }
355
356 rcode = Header::kResponseSuccess;
357
358 exit:
359 return rcode;
360 }
361
AddQuestionsFrom(const Request & aRequest)362 Server::ResponseCode Server::Response::AddQuestionsFrom(const Request &aRequest)
363 {
364 ResponseCode rcode = Header::kResponseServerFailure;
365 uint16_t offset;
366
367 mType = aRequest.mType;
368
369 // Read the name from `aRequest.mMessage` and append it as is to
370 // the response message. This ensures all name formats, including
371 // service instance names with dot characters in the instance
372 // label, are appended correctly.
373
374 SuccessOrExit(Name(*aRequest.mMessage, sizeof(Header)).AppendTo(*mMessage));
375
376 // Check the name to include the correct domain name and determine
377 // the domain name offset (for DNS name compression).
378
379 VerifyOrExit(ParseQueryName() == kErrorNone, rcode = Header::kResponseNameError);
380
381 mHeader.SetQuestionCount(aRequest.mHeader.GetQuestionCount());
382
383 offset = sizeof(Header);
384
385 for (uint16_t questionCount = 0; questionCount < mHeader.GetQuestionCount(); questionCount++)
386 {
387 Question question;
388
389 // The names and questions in `aRequest` are validated already
390 // from `ParseQuestions()`, so we can `IgnoreError()` here.
391
392 IgnoreError(Name::ParseName(*aRequest.mMessage, offset));
393 IgnoreError(aRequest.mMessage->Read(offset, question));
394 offset += sizeof(question);
395
396 if (questionCount != 0)
397 {
398 SuccessOrExit(AppendQueryName());
399 }
400
401 SuccessOrExit(mMessage->Append(question));
402 }
403
404 rcode = Header::kResponseSuccess;
405
406 exit:
407 return rcode;
408 }
409
ParseQueryName(void)410 Error Server::Response::ParseQueryName(void)
411 {
412 // Parses and validates the query name and updates
413 // the name compression offsets.
414
415 Error error = kErrorNone;
416 Name::Buffer name;
417 uint16_t offset;
418
419 offset = sizeof(Header);
420 SuccessOrExit(error = Name::ReadName(*mMessage, offset, name));
421
422 switch (mType)
423 {
424 case kPtrQuery:
425 // `mOffsets.mServiceName` may be updated as we read labels and if we
426 // determine that the query name is a sub-type service.
427 mOffsets.mServiceName = sizeof(Header);
428 break;
429
430 case kSrvQuery:
431 case kTxtQuery:
432 case kSrvTxtQuery:
433 mOffsets.mInstanceName = sizeof(Header);
434 break;
435
436 case kAaaaQuery:
437 mOffsets.mHostName = sizeof(Header);
438 break;
439 }
440
441 // Read the query name labels one by one to check if the name is
442 // service sub-type and also check that it is sub-domain of the
443 // default domain name and determine its offset
444
445 offset = sizeof(Header);
446
447 while (true)
448 {
449 Name::LabelBuffer label;
450 uint8_t labelLength = sizeof(label);
451 uint16_t comapreOffset;
452
453 SuccessOrExit(error = Name::ReadLabel(*mMessage, offset, label, labelLength));
454
455 if ((mType == kPtrQuery) && StringMatch(label, kSubLabel, kStringCaseInsensitiveMatch))
456 {
457 mOffsets.mServiceName = offset;
458 }
459
460 comapreOffset = offset;
461
462 if (Name::CompareName(*mMessage, comapreOffset, kDefaultDomainName) == kErrorNone)
463 {
464 mOffsets.mDomainName = offset;
465 ExitNow();
466 }
467 }
468
469 error = kErrorParse;
470
471 exit:
472 return error;
473 }
474
ReadQueryName(Name::Buffer & aName) const475 void Server::Response::ReadQueryName(Name::Buffer &aName) const { Server::ReadQueryName(*mMessage, aName); }
476
QueryNameMatches(const char * aName) const477 bool Server::Response::QueryNameMatches(const char *aName) const { return Server::QueryNameMatches(*mMessage, aName); }
478
AppendQueryName(void)479 Error Server::Response::AppendQueryName(void) { return Name::AppendPointerLabel(sizeof(Header), *mMessage); }
480
AppendPtrRecord(const char * aInstanceLabel,uint32_t aTtl)481 Error Server::Response::AppendPtrRecord(const char *aInstanceLabel, uint32_t aTtl)
482 {
483 Error error;
484 uint16_t recordOffset;
485 PtrRecord ptrRecord;
486
487 ptrRecord.Init();
488 ptrRecord.SetTtl(aTtl);
489
490 SuccessOrExit(error = AppendQueryName());
491
492 recordOffset = mMessage->GetLength();
493 SuccessOrExit(error = mMessage->Append(ptrRecord));
494
495 mOffsets.mInstanceName = mMessage->GetLength();
496 SuccessOrExit(error = Name::AppendLabel(aInstanceLabel, *mMessage));
497 SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mServiceName, *mMessage));
498
499 UpdateRecordLength(ptrRecord, recordOffset);
500
501 IncResourceRecordCount();
502
503 exit:
504 return error;
505 }
506
507 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
AppendSrvRecord(const Srp::Server::Service & aService)508 Error Server::Response::AppendSrvRecord(const Srp::Server::Service &aService)
509 {
510 uint32_t ttl = TimeMilli::MsecToSec(aService.GetExpireTime() - TimerMilli::GetNow());
511
512 return AppendSrvRecord(aService.GetHost().GetFullName(), ttl, aService.GetPriority(), aService.GetWeight(),
513 aService.GetPort());
514 }
515 #endif
516
AppendSrvRecord(const ServiceInstanceInfo & aInstanceInfo)517 Error Server::Response::AppendSrvRecord(const ServiceInstanceInfo &aInstanceInfo)
518 {
519 return AppendSrvRecord(aInstanceInfo.mHostName, aInstanceInfo.mTtl, aInstanceInfo.mPriority, aInstanceInfo.mWeight,
520 aInstanceInfo.mPort);
521 }
522
AppendSrvRecord(const char * aHostName,uint32_t aTtl,uint16_t aPriority,uint16_t aWeight,uint16_t aPort)523 Error Server::Response::AppendSrvRecord(const char *aHostName,
524 uint32_t aTtl,
525 uint16_t aPriority,
526 uint16_t aWeight,
527 uint16_t aPort)
528 {
529 Error error = kErrorNone;
530 SrvRecord srvRecord;
531 uint16_t recordOffset;
532 Name::Buffer hostLabels;
533
534 SuccessOrExit(error = Name::ExtractLabels(aHostName, kDefaultDomainName, hostLabels));
535
536 srvRecord.Init();
537 srvRecord.SetTtl(aTtl);
538 srvRecord.SetPriority(aPriority);
539 srvRecord.SetWeight(aWeight);
540 srvRecord.SetPort(aPort);
541
542 SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mInstanceName, *mMessage));
543
544 recordOffset = mMessage->GetLength();
545 SuccessOrExit(error = mMessage->Append(srvRecord));
546
547 mOffsets.mHostName = mMessage->GetLength();
548 SuccessOrExit(error = Name::AppendMultipleLabels(hostLabels, *mMessage));
549 SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mDomainName, *mMessage));
550
551 UpdateRecordLength(srvRecord, recordOffset);
552
553 IncResourceRecordCount();
554
555 exit:
556 return error;
557 }
558
559 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
AppendHostAddresses(const Srp::Server::Host & aHost)560 Error Server::Response::AppendHostAddresses(const Srp::Server::Host &aHost)
561 {
562 const Ip6::Address *addrs;
563 uint8_t addrsLength;
564 uint32_t ttl;
565
566 addrs = aHost.GetAddresses(addrsLength);
567 ttl = TimeMilli::MsecToSec(aHost.GetExpireTime() - TimerMilli::GetNow());
568
569 return AppendHostAddresses(addrs, addrsLength, ttl);
570 }
571 #endif
572
AppendHostAddresses(const HostInfo & aHostInfo)573 Error Server::Response::AppendHostAddresses(const HostInfo &aHostInfo)
574 {
575 return AppendHostAddresses(AsCoreTypePtr(aHostInfo.mAddresses), aHostInfo.mAddressNum, aHostInfo.mTtl);
576 }
577
AppendHostAddresses(const ServiceInstanceInfo & aInstanceInfo)578 Error Server::Response::AppendHostAddresses(const ServiceInstanceInfo &aInstanceInfo)
579 {
580 return AppendHostAddresses(AsCoreTypePtr(aInstanceInfo.mAddresses), aInstanceInfo.mAddressNum, aInstanceInfo.mTtl);
581 }
582
AppendHostAddresses(const Ip6::Address * aAddrs,uint16_t aAddrsLength,uint32_t aTtl)583 Error Server::Response::AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t aAddrsLength, uint32_t aTtl)
584 {
585 Error error = kErrorNone;
586
587 for (uint16_t index = 0; index < aAddrsLength; index++)
588 {
589 AaaaRecord aaaaRecord;
590
591 aaaaRecord.Init();
592 aaaaRecord.SetTtl(aTtl);
593 aaaaRecord.SetAddress(aAddrs[index]);
594
595 SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage));
596 SuccessOrExit(error = mMessage->Append(aaaaRecord));
597
598 IncResourceRecordCount();
599 }
600
601 exit:
602 return error;
603 }
604
605 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
AppendTxtRecord(const Srp::Server::Service & aService)606 Error Server::Response::AppendTxtRecord(const Srp::Server::Service &aService)
607 {
608 return AppendTxtRecord(aService.GetTxtData(), aService.GetTxtDataLength(),
609 TimeMilli::MsecToSec(aService.GetExpireTime() - TimerMilli::GetNow()));
610 }
611 #endif
612
AppendTxtRecord(const ServiceInstanceInfo & aInstanceInfo)613 Error Server::Response::AppendTxtRecord(const ServiceInstanceInfo &aInstanceInfo)
614 {
615 return AppendTxtRecord(aInstanceInfo.mTxtData, aInstanceInfo.mTxtLength, aInstanceInfo.mTtl);
616 }
617
AppendTxtRecord(const void * aTxtData,uint16_t aTxtLength,uint32_t aTtl)618 Error Server::Response::AppendTxtRecord(const void *aTxtData, uint16_t aTxtLength, uint32_t aTtl)
619 {
620 Error error = kErrorNone;
621 TxtRecord txtRecord;
622 uint8_t emptyTxt = 0;
623
624 if (aTxtLength == 0)
625 {
626 aTxtData = &emptyTxt;
627 aTxtLength = sizeof(emptyTxt);
628 }
629
630 txtRecord.Init();
631 txtRecord.SetTtl(aTtl);
632 txtRecord.SetLength(aTxtLength);
633
634 SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mInstanceName, *mMessage));
635 SuccessOrExit(error = mMessage->Append(txtRecord));
636 SuccessOrExit(error = mMessage->AppendBytes(aTxtData, aTxtLength));
637
638 IncResourceRecordCount();
639
640 exit:
641 return error;
642 }
643
UpdateRecordLength(ResourceRecord & aRecord,uint16_t aOffset)644 void Server::Response::UpdateRecordLength(ResourceRecord &aRecord, uint16_t aOffset)
645 {
646 // Calculates RR DATA length and updates and re-writes it in the
647 // response message. This should be called immediately
648 // after all the fields in the record are written in the message.
649 // `aOffset` gives the offset in the message to the start of the
650 // record.
651
652 aRecord.SetLength(mMessage->GetLength() - aOffset - sizeof(Dns::ResourceRecord));
653 mMessage->Write(aOffset, aRecord);
654 }
655
IncResourceRecordCount(void)656 void Server::Response::IncResourceRecordCount(void)
657 {
658 switch (mSection)
659 {
660 case kAnswerSection:
661 mHeader.SetAnswerCount(mHeader.GetAnswerCount() + 1);
662 break;
663 case kAdditionalDataSection:
664 mHeader.SetAdditionalRecordCount(mHeader.GetAdditionalRecordCount() + 1);
665 break;
666 }
667 }
668
GetNameLength(const char * aName)669 uint8_t Server::GetNameLength(const char *aName)
670 {
671 return static_cast<uint8_t>(StringLength(aName, Name::kMaxNameLength));
672 }
673
674 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
Log(void) const675 void Server::Response::Log(void) const
676 {
677 Name::Buffer name;
678
679 ReadQueryName(name);
680 LogInfo("%s query for '%s'", QueryTypeToString(mType), name);
681 }
682
QueryTypeToString(QueryType aType)683 const char *Server::Response::QueryTypeToString(QueryType aType)
684 {
685 static const char *const kTypeNames[] = {
686 "PTR", // (0) kPtrQuery
687 "SRV", // (1) kSrvQuery
688 "TXT", // (2) kTxtQuery
689 "SRV & TXT", // (3) kSrvTxtQuery
690 "AAAA", // (4) kAaaaQuery
691 };
692
693 static_assert(0 == kPtrQuery, "kPtrQuery value is incorrect");
694 static_assert(1 == kSrvQuery, "kSrvQuery value is incorrect");
695 static_assert(2 == kTxtQuery, "kTxtQuery value is incorrect");
696 static_assert(3 == kSrvTxtQuery, "kSrvTxtQuery value is incorrect");
697 static_assert(4 == kAaaaQuery, "kAaaaQuery value is incorrect");
698
699 return kTypeNames[aType];
700 }
701 #endif
702
703 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
704
ResolveBySrp(void)705 Error Server::Response::ResolveBySrp(void)
706 {
707 static const Section kSections[] = {kAnswerSection, kAdditionalDataSection};
708
709 Error error = kErrorNotFound;
710 const Srp::Server::Service *matchedService = nullptr;
711 bool found = false;
712 Section srvSection;
713 Section txtSection;
714
715 mSection = kAnswerSection;
716
717 for (const Srp::Server::Host &host : Get<Srp::Server>().GetHosts())
718 {
719 if (host.IsDeleted())
720 {
721 continue;
722 }
723
724 if (mType == kAaaaQuery)
725 {
726 if (QueryNameMatches(host.GetFullName()))
727 {
728 error = AppendHostAddresses(host);
729 ExitNow();
730 }
731
732 continue;
733 }
734
735 // `mType` is PTR or SRV/TXT query
736
737 for (const Srp::Server::Service &service : host.GetServices())
738 {
739 if (service.IsDeleted())
740 {
741 continue;
742 }
743
744 if (mType == kPtrQuery)
745 {
746 if (QueryNameMatchesService(service))
747 {
748 uint32_t ttl = TimeMilli::MsecToSec(service.GetExpireTime() - TimerMilli::GetNow());
749
750 SuccessOrExit(error = AppendPtrRecord(service.GetInstanceLabel(), ttl));
751 matchedService = &service;
752 }
753 }
754 else if (QueryNameMatches(service.GetInstanceName()))
755 {
756 matchedService = &service;
757 found = true;
758 break;
759 }
760 }
761
762 if (found)
763 {
764 break;
765 }
766 }
767
768 VerifyOrExit(matchedService != nullptr);
769
770 if (mType == kPtrQuery)
771 {
772 // Skip adding additional records, when answering a
773 // PTR query with more than one answer. This is the
774 // recommended behavior to keep the size of the
775 // response small.
776
777 VerifyOrExit(mHeader.GetAnswerCount() == 1);
778 }
779
780 srvSection = ((mType == kSrvQuery) || (mType == kSrvTxtQuery)) ? kAnswerSection : kAdditionalDataSection;
781 txtSection = ((mType == kTxtQuery) || (mType == kSrvTxtQuery)) ? kAnswerSection : kAdditionalDataSection;
782
783 for (Section section : kSections)
784 {
785 mSection = section;
786
787 if (mSection == kAdditionalDataSection)
788 {
789 VerifyOrExit(!(Get<Server>().mTestMode & kTestModeEmptyAdditionalSection));
790 }
791
792 if (srvSection == mSection)
793 {
794 SuccessOrExit(error = AppendSrvRecord(*matchedService));
795 }
796
797 if (txtSection == mSection)
798 {
799 SuccessOrExit(error = AppendTxtRecord(*matchedService));
800 }
801 }
802
803 SuccessOrExit(error = AppendHostAddresses(matchedService->GetHost()));
804
805 exit:
806 return error;
807 }
808
QueryNameMatchesService(const Srp::Server::Service & aService) const809 bool Server::Response::QueryNameMatchesService(const Srp::Server::Service &aService) const
810 {
811 // Check if the query name matches the base service name or any
812 // sub-type service names associated with `aService`.
813
814 bool matches = QueryNameMatches(aService.GetServiceName());
815
816 VerifyOrExit(!matches);
817
818 for (uint16_t index = 0; index < aService.GetNumberOfSubTypes(); index++)
819 {
820 matches = QueryNameMatches(aService.GetSubTypeServiceNameAt(index));
821 VerifyOrExit(!matches);
822 }
823
824 exit:
825 return matches;
826 }
827
828 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
829
830 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
ShouldForwardToUpstream(const Request & aRequest)831 bool Server::ShouldForwardToUpstream(const Request &aRequest)
832 {
833 bool shouldForward = false;
834 uint16_t readOffset;
835 Name::Buffer name;
836
837 VerifyOrExit(aRequest.mHeader.IsRecursionDesiredFlagSet());
838 readOffset = sizeof(Header);
839
840 for (uint16_t i = 0; i < aRequest.mHeader.GetQuestionCount(); i++)
841 {
842 SuccessOrExit(Name::ReadName(*aRequest.mMessage, readOffset, name));
843 readOffset += sizeof(Question);
844
845 VerifyOrExit(!Name::IsSubDomainOf(name, kDefaultDomainName));
846
847 for (const char *blockedDomain : kBlockedDomains)
848 {
849 VerifyOrExit(!Name::IsSameDomain(name, blockedDomain));
850 }
851 }
852
853 shouldForward = true;
854
855 exit:
856 return shouldForward;
857 }
858
OnUpstreamQueryDone(UpstreamQueryTransaction & aQueryTransaction,Message * aResponseMessage)859 void Server::OnUpstreamQueryDone(UpstreamQueryTransaction &aQueryTransaction, Message *aResponseMessage)
860 {
861 Error error = kErrorNone;
862
863 VerifyOrExit(aQueryTransaction.IsValid(), error = kErrorInvalidArgs);
864
865 if (aResponseMessage != nullptr)
866 {
867 error = mSocket.SendTo(*aResponseMessage, aQueryTransaction.GetMessageInfo());
868 }
869 else
870 {
871 error = kErrorResponseTimeout;
872 }
873
874 ResetUpstreamQueryTransaction(aQueryTransaction, error);
875
876 exit:
877 FreeMessageOnError(aResponseMessage, error);
878 }
879
AllocateUpstreamQueryTransaction(const Ip6::MessageInfo & aMessageInfo)880 Server::UpstreamQueryTransaction *Server::AllocateUpstreamQueryTransaction(const Ip6::MessageInfo &aMessageInfo)
881 {
882 UpstreamQueryTransaction *newTxn = nullptr;
883
884 for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions)
885 {
886 if (!txn.IsValid())
887 {
888 newTxn = &txn;
889 break;
890 }
891 }
892
893 VerifyOrExit(newTxn != nullptr, mCounters.mUpstreamDnsCounters.mFailures++);
894
895 newTxn->Init(aMessageInfo);
896 LogInfo("Upstream query transaction %d initialized.", static_cast<int>(newTxn - mUpstreamQueryTransactions));
897 mTimer.FireAtIfEarlier(newTxn->GetExpireTime());
898
899 exit:
900 return newTxn;
901 }
902
ResolveByUpstream(const Request & aRequest)903 Error Server::ResolveByUpstream(const Request &aRequest)
904 {
905 Error error = kErrorNone;
906 UpstreamQueryTransaction *txn;
907
908 txn = AllocateUpstreamQueryTransaction(*aRequest.mMessageInfo);
909 VerifyOrExit(txn != nullptr, error = kErrorNoBufs);
910
911 otPlatDnsStartUpstreamQuery(&GetInstance(), txn, aRequest.mMessage);
912 mCounters.mUpstreamDnsCounters.mQueries++;
913
914 exit:
915 return error;
916 }
917 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
918
ResolveByProxy(Response & aResponse,const Ip6::MessageInfo & aMessageInfo)919 void Server::ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessageInfo)
920 {
921 ProxyQuery *query;
922 ProxyQueryInfo info;
923 Name::Buffer name;
924
925 VerifyOrExit(mQuerySubscribe.IsSet());
926
927 // We try to convert `aResponse.mMessage` to a `ProxyQuery` by
928 // appending `ProxyQueryInfo` to it.
929
930 info.mType = aResponse.mType;
931 info.mMessageInfo = aMessageInfo;
932 info.mExpireTime = TimerMilli::GetNow() + kQueryTimeout;
933 info.mOffsets = aResponse.mOffsets;
934
935 if (aResponse.mMessage->Append(info) != kErrorNone)
936 {
937 aResponse.SetResponseCode(Header::kResponseServerFailure);
938 ExitNow();
939 }
940
941 // Take over the ownership of `aResponse.mMessage` and add it as a
942 // `ProxyQuery` in `mProxyQueries` list.
943
944 query = aResponse.mMessage.Release();
945
946 query->Write(0, aResponse.mHeader);
947 mProxyQueries.Enqueue(*query);
948
949 mTimer.FireAtIfEarlier(info.mExpireTime);
950
951 ReadQueryName(*query, name);
952 mQuerySubscribe.Invoke(name);
953
954 exit:
955 return;
956 }
957
ReadQueryName(const Message & aQuery,Name::Buffer & aName)958 void Server::ReadQueryName(const Message &aQuery, Name::Buffer &aName)
959 {
960 uint16_t offset = sizeof(Header);
961
962 IgnoreError(Name::ReadName(aQuery, offset, aName));
963 }
964
QueryNameMatches(const Message & aQuery,const char * aName)965 bool Server::QueryNameMatches(const Message &aQuery, const char *aName)
966 {
967 uint16_t offset = sizeof(Header);
968
969 return (Name::CompareName(aQuery, offset, aName) == kErrorNone);
970 }
971
ReadFrom(const ProxyQuery & aQuery)972 void Server::ProxyQueryInfo::ReadFrom(const ProxyQuery &aQuery)
973 {
974 SuccessOrAssert(aQuery.Read(aQuery.GetLength() - sizeof(ProxyQueryInfo), *this));
975 }
976
RemoveFrom(ProxyQuery & aQuery) const977 void Server::ProxyQueryInfo::RemoveFrom(ProxyQuery &aQuery) const
978 {
979 SuccessOrAssert(aQuery.SetLength(aQuery.GetLength() - sizeof(ProxyQueryInfo)));
980 }
981
UpdateIn(ProxyQuery & aQuery) const982 void Server::ProxyQueryInfo::UpdateIn(ProxyQuery &aQuery) const
983 {
984 aQuery.Write(aQuery.GetLength() - sizeof(ProxyQueryInfo), *this);
985 }
986
ExtractServiceInstanceLabel(const char * aInstanceName,Name::LabelBuffer & aLabel)987 Error Server::Response::ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel)
988 {
989 uint16_t offset;
990 Name::Buffer serviceName;
991
992 offset = mOffsets.mServiceName;
993 IgnoreError(Name::ReadName(*mMessage, offset, serviceName));
994
995 return Name::ExtractLabels(aInstanceName, serviceName, aLabel);
996 }
997
RemoveQueryAndPrepareResponse(ProxyQuery & aQuery,const ProxyQueryInfo & aInfo,Response & aResponse)998 void Server::RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Response &aResponse)
999 {
1000 Name::Buffer name;
1001
1002 mProxyQueries.Dequeue(aQuery);
1003 aInfo.RemoveFrom(aQuery);
1004
1005 ReadQueryName(aQuery, name);
1006 mQueryUnsubscribe.InvokeIfSet(name);
1007
1008 aResponse.InitFrom(aQuery, aInfo);
1009 }
1010
InitFrom(ProxyQuery & aQuery,const ProxyQueryInfo & aInfo)1011 void Server::Response::InitFrom(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo)
1012 {
1013 mMessage.Reset(&aQuery);
1014 IgnoreError(mMessage->Read(0, mHeader));
1015 mType = aInfo.mType;
1016 mOffsets = aInfo.mOffsets;
1017 }
1018
Answer(const ServiceInstanceInfo & aInstanceInfo,const Ip6::MessageInfo & aMessageInfo)1019 void Server::Response::Answer(const ServiceInstanceInfo &aInstanceInfo, const Ip6::MessageInfo &aMessageInfo)
1020 {
1021 static const Section kSections[] = {kAnswerSection, kAdditionalDataSection};
1022
1023 Error error = kErrorNone;
1024 Section srvSection = ((mType == kSrvQuery) || (mType == kSrvTxtQuery)) ? kAnswerSection : kAdditionalDataSection;
1025 Section txtSection = ((mType == kTxtQuery) || (mType == kSrvTxtQuery)) ? kAnswerSection : kAdditionalDataSection;
1026
1027 if (mType == kPtrQuery)
1028 {
1029 Name::LabelBuffer instanceLabel;
1030
1031 SuccessOrExit(error = ExtractServiceInstanceLabel(aInstanceInfo.mFullName, instanceLabel));
1032 mSection = kAnswerSection;
1033 SuccessOrExit(error = AppendPtrRecord(instanceLabel, aInstanceInfo.mTtl));
1034 }
1035
1036 for (Section section : kSections)
1037 {
1038 mSection = section;
1039
1040 if (mSection == kAdditionalDataSection)
1041 {
1042 VerifyOrExit(!(Get<Server>().mTestMode & kTestModeEmptyAdditionalSection));
1043 }
1044
1045 if (srvSection == mSection)
1046 {
1047 SuccessOrExit(error = AppendSrvRecord(aInstanceInfo));
1048 }
1049
1050 if (txtSection == mSection)
1051 {
1052 SuccessOrExit(error = AppendTxtRecord(aInstanceInfo));
1053 }
1054 }
1055
1056 error = AppendHostAddresses(aInstanceInfo);
1057
1058 exit:
1059 if (error != kErrorNone)
1060 {
1061 SetResponseCode(Header::kResponseServerFailure);
1062 }
1063
1064 Send(aMessageInfo);
1065 }
1066
Answer(const HostInfo & aHostInfo,const Ip6::MessageInfo & aMessageInfo)1067 void Server::Response::Answer(const HostInfo &aHostInfo, const Ip6::MessageInfo &aMessageInfo)
1068 {
1069 mSection = kAnswerSection;
1070
1071 if (AppendHostAddresses(aHostInfo) != kErrorNone)
1072 {
1073 SetResponseCode(Header::kResponseServerFailure);
1074 }
1075
1076 Send(aMessageInfo);
1077 }
1078
SetQueryCallbacks(SubscribeCallback aSubscribe,UnsubscribeCallback aUnsubscribe,void * aContext)1079 void Server::SetQueryCallbacks(SubscribeCallback aSubscribe, UnsubscribeCallback aUnsubscribe, void *aContext)
1080 {
1081 OT_ASSERT((aSubscribe == nullptr) == (aUnsubscribe == nullptr));
1082
1083 mQuerySubscribe.Set(aSubscribe, aContext);
1084 mQueryUnsubscribe.Set(aUnsubscribe, aContext);
1085 }
1086
HandleDiscoveredServiceInstance(const char * aServiceFullName,const ServiceInstanceInfo & aInstanceInfo)1087 void Server::HandleDiscoveredServiceInstance(const char *aServiceFullName, const ServiceInstanceInfo &aInstanceInfo)
1088 {
1089 OT_ASSERT(StringEndsWith(aServiceFullName, Name::kLabelSeparatorChar));
1090 OT_ASSERT(StringEndsWith(aInstanceInfo.mFullName, Name::kLabelSeparatorChar));
1091 OT_ASSERT(StringEndsWith(aInstanceInfo.mHostName, Name::kLabelSeparatorChar));
1092
1093 // It is safe to remove entries from `mProxyQueries` as we iterate
1094 // over it since it is a `MessageQueue`.
1095
1096 for (ProxyQuery &query : mProxyQueries)
1097 {
1098 bool canAnswer = false;
1099 ProxyQueryInfo info;
1100
1101 info.ReadFrom(query);
1102
1103 switch (info.mType)
1104 {
1105 case kPtrQuery:
1106 canAnswer = QueryNameMatches(query, aServiceFullName);
1107 break;
1108
1109 case kSrvQuery:
1110 case kTxtQuery:
1111 case kSrvTxtQuery:
1112 canAnswer = QueryNameMatches(query, aInstanceInfo.mFullName);
1113 break;
1114
1115 case kAaaaQuery:
1116 break;
1117 }
1118
1119 if (canAnswer)
1120 {
1121 Response response(GetInstance());
1122
1123 RemoveQueryAndPrepareResponse(query, info, response);
1124 response.Answer(aInstanceInfo, info.mMessageInfo);
1125 }
1126 }
1127 }
1128
HandleDiscoveredHost(const char * aHostFullName,const HostInfo & aHostInfo)1129 void Server::HandleDiscoveredHost(const char *aHostFullName, const HostInfo &aHostInfo)
1130 {
1131 OT_ASSERT(StringEndsWith(aHostFullName, Name::kLabelSeparatorChar));
1132
1133 for (ProxyQuery &query : mProxyQueries)
1134 {
1135 ProxyQueryInfo info;
1136
1137 info.ReadFrom(query);
1138
1139 if ((info.mType == kAaaaQuery) && QueryNameMatches(query, aHostFullName))
1140 {
1141 Response response(GetInstance());
1142
1143 RemoveQueryAndPrepareResponse(query, info, response);
1144 response.Answer(aHostInfo, info.mMessageInfo);
1145 }
1146 }
1147 }
1148
GetNextQuery(const otDnssdQuery * aQuery) const1149 const otDnssdQuery *Server::GetNextQuery(const otDnssdQuery *aQuery) const
1150 {
1151 const ProxyQuery *query = static_cast<const ProxyQuery *>(aQuery);
1152
1153 return (query == nullptr) ? mProxyQueries.GetHead() : query->GetNext();
1154 }
1155
GetQueryTypeAndName(const otDnssdQuery * aQuery,Dns::Name::Buffer & aName)1156 Server::DnsQueryType Server::GetQueryTypeAndName(const otDnssdQuery *aQuery, Dns::Name::Buffer &aName)
1157 {
1158 const ProxyQuery *query = static_cast<const ProxyQuery *>(aQuery);
1159 ProxyQueryInfo info;
1160 DnsQueryType type;
1161
1162 ReadQueryName(*query, aName);
1163 info.ReadFrom(*query);
1164
1165 type = kDnsQueryBrowse;
1166
1167 switch (info.mType)
1168 {
1169 case kPtrQuery:
1170 break;
1171
1172 case kSrvQuery:
1173 case kTxtQuery:
1174 case kSrvTxtQuery:
1175 type = kDnsQueryResolve;
1176 break;
1177
1178 case kAaaaQuery:
1179 type = kDnsQueryResolveHost;
1180 break;
1181 }
1182
1183 return type;
1184 }
1185
HandleTimer(void)1186 void Server::HandleTimer(void)
1187 {
1188 TimeMilli now = TimerMilli::GetNow();
1189 TimeMilli nextExpire = now.GetDistantFuture();
1190
1191 for (ProxyQuery &query : mProxyQueries)
1192 {
1193 ProxyQueryInfo info;
1194
1195 info.ReadFrom(query);
1196
1197 if (info.mExpireTime <= now)
1198 {
1199 Finalize(query, Header::kResponseSuccess);
1200 }
1201 else
1202 {
1203 nextExpire = Min(nextExpire, info.mExpireTime);
1204 }
1205 }
1206
1207 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
1208 for (UpstreamQueryTransaction &query : mUpstreamQueryTransactions)
1209 {
1210 if (!query.IsValid())
1211 {
1212 continue;
1213 }
1214
1215 if (query.GetExpireTime() <= now)
1216 {
1217 otPlatDnsCancelUpstreamQuery(&GetInstance(), &query);
1218 }
1219 else
1220 {
1221 nextExpire = Min(nextExpire, query.GetExpireTime());
1222 }
1223 }
1224 #endif
1225
1226 if (nextExpire != now.GetDistantFuture())
1227 {
1228 mTimer.FireAtIfEarlier(nextExpire);
1229 }
1230 }
1231
Finalize(ProxyQuery & aQuery,ResponseCode aResponseCode)1232 void Server::Finalize(ProxyQuery &aQuery, ResponseCode aResponseCode)
1233 {
1234 Response response(GetInstance());
1235 ProxyQueryInfo info;
1236
1237 info.ReadFrom(aQuery);
1238 RemoveQueryAndPrepareResponse(aQuery, info, response);
1239
1240 response.SetResponseCode(aResponseCode);
1241 response.Send(info.mMessageInfo);
1242 }
1243
UpdateResponseCounters(ResponseCode aResponseCode)1244 void Server::UpdateResponseCounters(ResponseCode aResponseCode)
1245 {
1246 switch (aResponseCode)
1247 {
1248 case UpdateHeader::kResponseSuccess:
1249 ++mCounters.mSuccessResponse;
1250 break;
1251 case UpdateHeader::kResponseServerFailure:
1252 ++mCounters.mServerFailureResponse;
1253 break;
1254 case UpdateHeader::kResponseFormatError:
1255 ++mCounters.mFormatErrorResponse;
1256 break;
1257 case UpdateHeader::kResponseNameError:
1258 ++mCounters.mNameErrorResponse;
1259 break;
1260 case UpdateHeader::kResponseNotImplemented:
1261 ++mCounters.mNotImplementedResponse;
1262 break;
1263 default:
1264 ++mCounters.mOtherResponse;
1265 break;
1266 }
1267 }
1268
1269 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
Init(const Ip6::MessageInfo & aMessageInfo)1270 void Server::UpstreamQueryTransaction::Init(const Ip6::MessageInfo &aMessageInfo)
1271 {
1272 mMessageInfo = aMessageInfo;
1273 mValid = true;
1274 mExpireTime = TimerMilli::GetNow() + kQueryTimeout;
1275 }
1276
ResetUpstreamQueryTransaction(UpstreamQueryTransaction & aTxn,Error aError)1277 void Server::ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error aError)
1278 {
1279 int index = static_cast<int>(&aTxn - mUpstreamQueryTransactions);
1280
1281 // Avoid the warnings when info / warn logging is disabled.
1282 OT_UNUSED_VARIABLE(index);
1283 if (aError == kErrorNone)
1284 {
1285 mCounters.mUpstreamDnsCounters.mResponses++;
1286 LogInfo("Upstream query transaction %d completed.", index);
1287 }
1288 else
1289 {
1290 mCounters.mUpstreamDnsCounters.mFailures++;
1291 LogWarn("Upstream query transaction %d closed: %s.", index, ErrorToString(aError));
1292 }
1293 aTxn.Reset();
1294 }
1295 #endif
1296
1297 } // namespace ServiceDiscovery
1298 } // namespace Dns
1299 } // namespace ot
1300
1301 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE && OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_MOCK_PLAT_APIS_ENABLE
otPlatDnsStartUpstreamQuery(otInstance * aInstance,otPlatDnsUpstreamQuery * aTxn,const otMessage * aQuery)1302 void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery)
1303 {
1304 OT_UNUSED_VARIABLE(aInstance);
1305 OT_UNUSED_VARIABLE(aTxn);
1306 OT_UNUSED_VARIABLE(aQuery);
1307 }
1308
otPlatDnsCancelUpstreamQuery(otInstance * aInstance,otPlatDnsUpstreamQuery * aTxn)1309 void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn)
1310 {
1311 otPlatDnsUpstreamQueryDone(aInstance, aTxn, nullptr);
1312 }
1313 #endif
1314
1315 #endif // OPENTHREAD_CONFIG_DNS_SERVER_ENABLE
1316