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