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