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