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      * &lsqb;fd00:0:0:0:d88a:618b:384d:e760&rsqb;: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