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/udp.h>
38 
39 #include "cli/cli.hpp"
40 #include "common/encoding.hpp"
41 
42 namespace ot {
43 namespace Cli {
44 
45 constexpr UdpExample::Command UdpExample::sCommands[];
46 
UdpExample(Interpreter & aInterpreter)47 UdpExample::UdpExample(Interpreter &aInterpreter)
48     : mInterpreter(aInterpreter)
49     , mLinkSecurityEnabled(true)
50 {
51     memset(&mSocket, 0, sizeof(mSocket));
52 }
53 
ProcessHelp(Arg aArgs[])54 otError UdpExample::ProcessHelp(Arg aArgs[])
55 {
56     OT_UNUSED_VARIABLE(aArgs);
57 
58     for (const Command &command : sCommands)
59     {
60         mInterpreter.OutputLine(command.mName);
61     }
62 
63     return OT_ERROR_NONE;
64 }
65 
ProcessBind(Arg aArgs[])66 otError UdpExample::ProcessBind(Arg aArgs[])
67 {
68     otError           error;
69     otSockAddr        sockaddr;
70     otNetifIdentifier netif = OT_NETIF_THREAD;
71 
72     if (aArgs[0] == "-u")
73     {
74         netif = OT_NETIF_UNSPECIFIED;
75         aArgs++;
76     }
77     else if (aArgs[0] == "-b")
78     {
79         netif = OT_NETIF_BACKBONE;
80         aArgs++;
81     }
82 
83     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
84     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
85     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
86 
87     error = otUdpBind(mInterpreter.mInstance, &mSocket, &sockaddr, netif);
88 
89 exit:
90     return error;
91 }
92 
ProcessConnect(Arg aArgs[])93 otError UdpExample::ProcessConnect(Arg aArgs[])
94 {
95     otError    error;
96     otSockAddr sockaddr;
97 
98     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
99     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
100     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
101 
102     error = otUdpConnect(mInterpreter.mInstance, &mSocket, &sockaddr);
103 
104 exit:
105     return error;
106 }
107 
ProcessClose(Arg aArgs[])108 otError UdpExample::ProcessClose(Arg aArgs[])
109 {
110     OT_UNUSED_VARIABLE(aArgs);
111 
112     return otUdpClose(mInterpreter.mInstance, &mSocket);
113 }
114 
ProcessOpen(Arg aArgs[])115 otError UdpExample::ProcessOpen(Arg aArgs[])
116 {
117     OT_UNUSED_VARIABLE(aArgs);
118 
119     otError error;
120 
121     VerifyOrExit(!otUdpIsOpen(mInterpreter.mInstance, &mSocket), error = OT_ERROR_ALREADY);
122     error = otUdpOpen(mInterpreter.mInstance, &mSocket, HandleUdpReceive, this);
123 
124 exit:
125     return error;
126 }
127 
ProcessSend(Arg aArgs[])128 otError UdpExample::ProcessSend(Arg aArgs[])
129 {
130     otError           error   = OT_ERROR_NONE;
131     otMessage *       message = nullptr;
132     otMessageInfo     messageInfo;
133     otMessageSettings messageSettings = {mLinkSecurityEnabled, OT_MESSAGE_PRIORITY_NORMAL};
134 
135     memset(&messageInfo, 0, sizeof(messageInfo));
136 
137     // Possible argument formats:
138     //
139     // send             <text>
140     // send             <type> <value>
141     // send <ip> <port> <text>
142     // send <ip> <port> <type> <value>
143 
144     if (!aArgs[2].IsEmpty())
145     {
146         SuccessOrExit(error = aArgs[0].ParseAsIp6Address(messageInfo.mPeerAddr));
147         SuccessOrExit(error = aArgs[1].ParseAsUint16(messageInfo.mPeerPort));
148         aArgs += 2;
149     }
150 
151     message = otUdpNewMessage(mInterpreter.mInstance, &messageSettings);
152     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
153 
154     if (aArgs[0] == "-s")
155     {
156         // Auto-generated payload with a given length
157 
158         uint16_t payloadLength;
159 
160         SuccessOrExit(error = aArgs[1].ParseAsUint16(payloadLength));
161         SuccessOrExit(error = PrepareAutoGeneratedPayload(*message, payloadLength));
162     }
163     else if (aArgs[0] == "-x")
164     {
165         // Binary hex data payload
166 
167         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
168         SuccessOrExit(error = PrepareHexStringPaylod(*message, aArgs[1].GetCString()));
169     }
170     else
171     {
172         // Text payload (same as without specifying the type)
173 
174         if (aArgs[0] == "-t")
175         {
176             aArgs++;
177         }
178 
179         VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
180         SuccessOrExit(error = otMessageAppend(message, aArgs[0].GetCString(), aArgs[0].GetLength()));
181     }
182 
183     SuccessOrExit(error = otUdpSend(mInterpreter.mInstance, &mSocket, message, &messageInfo));
184 
185     message = nullptr;
186 
187 exit:
188     if (message != nullptr)
189     {
190         otMessageFree(message);
191     }
192 
193     return error;
194 }
195 
ProcessLinkSecurity(Arg aArgs[])196 otError UdpExample::ProcessLinkSecurity(Arg aArgs[])
197 {
198     otError error = OT_ERROR_NONE;
199 
200     if (aArgs[0].IsEmpty())
201     {
202         mInterpreter.OutputEnabledDisabledStatus(mLinkSecurityEnabled);
203     }
204     else
205     {
206         error = Interpreter::ParseEnableOrDisable(aArgs[0], mLinkSecurityEnabled);
207     }
208 
209     return error;
210 }
211 
PrepareAutoGeneratedPayload(otMessage & aMessage,uint16_t aPayloadLength)212 otError UdpExample::PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength)
213 {
214     otError error     = OT_ERROR_NONE;
215     uint8_t character = '0';
216 
217     for (; aPayloadLength != 0; aPayloadLength--)
218     {
219         SuccessOrExit(error = otMessageAppend(&aMessage, &character, sizeof(character)));
220 
221         switch (character)
222         {
223         case '9':
224             character = 'A';
225             break;
226         case 'Z':
227             character = 'a';
228             break;
229         case 'z':
230             character = '0';
231             break;
232         default:
233             character++;
234             break;
235         }
236     }
237 
238 exit:
239     return error;
240 }
241 
PrepareHexStringPaylod(otMessage & aMessage,const char * aHexString)242 otError UdpExample::PrepareHexStringPaylod(otMessage &aMessage, const char *aHexString)
243 {
244     enum : uint8_t
245     {
246         kBufferSize = 50,
247     };
248 
249     otError  error;
250     uint8_t  buf[kBufferSize];
251     uint16_t length;
252     bool     done = false;
253 
254     while (!done)
255     {
256         length = sizeof(buf);
257         error  = Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
258 
259         VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING));
260         done = (error == OT_ERROR_NONE);
261 
262         SuccessOrExit(error = otMessageAppend(&aMessage, buf, length));
263     }
264 
265 exit:
266     return error;
267 }
268 
Process(Arg aArgs[])269 otError UdpExample::Process(Arg aArgs[])
270 {
271     otError        error = OT_ERROR_INVALID_ARGS;
272     const Command *command;
273 
274     if (aArgs[0].IsEmpty())
275     {
276         IgnoreError(ProcessHelp(aArgs));
277         ExitNow();
278     }
279 
280     command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
281     VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
282 
283     error = (this->*command->mHandler)(aArgs + 1);
284 
285 exit:
286     return error;
287 }
288 
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)289 void UdpExample::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
290 {
291     static_cast<UdpExample *>(aContext)->HandleUdpReceive(aMessage, aMessageInfo);
292 }
293 
HandleUdpReceive(otMessage * aMessage,const otMessageInfo * aMessageInfo)294 void UdpExample::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo)
295 {
296     char buf[1500];
297     int  length;
298 
299     mInterpreter.OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
300     mInterpreter.OutputIp6Address(aMessageInfo->mPeerAddr);
301     mInterpreter.OutputFormat(" %d ", aMessageInfo->mPeerPort);
302 
303     length      = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
304     buf[length] = '\0';
305 
306     mInterpreter.OutputLine("%s", buf);
307 }
308 
309 } // namespace Cli
310 } // namespace ot
311