1 /*
2  *  Copyright (c) 2017, 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 CoAP service.
32  */
33 
34 #include "cli_udp.hpp"
35 
36 #include <openthread/message.h>
37 #include <openthread/nat64.h>
38 #include <openthread/udp.h>
39 
40 #include "cli/cli.hpp"
41 #include "common/encoding.hpp"
42 
43 namespace ot {
44 namespace Cli {
45 
UdpExample(otInstance * aInstance,OutputImplementer & aOutputImplementer)46 UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
47     : Utils(aInstance, aOutputImplementer)
48     , mLinkSecurityEnabled(true)
49 {
50     ClearAllBytes(mSocket);
51 }
52 
53 /**
54  * @cli udp bind
55  * @code
56  * udp bind :: 1234
57  * Done
58  * @endcode
59  * @code
60  * udp bind -u :: 1234
61  * Done
62  * @endcode
63  * @code
64  * udp bind -b :: 1234
65  * Done
66  * @endcode
67  * @cparam udp bind [@ca{netif}] @ca{ip} @ca{port}
68  * - `netif`: The binding network interface, which is determined as follows:
69  *   - No value (leaving out this parameter from the command): Thread network interface is used.
70  *   - `-u`: Unspecified network interface, which means that the UDP/IPv6 stack determines which
71  *   network interface to bind the socket to.
72  *   - `-b`: Backbone network interface is used.
73  * - `ip`: Unicast IPv6 address to bind to. If you wish to have the UDP/IPv6 stack assign the binding
74  *   IPv6 address, or if you wish to bind to multicast IPv6 addresses, then you can use the following
75  *   value to use the unspecified IPv6 address: `::`. Each example uses the unspecified IPv6 address.
76  * - `port`: UDP port number to bind to. Each of the examples is using port number 1234.
77  * @par
78  * Assigns an IPv6 address and a port to an open socket, which binds the socket for communication.
79  * Assigning the IPv6 address and port is referred to as naming the socket. @moreinfo{@udp}.
80  * @sa otUdpBind
81  */
Process(Arg aArgs[])82 template <> otError UdpExample::Process<Cmd("bind")>(Arg aArgs[])
83 {
84     otError           error;
85     otSockAddr        sockaddr;
86     otNetifIdentifier netif = OT_NETIF_THREAD;
87 
88     if (aArgs[0] == "-u")
89     {
90         netif = OT_NETIF_UNSPECIFIED;
91         aArgs++;
92     }
93     else if (aArgs[0] == "-b")
94     {
95         netif = OT_NETIF_BACKBONE;
96         aArgs++;
97     }
98 
99     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
100     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
101     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
102 
103     error = otUdpBind(GetInstancePtr(), &mSocket, &sockaddr, netif);
104 
105 exit:
106     return error;
107 }
108 
109 /**
110  * @cli udp connect
111  * @code
112  * udp connect fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234
113  * Done
114  * @endcode
115  * @code
116  * udp connect 172.17.0.1 1234
117  * Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
118  * Done
119  * @endcode
120  * @cparam udp connect @ca{ip} @ca{port}
121  * The following parameters are required:
122  * - `ip`: IP address of the peer.
123  * - `port`: UDP port number of the peer.
124  * The address can be an IPv4 address, which gets synthesized to an IPv6 address
125  * using the preferred NAT64 prefix from the network data. The command returns
126  * `InvalidState` when the preferred NAT64 prefix is unavailable.
127  * @par api_copy
128  * #otUdpConnect
129  * @moreinfo{@udp}.
130  */
Process(Arg aArgs[])131 template <> otError UdpExample::Process<Cmd("connect")>(Arg aArgs[])
132 {
133     otError    error;
134     otSockAddr sockaddr;
135     bool       nat64Synth;
136 
137     SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth));
138 
139     if (nat64Synth)
140     {
141         OutputFormat("Connecting to synthesized IPv6 address: ");
142         OutputIp6AddressLine(sockaddr.mAddress);
143     }
144 
145     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
146     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
147 
148     error = otUdpConnect(GetInstancePtr(), &mSocket, &sockaddr);
149 
150 exit:
151     return error;
152 }
153 
154 /**
155  * @cli udp close
156  * @code
157  * udp close
158  * Done
159  * @endcode
160  * @par api_copy
161  * #otUdpClose
162  */
Process(Arg aArgs[])163 template <> otError UdpExample::Process<Cmd("close")>(Arg aArgs[])
164 {
165     OT_UNUSED_VARIABLE(aArgs);
166 
167     return otUdpClose(GetInstancePtr(), &mSocket);
168 }
169 
170 /**
171  * @cli udp open
172  * @code
173  * udp open
174  * Done
175  * @endcode
176  * @par api_copy
177  * #otUdpOpen
178  */
Process(Arg aArgs[])179 template <> otError UdpExample::Process<Cmd("open")>(Arg aArgs[])
180 {
181     OT_UNUSED_VARIABLE(aArgs);
182 
183     otError error;
184 
185     VerifyOrExit(!otUdpIsOpen(GetInstancePtr(), &mSocket), error = OT_ERROR_ALREADY);
186     error = otUdpOpen(GetInstancePtr(), &mSocket, HandleUdpReceive, this);
187 
188 exit:
189     return error;
190 }
191 
192 /**
193  * @cli udp send
194  * @code
195  * udp send hello
196  * Done
197  * @endcode
198  * @code
199  * udp send -t hello
200  * Done
201  * @endcode
202  * @code
203  * udp send -x 68656c6c6f
204  * Done
205  * @endcode
206  * @code
207  * udp send -s 800
208  * Done
209  * @endcode
210  * @code
211  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 hello
212  * Done
213  * @endcode
214  * @code
215  * udp send 172.17.0.1 1234 hello
216  * Sending to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
217  * Done
218  * @endcode
219  * @code
220  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 -t hello
221  * Done
222  * @endcode
223  * @code
224  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 -x 68656c6c6f
225  * Done
226  * @endcode
227  * @code
228  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 -s 800
229  * Done
230  * @endcode
231  * @cparam udp send [@ca{ip} @ca{port}] [@ca{type}] @ca{value}
232  * The `ip` and `port` are optional as a pair, but if you specify one you must
233  * specify the other. If `ip` and `port` are not specified, the socket peer address
234  * is used from `udp connect`.
235  * - `ip`: Destination address. This address can be either an IPv4 or IPv6 address,
236  *   An IPv4 address gets synthesized to an IPv6 address with the preferred
237  *   NAT64 prefix from the network data. (If the preferred NAT64 prefix
238  *   is unavailable, the command returns `InvalidState`).
239  * - `port`: UDP destination port.
240  * - `type`/`value` combinations:
241  *   - `-t`: The payload in the `value` parameter is treated as text. If no `type` value
242  *   is entered, the payload in the `value` parameter is also treated as text.
243  *   - `-s`: Auto-generated payload with the specified length given in the `value` parameter.
244  *   - `-x`: Binary data in hexadecimal representation given in the `value` parameter.
245  * @par
246  * Sends a UDP message using the socket. @moreinfo{@udp}.
247  * @csa{udp open}
248  * @csa{udp bind}
249  * @csa{udp connect}
250  * @sa otUdpSend
251  */
Process(Arg aArgs[])252 template <> otError UdpExample::Process<Cmd("send")>(Arg aArgs[])
253 {
254     otError           error   = OT_ERROR_NONE;
255     otMessage        *message = nullptr;
256     otMessageInfo     messageInfo;
257     otMessageSettings messageSettings = {mLinkSecurityEnabled, OT_MESSAGE_PRIORITY_NORMAL};
258 
259     VerifyOrExit(otUdpIsOpen(GetInstancePtr(), &mSocket), error = OT_ERROR_INVALID_STATE);
260 
261     ClearAllBytes(messageInfo);
262 
263     // Possible argument formats:
264     //
265     // send             <text>
266     // send             <type> <value>
267     // send <ip> <port> <text>
268     // send <ip> <port> <type> <value>
269 
270     if (!aArgs[2].IsEmpty())
271     {
272         bool nat64Synth;
273 
274         SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr, nat64Synth));
275 
276         if (nat64Synth)
277         {
278             OutputFormat("Sending to synthesized IPv6 address: ");
279             OutputIp6AddressLine(messageInfo.mPeerAddr);
280         }
281 
282         SuccessOrExit(error = aArgs[1].ParseAsUint16(messageInfo.mPeerPort));
283         aArgs += 2;
284     }
285 
286     message = otUdpNewMessage(GetInstancePtr(), &messageSettings);
287     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
288 
289     if (aArgs[0] == "-s")
290     {
291         // Auto-generated payload with a given length
292 
293         uint16_t payloadLength;
294 
295         SuccessOrExit(error = aArgs[1].ParseAsUint16(payloadLength));
296         SuccessOrExit(error = PrepareAutoGeneratedPayload(*message, payloadLength));
297     }
298     else if (aArgs[0] == "-x")
299     {
300         // Binary hex data payload
301 
302         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
303         SuccessOrExit(error = PrepareHexStringPayload(*message, aArgs[1].GetCString()));
304     }
305     else
306     {
307         // Text payload (same as without specifying the type)
308 
309         if (aArgs[0] == "-t")
310         {
311             aArgs++;
312         }
313 
314         VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
315         SuccessOrExit(error = otMessageAppend(message, aArgs[0].GetCString(), aArgs[0].GetLength()));
316     }
317 
318     SuccessOrExit(error = otUdpSend(GetInstancePtr(), &mSocket, message, &messageInfo));
319 
320     message = nullptr;
321 
322 exit:
323     if (message != nullptr)
324     {
325         otMessageFree(message);
326     }
327 
328     return error;
329 }
330 
Process(Arg aArgs[])331 template <> otError UdpExample::Process<Cmd("linksecurity")>(Arg aArgs[])
332 {
333     otError error = OT_ERROR_NONE;
334 
335     /**
336      * @cli udp linksecurity
337      * @code
338      * udp linksecurity
339      * Enabled
340      * Done
341      * @endcode
342      * @par
343      * Indicates whether link security is enabled or disabled.
344      */
345     if (aArgs[0].IsEmpty())
346     {
347         OutputEnabledDisabledStatus(mLinkSecurityEnabled);
348     }
349     /**
350      * @cli udp linksecurity (enable,disable)
351      * @code
352      * udp linksecurity enable
353      * Done
354      * @endcode
355      * @code
356      * udp linksecurity disable
357      * Done
358      * @endcode
359      * @par
360      * Enables or disables link security.
361      */
362     else
363     {
364         error = ParseEnableOrDisable(aArgs[0], mLinkSecurityEnabled);
365     }
366 
367     return error;
368 }
369 
PrepareAutoGeneratedPayload(otMessage & aMessage,uint16_t aPayloadLength)370 otError UdpExample::PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength)
371 {
372     otError error     = OT_ERROR_NONE;
373     uint8_t character = '0';
374 
375     for (; aPayloadLength != 0; aPayloadLength--)
376     {
377         SuccessOrExit(error = otMessageAppend(&aMessage, &character, sizeof(character)));
378 
379         switch (character)
380         {
381         case '9':
382             character = 'A';
383             break;
384         case 'Z':
385             character = 'a';
386             break;
387         case 'z':
388             character = '0';
389             break;
390         default:
391             character++;
392             break;
393         }
394     }
395 
396 exit:
397     return error;
398 }
399 
PrepareHexStringPayload(otMessage & aMessage,const char * aHexString)400 otError UdpExample::PrepareHexStringPayload(otMessage &aMessage, const char *aHexString)
401 {
402     static constexpr uint16_t kBufferSize = 50;
403 
404     otError  error;
405     uint8_t  buf[kBufferSize];
406     uint16_t length;
407     bool     done = false;
408 
409     while (!done)
410     {
411         length = sizeof(buf);
412         error  = ot::Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
413 
414         VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING));
415         done = (error == OT_ERROR_NONE);
416 
417         SuccessOrExit(error = otMessageAppend(&aMessage, buf, length));
418     }
419 
420 exit:
421     return error;
422 }
423 
Process(Arg aArgs[])424 otError UdpExample::Process(Arg aArgs[])
425 {
426 #define CmdEntry(aCommandString)                                  \
427     {                                                             \
428         aCommandString, &UdpExample::Process<Cmd(aCommandString)> \
429     }
430 
431     static constexpr Command kCommands[] = {
432         CmdEntry("bind"),         CmdEntry("close"), CmdEntry("connect"),
433         CmdEntry("linksecurity"), CmdEntry("open"),  CmdEntry("send"),
434     };
435 
436     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
437 
438     otError        error = OT_ERROR_INVALID_COMMAND;
439     const Command *command;
440 
441     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
442     {
443         OutputCommandTable(kCommands);
444         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
445     }
446 
447     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
448     VerifyOrExit(command != nullptr);
449 
450     error = (this->*command->mHandler)(aArgs + 1);
451 
452 exit:
453     return error;
454 }
455 
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)456 void UdpExample::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
457 {
458     static_cast<UdpExample *>(aContext)->HandleUdpReceive(aMessage, aMessageInfo);
459 }
460 
HandleUdpReceive(otMessage * aMessage,const otMessageInfo * aMessageInfo)461 void UdpExample::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo)
462 {
463     char buf[1500];
464     int  length;
465 
466     OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
467     OutputIp6Address(aMessageInfo->mPeerAddr);
468     OutputFormat(" %d ", aMessageInfo->mPeerPort);
469 
470     length      = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
471     buf[length] = '\0';
472 
473     OutputLine("%s", buf);
474 }
475 
476 } // namespace Cli
477 } // namespace ot
478