1 /*
2  *  Copyright (c) 2024, 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 CLI for mDNS.
32  */
33 
34 #include <string.h>
35 
36 #include "cli_mdns.hpp"
37 
38 #include <openthread/nat64.h>
39 #include "cli/cli.hpp"
40 
41 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
42 
43 namespace ot {
44 namespace Cli {
45 
Process(Arg aArgs[])46 template <> otError Mdns::Process<Cmd("enable")>(Arg aArgs[])
47 {
48     otError  error;
49     uint32_t infraIfIndex;
50 
51     SuccessOrExit(error = aArgs[0].ParseAsUint32(infraIfIndex));
52     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
53 
54     SuccessOrExit(error = otMdnsSetEnabled(GetInstancePtr(), true, infraIfIndex));
55 
56     mInfraIfIndex = infraIfIndex;
57 
58 exit:
59     return error;
60 }
61 
Process(Arg aArgs[])62 template <> otError Mdns::Process<Cmd("disable")>(Arg aArgs[])
63 {
64     otError error = OT_ERROR_NONE;
65 
66     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
67     error = otMdnsSetEnabled(GetInstancePtr(), false, /* aInfraIfIndex */ 0);
68 
69 exit:
70     return error;
71 }
72 
Process(Arg aArgs[])73 template <> otError Mdns::Process<Cmd("state")>(Arg aArgs[])
74 {
75     otError error = OT_ERROR_NONE;
76 
77     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
78     OutputEnabledDisabledStatus(otMdnsIsEnabled(GetInstancePtr()));
79 
80 exit:
81     return error;
82 }
83 
Process(Arg aArgs[])84 template <> otError Mdns::Process<Cmd("unicastquestion")>(Arg aArgs[])
85 {
86     return ProcessEnableDisable(aArgs, otMdnsIsQuestionUnicastAllowed, otMdnsSetQuestionUnicastAllowed);
87 }
88 
OutputHost(const otMdnsHost & aHost)89 void Mdns::OutputHost(const otMdnsHost &aHost)
90 {
91     OutputLine("Host %s", aHost.mHostName);
92     OutputLine(kIndentSize, "%u address:", aHost.mAddressesLength);
93 
94     for (uint16_t index = 0; index < aHost.mAddressesLength; index++)
95     {
96         OutputFormat(kIndentSize, "  ");
97         OutputIp6AddressLine(aHost.mAddresses[index]);
98     }
99 
100     OutputLine(kIndentSize, "ttl: %lu", ToUlong(aHost.mTtl));
101 }
102 
OutputService(const otMdnsService & aService)103 void Mdns::OutputService(const otMdnsService &aService)
104 {
105     OutputLine("Service %s for %s", aService.mServiceInstance, aService.mServiceType);
106     OutputLine(kIndentSize, "host: %s", aService.mHostName);
107 
108     if (aService.mSubTypeLabelsLength > 0)
109     {
110         OutputLine(kIndentSize, "%u sub-type:", aService.mSubTypeLabelsLength);
111 
112         for (uint16_t index = 0; index < aService.mSubTypeLabelsLength; index++)
113         {
114             OutputLine(kIndentSize * 2, "%s", aService.mSubTypeLabels[index]);
115         }
116     }
117 
118     OutputLine(kIndentSize, "port: %u", aService.mPort);
119     OutputLine(kIndentSize, "priority: %u", aService.mPriority);
120     OutputLine(kIndentSize, "weight: %u", aService.mWeight);
121     OutputLine(kIndentSize, "ttl: %lu", ToUlong(aService.mTtl));
122 
123     if ((aService.mTxtData == nullptr) || (aService.mTxtDataLength == 0))
124     {
125         OutputLine(kIndentSize, "txt-data: (empty)");
126     }
127     else
128     {
129         OutputFormat(kIndentSize, "txt-data: ");
130         OutputBytesLine(aService.mTxtData, aService.mTxtDataLength);
131     }
132 }
133 
OutputKey(const otMdnsKey & aKey)134 void Mdns::OutputKey(const otMdnsKey &aKey)
135 {
136     if (aKey.mServiceType != nullptr)
137     {
138         OutputLine("Key %s for %s (service)", aKey.mName, aKey.mServiceType);
139     }
140     else
141     {
142         OutputLine("Key %s (host)", aKey.mName);
143     }
144 
145     OutputFormat(kIndentSize, "key-data: ");
146     OutputBytesLine(aKey.mKeyData, aKey.mKeyDataLength);
147 
148     OutputLine(kIndentSize, "ttl: %lu", ToUlong(aKey.mTtl));
149 }
150 
OutputState(otMdnsEntryState aState)151 void Mdns::OutputState(otMdnsEntryState aState)
152 {
153     const char *stateString = "";
154 
155     switch (aState)
156     {
157     case OT_MDNS_ENTRY_STATE_PROBING:
158         stateString = "probing";
159         break;
160     case OT_MDNS_ENTRY_STATE_REGISTERED:
161         stateString = "registered";
162         break;
163     case OT_MDNS_ENTRY_STATE_CONFLICT:
164         stateString = "conflict";
165         break;
166     case OT_MDNS_ENTRY_STATE_REMOVING:
167         stateString = "removing";
168         break;
169     }
170 
171     OutputLine(kIndentSize, "state: %s", stateString);
172 }
173 
OutputCacheInfo(const otMdnsCacheInfo & aInfo)174 void Mdns::OutputCacheInfo(const otMdnsCacheInfo &aInfo)
175 {
176     OutputLine(kIndentSize, "active: %s", aInfo.mIsActive ? "yes" : "no");
177     OutputLine(kIndentSize, "cached-results: %s", aInfo.mHasCachedResults ? "yes" : "no");
178 }
179 
Process(Arg aArgs[])180 template <> otError Mdns::Process<Cmd("register")>(Arg aArgs[])
181 {
182     // mdns [async] [host|service|key] <entry specific args>
183 
184     otError error   = OT_ERROR_NONE;
185     bool    isAsync = false;
186 
187     if (aArgs[0] == "async")
188     {
189         isAsync = true;
190         aArgs++;
191     }
192 
193     if (aArgs[0] == "host")
194     {
195         SuccessOrExit(error = ProcessRegisterHost(aArgs + 1));
196     }
197     else if (aArgs[0] == "service")
198     {
199         SuccessOrExit(error = ProcessRegisterService(aArgs + 1));
200     }
201     else if (aArgs[0] == "key")
202     {
203         SuccessOrExit(error = ProcessRegisterKey(aArgs + 1));
204     }
205     else
206     {
207         ExitNow(error = OT_ERROR_INVALID_ARGS);
208     }
209 
210     if (isAsync)
211     {
212         OutputLine("mDNS request id: %lu", ToUlong(mRequestId));
213     }
214     else
215     {
216         error               = OT_ERROR_PENDING;
217         mWaitingForCallback = true;
218     }
219 
220 exit:
221     return error;
222 }
223 
ProcessRegisterHost(Arg aArgs[])224 otError Mdns::ProcessRegisterHost(Arg aArgs[])
225 {
226     // register host <name> [<zero or more addresses>] [<ttl>]
227 
228     otError      error = OT_ERROR_NONE;
229     otMdnsHost   host;
230     otIp6Address addresses[kMaxAddresses];
231 
232     memset(&host, 0, sizeof(host));
233 
234     VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
235     host.mHostName = aArgs->GetCString();
236     aArgs++;
237 
238     host.mAddresses = addresses;
239 
240     for (; !aArgs->IsEmpty(); aArgs++)
241     {
242         otIp6Address address;
243         uint32_t     ttl;
244 
245         if (aArgs->ParseAsIp6Address(address) == OT_ERROR_NONE)
246         {
247             VerifyOrExit(host.mAddressesLength < kMaxAddresses, error = OT_ERROR_NO_BUFS);
248             addresses[host.mAddressesLength] = address;
249             host.mAddressesLength++;
250         }
251         else if (aArgs->ParseAsUint32(ttl) == OT_ERROR_NONE)
252         {
253             host.mTtl = ttl;
254             VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
255         }
256         else
257         {
258             ExitNow(error = OT_ERROR_INVALID_ARGS);
259         }
260     }
261 
262     OutputHost(host);
263 
264     mRequestId++;
265     error = otMdnsRegisterHost(GetInstancePtr(), &host, mRequestId, HandleRegisterationDone);
266 
267 exit:
268     return error;
269 }
270 
ProcessRegisterService(Arg aArgs[])271 otError Mdns::ProcessRegisterService(Arg aArgs[])
272 {
273     otError       error;
274     otMdnsService service;
275     Buffers       buffers;
276 
277     SuccessOrExit(error = ParseServiceArgs(aArgs, service, buffers));
278 
279     OutputService(service);
280 
281     mRequestId++;
282     error = otMdnsRegisterService(GetInstancePtr(), &service, mRequestId, HandleRegisterationDone);
283 
284 exit:
285     return error;
286 }
287 
ParseServiceArgs(Arg aArgs[],otMdnsService & aService,Buffers & aBuffers)288 otError Mdns::ParseServiceArgs(Arg aArgs[], otMdnsService &aService, Buffers &aBuffers)
289 {
290     // mdns register service <instance-label> <service-type,sub_types> <host-name> <port> [<prio>] [<weight>] [<ttl>]
291     // [<txtdata>]
292 
293     otError  error = OT_ERROR_INVALID_ARGS;
294     char    *label;
295     uint16_t len;
296 
297     memset(&aService, 0, sizeof(aService));
298 
299     VerifyOrExit(!aArgs->IsEmpty());
300     aService.mServiceInstance = aArgs->GetCString();
301     aArgs++;
302 
303     // Copy service type into `aBuffer.mString`, then search for
304     // `,` in the string to parse the list of sub-types (if any).
305 
306     VerifyOrExit(!aArgs->IsEmpty());
307     len = aArgs->GetLength();
308     VerifyOrExit(len + 1 < kStringSize, error = OT_ERROR_NO_BUFS);
309     memcpy(aBuffers.mString, aArgs->GetCString(), len + 1);
310 
311     aService.mServiceType   = aBuffers.mString;
312     aService.mSubTypeLabels = aBuffers.mSubTypeLabels;
313 
314     label = strchr(aBuffers.mString, ',');
315 
316     if (label != nullptr)
317     {
318         while (true)
319         {
320             *label++ = '\0';
321 
322             VerifyOrExit(aService.mSubTypeLabelsLength < kMaxSubTypes, error = OT_ERROR_NO_BUFS);
323             aBuffers.mSubTypeLabels[aService.mSubTypeLabelsLength] = label;
324             aService.mSubTypeLabelsLength++;
325 
326             label = strchr(label, ',');
327 
328             if (label == nullptr)
329             {
330                 break;
331             }
332         }
333     }
334 
335     aArgs++;
336     VerifyOrExit(!aArgs->IsEmpty());
337     aService.mHostName = aArgs->GetCString();
338 
339     aArgs++;
340     SuccessOrExit(aArgs->ParseAsUint16(aService.mPort));
341 
342     // The rest of `Args` are optional.
343 
344     error = OT_ERROR_NONE;
345 
346     aArgs++;
347     VerifyOrExit(!aArgs->IsEmpty());
348     SuccessOrExit(error = aArgs->ParseAsUint16(aService.mPriority));
349 
350     aArgs++;
351     VerifyOrExit(!aArgs->IsEmpty());
352     SuccessOrExit(error = aArgs->ParseAsUint16(aService.mWeight));
353 
354     aArgs++;
355     VerifyOrExit(!aArgs->IsEmpty());
356     SuccessOrExit(error = aArgs->ParseAsUint32(aService.mTtl));
357 
358     aArgs++;
359     VerifyOrExit(!aArgs->IsEmpty());
360     len = kMaxTxtDataSize;
361     SuccessOrExit(error = aArgs->ParseAsHexString(len, aBuffers.mTxtData));
362     aService.mTxtData       = aBuffers.mTxtData;
363     aService.mTxtDataLength = len;
364 
365     aArgs++;
366     VerifyOrExit(aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
367 
368 exit:
369     return error;
370 }
371 
ProcessRegisterKey(Arg aArgs[])372 otError Mdns::ProcessRegisterKey(Arg aArgs[])
373 {
374     otError   error = OT_ERROR_INVALID_ARGS;
375     otMdnsKey key;
376     uint16_t  len;
377     uint8_t   data[kMaxKeyDataSize];
378 
379     memset(&key, 0, sizeof(key));
380 
381     VerifyOrExit(!aArgs->IsEmpty());
382     key.mName = aArgs->GetCString();
383 
384     aArgs++;
385     VerifyOrExit(!aArgs->IsEmpty());
386 
387     if (aArgs->GetCString()[0] == '_')
388     {
389         key.mServiceType = aArgs->GetCString();
390         aArgs++;
391         VerifyOrExit(!aArgs->IsEmpty());
392     }
393 
394     len = kMaxKeyDataSize;
395     SuccessOrExit(error = aArgs->ParseAsHexString(len, data));
396 
397     key.mKeyData       = data;
398     key.mKeyDataLength = len;
399 
400     // ttl is optional
401 
402     aArgs++;
403 
404     if (!aArgs->IsEmpty())
405     {
406         SuccessOrExit(error = aArgs->ParseAsUint32(key.mTtl));
407         aArgs++;
408         VerifyOrExit(aArgs->IsEmpty(), error = kErrorInvalidArgs);
409     }
410 
411     OutputKey(key);
412 
413     mRequestId++;
414     error = otMdnsRegisterKey(GetInstancePtr(), &key, mRequestId, HandleRegisterationDone);
415 
416 exit:
417     return error;
418 }
419 
HandleRegisterationDone(otInstance * aInstance,otMdnsRequestId aRequestId,otError aError)420 void Mdns::HandleRegisterationDone(otInstance *aInstance, otMdnsRequestId aRequestId, otError aError)
421 {
422     OT_UNUSED_VARIABLE(aInstance);
423 
424     Interpreter::GetInterpreter().mMdns.HandleRegisterationDone(aRequestId, aError);
425 }
426 
HandleRegisterationDone(otMdnsRequestId aRequestId,otError aError)427 void Mdns::HandleRegisterationDone(otMdnsRequestId aRequestId, otError aError)
428 {
429     if (mWaitingForCallback && (aRequestId == mRequestId))
430     {
431         mWaitingForCallback = false;
432         Interpreter::GetInterpreter().OutputResult(aError);
433     }
434     else
435     {
436         OutputLine("mDNS registration for request id %lu outcome: %s", ToUlong(aRequestId),
437                    otThreadErrorToString(aError));
438     }
439 }
440 
Process(Arg aArgs[])441 template <> otError Mdns::Process<Cmd("unregister")>(Arg aArgs[])
442 {
443     otError error = OT_ERROR_INVALID_ARGS;
444 
445     if (aArgs[0] == "host")
446     {
447         otMdnsHost host;
448 
449         memset(&host, 0, sizeof(host));
450         VerifyOrExit(!aArgs[1].IsEmpty());
451         host.mHostName = aArgs[1].GetCString();
452         VerifyOrExit(aArgs[2].IsEmpty());
453 
454         error = otMdnsUnregisterHost(GetInstancePtr(), &host);
455     }
456     else if (aArgs[0] == "service")
457     {
458         otMdnsService service;
459 
460         memset(&service, 0, sizeof(service));
461         VerifyOrExit(!aArgs[1].IsEmpty());
462         service.mServiceInstance = aArgs[1].GetCString();
463         VerifyOrExit(!aArgs[2].IsEmpty());
464         service.mServiceType = aArgs[2].GetCString();
465         VerifyOrExit(aArgs[3].IsEmpty());
466 
467         error = otMdnsUnregisterService(GetInstancePtr(), &service);
468     }
469     else if (aArgs[0] == "key")
470     {
471         otMdnsKey key;
472 
473         memset(&key, 0, sizeof(key));
474         VerifyOrExit(!aArgs[1].IsEmpty());
475         key.mName = aArgs[1].GetCString();
476 
477         if (!aArgs[2].IsEmpty())
478         {
479             key.mServiceType = aArgs[2].GetCString();
480             VerifyOrExit(aArgs[3].IsEmpty());
481         }
482 
483         error = otMdnsUnregisterKey(GetInstancePtr(), &key);
484     }
485 
486 exit:
487     return error;
488 }
489 
490 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
491 
Process(Arg aArgs[])492 template <> otError Mdns::Process<Cmd("hosts")>(Arg aArgs[])
493 {
494     otError          error    = OT_ERROR_NONE;
495     otMdnsIterator  *iterator = nullptr;
496     otMdnsHost       host;
497     otMdnsEntryState state;
498 
499     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
500 
501     iterator = otMdnsAllocateIterator(GetInstancePtr());
502     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
503 
504     while (true)
505     {
506         error = otMdnsGetNextHost(GetInstancePtr(), iterator, &host, &state);
507 
508         if (error == OT_ERROR_NOT_FOUND)
509         {
510             error = OT_ERROR_NONE;
511             ExitNow();
512         }
513 
514         SuccessOrExit(error);
515 
516         OutputHost(host);
517         OutputState(state);
518     }
519 
520 exit:
521     if (iterator != nullptr)
522     {
523         otMdnsFreeIterator(GetInstancePtr(), iterator);
524     }
525 
526     return error;
527 }
528 
Process(Arg aArgs[])529 template <> otError Mdns::Process<Cmd("services")>(Arg aArgs[])
530 {
531     otError          error    = OT_ERROR_NONE;
532     otMdnsIterator  *iterator = nullptr;
533     otMdnsService    service;
534     otMdnsEntryState state;
535 
536     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
537 
538     iterator = otMdnsAllocateIterator(GetInstancePtr());
539     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
540 
541     while (true)
542     {
543         error = otMdnsGetNextService(GetInstancePtr(), iterator, &service, &state);
544 
545         if (error == OT_ERROR_NOT_FOUND)
546         {
547             error = OT_ERROR_NONE;
548             ExitNow();
549         }
550 
551         SuccessOrExit(error);
552 
553         OutputService(service);
554         OutputState(state);
555     }
556 
557 exit:
558     if (iterator != nullptr)
559     {
560         otMdnsFreeIterator(GetInstancePtr(), iterator);
561     }
562 
563     return error;
564 }
565 
Process(Arg aArgs[])566 template <> otError Mdns::Process<Cmd("keys")>(Arg aArgs[])
567 {
568     otError          error    = OT_ERROR_NONE;
569     otMdnsIterator  *iterator = nullptr;
570     otMdnsKey        key;
571     otMdnsEntryState state;
572 
573     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
574 
575     iterator = otMdnsAllocateIterator(GetInstancePtr());
576     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
577 
578     while (true)
579     {
580         error = otMdnsGetNextKey(GetInstancePtr(), iterator, &key, &state);
581 
582         if (error == OT_ERROR_NOT_FOUND)
583         {
584             error = OT_ERROR_NONE;
585             ExitNow();
586         }
587 
588         SuccessOrExit(error);
589 
590         OutputKey(key);
591         OutputState(state);
592     }
593 
594 exit:
595     if (iterator != nullptr)
596     {
597         otMdnsFreeIterator(GetInstancePtr(), iterator);
598     }
599 
600     return error;
601 }
602 
603 #endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
604 
ParseStartOrStop(const Arg & aArg,bool & aIsStart)605 otError Mdns::ParseStartOrStop(const Arg &aArg, bool &aIsStart)
606 {
607     otError error = OT_ERROR_NONE;
608 
609     if (aArg == "start")
610     {
611         aIsStart = true;
612     }
613     else if (aArg == "stop")
614     {
615         aIsStart = false;
616     }
617     else
618     {
619         error = OT_ERROR_INVALID_ARGS;
620     }
621 
622     return error;
623 }
624 
Process(Arg aArgs[])625 template <> otError Mdns::Process<Cmd("browser")>(Arg aArgs[])
626 {
627     // mdns browser start|stop <service-type> [<sub-type>]
628 
629     otError       error;
630     otMdnsBrowser browser;
631     bool          isStart;
632 
633     ClearAllBytes(browser);
634 
635     SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
636     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
637 
638     browser.mServiceType = aArgs[1].GetCString();
639 
640     if (!aArgs[2].IsEmpty())
641     {
642         browser.mSubTypeLabel = aArgs[2].GetCString();
643         VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
644     }
645 
646     browser.mInfraIfIndex = mInfraIfIndex;
647     browser.mCallback     = HandleBrowseResult;
648 
649     if (isStart)
650     {
651         error = otMdnsStartBrowser(GetInstancePtr(), &browser);
652     }
653     else
654     {
655         error = otMdnsStopBrowser(GetInstancePtr(), &browser);
656     }
657 
658 exit:
659     return error;
660 }
661 
HandleBrowseResult(otInstance * aInstance,const otMdnsBrowseResult * aResult)662 void Mdns::HandleBrowseResult(otInstance *aInstance, const otMdnsBrowseResult *aResult)
663 {
664     OT_UNUSED_VARIABLE(aInstance);
665 
666     Interpreter::GetInterpreter().mMdns.HandleBrowseResult(*aResult);
667 }
668 
HandleBrowseResult(const otMdnsBrowseResult & aResult)669 void Mdns::HandleBrowseResult(const otMdnsBrowseResult &aResult)
670 {
671     OutputFormat("mDNS browse result for %s", aResult.mServiceType);
672 
673     if (aResult.mSubTypeLabel)
674     {
675         OutputLine(" sub-type %s", aResult.mSubTypeLabel);
676     }
677     else
678     {
679         OutputNewLine();
680     }
681 
682     OutputLine(kIndentSize, "instance: %s", aResult.mServiceInstance);
683     OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
684     OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
685 }
686 
Process(Arg aArgs[])687 template <> otError Mdns::Process<Cmd("srvresolver")>(Arg aArgs[])
688 {
689     // mdns srvresolver start|stop <service-instance> <service-type>
690 
691     otError           error;
692     otMdnsSrvResolver resolver;
693     bool              isStart;
694 
695     ClearAllBytes(resolver);
696 
697     SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
698     VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
699 
700     resolver.mServiceInstance = aArgs[1].GetCString();
701     resolver.mServiceType     = aArgs[2].GetCString();
702     resolver.mInfraIfIndex    = mInfraIfIndex;
703     resolver.mCallback        = HandleSrvResult;
704 
705     if (isStart)
706     {
707         error = otMdnsStartSrvResolver(GetInstancePtr(), &resolver);
708     }
709     else
710     {
711         error = otMdnsStopSrvResolver(GetInstancePtr(), &resolver);
712     }
713 
714 exit:
715     return error;
716 }
717 
HandleSrvResult(otInstance * aInstance,const otMdnsSrvResult * aResult)718 void Mdns::HandleSrvResult(otInstance *aInstance, const otMdnsSrvResult *aResult)
719 {
720     OT_UNUSED_VARIABLE(aInstance);
721 
722     Interpreter::GetInterpreter().mMdns.HandleSrvResult(*aResult);
723 }
724 
HandleSrvResult(const otMdnsSrvResult & aResult)725 void Mdns::HandleSrvResult(const otMdnsSrvResult &aResult)
726 {
727     OutputLine("mDNS SRV result for %s for %s", aResult.mServiceInstance, aResult.mServiceType);
728 
729     if (aResult.mTtl != 0)
730     {
731         OutputLine(kIndentSize, "host: %s", aResult.mHostName);
732         OutputLine(kIndentSize, "port: %u", aResult.mPort);
733         OutputLine(kIndentSize, "priority: %u", aResult.mPriority);
734         OutputLine(kIndentSize, "weight: %u", aResult.mWeight);
735     }
736 
737     OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
738     OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
739 }
740 
Process(Arg aArgs[])741 template <> otError Mdns::Process<Cmd("txtresolver")>(Arg aArgs[])
742 {
743     // mdns txtresolver start|stop <service-instance> <service-type>
744 
745     otError           error;
746     otMdnsTxtResolver resolver;
747     bool              isStart;
748 
749     ClearAllBytes(resolver);
750 
751     SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
752     VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
753 
754     resolver.mServiceInstance = aArgs[1].GetCString();
755     resolver.mServiceType     = aArgs[2].GetCString();
756     resolver.mInfraIfIndex    = mInfraIfIndex;
757     resolver.mCallback        = HandleTxtResult;
758 
759     if (isStart)
760     {
761         error = otMdnsStartTxtResolver(GetInstancePtr(), &resolver);
762     }
763     else
764     {
765         error = otMdnsStopTxtResolver(GetInstancePtr(), &resolver);
766     }
767 
768 exit:
769     return error;
770 }
771 
HandleTxtResult(otInstance * aInstance,const otMdnsTxtResult * aResult)772 void Mdns::HandleTxtResult(otInstance *aInstance, const otMdnsTxtResult *aResult)
773 {
774     OT_UNUSED_VARIABLE(aInstance);
775 
776     Interpreter::GetInterpreter().mMdns.HandleTxtResult(*aResult);
777 }
778 
HandleTxtResult(const otMdnsTxtResult & aResult)779 void Mdns::HandleTxtResult(const otMdnsTxtResult &aResult)
780 {
781     OutputLine("mDNS TXT result for %s for %s", aResult.mServiceInstance, aResult.mServiceType);
782 
783     if (aResult.mTtl != 0)
784     {
785         OutputFormat(kIndentSize, "txt-data: ");
786         OutputBytesLine(aResult.mTxtData, aResult.mTxtDataLength);
787     }
788 
789     OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
790     OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
791 }
Process(Arg aArgs[])792 template <> otError Mdns::Process<Cmd("ip6resolver")>(Arg aArgs[])
793 {
794     // mdns ip6resolver start|stop <host-name>
795 
796     otError               error;
797     otMdnsAddressResolver resolver;
798     bool                  isStart;
799 
800     ClearAllBytes(resolver);
801 
802     SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
803     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
804 
805     resolver.mHostName     = aArgs[1].GetCString();
806     resolver.mInfraIfIndex = mInfraIfIndex;
807     resolver.mCallback     = HandleIp6AddressResult;
808 
809     if (isStart)
810     {
811         error = otMdnsStartIp6AddressResolver(GetInstancePtr(), &resolver);
812     }
813     else
814     {
815         error = otMdnsStopIp6AddressResolver(GetInstancePtr(), &resolver);
816     }
817 
818 exit:
819     return error;
820 }
821 
HandleIp6AddressResult(otInstance * aInstance,const otMdnsAddressResult * aResult)822 void Mdns::HandleIp6AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult)
823 {
824     OT_UNUSED_VARIABLE(aInstance);
825 
826     Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp6Address);
827 }
828 
HandleAddressResult(const otMdnsAddressResult & aResult,IpAddressType aType)829 void Mdns::HandleAddressResult(const otMdnsAddressResult &aResult, IpAddressType aType)
830 {
831     OutputLine("mDNS %s address result for %s", aType == kIp6Address ? "IPv6" : "IPv4", aResult.mHostName);
832 
833     OutputLine(kIndentSize, "%u address:", aResult.mAddressesLength);
834 
835     for (uint16_t index = 0; index < aResult.mAddressesLength; index++)
836     {
837         OutputFormat(kIndentSize, "  ");
838         OutputIp6Address(aResult.mAddresses[index].mAddress);
839         OutputLine(" ttl:%lu", ToUlong(aResult.mAddresses[index].mTtl));
840     }
841 
842     OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
843 }
844 
Process(Arg aArgs[])845 template <> otError Mdns::Process<Cmd("ip4resolver")>(Arg aArgs[])
846 {
847     // mdns ip4resolver start|stop <host-name>
848 
849     otError               error;
850     otMdnsAddressResolver resolver;
851     bool                  isStart;
852 
853     ClearAllBytes(resolver);
854 
855     SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
856     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
857 
858     resolver.mHostName     = aArgs[1].GetCString();
859     resolver.mInfraIfIndex = mInfraIfIndex;
860     resolver.mCallback     = HandleIp4AddressResult;
861 
862     if (isStart)
863     {
864         error = otMdnsStartIp4AddressResolver(GetInstancePtr(), &resolver);
865     }
866     else
867     {
868         error = otMdnsStopIp4AddressResolver(GetInstancePtr(), &resolver);
869     }
870 
871 exit:
872     return error;
873 }
874 
HandleIp4AddressResult(otInstance * aInstance,const otMdnsAddressResult * aResult)875 void Mdns::HandleIp4AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult)
876 {
877     OT_UNUSED_VARIABLE(aInstance);
878 
879     Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp4Address);
880 }
881 
882 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
883 
Process(Arg aArgs[])884 template <> otError Mdns::Process<Cmd("browsers")>(Arg aArgs[])
885 {
886     // mdns browsers
887 
888     otError         error;
889     otMdnsIterator *iterator = nullptr;
890     otMdnsCacheInfo info;
891     otMdnsBrowser   browser;
892 
893     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
894 
895     iterator = otMdnsAllocateIterator(GetInstancePtr());
896     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
897 
898     while (true)
899     {
900         error = otMdnsGetNextBrowser(GetInstancePtr(), iterator, &browser, &info);
901 
902         if (error == OT_ERROR_NOT_FOUND)
903         {
904             error = OT_ERROR_NONE;
905             ExitNow();
906         }
907 
908         SuccessOrExit(error);
909 
910         OutputFormat("Browser %s", browser.mServiceType);
911 
912         if (browser.mSubTypeLabel != nullptr)
913         {
914             OutputFormat(" for sub-type %s", browser.mSubTypeLabel);
915         }
916 
917         OutputNewLine();
918         OutputCacheInfo(info);
919     }
920 
921 exit:
922     if (iterator != nullptr)
923     {
924         otMdnsFreeIterator(GetInstancePtr(), iterator);
925     }
926 
927     return error;
928 }
929 
Process(Arg aArgs[])930 template <> otError Mdns::Process<Cmd("srvresolvers")>(Arg aArgs[])
931 {
932     // mdns srvresolvers
933 
934     otError           error;
935     otMdnsIterator   *iterator = nullptr;
936     otMdnsCacheInfo   info;
937     otMdnsSrvResolver resolver;
938 
939     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
940 
941     iterator = otMdnsAllocateIterator(GetInstancePtr());
942     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
943 
944     while (true)
945     {
946         error = otMdnsGetNextSrvResolver(GetInstancePtr(), iterator, &resolver, &info);
947 
948         if (error == OT_ERROR_NOT_FOUND)
949         {
950             error = OT_ERROR_NONE;
951             ExitNow();
952         }
953 
954         SuccessOrExit(error);
955 
956         OutputLine("SRV resolver %s for %s", resolver.mServiceInstance, resolver.mServiceType);
957         OutputCacheInfo(info);
958     }
959 
960 exit:
961     if (iterator != nullptr)
962     {
963         otMdnsFreeIterator(GetInstancePtr(), iterator);
964     }
965 
966     return error;
967 }
968 
Process(Arg aArgs[])969 template <> otError Mdns::Process<Cmd("txtresolvers")>(Arg aArgs[])
970 {
971     // mdns txtresolvers
972 
973     otError           error;
974     otMdnsIterator   *iterator = nullptr;
975     otMdnsCacheInfo   info;
976     otMdnsTxtResolver resolver;
977 
978     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
979 
980     iterator = otMdnsAllocateIterator(GetInstancePtr());
981     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
982 
983     while (true)
984     {
985         error = otMdnsGetNextTxtResolver(GetInstancePtr(), iterator, &resolver, &info);
986 
987         if (error == OT_ERROR_NOT_FOUND)
988         {
989             error = OT_ERROR_NONE;
990             ExitNow();
991         }
992 
993         SuccessOrExit(error);
994 
995         OutputLine("TXT resolver %s for %s", resolver.mServiceInstance, resolver.mServiceType);
996         OutputCacheInfo(info);
997     }
998 
999 exit:
1000     if (iterator != nullptr)
1001     {
1002         otMdnsFreeIterator(GetInstancePtr(), iterator);
1003     }
1004 
1005     return error;
1006 }
1007 
Process(Arg aArgs[])1008 template <> otError Mdns::Process<Cmd("ip6resolvers")>(Arg aArgs[])
1009 {
1010     // mdns ip6resolvers
1011 
1012     otError               error;
1013     otMdnsIterator       *iterator = nullptr;
1014     otMdnsCacheInfo       info;
1015     otMdnsAddressResolver resolver;
1016 
1017     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1018 
1019     iterator = otMdnsAllocateIterator(GetInstancePtr());
1020     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
1021 
1022     while (true)
1023     {
1024         error = otMdnsGetNextIp6AddressResolver(GetInstancePtr(), iterator, &resolver, &info);
1025 
1026         if (error == OT_ERROR_NOT_FOUND)
1027         {
1028             error = OT_ERROR_NONE;
1029             ExitNow();
1030         }
1031 
1032         SuccessOrExit(error);
1033 
1034         OutputLine("IPv6 address resolver %s", resolver.mHostName);
1035         OutputCacheInfo(info);
1036     }
1037 
1038 exit:
1039     if (iterator != nullptr)
1040     {
1041         otMdnsFreeIterator(GetInstancePtr(), iterator);
1042     }
1043 
1044     return error;
1045 }
1046 
Process(Arg aArgs[])1047 template <> otError Mdns::Process<Cmd("ip4resolvers")>(Arg aArgs[])
1048 {
1049     // mdns ip4resolvers
1050 
1051     otError               error;
1052     otMdnsIterator       *iterator = nullptr;
1053     otMdnsCacheInfo       info;
1054     otMdnsAddressResolver resolver;
1055 
1056     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1057 
1058     iterator = otMdnsAllocateIterator(GetInstancePtr());
1059     VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
1060 
1061     while (true)
1062     {
1063         error = otMdnsGetNextIp4AddressResolver(GetInstancePtr(), iterator, &resolver, &info);
1064 
1065         if (error == OT_ERROR_NOT_FOUND)
1066         {
1067             error = OT_ERROR_NONE;
1068             ExitNow();
1069         }
1070 
1071         SuccessOrExit(error);
1072 
1073         OutputLine("IPv4 address resolver %s", resolver.mHostName);
1074         OutputCacheInfo(info);
1075     }
1076 
1077 exit:
1078     if (iterator != nullptr)
1079     {
1080         otMdnsFreeIterator(GetInstancePtr(), iterator);
1081     }
1082 
1083     return error;
1084 }
1085 
1086 #endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1087 
Process(Arg aArgs[])1088 otError Mdns::Process(Arg aArgs[])
1089 {
1090 #define CmdEntry(aCommandString)                            \
1091     {                                                       \
1092         aCommandString, &Mdns::Process<Cmd(aCommandString)> \
1093     }
1094 
1095     static constexpr Command kCommands[] = {
1096         CmdEntry("browser"),
1097 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1098         CmdEntry("browsers"),
1099 #endif
1100         CmdEntry("disable"),
1101         CmdEntry("enable"),
1102 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1103         CmdEntry("hosts"),
1104 #endif
1105         CmdEntry("ip4resolver"),
1106 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1107         CmdEntry("ip4resolvers"),
1108 #endif
1109         CmdEntry("ip6resolver"),
1110 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1111         CmdEntry("ip6resolvers"),
1112         CmdEntry("keys"),
1113 #endif
1114         CmdEntry("register"),
1115 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1116         CmdEntry("services"),
1117 #endif
1118         CmdEntry("srvresolver"),
1119 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1120         CmdEntry("srvresolvers"),
1121 #endif
1122         CmdEntry("state"),
1123         CmdEntry("txtresolver"),
1124 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
1125         CmdEntry("txtresolvers"),
1126 #endif
1127         CmdEntry("unicastquestion"),
1128         CmdEntry("unregister"),
1129     };
1130 
1131 #undef CmdEntry
1132 
1133     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
1134 
1135     otError        error = OT_ERROR_INVALID_COMMAND;
1136     const Command *command;
1137 
1138     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
1139     {
1140         OutputCommandTable(kCommands);
1141         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
1142     }
1143 
1144     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
1145     VerifyOrExit(command != nullptr);
1146 
1147     error = (this->*command->mHandler)(aArgs + 1);
1148 
1149 exit:
1150     return error;
1151 }
1152 
1153 } // namespace Cli
1154 } // namespace ot
1155 
1156 #endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
1157