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