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 Client.
32 */
33
34 #include "cli_srp_client.hpp"
35
36 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
37
38 #include <string.h>
39
40 #include "cli/cli.hpp"
41
42 namespace ot {
43 namespace Cli {
44
CopyString(char * aDest,uint16_t aDestSize,const char * aSource)45 static otError CopyString(char *aDest, uint16_t aDestSize, const char *aSource)
46 {
47 // Copies a string from `aSource` to `aDestination` (char array),
48 // verifying that the string fits in the destination array.
49
50 otError error = OT_ERROR_NONE;
51 size_t len = strlen(aSource);
52
53 VerifyOrExit(len + 1 <= aDestSize, error = OT_ERROR_INVALID_ARGS);
54 memcpy(aDest, aSource, len + 1);
55
56 exit:
57 return error;
58 }
59
SrpClient(otInstance * aInstance,OutputImplementer & aOutputImplementer)60 SrpClient::SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer)
61 : Utils(aInstance, aOutputImplementer)
62 , mCallbackEnabled(false)
63 {
64 otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this);
65 }
66
67 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
68
Process(Arg aArgs[])69 template <> otError SrpClient::Process<Cmd("autostart")>(Arg aArgs[])
70 {
71 otError error = OT_ERROR_NONE;
72 bool enable;
73
74 /**
75 * @cli srp client autostart (get)
76 * @code
77 * srp client autostart
78 * Disabled
79 * Done
80 * @endcode
81 * @par
82 * Indicates the current state of auto-start mode (enabled or disabled).
83 * @moreinfo{@srp}.
84 * @sa otSrpClientIsAutoStartModeEnabled
85 */
86 if (aArgs[0].IsEmpty())
87 {
88 OutputEnabledDisabledStatus(otSrpClientIsAutoStartModeEnabled(GetInstancePtr()));
89 ExitNow();
90 }
91
92 SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
93
94 /**
95 * @cli srp client autostart enable
96 * @code
97 * srp client autostart enable
98 * Done
99 * @endcode
100 * @par
101 * Enables auto-start mode.
102 * @par
103 * When auto-start is enabled, the SRP client monitors Thread
104 * network data to discover SRP servers, to select the preferred
105 * server, and to automatically start and stop the client when
106 * an SRP server is detected.
107 * @par
108 * Three categories of network data entries indicate the presence of an SRP sever,
109 * and are preferred in the following order:
110 * -# Unicast entries in which the server address is included in the service
111 * data. If there are multiple options, the option with the lowest numerical
112 * IPv6 address is preferred.
113 * -# Anycast entries that each have a sequence number. The largest sequence
114 * number as specified by Serial Number Arithmetic Logic
115 * in RFC-1982 is preferred.
116 * -# Unicast entries in which the server address information is included
117 * with the server data. If there are multiple options, the option with the
118 * lowest numerical IPv6 address is preferred.
119 * @sa otSrpClientEnableAutoStartMode
120 */
121 if (enable)
122 {
123 otSrpClientEnableAutoStartMode(GetInstancePtr(), /* aCallback */ nullptr, /* aContext */ nullptr);
124 }
125 /**
126 * @cli srp client autostart disable
127 * @code
128 * srp client autostart disable
129 * Done
130 * @endcode
131 * @par
132 * Disables the auto-start mode.
133 * @par
134 * Disabling auto-start mode does not stop a running client.
135 * However, the SRP client stops monitoring Thread network data.
136 * @sa otSrpClientDisableAutoStartMode
137 */
138 else
139 {
140 otSrpClientDisableAutoStartMode(GetInstancePtr());
141 }
142
143 exit:
144 return error;
145 }
146
147 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
148
149 /**
150 * @cli srp client callback (get,enable,disable)
151 * @code
152 * srp client callback enable
153 * Done
154 * @endcode
155 * @code
156 * srp client callback
157 * Enabled
158 * Done
159 * @endcode
160 * @cparam srp client callback [@ca{enable}|@ca{disable}]
161 * @par
162 * Gets or enables/disables printing callback events from the SRP client.
163 * @moreinfo{@srp}.
164 * @sa otSrpClientSetCallback
165 */
Process(Arg aArgs[])166 template <> otError SrpClient::Process<Cmd("callback")>(Arg aArgs[])
167 {
168 otError error = OT_ERROR_NONE;
169
170 if (aArgs[0].IsEmpty())
171 {
172 OutputEnabledDisabledStatus(mCallbackEnabled);
173 ExitNow();
174 }
175
176 error = ParseEnableOrDisable(aArgs[0], mCallbackEnabled);
177
178 exit:
179 return error;
180 }
181
Process(Arg aArgs[])182 template <> otError SrpClient::Process<Cmd("host")>(Arg aArgs[])
183 {
184 otError error = OT_ERROR_NONE;
185
186 /**
187 * @cli srp client host
188 * @code
189 * srp client host
190 * name:"dev4312", state:Registered, addrs:[fd00:0:0:0:0:0:0:1234, fd00:0:0:0:0:0:0:beef]
191 * Done
192 * @endcode
193 * @par api_copy
194 * #otSrpClientGetHostInfo
195 */
196 if (aArgs[0].IsEmpty())
197 {
198 OutputHostInfo(0, *otSrpClientGetHostInfo(GetInstancePtr()));
199 }
200 /**
201 * @cli srp client host name (get,set)
202 * @code
203 * srp client host name dev4312
204 * Done
205 * @endcode
206 * @code
207 * srp client host name
208 * dev4312
209 * Done
210 * @endcode
211 * @cparam srp client host name [@ca{name}]
212 * To set the client host name when the host has either been removed or not yet
213 * registered with the server, use the `name` parameter.
214 * @par
215 * Gets or sets the host name of the SRP client. @moreinfo{@srp}.
216 * @sa otSrpClientSetHostName
217 */
218 else if (aArgs[0] == "name")
219 {
220 if (aArgs[1].IsEmpty())
221 {
222 const char *name = otSrpClientGetHostInfo(GetInstancePtr())->mName;
223 OutputLine("%s", (name != nullptr) ? name : "(null)");
224 }
225 else
226 {
227 uint16_t len;
228 uint16_t size;
229 char *hostName;
230
231 VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
232 hostName = otSrpClientBuffersGetHostNameString(GetInstancePtr(), &size);
233
234 len = aArgs[1].GetLength();
235 VerifyOrExit(len + 1 <= size, error = OT_ERROR_INVALID_ARGS);
236
237 // We first make sure we can set the name, and if so
238 // we copy it to the persisted string buffer and set
239 // the host name again now with the persisted buffer.
240 // This ensures that we do not overwrite a previous
241 // buffer with a host name that cannot be set.
242
243 SuccessOrExit(error = otSrpClientSetHostName(GetInstancePtr(), aArgs[1].GetCString()));
244 memcpy(hostName, aArgs[1].GetCString(), len + 1);
245
246 IgnoreError(otSrpClientSetHostName(GetInstancePtr(), hostName));
247 }
248 }
249 /**
250 * @cli srp client host state
251 * @code
252 * srp client host state
253 * Registered
254 * Done
255 * @endcode
256 * @par
257 * Returns the state of the SRP client host. Possible states:
258 * * `ToAdd`: Item to be added/registered.
259 * * `Adding`: Item is being added/registered.
260 * * `ToRefresh`: Item to be refreshed for lease renewal.
261 * * `Refreshing`: Item is beig refreshed.
262 * * `ToRemove`: Item to be removed.
263 * * `Removing`: Item is being removed.
264 * * `Registered`: Item is registered with server.
265 * * `Removed`: Item has been removed.
266 */
267 else if (aArgs[0] == "state")
268 {
269 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
270 OutputLine("%s", otSrpClientItemStateToString(otSrpClientGetHostInfo(GetInstancePtr())->mState));
271 }
272 else if (aArgs[0] == "address")
273 {
274 /**
275 * @cli srp client host address (get)
276 * @code
277 * srp client host address
278 * auto
279 * Done
280 * @endcode
281 * @code
282 * srp client host address
283 * fd00:0:0:0:0:0:0:1234
284 * fd00:0:0:0:0:0:0:beef
285 * Done
286 * @endcode
287 * @par
288 * Indicates whether auto address mode is enabled. If auto address mode is not
289 * enabled, then the list of SRP client host addresses is returned.
290 * @moreinfo{@srp}.
291 * @sa otSrpClientGetHostInfo
292 */
293 if (aArgs[1].IsEmpty())
294 {
295 const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(GetInstancePtr());
296
297 if (hostInfo->mAutoAddress)
298 {
299 OutputLine("auto");
300 }
301 else
302 {
303 for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++)
304 {
305 OutputIp6AddressLine(hostInfo->mAddresses[index]);
306 }
307 }
308 }
309 /**
310 * @cli srp client host address (set)
311 * @code
312 * srp client host address auto
313 * Done
314 * @endcode
315 * @code
316 * srp client host address fd00::cafe
317 * Done
318 * @endcode
319 * @cparam srp client host address [auto|@ca{address...}]
320 * * Use the `auto` parameter to enable auto host address mode.
321 * When enabled, the client automatically uses all preferred Thread
322 * `netif` unicast addresses except for link-local and mesh-local
323 * addresses. If there is no valid address, the mesh local
324 * EID address gets added. The SRP client automatically
325 * re-registers if addresses on the Thread `netif` are
326 * added or removed or marked as non-preferred.
327 * * Explicitly specify the list of host addresses, separating
328 * each address by a space. You can set this list while the client is
329 * running. This will also disable auto host address mode.
330 * @par
331 * Enable auto host address mode or explicitly set the list of host
332 * addresses. @moreinfo{@srp}.
333 * @sa otSrpClientEnableAutoHostAddress
334 * @sa otSrpClientSetHostAddresses
335 */
336 else if (aArgs[1] == "auto")
337 {
338 error = otSrpClientEnableAutoHostAddress(GetInstancePtr());
339 }
340 else
341 {
342 uint8_t numAddresses = 0;
343 otIp6Address addresses[kMaxHostAddresses];
344 uint8_t arrayLength;
345 otIp6Address *hostAddressArray;
346
347 hostAddressArray = otSrpClientBuffersGetHostAddressesArray(GetInstancePtr(), &arrayLength);
348
349 // We first make sure we can set the addresses, and if so
350 // we copy the address list into the persisted address array
351 // and set it again. This ensures that we do not overwrite
352 // a previous list before we know it is safe to set/change
353 // the address list.
354
355 if (arrayLength > kMaxHostAddresses)
356 {
357 arrayLength = kMaxHostAddresses;
358 }
359
360 for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
361 {
362 VerifyOrExit(numAddresses < arrayLength, error = OT_ERROR_NO_BUFS);
363 SuccessOrExit(error = arg->ParseAsIp6Address(addresses[numAddresses]));
364 numAddresses++;
365 }
366
367 SuccessOrExit(error = otSrpClientSetHostAddresses(GetInstancePtr(), addresses, numAddresses));
368
369 memcpy(hostAddressArray, addresses, numAddresses * sizeof(hostAddressArray[0]));
370 IgnoreError(otSrpClientSetHostAddresses(GetInstancePtr(), hostAddressArray, numAddresses));
371 }
372 }
373 /**
374 * @cli srp client host remove
375 * @code
376 * srp client host remove 1
377 * Done
378 * @endcode
379 * @cparam srp client host remove [@ca{removekeylease}] [@ca{sendunregtoserver}]
380 * * The parameter `removekeylease` is an optional boolean value that indicates
381 * whether the host key lease should also be removed (default is `false`).
382 * * The parameter `sendunregtoserver` is an optional boolean value that indicates
383 * whether the client host should send an "update" message to the server
384 * even when the client host information has not yet been registered with the
385 * server (default is `false`). This parameter can be specified only if the
386 * `removekeylease` parameter is specified first in the command.
387 * @par
388 * Removes SRP client host information and all services from the SRP server.
389 * @moreinfo{@srp}.
390 * @sa otSrpClientRemoveHostAndServices
391 * @sa otSrpClientSetHostName
392 */
393 else if (aArgs[0] == "remove")
394 {
395 bool removeKeyLease = false;
396 bool sendUnregToServer = false;
397
398 if (!aArgs[1].IsEmpty())
399 {
400 SuccessOrExit(error = aArgs[1].ParseAsBool(removeKeyLease));
401
402 if (!aArgs[2].IsEmpty())
403 {
404 SuccessOrExit(error = aArgs[2].ParseAsBool(sendUnregToServer));
405 VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
406 }
407 }
408
409 error = otSrpClientRemoveHostAndServices(GetInstancePtr(), removeKeyLease, sendUnregToServer);
410 }
411 /**
412 * @cli srp client host clear
413 * @code
414 * srp client host clear
415 * Done
416 * @endcode
417 * @par
418 * Clears all host information and all services.
419 * @sa otSrpClientBuffersFreeAllServices
420 * @sa otSrpClientClearHostAndServices
421 */
422 else if (aArgs[0] == "clear")
423 {
424 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
425 otSrpClientClearHostAndServices(GetInstancePtr());
426 otSrpClientBuffersFreeAllServices(GetInstancePtr());
427 }
428 else
429 {
430 error = OT_ERROR_INVALID_COMMAND;
431 }
432
433 exit:
434 return error;
435 }
436
437 /**
438 * @cli srp client leaseinterval (get,set)
439 * @code
440 * srp client leaseinterval 3600
441 * Done
442 * @endcode
443 * @code
444 * srp client leaseinterval
445 * 3600
446 * Done
447 * @endcode
448 * @cparam srp client leaseinterval [@ca{interval}]
449 * @par
450 * Gets or sets the lease interval in seconds.
451 * @sa otSrpClientGetLeaseInterval
452 * @sa otSrpClientSetLeaseInterval
453 */
Process(Arg aArgs[])454 template <> otError SrpClient::Process<Cmd("leaseinterval")>(Arg aArgs[])
455 {
456 return ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
457 }
458
459 /**
460 * @cli srp client keyleaseinterval (get,set)
461 * @code
462 * srp client keyleaseinterval 864000
463 * Done
464 * @endcode
465 * @code
466 * srp client keyleaseinterval
467 * 864000
468 * Done
469 * @endcode
470 * @cparam srp client keyleaseinterval [@ca{interval}]
471 * @par
472 * Gets or sets the key lease interval in seconds.
473 * @sa otSrpClientGetKeyLeaseInterval
474 * @sa otSrpClientSetKeyLeaseInterval
475 */
Process(Arg aArgs[])476 template <> otError SrpClient::Process<Cmd("keyleaseinterval")>(Arg aArgs[])
477 {
478 return ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval, otSrpClientSetKeyLeaseInterval);
479 }
480
Process(Arg aArgs[])481 template <> otError SrpClient::Process<Cmd("server")>(Arg aArgs[])
482 {
483 otError error = OT_ERROR_NONE;
484 const otSockAddr *serverSockAddr = otSrpClientGetServerAddress(GetInstancePtr());
485
486 /**
487 * @cli srp client server
488 * @code
489 * srp client server
490 * [fd00:0:0:0:d88a:618b:384d:e760]:4724
491 * Done
492 * @endcode
493 * @par
494 * Gets the socket address (IPv6 address and port number) of the SRP server
495 * that is being used by the SRP client. If the client is not running, the address
496 * is unspecified (all zeros) with a port number of 0. @moreinfo{@srp}.
497 * @sa otSrpClientGetServerAddress
498 */
499 if (aArgs[0].IsEmpty())
500 {
501 OutputSockAddrLine(*serverSockAddr);
502 ExitNow();
503 }
504
505 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
506
507 /**
508 * @cli srp client server address
509 * @code
510 * srp client server address
511 * fd00:0:0:0:d88a:618b:384d:e760
512 * Done
513 * @endcode
514 * @par
515 * Returns the server's IPv6 address.
516 */
517 if (aArgs[0] == "address")
518 {
519 OutputIp6AddressLine(serverSockAddr->mAddress);
520 }
521 /**
522 * @cli srp client server port
523 * @code
524 * srp client server port
525 * 4724
526 * Done
527 * @endcode
528 * @par
529 * Returns the server's port number.
530 */
531 else if (aArgs[0] == "port")
532 {
533 OutputLine("%u", serverSockAddr->mPort);
534 }
535 else
536 {
537 error = OT_ERROR_INVALID_COMMAND;
538 }
539
540 exit:
541 return error;
542 }
543
Process(Arg aArgs[])544 template <> otError SrpClient::Process<Cmd("service")>(Arg aArgs[])
545 {
546 otError error = OT_ERROR_NONE;
547 bool isRemove;
548
549 /**
550 * @cli srp client service
551 * @code
552 * srp client service
553 * instance:"ins2", name:"_test2._udp,_sub1,_sub2", state:Registered, port:111, priority:1, weight:1
554 * instance:"ins1", name:"_test1._udp", state:Registered, port:777, priority:0, weight:0
555 * Done
556 * @endcode
557 * @par api_copy
558 * #otSrpClientGetServices
559 */
560 if (aArgs[0].IsEmpty())
561 {
562 OutputServiceList(0, otSrpClientGetServices(GetInstancePtr()));
563 }
564 /**
565 * @cli srp client service add
566 * @code
567 * srp client service add ins1 _test1._udp 777
568 * Done
569 * @endcode
570 * @code
571 * srp client service add ins2 _test2._udp,_sub1,_sub2 111 1 1
572 * Done
573 * @endcode
574 * @cparam srp client service add @ca{instancename} @ca{servicename} <!--
575 * * --> @ca{port} [@ca{priority}] <!--
576 * * --> [@ca{weight}] [@ca{txt}]
577 * The `servicename` parameter can optionally include a list of service subtype labels that are
578 * separated by commas. The examples here use generic naming. The `priority` and `weight` (both are `uint16_t`
579 * values) parameters are optional, and if not provided zero is used. The optional `txt` parameter sets the TXT
580 * data associated with the service. The `txt` value must be in hex-string format and is treated as an already
581 * encoded TXT data byte sequence.
582 * @par
583 * Adds a service with a given instance name, service name, and port number.
584 * @moreinfo{@srp}.
585 * @sa otSrpClientAddService
586 */
587 else if (aArgs[0] == "add")
588 {
589 error = ProcessServiceAdd(aArgs);
590 }
591 /**
592 * @cli srp client service remove
593 * @code
594 * srp client service remove ins2 _test2._udp
595 * Done
596 * @endcode
597 * @cparam srp client service remove @ca{instancename} @ca{servicename}
598 * @par
599 * Requests a service to be unregistered with the SRP server.
600 * @sa otSrpClientRemoveService
601 */
602 /**
603 * @cli srp client service name clear
604 * @code
605 * srp client service clear ins2 _test2._udp
606 * Done
607 * @endcode
608 * @cparam srp client service clear @ca{instancename} @ca{servicename}
609 * @par
610 * Clears a service, immediately removing it from the client service list,
611 * with no interaction with the SRP server.
612 * @sa otSrpClientClearService
613 */
614 else if ((isRemove = (aArgs[0] == "remove")) || (aArgs[0] == "clear"))
615 {
616 // `remove`|`clear` <instance-name> <service-name>
617
618 const otSrpClientService *service;
619
620 VerifyOrExit(!aArgs[2].IsEmpty() && aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
621
622 for (service = otSrpClientGetServices(GetInstancePtr()); service != nullptr; service = service->mNext)
623 {
624 if ((aArgs[1] == service->mInstanceName) && (aArgs[2] == service->mName))
625 {
626 break;
627 }
628 }
629
630 VerifyOrExit(service != nullptr, error = OT_ERROR_NOT_FOUND);
631
632 if (isRemove)
633 {
634 error = otSrpClientRemoveService(GetInstancePtr(), const_cast<otSrpClientService *>(service));
635 }
636 else
637 {
638 SuccessOrExit(error = otSrpClientClearService(GetInstancePtr(), const_cast<otSrpClientService *>(service)));
639
640 otSrpClientBuffersFreeService(GetInstancePtr(), reinterpret_cast<otSrpClientBuffersServiceEntry *>(
641 const_cast<otSrpClientService *>(service)));
642 }
643 }
644 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
645 /**
646 * @cli srp client service key (get,set)
647 * @code
648 * srp client service key enable
649 * Done
650 * @endcode
651 * @code
652 * srp client service key
653 * Enabled
654 * Done
655 * @endcode
656 * @par
657 * Gets or sets the service key record inclusion mode in the SRP client.
658 * This command is intended for testing only, and requires that
659 * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` be enabled. @moreinfo{@srp}.
660 * @sa otSrpClientIsServiceKeyRecordEnabled
661 */
662 else if (aArgs[0] == "key")
663 {
664 // `key [enable/disable]`
665
666 error = ProcessEnableDisable(aArgs + 1, otSrpClientIsServiceKeyRecordEnabled,
667 otSrpClientSetServiceKeyRecordEnabled);
668 }
669 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
670 else
671 {
672 error = OT_ERROR_INVALID_COMMAND;
673 }
674
675 exit:
676 return error;
677 }
678
ProcessServiceAdd(Arg aArgs[])679 otError SrpClient::ProcessServiceAdd(Arg aArgs[])
680 {
681 // `add` <instance-name> <service-name> <port> [priority] [weight] [txt] [lease] [key-lease]
682
683 otSrpClientBuffersServiceEntry *entry = nullptr;
684 uint16_t size;
685 char *string;
686 otError error;
687 char *label;
688
689 entry = otSrpClientBuffersAllocateService(GetInstancePtr());
690
691 VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS);
692
693 SuccessOrExit(error = aArgs[3].ParseAsUint16(entry->mService.mPort));
694
695 // Successfully parsing aArgs[3] indicates that aArgs[1] and
696 // aArgs[2] are also non-empty.
697
698 string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size);
699 SuccessOrExit(error = CopyString(string, size, aArgs[1].GetCString()));
700
701 string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size);
702 SuccessOrExit(error = CopyString(string, size, aArgs[2].GetCString()));
703
704 // Service subtypes are added as part of service name as a comma separated list
705 // e.g., "_service._udp,_sub1,_sub2"
706
707 label = strchr(string, ',');
708
709 if (label != nullptr)
710 {
711 uint16_t arrayLength;
712 const char **subTypeLabels = otSrpClientBuffersGetSubTypeLabelsArray(entry, &arrayLength);
713
714 // Leave the last array element as `nullptr` to indicate end of array.
715 for (uint16_t index = 0; index + 1 < arrayLength; index++)
716 {
717 *label++ = '\0';
718 subTypeLabels[index] = label;
719
720 label = strchr(label, ',');
721
722 if (label == nullptr)
723 {
724 break;
725 }
726 }
727
728 VerifyOrExit(label == nullptr, error = OT_ERROR_NO_BUFS);
729 }
730
731 SuccessOrExit(error = aArgs[3].ParseAsUint16(entry->mService.mPort));
732
733 if (!aArgs[4].IsEmpty())
734 {
735 SuccessOrExit(error = aArgs[4].ParseAsUint16(entry->mService.mPriority));
736 }
737
738 if (!aArgs[5].IsEmpty())
739 {
740 SuccessOrExit(error = aArgs[5].ParseAsUint16(entry->mService.mWeight));
741 }
742
743 if (!aArgs[6].IsEmpty() && (aArgs[6] != "-"))
744 {
745 uint8_t *txtBuffer;
746
747 txtBuffer = otSrpClientBuffersGetServiceEntryTxtBuffer(entry, &size);
748 entry->mTxtEntry.mValueLength = size;
749
750 SuccessOrExit(error = aArgs[6].ParseAsHexString(entry->mTxtEntry.mValueLength, txtBuffer));
751 }
752 else
753 {
754 entry->mService.mNumTxtEntries = 0;
755 }
756
757 if (!aArgs[7].IsEmpty())
758 {
759 SuccessOrExit(error = aArgs[7].ParseAsUint32(entry->mService.mLease));
760 }
761
762 if (!aArgs[8].IsEmpty())
763 {
764 SuccessOrExit(error = aArgs[8].ParseAsUint32(entry->mService.mKeyLease));
765 VerifyOrExit(aArgs[9].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
766 }
767
768 SuccessOrExit(error = otSrpClientAddService(GetInstancePtr(), &entry->mService));
769
770 entry = nullptr;
771
772 exit:
773 if (entry != nullptr)
774 {
775 otSrpClientBuffersFreeService(GetInstancePtr(), entry);
776 }
777
778 return error;
779 }
780
OutputHostInfo(uint8_t aIndentSize,const otSrpClientHostInfo & aHostInfo)781 void SrpClient::OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &aHostInfo)
782 {
783 OutputFormat(aIndentSize, "name:");
784
785 if (aHostInfo.mName != nullptr)
786 {
787 OutputFormat("\"%s\"", aHostInfo.mName);
788 }
789 else
790 {
791 OutputFormat("(null)");
792 }
793
794 OutputFormat(", state:%s, addrs:", otSrpClientItemStateToString(aHostInfo.mState));
795
796 if (aHostInfo.mAutoAddress)
797 {
798 OutputLine("auto");
799 }
800 else
801 {
802 OutputFormat("[");
803
804 for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++)
805 {
806 if (index > 0)
807 {
808 OutputFormat(", ");
809 }
810
811 OutputIp6Address(aHostInfo.mAddresses[index]);
812 }
813
814 OutputLine("]");
815 }
816 }
817
OutputServiceList(uint8_t aIndentSize,const otSrpClientService * aServices)818 void SrpClient::OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices)
819 {
820 while (aServices != nullptr)
821 {
822 OutputService(aIndentSize, *aServices);
823 aServices = aServices->mNext;
824 }
825 }
826
OutputService(uint8_t aIndentSize,const otSrpClientService & aService)827 void SrpClient::OutputService(uint8_t aIndentSize, const otSrpClientService &aService)
828 {
829 OutputFormat(aIndentSize, "instance:\"%s\", name:\"%s", aService.mInstanceName, aService.mName);
830
831 if (aService.mSubTypeLabels != nullptr)
832 {
833 for (uint16_t index = 0; aService.mSubTypeLabels[index] != nullptr; index++)
834 {
835 OutputFormat(",%s", aService.mSubTypeLabels[index]);
836 }
837 }
838
839 OutputLine("\", state:%s, port:%d, priority:%d, weight:%d", otSrpClientItemStateToString(aService.mState),
840 aService.mPort, aService.mPriority, aService.mWeight);
841 }
842
843 /**
844 * @cli srp client start
845 * @code
846 * srp client start fd00::d88a:618b:384d:e760 4724
847 * Done
848 * @endcode
849 * @cparam srp client start @ca{serveraddr} @ca{serverport}
850 * @par
851 * Starts the SRP client operation. @moreinfo{@srp}.
852 * @sa otSrpClientStart
853 */
Process(Arg aArgs[])854 template <> otError SrpClient::Process<Cmd("start")>(Arg aArgs[])
855 {
856 otError error = OT_ERROR_NONE;
857 otSockAddr serverSockAddr;
858
859 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(serverSockAddr.mAddress));
860 SuccessOrExit(error = aArgs[1].ParseAsUint16(serverSockAddr.mPort));
861 VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
862
863 error = otSrpClientStart(GetInstancePtr(), &serverSockAddr);
864
865 exit:
866 return error;
867 }
868
869 /**
870 * @cli srp client state
871 * @code
872 * srp client state
873 * Enabled
874 * Done
875 * @endcode
876 * @par api_copy
877 * #otSrpClientIsRunning
878 * @moreinfo{@srp}.
879 */
Process(Arg aArgs[])880 template <> otError SrpClient::Process<Cmd("state")>(Arg aArgs[])
881 {
882 otError error = OT_ERROR_NONE;
883
884 VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
885
886 OutputEnabledDisabledStatus(otSrpClientIsRunning(GetInstancePtr()));
887
888 exit:
889 return error;
890 }
891
892 /**
893 * @cli srp client stop
894 * @code
895 * srp client stop
896 * Done
897 * @endcode
898 * @par api_copy
899 * #otSrpClientStop
900 */
Process(Arg aArgs[])901 template <> otError SrpClient::Process<Cmd("stop")>(Arg aArgs[])
902 {
903 otError error = OT_ERROR_NONE;
904
905 VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
906 otSrpClientStop(GetInstancePtr());
907
908 exit:
909 return error;
910 }
911
912 /**
913 * @cli srp client ttl (get,set)
914 * @code
915 * srp client ttl 3600
916 * Done
917 * @endcode
918 * @code
919 * srp client ttl
920 * 3600
921 * Done
922 * @endcode
923 * @cparam srp client ttl [@ca{value}]
924 * @par
925 * Gets or sets the `ttl`(time to live) value in seconds.
926 * @sa otSrpClientGetTtl
927 * @sa otSrpClientSetTtl
928 */
Process(Arg aArgs[])929 template <> otError SrpClient::Process<Cmd("ttl")>(Arg aArgs[])
930 {
931 return ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl);
932 }
933
HandleCallback(otError aError,const otSrpClientHostInfo * aHostInfo,const otSrpClientService * aServices,const otSrpClientService * aRemovedServices,void * aContext)934 void SrpClient::HandleCallback(otError aError,
935 const otSrpClientHostInfo *aHostInfo,
936 const otSrpClientService *aServices,
937 const otSrpClientService *aRemovedServices,
938 void *aContext)
939 {
940 static_cast<SrpClient *>(aContext)->HandleCallback(aError, aHostInfo, aServices, aRemovedServices);
941 }
942
HandleCallback(otError aError,const otSrpClientHostInfo * aHostInfo,const otSrpClientService * aServices,const otSrpClientService * aRemovedServices)943 void SrpClient::HandleCallback(otError aError,
944 const otSrpClientHostInfo *aHostInfo,
945 const otSrpClientService *aServices,
946 const otSrpClientService *aRemovedServices)
947 {
948 otSrpClientService *next;
949
950 if (mCallbackEnabled)
951 {
952 OutputLine("SRP client callback - error:%s", otThreadErrorToString(aError));
953 OutputLine("Host info:");
954 OutputHostInfo(kIndentSize, *aHostInfo);
955
956 OutputLine("Service list:");
957 OutputServiceList(kIndentSize, aServices);
958
959 if (aRemovedServices != nullptr)
960 {
961 OutputLine("Removed service list:");
962 OutputServiceList(kIndentSize, aRemovedServices);
963 }
964 }
965
966 // Go through removed services and free all removed services
967
968 for (const otSrpClientService *service = aRemovedServices; service != nullptr; service = next)
969 {
970 next = service->mNext;
971
972 otSrpClientBuffersFreeService(GetInstancePtr(), reinterpret_cast<otSrpClientBuffersServiceEntry *>(
973 const_cast<otSrpClientService *>(service)));
974 }
975 }
976
Process(Arg aArgs[])977 otError SrpClient::Process(Arg aArgs[])
978 {
979 #define CmdEntry(aCommandString) \
980 { \
981 aCommandString, &SrpClient::Process<Cmd(aCommandString)> \
982 }
983
984 static constexpr Command kCommands[] = {
985 CmdEntry("autostart"), CmdEntry("callback"), CmdEntry("host"), CmdEntry("keyleaseinterval"),
986 CmdEntry("leaseinterval"), CmdEntry("server"), CmdEntry("service"), CmdEntry("start"),
987 CmdEntry("state"), CmdEntry("stop"), CmdEntry("ttl"),
988 };
989
990 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
991
992 otError error = OT_ERROR_INVALID_COMMAND;
993 const Command *command;
994
995 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
996 {
997 OutputCommandTable(kCommands);
998 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
999 }
1000
1001 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
1002 VerifyOrExit(command != nullptr);
1003
1004 error = (this->*command->mHandler)(aArgs + 1);
1005
1006 exit:
1007 return error;
1008 }
1009
1010 } // namespace Cli
1011 } // namespace ot
1012
1013 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
1014