1 /*
2  *  Copyright (c) 2021, 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 TCP CLI tool.
32  */
33 
34 #include "openthread-core-config.h"
35 
36 #include "cli_config.h"
37 
38 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
39 
40 #include "cli_tcp.hpp"
41 
42 #include <openthread/nat64.h>
43 #include <openthread/tcp.h>
44 
45 #include "cli/cli.hpp"
46 #include "common/encoding.hpp"
47 #include "common/timer.hpp"
48 
49 #if OPENTHREAD_CONFIG_TLS_ENABLE
50 #include <mbedtls/debug.h>
51 #include <mbedtls/ecjpake.h>
52 #include "crypto/mbedtls.hpp"
53 #endif
54 
55 namespace ot {
56 namespace Cli {
57 
58 #if OPENTHREAD_CONFIG_TLS_ENABLE
59 const int TcpExample::sCipherSuites[] = {MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8,
60                                          MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, 0};
61 #endif
62 
TcpExample(otInstance * aInstance,OutputImplementer & aOutputImplementer)63 TcpExample::TcpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
64     : Utils(aInstance, aOutputImplementer)
65     , mInitialized(false)
66     , mEndpointConnected(false)
67     , mEndpointConnectedFastOpen(false)
68     , mSendBusy(false)
69     , mUseCircularSendBuffer(true)
70     , mUseTls(false)
71     , mTlsHandshakeComplete(false)
72     , mBenchmarkBytesTotal(0)
73     , mBenchmarkBytesUnsent(0)
74     , mBenchmarkTimeUsed(0)
75 {
76     mEndpointAndCircularSendBuffer.mEndpoint   = &mEndpoint;
77     mEndpointAndCircularSendBuffer.mSendBuffer = &mSendBuffer;
78 }
79 
80 #if OPENTHREAD_CONFIG_TLS_ENABLE
MbedTlsDebugOutput(void * ctx,int level,const char * file,int line,const char * str)81 void TcpExample::MbedTlsDebugOutput(void *ctx, int level, const char *file, int line, const char *str)
82 {
83     TcpExample &tcpExample = *static_cast<TcpExample *>(ctx);
84 
85     tcpExample.OutputLine("%s:%d:%d: %s", file, line, level, str);
86 }
87 #endif
88 
89 /**
90  * @cli tcp init
91  * @code
92  * tcp init tls
93  * Done
94  * @endcode
95  * @cparam tcp init [@ca{mode}] [@ca{size}]
96  * * The `mode` has three possible values:
97  *   * `tls`: Specifies that the TCP connection between two nodes should also
98  *     use the TLS protocol on top of TCP. When two nodes communicate over TCP,
99  *     both nodes must either use TLS or neither node should use TLS because
100  *     a non-TLS endpoint cannot communicate with a TLS endpoint.
101  *   * `linked` or `circular`: Either one of these options means that TLS
102  *     is not to be used, and the specified buffering type should be used for TCP
103  *     buffering. The behavior of `linked` and `circular` is identical. Examine the code
104  *     for the differences between these two buffering types.
105  *     Two endpoints of a TCP connection are not required to use the same buffering type.
106  * * The `size` parameter sets the size of the receive buffer to associate with the
107  *   example TCP endpoint. If left unspecified, the maximum size is used. The
108  *   maximum size is set in `OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE`.
109  * @par
110  * Initializes the example TCP listener and the example TCP endpoint provided
111  * by the `tcp` CLI.
112  * @sa otTcpListenerInitialize
113  * @sa otTcpEndpointInitialize
114  */
Process(Arg aArgs[])115 template <> otError TcpExample::Process<Cmd("init")>(Arg aArgs[])
116 {
117     otError error = OT_ERROR_NONE;
118     size_t  receiveBufferSize;
119 
120     VerifyOrExit(!mInitialized, error = OT_ERROR_ALREADY);
121 
122     if (aArgs[0].IsEmpty())
123     {
124         mUseCircularSendBuffer = true;
125         mUseTls                = false;
126         receiveBufferSize      = sizeof(mReceiveBufferBytes);
127     }
128     else
129     {
130         if (aArgs[0] == "linked")
131         {
132             mUseCircularSendBuffer = false;
133             mUseTls                = false;
134         }
135         else if (aArgs[0] == "circular")
136         {
137             mUseCircularSendBuffer = true;
138             mUseTls                = false;
139         }
140 #if OPENTHREAD_CONFIG_TLS_ENABLE
141         else if (aArgs[0] == "tls")
142         {
143             mUseCircularSendBuffer = true;
144             mUseTls                = true;
145 
146             mbedtls_x509_crt_init(&mSrvCert);
147             mbedtls_pk_init(&mPKey);
148 
149             mbedtls_ssl_init(&mSslContext);
150             mbedtls_ssl_config_init(&mSslConfig);
151             mbedtls_ssl_conf_rng(&mSslConfig, Crypto::MbedTls::CryptoSecurePrng, nullptr);
152             mbedtls_ssl_conf_authmode(&mSslConfig, MBEDTLS_SSL_VERIFY_NONE);
153             mbedtls_ssl_conf_ciphersuites(&mSslConfig, sCipherSuites);
154 
155 #if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
156             mbedtls_ssl_conf_min_tls_version(&mSslConfig, MBEDTLS_SSL_VERSION_TLS1_2);
157             mbedtls_ssl_conf_max_tls_version(&mSslConfig, MBEDTLS_SSL_VERSION_TLS1_2);
158 #else
159             mbedtls_ssl_conf_min_version(&mSslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
160             mbedtls_ssl_conf_max_version(&mSslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
161 #endif
162 
163 #if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
164 #include "crypto/mbedtls.hpp"
165             int rv = mbedtls_pk_parse_key(&mPKey, reinterpret_cast<const unsigned char *>(sSrvKey), sSrvKeyLength,
166                                           nullptr, 0, Crypto::MbedTls::CryptoSecurePrng, nullptr);
167 #else
168             int rv = mbedtls_pk_parse_key(&mPKey, reinterpret_cast<const unsigned char *>(sSrvKey), sSrvKeyLength,
169                                           nullptr, 0);
170 #endif
171             if (rv != 0)
172             {
173                 OutputLine("mbedtls_pk_parse_key returned %d", rv);
174             }
175 
176             rv = mbedtls_x509_crt_parse(&mSrvCert, reinterpret_cast<const unsigned char *>(sSrvPem), sSrvPemLength);
177             if (rv != 0)
178             {
179                 OutputLine("mbedtls_x509_crt_parse (1) returned %d", rv);
180             }
181             rv = mbedtls_x509_crt_parse(&mSrvCert, reinterpret_cast<const unsigned char *>(sCasPem), sCasPemLength);
182             if (rv != 0)
183             {
184                 OutputLine("mbedtls_x509_crt_parse (2) returned %d", rv);
185             }
186             rv = mbedtls_ssl_setup(&mSslContext, &mSslConfig);
187             if (rv != 0)
188             {
189                 OutputLine("mbedtls_ssl_setup returned %d", rv);
190             }
191         }
192 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
193         else
194         {
195             ExitNow(error = OT_ERROR_INVALID_ARGS);
196         }
197 
198         if (aArgs[1].IsEmpty())
199         {
200             receiveBufferSize = sizeof(mReceiveBufferBytes);
201         }
202         else
203         {
204             uint32_t windowSize;
205 
206             SuccessOrExit(error = aArgs[1].ParseAsUint32(windowSize));
207 
208             receiveBufferSize = windowSize + ((windowSize + 7) >> 3);
209             VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBufferBytes) && receiveBufferSize != 0,
210                          error = OT_ERROR_INVALID_ARGS);
211         }
212     }
213 
214     otTcpCircularSendBufferInitialize(&mSendBuffer, mSendBufferBytes, sizeof(mSendBufferBytes));
215 
216     {
217         otTcpEndpointInitializeArgs endpointArgs;
218 
219         ClearAllBytes(endpointArgs);
220         endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback;
221 
222         if (mUseCircularSendBuffer)
223         {
224             endpointArgs.mForwardProgressCallback = HandleTcpForwardProgressCallback;
225         }
226         else
227         {
228             endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback;
229         }
230 
231         endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback;
232         endpointArgs.mDisconnectedCallback     = HandleTcpDisconnectedCallback;
233         endpointArgs.mContext                  = this;
234         endpointArgs.mReceiveBuffer            = mReceiveBufferBytes;
235         endpointArgs.mReceiveBufferSize        = receiveBufferSize;
236 
237         SuccessOrExit(error = otTcpEndpointInitialize(GetInstancePtr(), &mEndpoint, &endpointArgs));
238     }
239 
240     {
241         otTcpListenerInitializeArgs listenerArgs;
242 
243         ClearAllBytes(listenerArgs);
244         listenerArgs.mAcceptReadyCallback = HandleTcpAcceptReadyCallback;
245         listenerArgs.mAcceptDoneCallback  = HandleTcpAcceptDoneCallback;
246         listenerArgs.mContext             = this;
247 
248         error = otTcpListenerInitialize(GetInstancePtr(), &mListener, &listenerArgs);
249 
250         if (error != OT_ERROR_NONE)
251         {
252             IgnoreReturnValue(otTcpEndpointDeinitialize(&mEndpoint));
253             ExitNow();
254         }
255     }
256 
257     mInitialized = true;
258 
259 exit:
260     if (error != OT_ERROR_NONE)
261     {
262 #if OPENTHREAD_CONFIG_TLS_ENABLE
263         if (mUseTls)
264         {
265             mbedtls_ssl_config_free(&mSslConfig);
266             mbedtls_ssl_free(&mSslContext);
267 
268             mbedtls_pk_free(&mPKey);
269             mbedtls_x509_crt_free(&mSrvCert);
270         }
271 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
272 
273         otTcpCircularSendBufferForceDiscardAll(&mSendBuffer);
274         OT_UNUSED_VARIABLE(otTcpCircularSendBufferDeinitialize(&mSendBuffer));
275     }
276 
277     return error;
278 }
279 
280 /**
281  * @cli tcp deinit
282  * @code
283  * tcp deinit
284  * Done
285  * @endcode
286  * @par api_copy
287  * #otTcpEndpointDeinitialize
288  */
Process(Arg aArgs[])289 template <> otError TcpExample::Process<Cmd("deinit")>(Arg aArgs[])
290 {
291     otError error = OT_ERROR_NONE;
292     otError endpointError;
293     otError bufferError;
294     otError listenerError;
295 
296     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
297     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
298 
299 #if OPENTHREAD_CONFIG_TLS_ENABLE
300     if (mUseTls)
301     {
302         mbedtls_ssl_config_free(&mSslConfig);
303         mbedtls_ssl_free(&mSslContext);
304 
305         mbedtls_pk_free(&mPKey);
306         mbedtls_x509_crt_free(&mSrvCert);
307 
308         mUseTls = false;
309     }
310 #endif
311 
312     endpointError = otTcpEndpointDeinitialize(&mEndpoint);
313     mSendBusy     = false;
314 
315     otTcpCircularSendBufferForceDiscardAll(&mSendBuffer);
316     bufferError = otTcpCircularSendBufferDeinitialize(&mSendBuffer);
317 
318     listenerError = otTcpListenerDeinitialize(&mListener);
319     mInitialized  = false;
320 
321     SuccessOrExit(error = endpointError);
322     SuccessOrExit(error = bufferError);
323     SuccessOrExit(error = listenerError);
324 
325 exit:
326     return error;
327 }
328 
329 /**
330  * @cli tcp bind
331  * @code
332  * tcp bind :: 30000
333  * Done
334  * @endcode
335  * @cparam tcp bind @ca{ip} @ca{port}
336  * * `ip`: IPv6 address to bind to. If you wish to have the TCP/IPv6 stack assign
337  *   the binding IPv6 address, use the unspecified IPv6 address: `::`.
338  * * `port`: TCP port number to bind to.
339  * @par
340  * Associates an IPv6 address and a port to the example TCP endpoint provided by
341  * the `tcp` CLI. Associating the TCP endpoint to an IPv6
342  * address and port is referred to as "naming the TCP endpoint." This binds the
343  * endpoint for communication. @moreinfo{@tcp}.
344  * @sa otTcpBind
345  */
Process(Arg aArgs[])346 template <> otError TcpExample::Process<Cmd("bind")>(Arg aArgs[])
347 {
348     otError    error;
349     otSockAddr sockaddr;
350 
351     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
352 
353     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
354     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
355     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
356 
357     error = otTcpBind(&mEndpoint, &sockaddr);
358 
359 exit:
360     return error;
361 }
362 
363 /**
364  * @cli tcp connect
365  * @code
366  * tcp connect fe80:0:0:0:a8df:580a:860:ffa4 30000
367  * Done
368  * TCP: Connection established
369  * @endcode
370  * @code
371  * tcp connect 172.17.0.1 1234
372  * Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
373  * Done
374  * @endcode
375  * @cparam tcp connect @ca{ip} @ca{port} [@ca{fastopen}]
376  * * `ip`: IP address of the peer The address can be an IPv4 address,
377  *   which gets synthesized to an IPv6 address using the preferred
378  *   NAT64 prefix from the network data. The command returns `InvalidState`
379  *   when the preferred NAT64 prefix is unavailable.
380  * * `port`: TCP port number of the peer.
381  * * `fastopen`: This parameter is optional. If set to `fast`, TCP Fast Open is enabled
382  *   for this connection. Otherwise, if this parameter is set to `slow` or not used,
383  *   TCP Fast Open is disabled.
384  * @par
385  * Establishes a connection with the specified peer.
386  * @par
387  * If the connection establishment is successful, the resulting TCP connection
388  * is associated with the example TCP endpoint. @moreinfo{@tcp}.
389  * @sa otTcpConnect
390  */
Process(Arg aArgs[])391 template <> otError TcpExample::Process<Cmd("connect")>(Arg aArgs[])
392 {
393     otError    error;
394     otSockAddr sockaddr;
395     bool       nat64Synth;
396     uint32_t   flags;
397 
398     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
399 
400     SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth));
401 
402     if (nat64Synth)
403     {
404         OutputFormat("Connecting to synthesized IPv6 address: ");
405         OutputIp6AddressLine(sockaddr.mAddress);
406     }
407 
408     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
409 
410     if (aArgs[2].IsEmpty())
411     {
412         flags = OT_TCP_CONNECT_NO_FAST_OPEN;
413     }
414     else
415     {
416         if (aArgs[2] == "slow")
417         {
418             flags = OT_TCP_CONNECT_NO_FAST_OPEN;
419         }
420         else if (aArgs[2] == "fast")
421         {
422             flags = 0;
423         }
424         else
425         {
426             ExitNow(error = OT_ERROR_INVALID_ARGS);
427         }
428 
429         VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
430     }
431 
432 #if OPENTHREAD_CONFIG_TLS_ENABLE
433     if (mUseTls)
434     {
435         int rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
436                                              MBEDTLS_SSL_PRESET_DEFAULT);
437         if (rv != 0)
438         {
439             OutputLine("mbedtls_ssl_config_defaults returned %d", rv);
440         }
441     }
442 #endif
443 
444     SuccessOrExit(error = otTcpConnect(&mEndpoint, &sockaddr, flags));
445     mEndpointConnected         = true;
446     mEndpointConnectedFastOpen = ((flags & OT_TCP_CONNECT_NO_FAST_OPEN) == 0);
447 
448 #if OPENTHREAD_CONFIG_TLS_ENABLE
449     if (mUseTls && mEndpointConnectedFastOpen)
450     {
451         PrepareTlsHandshake();
452         ContinueTlsHandshake();
453     }
454 #endif
455 
456 exit:
457     return error;
458 }
459 
460 /**
461  * @cli tcp send
462  * @code
463  * tcp send hello
464  * Done
465  * @endcode
466  * @cparam tcp send @ca{message}
467  * The `message` parameter contains the message you want to send to the
468  * remote TCP endpoint.
469  * @par
470  * Sends data over the TCP connection associated with the example TCP endpoint
471  * that is provided with the `tcp` CLI. @moreinfo{@tcp}.
472  */
Process(Arg aArgs[])473 template <> otError TcpExample::Process<Cmd("send")>(Arg aArgs[])
474 {
475     otError error;
476 
477     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
478     VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY);
479     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
480     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
481 
482     if (mUseCircularSendBuffer)
483     {
484 #if OPENTHREAD_CONFIG_TLS_ENABLE
485         if (mUseTls)
486         {
487             int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<unsigned char *>(aArgs[0].GetCString()),
488                                        aArgs[0].GetLength());
489 
490             if (rv < 0 && rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ)
491             {
492                 ExitNow(error = OT_ERROR_FAILED);
493             }
494 
495             error = OT_ERROR_NONE;
496         }
497         else
498 #endif
499         {
500             size_t written;
501 
502             SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, aArgs[0].GetCString(),
503                                                                aArgs[0].GetLength(), &written, 0));
504         }
505     }
506     else
507     {
508         VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY);
509 
510         mSendLink.mNext   = nullptr;
511         mSendLink.mData   = mSendBufferBytes;
512         mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBufferBytes));
513         memcpy(mSendBufferBytes, aArgs[0].GetCString(), mSendLink.mLength);
514 
515         SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0));
516         mSendBusy = true;
517     }
518 
519 exit:
520     return error;
521 }
522 
Process(Arg aArgs[])523 template <> otError TcpExample::Process<Cmd("benchmark")>(Arg aArgs[])
524 {
525     otError error = OT_ERROR_NONE;
526 
527     /**
528      * @cli tcp benchmark result
529      * @code
530      * tcp benchmark result
531      * TCP Benchmark Status: Ongoing
532      * Done
533      * @endcode
534      * @code
535      * tcp benchmark result
536      * TCP Benchmark Status: Completed
537      * TCP Benchmark Complete: Transferred 73728 bytes in 7056 milliseconds
538      * TCP Goodput: 83.592 kb/s
539      * @endcode
540      * @par
541      * Shows the latest result of the TCP benchmark test. Possible status values:
542      * * Ongoing
543      * * Completed
544      * * Untested
545      * @par
546      * This command is primarily intended for creating scripts that automate
547      * the TCP benchmark test.
548      */
549     if (aArgs[0] == "result")
550     {
551         OutputFormat("TCP Benchmark Status: ");
552 
553         if (mBenchmarkBytesTotal != 0)
554         {
555             OutputLine("Ongoing");
556         }
557         else if (mBenchmarkTimeUsed != 0)
558         {
559             OutputLine("Completed");
560             OutputBenchmarkResult();
561         }
562         else
563         {
564             OutputLine("Untested");
565         }
566     }
567     /**
568      * @cli tcp benchmark run
569      * @code
570      * tcp benchmark run
571      * Done
572      * TCP Benchmark Complete: Transferred 73728 bytes in 7233 milliseconds
573      * TCP Goodput: 81.546 kb/s
574      * @endcode
575      * @cparam tcp benchmark run [@ca{size}]
576      * Use the `size` parameter to specify the number of bytes to send
577      * for the benchmark. If you do not use the `size` parameter, the default
578      * value (`OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE`) is used.
579      * @par
580      * Transfers the specified number of bytes using the TCP connection
581      * currently associated with the example TCP endpoint provided by the `tcp` CLI.
582      * @note You must establish a TCP connection before you run this command.
583      */
584     else if (aArgs[0] == "run")
585     {
586         VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY);
587         VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY);
588 
589         if (aArgs[1].IsEmpty())
590         {
591             mBenchmarkBytesTotal = OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE;
592         }
593         else
594         {
595             SuccessOrExit(error = aArgs[1].ParseAsUint32(mBenchmarkBytesTotal));
596             VerifyOrExit(mBenchmarkBytesTotal != 0, error = OT_ERROR_INVALID_ARGS);
597         }
598 
599         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
600 
601         mBenchmarkStart       = TimerMilli::GetNow();
602         mBenchmarkBytesUnsent = mBenchmarkBytesTotal;
603 
604         if (mUseCircularSendBuffer)
605         {
606             SuccessOrExit(error = ContinueBenchmarkCircularSend());
607         }
608         else
609         {
610             uint32_t benchmarkLinksLeft =
611                 (mBenchmarkBytesTotal + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes);
612             uint32_t toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), benchmarkLinksLeft);
613 
614             /* We could also point the linked buffers directly to sBenchmarkData. */
615             memset(mSendBufferBytes, 'a', sizeof(mSendBufferBytes));
616 
617             for (uint32_t i = 0; i != toSendOut; i++)
618             {
619                 mBenchmarkLinks[i].mNext   = nullptr;
620                 mBenchmarkLinks[i].mData   = mSendBufferBytes;
621                 mBenchmarkLinks[i].mLength = sizeof(mSendBufferBytes);
622 
623                 if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBufferBytes) != 0)
624                 {
625                     mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBufferBytes);
626                 }
627 
628                 error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i],
629                                              i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME);
630                 VerifyOrExit(error == OT_ERROR_NONE, mBenchmarkBytesTotal = 0);
631             }
632         }
633     }
634     else
635     {
636         error = OT_ERROR_INVALID_ARGS;
637     }
638 
639 exit:
640     return error;
641 }
642 
643 /**
644  * @cli tcp sendend
645  * @code
646  * tcp sendend
647  * Done
648  * @endcode
649  * @par
650  * Sends the "end of stream" signal over the TCP connection
651  * associated with the example TCP endpoint provided by the `tcp` CLI. This
652  * alerts the peer that it will not receive any more data over this TCP connection.
653  * @sa otTcpSendEndOfStream
654  */
Process(Arg aArgs[])655 template <> otError TcpExample::Process<Cmd("sendend")>(Arg aArgs[])
656 {
657     otError error;
658 
659     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
660     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
661 
662     error = otTcpSendEndOfStream(&mEndpoint);
663 
664 exit:
665     return error;
666 }
667 
668 /**
669  * @cli tcp abort
670  * @code
671  * tcp abort
672  * TCP: Connection reset
673  * Done
674  * @endcode
675  * @par
676  * Unceremoniously ends the TCP connection associated with the
677  * example TCP endpoint, transitioning the TCP endpoint to the closed state.
678  * @sa otTcpAbort
679  */
Process(Arg aArgs[])680 template <> otError TcpExample::Process<Cmd("abort")>(Arg aArgs[])
681 {
682     otError error;
683 
684     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
685     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
686 
687     SuccessOrExit(error = otTcpAbort(&mEndpoint));
688     mEndpointConnected         = false;
689     mEndpointConnectedFastOpen = false;
690 
691 exit:
692     return error;
693 }
694 
695 /**
696  * @cli tcp listen
697  * @code
698  * tcp listen :: 30000
699  * Done
700  * @endcode
701  * @cparam tcp listen @ca{ip} @ca{port}
702  * The following parameters are required:
703  * * `ip`: IPv6 address or the unspecified IPv6 address (`::`) of the example
704  *   TCP listener provided by the `tcp` CLI.
705  * * `port`: TCP port of the example TCP listener.
706  *   If no TCP connection is associated with the example TCP endpoint, then any
707  *   incoming connections matching the specified IPv6 address and port are accepted
708  *   and are associated with the example TCP endpoint.
709  * @par
710  * Uses the example TCP listener to listen for incoming connections on the
711  * specified IPv6 address and port. @moreinfo{@tcp}.
712  * @sa otTcpListen
713  */
Process(Arg aArgs[])714 template <> otError TcpExample::Process<Cmd("listen")>(Arg aArgs[])
715 {
716     otError    error;
717     otSockAddr sockaddr;
718 
719     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
720 
721     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
722     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
723     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
724 
725     SuccessOrExit(error = otTcpStopListening(&mListener));
726     error = otTcpListen(&mListener, &sockaddr);
727 
728 exit:
729     return error;
730 }
731 
732 /**
733  * @cli tcp stoplistening
734  * @code
735  * tcp stoplistening
736  * Done
737  * @endcode
738  * @par
739  * Instructs the example TCP listener to stop listening for incoming TCP connections.
740  * @sa otTcpStopListening
741  */
Process(Arg aArgs[])742 template <> otError TcpExample::Process<Cmd("stoplistening")>(Arg aArgs[])
743 {
744     otError error;
745 
746     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
747     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
748 
749     error = otTcpStopListening(&mListener);
750 
751 exit:
752     return error;
753 }
754 
Process(Arg aArgs[])755 otError TcpExample::Process(Arg aArgs[])
756 {
757 #define CmdEntry(aCommandString)                                  \
758     {                                                             \
759         aCommandString, &TcpExample::Process<Cmd(aCommandString)> \
760     }
761 
762     static constexpr Command kCommands[] = {
763         CmdEntry("abort"), CmdEntry("benchmark"), CmdEntry("bind"), CmdEntry("connect"), CmdEntry("deinit"),
764         CmdEntry("init"),  CmdEntry("listen"),    CmdEntry("send"), CmdEntry("sendend"), CmdEntry("stoplistening"),
765     };
766 
767     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
768 
769     otError        error = OT_ERROR_INVALID_COMMAND;
770     const Command *command;
771 
772     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
773     {
774         OutputCommandTable(kCommands);
775         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
776     }
777 
778     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
779     VerifyOrExit(command != nullptr);
780 
781     error = (this->*command->mHandler)(aArgs + 1);
782 
783 exit:
784     return error;
785 }
786 
HandleTcpEstablishedCallback(otTcpEndpoint * aEndpoint)787 void TcpExample::HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint)
788 {
789     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpEstablished(aEndpoint);
790 }
791 
HandleTcpSendDoneCallback(otTcpEndpoint * aEndpoint,otLinkedBuffer * aData)792 void TcpExample::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
793 {
794     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData);
795 }
796 
HandleTcpForwardProgressCallback(otTcpEndpoint * aEndpoint,size_t aInSendBuffer,size_t aBacklog)797 void TcpExample::HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog)
798 {
799     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))
800         ->HandleTcpForwardProgress(aEndpoint, aInSendBuffer, aBacklog);
801 }
802 
HandleTcpReceiveAvailableCallback(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)803 void TcpExample::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint,
804                                                    size_t         aBytesAvailable,
805                                                    bool           aEndOfStream,
806                                                    size_t         aBytesRemaining)
807 {
808     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))
809         ->HandleTcpReceiveAvailable(aEndpoint, aBytesAvailable, aEndOfStream, aBytesRemaining);
810 }
811 
HandleTcpDisconnectedCallback(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)812 void TcpExample::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
813 {
814     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason);
815 }
816 
HandleTcpAcceptReadyCallback(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)817 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReadyCallback(otTcpListener    *aListener,
818                                                                        const otSockAddr *aPeer,
819                                                                        otTcpEndpoint   **aAcceptInto)
820 {
821     return static_cast<TcpExample *>(otTcpListenerGetContext(aListener))
822         ->HandleTcpAcceptReady(aListener, aPeer, aAcceptInto);
823 }
824 
HandleTcpAcceptDoneCallback(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)825 void TcpExample::HandleTcpAcceptDoneCallback(otTcpListener    *aListener,
826                                              otTcpEndpoint    *aEndpoint,
827                                              const otSockAddr *aPeer)
828 {
829     static_cast<TcpExample *>(otTcpListenerGetContext(aListener))->HandleTcpAcceptDone(aListener, aEndpoint, aPeer);
830 }
831 
HandleTcpEstablished(otTcpEndpoint * aEndpoint)832 void TcpExample::HandleTcpEstablished(otTcpEndpoint *aEndpoint)
833 {
834     OT_UNUSED_VARIABLE(aEndpoint);
835     OutputLine("TCP: Connection established");
836 #if OPENTHREAD_CONFIG_TLS_ENABLE
837     if (mUseTls && !mEndpointConnectedFastOpen)
838     {
839         PrepareTlsHandshake();
840         ContinueTlsHandshake();
841     }
842 #endif
843 }
844 
HandleTcpSendDone(otTcpEndpoint * aEndpoint,otLinkedBuffer * aData)845 void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
846 {
847     OT_UNUSED_VARIABLE(aEndpoint);
848     OT_ASSERT(!mUseCircularSendBuffer); // this callback is not used when using the circular send buffer
849 
850     if (mBenchmarkBytesTotal == 0)
851     {
852         // If the benchmark encountered an error, we might end up here. So,
853         // tolerate some benchmark links finishing in this case.
854         if (aData == &mSendLink)
855         {
856             OT_ASSERT(mSendBusy);
857             mSendBusy = false;
858         }
859     }
860     else
861     {
862         OT_ASSERT(aData != &mSendLink);
863         OT_ASSERT(mBenchmarkBytesUnsent >= aData->mLength);
864 
865         mBenchmarkBytesUnsent -= aData->mLength; // could be less than sizeof(mSendBufferBytes) for the first link
866 
867         if (mBenchmarkBytesUnsent >= OT_ARRAY_LENGTH(mBenchmarkLinks) * sizeof(mSendBufferBytes))
868         {
869             aData->mLength = sizeof(mSendBufferBytes);
870 
871             if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE)
872             {
873                 OutputLine("TCP Benchmark Failed");
874                 mBenchmarkBytesTotal = 0;
875             }
876         }
877         else if (mBenchmarkBytesUnsent == 0)
878         {
879             CompleteBenchmark();
880         }
881     }
882 }
883 
HandleTcpForwardProgress(otTcpEndpoint * aEndpoint,size_t aInSendBuffer,size_t aBacklog)884 void TcpExample::HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog)
885 {
886     OT_UNUSED_VARIABLE(aEndpoint);
887     OT_UNUSED_VARIABLE(aBacklog);
888     OT_ASSERT(mUseCircularSendBuffer); // this callback is only used when using the circular send buffer
889 
890     otTcpCircularSendBufferHandleForwardProgress(&mSendBuffer, aInSendBuffer);
891 
892 #if OPENTHREAD_CONFIG_TLS_ENABLE
893     if (mUseTls)
894     {
895         ContinueTlsHandshake();
896     }
897 #endif
898 
899     /* Handle case where we're in a benchmark. */
900     if (mBenchmarkBytesTotal != 0)
901     {
902         if (mBenchmarkBytesUnsent != 0)
903         {
904             /* Continue sending out data if there's data we haven't sent. */
905             IgnoreError(ContinueBenchmarkCircularSend());
906         }
907         else if (aInSendBuffer == 0)
908         {
909             /* Handle case where all data is sent out and the send buffer has drained. */
910             CompleteBenchmark();
911         }
912     }
913 }
914 
HandleTcpReceiveAvailable(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)915 void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint,
916                                            size_t         aBytesAvailable,
917                                            bool           aEndOfStream,
918                                            size_t         aBytesRemaining)
919 {
920     OT_UNUSED_VARIABLE(aBytesRemaining);
921     OT_ASSERT(aEndpoint == &mEndpoint);
922 
923     /* If we get data before the handshake completes, then this is a TFO connection. */
924     if (!mEndpointConnected)
925     {
926         mEndpointConnected         = true;
927         mEndpointConnectedFastOpen = true;
928 
929 #if OPENTHREAD_CONFIG_TLS_ENABLE
930         if (mUseTls)
931         {
932             PrepareTlsHandshake();
933         }
934 #endif
935     }
936 
937 #if OPENTHREAD_CONFIG_TLS_ENABLE
938     if (mUseTls && ContinueTlsHandshake())
939     {
940         ExitNow();
941     }
942 #endif
943 
944     if ((mTlsHandshakeComplete || !mUseTls) && aBytesAvailable > 0)
945     {
946 #if OPENTHREAD_CONFIG_TLS_ENABLE
947         if (mUseTls)
948         {
949             uint8_t buffer[500];
950 
951             for (;;)
952             {
953                 int rv = mbedtls_ssl_read(&mSslContext, buffer, sizeof(buffer));
954 
955                 if (rv < 0)
956                 {
957                     if (rv == MBEDTLS_ERR_SSL_WANT_READ)
958                     {
959                         break;
960                     }
961 
962                     OutputLine("TLS receive failure: %d", rv);
963                 }
964                 else
965                 {
966                     OutputLine("TLS: Received %u bytes: %.*s", static_cast<unsigned>(rv), rv,
967                                reinterpret_cast<const char *>(buffer));
968                 }
969             }
970             OutputLine("(TCP: Received %u bytes)", static_cast<unsigned>(aBytesAvailable));
971         }
972         else
973 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
974         {
975             const otLinkedBuffer *data;
976             size_t                totalReceived = 0;
977 
978             IgnoreError(otTcpReceiveByReference(aEndpoint, &data));
979 
980             for (; data != nullptr; data = data->mNext)
981             {
982                 OutputLine("TCP: Received %u bytes: %.*s", static_cast<unsigned>(data->mLength),
983                            static_cast<unsigned>(data->mLength), reinterpret_cast<const char *>(data->mData));
984                 totalReceived += data->mLength;
985             }
986 
987             OT_ASSERT(aBytesAvailable == totalReceived);
988             IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0));
989         }
990     }
991 
992     if (aEndOfStream)
993     {
994         OutputLine("TCP: Reached end of stream");
995     }
996 
997     ExitNow();
998 
999 exit:
1000     return;
1001 }
1002 
HandleTcpDisconnected(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)1003 void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
1004 {
1005     static const char *const kReasonStrings[] = {
1006         "Disconnected",            // (0) OT_TCP_DISCONNECTED_REASON_NORMAL
1007         "Connection refused",      // (1) OT_TCP_DISCONNECTED_REASON_REFUSED
1008         "Connection reset",        // (2) OT_TCP_DISCONNECTED_REASON_RESET
1009         "Entered TIME-WAIT state", // (3) OT_TCP_DISCONNECTED_REASON_TIME_WAIT
1010         "Connection timed out",    // (4) OT_TCP_DISCONNECTED_REASON_TIMED_OUT
1011     };
1012 
1013     OT_UNUSED_VARIABLE(aEndpoint);
1014 
1015     static_assert(0 == OT_TCP_DISCONNECTED_REASON_NORMAL, "OT_TCP_DISCONNECTED_REASON_NORMAL value is incorrect");
1016     static_assert(1 == OT_TCP_DISCONNECTED_REASON_REFUSED, "OT_TCP_DISCONNECTED_REASON_REFUSED value is incorrect");
1017     static_assert(2 == OT_TCP_DISCONNECTED_REASON_RESET, "OT_TCP_DISCONNECTED_REASON_RESET value is incorrect");
1018     static_assert(3 == OT_TCP_DISCONNECTED_REASON_TIME_WAIT, "OT_TCP_DISCONNECTED_REASON_TIME_WAIT value is incorrect");
1019     static_assert(4 == OT_TCP_DISCONNECTED_REASON_TIMED_OUT, "OT_TCP_DISCONNECTED_REASON_TIMED_OUT value is incorrect");
1020 
1021     OutputLine("TCP: %s", Stringify(aReason, kReasonStrings));
1022 
1023 #if OPENTHREAD_CONFIG_TLS_ENABLE
1024     if (mUseTls)
1025     {
1026         mbedtls_ssl_session_reset(&mSslContext);
1027     }
1028 #endif
1029 
1030     // We set this to false even for the TIME-WAIT state, so that we can reuse
1031     // the active socket if an incoming connection comes in instead of waiting
1032     // for the 2MSL timeout.
1033     mEndpointConnected         = false;
1034     mEndpointConnectedFastOpen = false;
1035     mSendBusy                  = false;
1036 
1037     // Mark the benchmark as inactive if the connection was disconnected.
1038     mBenchmarkBytesTotal  = 0;
1039     mBenchmarkBytesUnsent = 0;
1040 
1041     otTcpCircularSendBufferForceDiscardAll(&mSendBuffer);
1042 }
1043 
HandleTcpAcceptReady(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)1044 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener    *aListener,
1045                                                                const otSockAddr *aPeer,
1046                                                                otTcpEndpoint   **aAcceptInto)
1047 {
1048     otTcpIncomingConnectionAction action;
1049 
1050     OT_UNUSED_VARIABLE(aListener);
1051 
1052     if (mEndpointConnected)
1053     {
1054         OutputFormat("TCP: Ignoring incoming connection request from ");
1055         OutputSockAddr(*aPeer);
1056         OutputLine(" (active socket is busy)");
1057 
1058         ExitNow(action = OT_TCP_INCOMING_CONNECTION_ACTION_DEFER);
1059     }
1060 
1061     *aAcceptInto = &mEndpoint;
1062     action       = OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT;
1063 
1064 #if OPENTHREAD_CONFIG_TLS_ENABLE
1065     /*
1066      * Natural to wait until the AcceptDone callback but with TFO we could get data before that
1067      * so it doesn't make sense to wait until then.
1068      */
1069     if (mUseTls)
1070     {
1071         int rv;
1072 
1073         rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
1074                                          MBEDTLS_SSL_PRESET_DEFAULT);
1075         if (rv != 0)
1076         {
1077             OutputLine("mbedtls_ssl_config_defaults returned %d", rv);
1078         }
1079 
1080         mbedtls_ssl_conf_ca_chain(&mSslConfig, mSrvCert.next, nullptr);
1081         rv = mbedtls_ssl_conf_own_cert(&mSslConfig, &mSrvCert, &mPKey);
1082 
1083         if (rv != 0)
1084         {
1085             OutputLine("mbedtls_ssl_conf_own_cert returned %d", rv);
1086         }
1087     }
1088 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
1089 
1090 exit:
1091     return action;
1092 }
1093 
HandleTcpAcceptDone(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)1094 void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer)
1095 {
1096     OT_UNUSED_VARIABLE(aListener);
1097     OT_UNUSED_VARIABLE(aEndpoint);
1098 
1099     mEndpointConnected = true;
1100     OutputFormat("Accepted connection from ");
1101     OutputSockAddrLine(*aPeer);
1102 }
1103 
ContinueBenchmarkCircularSend(void)1104 otError TcpExample::ContinueBenchmarkCircularSend(void)
1105 {
1106     otError error = OT_ERROR_NONE;
1107     size_t  freeSpace;
1108 
1109     while (mBenchmarkBytesUnsent != 0 && (freeSpace = otTcpCircularSendBufferGetFreeSpace(&mSendBuffer)) != 0)
1110     {
1111         size_t   toSendThisIteration = OT_MIN(mBenchmarkBytesUnsent, sBenchmarkDataLength);
1112         uint32_t flag                = (toSendThisIteration < freeSpace && toSendThisIteration < mBenchmarkBytesUnsent)
1113                                            ? OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME
1114                                            : 0;
1115         size_t   written             = 0;
1116 
1117 #if OPENTHREAD_CONFIG_TLS_ENABLE
1118         if (mUseTls)
1119         {
1120             int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<const unsigned char *>(sBenchmarkData),
1121                                        toSendThisIteration);
1122 
1123             if (rv > 0)
1124             {
1125                 written = static_cast<size_t>(rv);
1126                 OT_ASSERT(written <= mBenchmarkBytesUnsent);
1127             }
1128             else if (rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ)
1129             {
1130                 ExitNow(error = OT_ERROR_FAILED);
1131             }
1132 
1133             error = OT_ERROR_NONE;
1134         }
1135         else
1136 #endif
1137         {
1138             SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, sBenchmarkData,
1139                                                                toSendThisIteration, &written, flag));
1140         }
1141         mBenchmarkBytesUnsent -= written;
1142     }
1143 
1144 exit:
1145     if (error != OT_ERROR_NONE)
1146     {
1147         OutputLine("TCP Benchmark Failed");
1148         mBenchmarkBytesTotal  = 0;
1149         mBenchmarkBytesUnsent = 0;
1150     }
1151 
1152     return error;
1153 }
1154 
OutputBenchmarkResult(void)1155 void TcpExample::OutputBenchmarkResult(void)
1156 {
1157     uint32_t thousandTimesGoodput =
1158         (1000 * (mBenchmarkLastBytesTotal << 3) + (mBenchmarkTimeUsed >> 1)) / mBenchmarkTimeUsed;
1159 
1160     OutputLine("TCP Benchmark Complete: Transferred %lu bytes in %lu milliseconds", ToUlong(mBenchmarkLastBytesTotal),
1161                ToUlong(mBenchmarkTimeUsed));
1162     OutputLine("TCP Goodput: %lu.%03u kb/s", ToUlong(thousandTimesGoodput / 1000),
1163                static_cast<uint16_t>(thousandTimesGoodput % 1000));
1164 }
1165 
CompleteBenchmark(void)1166 void TcpExample::CompleteBenchmark(void)
1167 {
1168     mBenchmarkTimeUsed       = TimerMilli::GetNow() - mBenchmarkStart;
1169     mBenchmarkLastBytesTotal = mBenchmarkBytesTotal;
1170 
1171     OutputBenchmarkResult();
1172 
1173     mBenchmarkBytesTotal = 0;
1174 }
1175 
1176 #if OPENTHREAD_CONFIG_TLS_ENABLE
PrepareTlsHandshake(void)1177 void TcpExample::PrepareTlsHandshake(void)
1178 {
1179     int rv;
1180 
1181     rv = mbedtls_ssl_set_hostname(&mSslContext, "localhost");
1182 
1183     if (rv != 0)
1184     {
1185         OutputLine("mbedtls_ssl_set_hostname returned %d", rv);
1186     }
1187 
1188     rv = mbedtls_ssl_set_hs_ecjpake_password(&mSslContext, reinterpret_cast<const unsigned char *>(sEcjpakePassword),
1189                                              sEcjpakePasswordLength);
1190     if (rv != 0)
1191     {
1192         OutputLine("mbedtls_ssl_set_hs_ecjpake_password returned %d", rv);
1193     }
1194 
1195     mbedtls_ssl_set_bio(&mSslContext, &mEndpointAndCircularSendBuffer, otTcpMbedTlsSslSendCallback,
1196                         otTcpMbedTlsSslRecvCallback, nullptr);
1197     mTlsHandshakeComplete = false;
1198 }
1199 
ContinueTlsHandshake(void)1200 bool TcpExample::ContinueTlsHandshake(void)
1201 {
1202     bool wasNotAlreadyDone = false;
1203     int  rv;
1204 
1205     if (!mTlsHandshakeComplete)
1206     {
1207         rv = mbedtls_ssl_handshake(&mSslContext);
1208 
1209         if (rv == 0)
1210         {
1211             OutputLine("TLS Handshake Complete");
1212             mTlsHandshakeComplete = true;
1213         }
1214         else if (rv != MBEDTLS_ERR_SSL_WANT_READ && rv != MBEDTLS_ERR_SSL_WANT_WRITE)
1215         {
1216             OutputLine("TLS Handshake Failed: %d", rv);
1217         }
1218 
1219         wasNotAlreadyDone = true;
1220     }
1221 
1222     return wasNotAlreadyDone;
1223 }
1224 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
1225 
1226 } // namespace Cli
1227 } // namespace ot
1228 
1229 #endif // OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
1230