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