1 /*
2 * Copyright (c) 2020, 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 a simple CLI for the SRP server.
32 */
33
34 #include "cli_srp_server.hpp"
35
36 #include <inttypes.h>
37
38 #include "cli/cli.hpp"
39 #include "common/string.hpp"
40
41 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
42
43 namespace ot {
44 namespace Cli {
45
46 /**
47 * @cli srp server addrmode (get,set)
48 * @code
49 * srp server addrmode anycast
50 * Done
51 * @endcode
52 * @code
53 * srp server addrmode
54 * anycast
55 * Done
56 * @endcode
57 * @cparam srp server addrmode [@ca{anycast}|@ca{unicast}|@ca{unicast-force-add}]
58 * @par
59 * Gets or sets the address mode used by the SRP server.
60 * @par
61 * The address mode tells the SRP server how to determine its address and port number,
62 * which then get published in the Thread network data.
63 * @sa otSrpServerGetAddressMode
64 * @sa otSrpServerSetAddressMode
65 */
Process(Arg aArgs[])66 template <> otError SrpServer::Process<Cmd("addrmode")>(Arg aArgs[])
67 {
68 otError error = OT_ERROR_INVALID_ARGS;
69
70 if (aArgs[0].IsEmpty())
71 {
72 switch (otSrpServerGetAddressMode(GetInstancePtr()))
73 {
74 case OT_SRP_SERVER_ADDRESS_MODE_UNICAST:
75 OutputLine("unicast");
76 break;
77
78 case OT_SRP_SERVER_ADDRESS_MODE_ANYCAST:
79 OutputLine("anycast");
80 break;
81
82 case OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD:
83 OutputLine("unicast-force-add");
84 break;
85 }
86
87 error = OT_ERROR_NONE;
88 }
89 else if (aArgs[0] == "unicast")
90 {
91 error = otSrpServerSetAddressMode(GetInstancePtr(), OT_SRP_SERVER_ADDRESS_MODE_UNICAST);
92 }
93 else if (aArgs[0] == "anycast")
94 {
95 error = otSrpServerSetAddressMode(GetInstancePtr(), OT_SRP_SERVER_ADDRESS_MODE_ANYCAST);
96 }
97 else if (aArgs[0] == "unicast-force-add")
98 {
99 error = otSrpServerSetAddressMode(GetInstancePtr(), OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD);
100 }
101
102 return error;
103 }
104
105 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
106
107 /**
108 * @cli srp server auto (enable,disable)
109 * @code
110 * srp server auto enable
111 * Done
112 * @endcode
113 * @code
114 * srp server auto
115 * Enabled
116 * Done
117 * @endcode
118 * @cparam srp server auto [@ca{enable}|@ca{disable}]
119 * @par
120 * Enables or disables the auto-enable mode on the SRP server.
121 * @par
122 * When this mode is enabled, the Border Routing Manager controls if and when
123 * to enable or disable the SRP server.
124 * @par
125 * This command requires that `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` be enabled.
126 * @moreinfo{@srp}.
127 * @sa otSrpServerIsAutoEnableMode
128 * @sa otSrpServerSetAutoEnableMode
129 */
Process(Arg aArgs[])130 template <> otError SrpServer::Process<Cmd("auto")>(Arg aArgs[])
131 {
132 return ProcessEnableDisable(aArgs, otSrpServerIsAutoEnableMode, otSrpServerSetAutoEnableMode);
133 }
134 #endif
135
136 /**
137 * @cli srp server domain (get,set)
138 * @code
139 * srp server domain thread.service.arpa.
140 * Done
141 * @endcode
142 * @code
143 * srp server domain
144 * thread.service.arpa.
145 * Done
146 * @endcode
147 * @cparam srp server domain [@ca{domain-name}]
148 * @par
149 * Gets or sets the domain name of the SRP server.
150 * @sa otSrpServerGetDomain
151 * @sa otSrpServerSetDomain
152 */
Process(Arg aArgs[])153 template <> otError SrpServer::Process<Cmd("domain")>(Arg aArgs[])
154 {
155 otError error = OT_ERROR_NONE;
156
157 if (aArgs[0].IsEmpty())
158 {
159 OutputLine("%s", otSrpServerGetDomain(GetInstancePtr()));
160 }
161 else
162 {
163 error = otSrpServerSetDomain(GetInstancePtr(), aArgs[0].GetCString());
164 }
165
166 return error;
167 }
168
169 #if OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE
170 /**
171 * @cli srp server faststart (enable)
172 * @code
173 * srp server faststart enable
174 * Done
175 * @endcode
176 * @code
177 * srp server faststart
178 * Enabled
179 * Done
180 * @endcode
181 * @cparam srp server faststart [@ca{enable}]
182 * @par
183 * Enables the "Fast Start Mode" on the SRP server.
184 * @par
185 * The Fast Start Mode is designed for scenarios where a device, often a mobile device, needs to act as a provisional
186 * SRP server (e.g., functioning as a temporary Border Router). The SRP server function is enabled only if no other
187 * Border Routers (BRs) are already providing the SRP service within the Thread network. Importantly, Fast Start Mode
188 * allows the device to quickly start its SRP server functionality upon joining the network, allowing other Thread
189 * devices to quickly connect and register their services without the typical delays associated with standard Border
190 * Router initialization (and SRP server startup).
191 * @par
192 * The Fast Start Mode can be enabled when the device is in the detached or disabled state, the SRP server is currently
193 * disabled, and "auto-enable mode" is not in use.
194 * @par
195 * After successfully enabling Fast Start Mode, it can be disabled by a direct command to enable/disable the SRP
196 * server, using `srp server [enable/disable]`.
197 * @par
198 * This command requires that `OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE` be enabled.
199 * @moreinfo{@srp}.
200 * @sa otSrpServerIsFastStartModeEnabled
201 * @sa otSrpServerEnableFastStartMode
202 */
Process(Arg aArgs[])203 template <> otError SrpServer::Process<Cmd("faststart")>(Arg aArgs[])
204 {
205 otError error = OT_ERROR_NONE;
206
207 if (aArgs[0].IsEmpty())
208 {
209 OutputEnabledDisabledStatus(otSrpServerIsFastStartModeEnabled(GetInstancePtr()));
210 }
211 else if (aArgs[0] == "enable")
212 {
213 error = otSrpServerEnableFastStartMode(GetInstancePtr());
214 }
215 else
216 {
217 error = OT_ERROR_INVALID_ARGS;
218 }
219
220 return error;
221 }
222
223 #endif // OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE
224
225 /**
226 * @cli srp server state
227 * @code
228 * srp server state
229 * running
230 * Done
231 * @endcode
232 * @par
233 * Returns one of the following possible states of the SRP server:
234 * * `disabled`: The SRP server is not enabled.
235 * * `stopped`: The SRP server is enabled but not active due to existing
236 * SRP servers that are already active in the Thread network.
237 * The SRP server may become active when the existing
238 * SRP servers are no longer active within the Thread network.
239 * * `running`: The SRP server is active and can handle service registrations.
240 * @par
241 * @moreinfo{@srp}.
242 * @sa otSrpServerGetState
243 */
Process(Arg aArgs[])244 template <> otError SrpServer::Process<Cmd("state")>(Arg aArgs[])
245 {
246 static const char *const kStateStrings[] = {
247 "disabled", // (0) OT_SRP_SERVER_STATE_DISABLED
248 "running", // (1) OT_SRP_SERVER_STATE_RUNNING
249 "stopped", // (2) OT_SRP_SERVER_STATE_STOPPED
250 };
251
252 OT_UNUSED_VARIABLE(aArgs);
253
254 static_assert(0 == OT_SRP_SERVER_STATE_DISABLED, "OT_SRP_SERVER_STATE_DISABLED value is incorrect");
255 static_assert(1 == OT_SRP_SERVER_STATE_RUNNING, "OT_SRP_SERVER_STATE_RUNNING value is incorrect");
256 static_assert(2 == OT_SRP_SERVER_STATE_STOPPED, "OT_SRP_SERVER_STATE_STOPPED value is incorrect");
257
258 OutputLine("%s", Stringify(otSrpServerGetState(GetInstancePtr()), kStateStrings));
259
260 return OT_ERROR_NONE;
261 }
262
Process(Arg aArgs[])263 template <> otError SrpServer::Process<Cmd("enable")>(Arg aArgs[])
264 {
265 OT_UNUSED_VARIABLE(aArgs);
266
267 otSrpServerSetEnabled(GetInstancePtr(), /* aEnabled */ true);
268
269 return OT_ERROR_NONE;
270 }
271
272 /**
273 * @cli srp server (enable,disable)
274 * @code
275 * srp server disable
276 * Done
277 * @endcode
278 * @cparam srp server [@ca{enable}|@ca{disable}]
279 * @par
280 * Enables or disables the SRP server. @moreinfo{@srp}.
281 * @sa otSrpServerSetEnabled
282 */
Process(Arg aArgs[])283 template <> otError SrpServer::Process<Cmd("disable")>(Arg aArgs[])
284 {
285 OT_UNUSED_VARIABLE(aArgs);
286
287 otSrpServerSetEnabled(GetInstancePtr(), /* aEnabled */ false);
288
289 return OT_ERROR_NONE;
290 }
291
Process(Arg aArgs[])292 template <> otError SrpServer::Process<Cmd("ttl")>(Arg aArgs[])
293 {
294 otError error = OT_ERROR_NONE;
295 otSrpServerTtlConfig ttlConfig;
296
297 if (aArgs[0].IsEmpty())
298 {
299 otSrpServerGetTtlConfig(GetInstancePtr(), &ttlConfig);
300 OutputLine("min ttl: %lu", ToUlong(ttlConfig.mMinTtl));
301 OutputLine("max ttl: %lu", ToUlong(ttlConfig.mMaxTtl));
302 }
303 else
304 {
305 SuccessOrExit(error = aArgs[0].ParseAsUint32(ttlConfig.mMinTtl));
306 SuccessOrExit(error = aArgs[1].ParseAsUint32(ttlConfig.mMaxTtl));
307 VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
308
309 error = otSrpServerSetTtlConfig(GetInstancePtr(), &ttlConfig);
310 }
311
312 exit:
313 return error;
314 }
315
316 /**
317 * @cli srp server lease (get,set)
318 * @code
319 * srp server lease 1800 7200 86400 1209600
320 * Done
321 * @endcode
322 * @code
323 * srp server lease
324 * min lease: 1800
325 * max lease: 7200
326 * min key-lease: 86400
327 * max key-lease: 1209600
328 * Done
329 * @endcode
330 * @cparam srp server lease [@ca{min-lease} @ca{max-lease} @ca{min-key-lease} @ca{max-key-lease}]
331 * @par
332 * Gets or sets the SRP server lease values in number of seconds.
333 * @sa otSrpServerGetLeaseConfig
334 * @sa otSrpServerSetLeaseConfig
335 */
Process(Arg aArgs[])336 template <> otError SrpServer::Process<Cmd("lease")>(Arg aArgs[])
337 {
338 otError error = OT_ERROR_NONE;
339 otSrpServerLeaseConfig leaseConfig;
340
341 if (aArgs[0].IsEmpty())
342 {
343 otSrpServerGetLeaseConfig(GetInstancePtr(), &leaseConfig);
344 OutputLine("min lease: %lu", ToUlong(leaseConfig.mMinLease));
345 OutputLine("max lease: %lu", ToUlong(leaseConfig.mMaxLease));
346 OutputLine("min key-lease: %lu", ToUlong(leaseConfig.mMinKeyLease));
347 OutputLine("max key-lease: %lu", ToUlong(leaseConfig.mMaxKeyLease));
348 }
349 else
350 {
351 SuccessOrExit(error = aArgs[0].ParseAsUint32(leaseConfig.mMinLease));
352 SuccessOrExit(error = aArgs[1].ParseAsUint32(leaseConfig.mMaxLease));
353 SuccessOrExit(error = aArgs[2].ParseAsUint32(leaseConfig.mMinKeyLease));
354 SuccessOrExit(error = aArgs[3].ParseAsUint32(leaseConfig.mMaxKeyLease));
355 VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
356
357 error = otSrpServerSetLeaseConfig(GetInstancePtr(), &leaseConfig);
358 }
359
360 exit:
361 return error;
362 }
363
364 /**
365 * @cli srp server host
366 * @code
367 * srp server host
368 * srp-api-test-1.default.service.arpa.
369 * deleted: false
370 * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10]
371 * srp-api-test-0.default.service.arpa.
372 * deleted: false
373 * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10]
374 * Done
375 * @endcode
376 * @par
377 * Returns information about all registered hosts. @moreinfo{@srp}.
378 * @sa otSrpServerGetNextHost
379 * @sa otSrpServerHostGetAddresses
380 * @sa otSrpServerHostGetFullName
381 */
Process(Arg aArgs[])382 template <> otError SrpServer::Process<Cmd("host")>(Arg aArgs[])
383 {
384 otError error = OT_ERROR_NONE;
385 const otSrpServerHost *host;
386
387 VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
388
389 host = nullptr;
390 while ((host = otSrpServerGetNextHost(GetInstancePtr(), host)) != nullptr)
391 {
392 const otIp6Address *addresses;
393 uint8_t addressesNum;
394 bool isDeleted = otSrpServerHostIsDeleted(host);
395
396 OutputLine("%s", otSrpServerHostGetFullName(host));
397 OutputLine(kIndentSize, "deleted: %s", isDeleted ? "true" : "false");
398 if (isDeleted)
399 {
400 continue;
401 }
402
403 OutputSpaces(kIndentSize);
404 OutputFormat("addresses: [");
405
406 addresses = otSrpServerHostGetAddresses(host, &addressesNum);
407
408 for (uint8_t i = 0; i < addressesNum; ++i)
409 {
410 OutputIp6Address(addresses[i]);
411 if (i < addressesNum - 1)
412 {
413 OutputFormat(", ");
414 }
415 }
416
417 OutputLine("]");
418 }
419
420 exit:
421 return error;
422 }
423
OutputHostAddresses(const otSrpServerHost * aHost)424 void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost)
425 {
426 const otIp6Address *addresses;
427 uint8_t addressesNum;
428
429 addresses = otSrpServerHostGetAddresses(aHost, &addressesNum);
430
431 OutputFormat("[");
432 for (uint8_t i = 0; i < addressesNum; ++i)
433 {
434 if (i != 0)
435 {
436 OutputFormat(", ");
437 }
438
439 OutputIp6Address(addresses[i]);
440 }
441 OutputFormat("]");
442 }
443
444 /**
445 * @cli srp server service
446 * @code
447 * srp server service
448 * srp-api-test-1._ipps._tcp.default.service.arpa.
449 * deleted: false
450 * subtypes: (null)
451 * port: 49152
452 * priority: 0
453 * weight: 0
454 * ttl: 7200
455 * lease: 7200
456 * key-lease: 1209600
457 * TXT: [616263, xyz=585960]
458 * host: srp-api-test-1.default.service.arpa.
459 * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10]
460 * srp-api-test-0._ipps._tcp.default.service.arpa.
461 * deleted: false
462 * subtypes: _sub1,_sub2
463 * port: 49152
464 * priority: 0
465 * weight: 0
466 * ttl: 3600
467 * lease: 3600
468 * key-lease: 1209600
469 * TXT: [616263, xyz=585960]
470 * host: srp-api-test-0.default.service.arpa.
471 * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10]
472 * Done
473 * @endcode
474 * @par
475 * Returns information about registered services.
476 * @par
477 * The `TXT` record is displayed
478 * as an array of entries. If an entry contains a key, the key is printed in
479 * ASCII format. The value portion is printed in hexadecimal bytes.
480 * @moreinfo{@srp}.
481 * @sa otSrpServerServiceGetInstanceName
482 * @sa otSrpServerServiceGetServiceName
483 * @sa otSrpServerServiceGetSubTypeServiceNameAt
484 */
Process(Arg aArgs[])485 template <> otError SrpServer::Process<Cmd("service")>(Arg aArgs[])
486 {
487 otError error = OT_ERROR_NONE;
488 const otSrpServerHost *host = nullptr;
489
490 VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
491
492 while ((host = otSrpServerGetNextHost(GetInstancePtr(), host)) != nullptr)
493 {
494 const otSrpServerService *service = nullptr;
495
496 while ((service = otSrpServerHostGetNextService(host, service)) != nullptr)
497 {
498 bool isDeleted = otSrpServerServiceIsDeleted(service);
499 const uint8_t *txtData;
500 uint16_t txtDataLength;
501 bool hasSubType = false;
502 otSrpServerLeaseInfo leaseInfo;
503
504 OutputLine("%s", otSrpServerServiceGetInstanceName(service));
505 OutputLine(kIndentSize, "deleted: %s", isDeleted ? "true" : "false");
506
507 if (isDeleted)
508 {
509 continue;
510 }
511
512 otSrpServerServiceGetLeaseInfo(service, &leaseInfo);
513
514 OutputFormat(kIndentSize, "subtypes: ");
515
516 for (uint16_t index = 0;; index++)
517 {
518 char subLabel[OT_DNS_MAX_LABEL_SIZE];
519 const char *subTypeName = otSrpServerServiceGetSubTypeServiceNameAt(service, index);
520
521 if (subTypeName == nullptr)
522 {
523 break;
524 }
525
526 IgnoreError(otSrpServerParseSubTypeServiceName(subTypeName, subLabel, sizeof(subLabel)));
527 OutputFormat("%s%s", hasSubType ? "," : "", subLabel);
528 hasSubType = true;
529 }
530
531 OutputLine(hasSubType ? "" : "(null)");
532
533 OutputLine(kIndentSize, "port: %u", otSrpServerServiceGetPort(service));
534 OutputLine(kIndentSize, "priority: %u", otSrpServerServiceGetPriority(service));
535 OutputLine(kIndentSize, "weight: %u", otSrpServerServiceGetWeight(service));
536 OutputLine(kIndentSize, "ttl: %lu", ToUlong(otSrpServerServiceGetTtl(service)));
537 OutputLine(kIndentSize, "lease: %lu", ToUlong(leaseInfo.mLease / 1000));
538 OutputLine(kIndentSize, "key-lease: %lu", ToUlong(leaseInfo.mKeyLease / 1000));
539
540 txtData = otSrpServerServiceGetTxtData(service, &txtDataLength);
541 OutputFormat(kIndentSize, "TXT: ");
542 OutputDnsTxtData(txtData, txtDataLength);
543 OutputNewLine();
544
545 OutputLine(kIndentSize, "host: %s", otSrpServerHostGetFullName(host));
546
547 OutputFormat(kIndentSize, "addresses: ");
548 OutputHostAddresses(host);
549 OutputNewLine();
550 }
551 }
552
553 exit:
554 return error;
555 }
556
557 /**
558 * @cli srp server seqnum (get,set)
559 * @code
560 * srp server seqnum 20
561 * Done
562 * @endcode
563 * @code
564 * srp server seqnum
565 * 20
566 * Done
567 * @endcode
568 * @cparam srp server seqnum [@ca{seqnum}]
569 * @par
570 * Gets or sets the sequence number used with the anycast address mode.
571 * The sequence number is included in the "DNS/SRP Service Anycast Address"
572 * entry that is published in the Network Data.
573 * @sa otSrpServerGetAnycastModeSequenceNumber
574 * @sa otSrpServerSetAnycastModeSequenceNumber
575 */
Process(Arg aArgs[])576 template <> otError SrpServer::Process<Cmd("seqnum")>(Arg aArgs[])
577 {
578 otError error = OT_ERROR_NONE;
579
580 if (aArgs[0].IsEmpty())
581 {
582 OutputLine("%u", otSrpServerGetAnycastModeSequenceNumber(GetInstancePtr()));
583 }
584 else
585 {
586 uint8_t sequenceNumber;
587
588 SuccessOrExit(error = aArgs[0].ParseAsUint8(sequenceNumber));
589 error = otSrpServerSetAnycastModeSequenceNumber(GetInstancePtr(), sequenceNumber);
590 }
591
592 exit:
593 return error;
594 }
595
Process(Arg aArgs[])596 otError SrpServer::Process(Arg aArgs[])
597 {
598 #define CmdEntry(aCommandString) \
599 { \
600 aCommandString, &SrpServer::Process<Cmd(aCommandString)> \
601 }
602
603 static constexpr Command kCommands[] = {
604 CmdEntry("addrmode"),
605 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
606 CmdEntry("auto"),
607 #endif
608 CmdEntry("disable"),
609 CmdEntry("domain"),
610 CmdEntry("enable"),
611 #if OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE
612 CmdEntry("faststart"),
613 #endif
614 CmdEntry("host"),
615 CmdEntry("lease"),
616 CmdEntry("seqnum"),
617 CmdEntry("service"),
618 CmdEntry("state"),
619 CmdEntry("ttl"),
620 };
621
622 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
623
624 otError error = OT_ERROR_INVALID_COMMAND;
625 const Command *command;
626
627 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
628 {
629 OutputCommandTable(kCommands);
630 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
631 }
632
633 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
634 VerifyOrExit(command != nullptr);
635
636 error = (this->*command->mHandler)(aArgs + 1);
637
638 exit:
639 return error;
640 }
641
642 } // namespace Cli
643 } // namespace ot
644
645 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
646