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