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 #endif
309 
310     endpointError = otTcpEndpointDeinitialize(&mEndpoint);
311     mSendBusy     = false;
312 
313     otTcpCircularSendBufferForceDiscardAll(&mSendBuffer);
314     bufferError = otTcpCircularSendBufferDeinitialize(&mSendBuffer);
315 
316     listenerError = otTcpListenerDeinitialize(&mListener);
317     mInitialized  = false;
318 
319     SuccessOrExit(error = endpointError);
320     SuccessOrExit(error = bufferError);
321     SuccessOrExit(error = listenerError);
322 
323 exit:
324     return error;
325 }
326 
327 /**
328  * @cli tcp bind
329  * @code
330  * tcp bind :: 30000
331  * Done
332  * @endcode
333  * @cparam tcp bind @ca{ip} @ca{port}
334  * * `ip`: IPv6 address to bind to. If you wish to have the TCP/IPv6 stack assign
335  *   the binding IPv6 address, use the unspecified IPv6 address: `::`.
336  * * `port`: TCP port number to bind to.
337  * @par
338  * Associates an IPv6 address and a port to the example TCP endpoint provided by
339  * the `tcp` CLI. Associating the TCP endpoint to an IPv6
340  * address and port is referred to as "naming the TCP endpoint." This binds the
341  * endpoint for communication. @moreinfo{@tcp}.
342  * @sa otTcpBind
343  */
Process(Arg aArgs[])344 template <> otError TcpExample::Process<Cmd("bind")>(Arg aArgs[])
345 {
346     otError    error;
347     otSockAddr sockaddr;
348 
349     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
350 
351     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
352     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
353     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
354 
355     error = otTcpBind(&mEndpoint, &sockaddr);
356 
357 exit:
358     return error;
359 }
360 
361 /**
362  * @cli tcp connect
363  * @code
364  * tcp connect fe80:0:0:0:a8df:580a:860:ffa4 30000
365  * Done
366  * TCP: Connection established
367  * @endcode
368  * @code
369  * tcp connect 172.17.0.1 1234
370  * Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
371  * Done
372  * @endcode
373  * @cparam tcp connect @ca{ip} @ca{port} [@ca{fastopen}]
374  * * `ip`: IP address of the peer The address can be an IPv4 address,
375  *   which gets synthesized to an IPv6 address using the preferred
376  *   NAT64 prefix from the network data. The command returns `InvalidState`
377  *   when the preferred NAT64 prefix is unavailable.
378  * * `port`: TCP port number of the peer.
379  * * `fastopen`: This parameter is optional. If set to `fast`, TCP Fast Open is enabled
380  *   for this connection. Otherwise, if this parameter is set to `slow` or not used,
381  *   TCP Fast Open is disabled.
382  * @par
383  * Establishes a connection with the specified peer.
384  * @par
385  * If the connection establishment is successful, the resulting TCP connection
386  * is associated with the example TCP endpoint. @moreinfo{@tcp}.
387  * @sa otTcpConnect
388  */
Process(Arg aArgs[])389 template <> otError TcpExample::Process<Cmd("connect")>(Arg aArgs[])
390 {
391     otError    error;
392     otSockAddr sockaddr;
393     bool       nat64Synth;
394     uint32_t   flags;
395 
396     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
397 
398     SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth));
399 
400     if (nat64Synth)
401     {
402         OutputFormat("Connecting to synthesized IPv6 address: ");
403         OutputIp6AddressLine(sockaddr.mAddress);
404     }
405 
406     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
407 
408     if (aArgs[2].IsEmpty())
409     {
410         flags = OT_TCP_CONNECT_NO_FAST_OPEN;
411     }
412     else
413     {
414         if (aArgs[2] == "slow")
415         {
416             flags = OT_TCP_CONNECT_NO_FAST_OPEN;
417         }
418         else if (aArgs[2] == "fast")
419         {
420             flags = 0;
421         }
422         else
423         {
424             ExitNow(error = OT_ERROR_INVALID_ARGS);
425         }
426 
427         VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
428     }
429 
430 #if OPENTHREAD_CONFIG_TLS_ENABLE
431     if (mUseTls)
432     {
433         int rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
434                                              MBEDTLS_SSL_PRESET_DEFAULT);
435         if (rv != 0)
436         {
437             OutputLine("mbedtls_ssl_config_defaults returned %d", rv);
438         }
439     }
440 #endif
441 
442     SuccessOrExit(error = otTcpConnect(&mEndpoint, &sockaddr, flags));
443     mEndpointConnected         = true;
444     mEndpointConnectedFastOpen = ((flags & OT_TCP_CONNECT_NO_FAST_OPEN) == 0);
445 
446 #if OPENTHREAD_CONFIG_TLS_ENABLE
447     if (mUseTls && mEndpointConnectedFastOpen)
448     {
449         PrepareTlsHandshake();
450         ContinueTlsHandshake();
451     }
452 #endif
453 
454 exit:
455     return error;
456 }
457 
458 /**
459  * @cli tcp send
460  * @code
461  * tcp send hello
462  * Done
463  * @endcode
464  * @cparam tcp send @ca{message}
465  * The `message` parameter contains the message you want to send to the
466  * remote TCP endpoint.
467  * @par
468  * Sends data over the TCP connection associated with the example TCP endpoint
469  * that is provided with the `tcp` CLI. @moreinfo{@tcp}.
470  */
Process(Arg aArgs[])471 template <> otError TcpExample::Process<Cmd("send")>(Arg aArgs[])
472 {
473     otError error;
474 
475     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
476     VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY);
477     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
478     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
479 
480     if (mUseCircularSendBuffer)
481     {
482 #if OPENTHREAD_CONFIG_TLS_ENABLE
483         if (mUseTls)
484         {
485             int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<unsigned char *>(aArgs[0].GetCString()),
486                                        aArgs[0].GetLength());
487 
488             if (rv < 0 && rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ)
489             {
490                 ExitNow(error = OT_ERROR_FAILED);
491             }
492 
493             error = OT_ERROR_NONE;
494         }
495         else
496 #endif
497         {
498             size_t written;
499 
500             SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, aArgs[0].GetCString(),
501                                                                aArgs[0].GetLength(), &written, 0));
502         }
503     }
504     else
505     {
506         VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY);
507 
508         mSendLink.mNext   = nullptr;
509         mSendLink.mData   = mSendBufferBytes;
510         mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBufferBytes));
511         memcpy(mSendBufferBytes, aArgs[0].GetCString(), mSendLink.mLength);
512 
513         SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0));
514         mSendBusy = true;
515     }
516 
517 exit:
518     return error;
519 }
520 
Process(Arg aArgs[])521 template <> otError TcpExample::Process<Cmd("benchmark")>(Arg aArgs[])
522 {
523     otError error = OT_ERROR_NONE;
524 
525     /**
526      * @cli tcp benchmark result
527      * @code
528      * tcp benchmark result
529      * TCP Benchmark Status: Ongoing
530      * Done
531      * @endcode
532      * @code
533      * tcp benchmark result
534      * TCP Benchmark Status: Completed
535      * TCP Benchmark Complete: Transferred 73728 bytes in 7056 milliseconds
536      * TCP Goodput: 83.592 kb/s
537      * @endcode
538      * @par
539      * Shows the latest result of the TCP benchmark test. Possible status values:
540      * * Ongoing
541      * * Completed
542      * * Untested
543      * @par
544      * This command is primarily intended for creating scripts that automate
545      * the TCP benchmark test.
546      */
547     if (aArgs[0] == "result")
548     {
549         OutputFormat("TCP Benchmark Status: ");
550 
551         if (mBenchmarkBytesTotal != 0)
552         {
553             OutputLine("Ongoing");
554         }
555         else if (mBenchmarkTimeUsed != 0)
556         {
557             OutputLine("Completed");
558             OutputBenchmarkResult();
559         }
560         else
561         {
562             OutputLine("Untested");
563         }
564     }
565     /**
566      * @cli tcp benchmark run
567      * @code
568      * tcp benchmark run
569      * Done
570      * TCP Benchmark Complete: Transferred 73728 bytes in 7233 milliseconds
571      * TCP Goodput: 81.546 kb/s
572      * @endcode
573      * @cparam tcp benchmark run [@ca{size}]
574      * Use the `size` parameter to specify the number of bytes to send
575      * for the benchmark. If you do not use the `size` parameter, the default
576      * value (`OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE`) is used.
577      * @par
578      * Transfers the specified number of bytes using the TCP connection
579      * currently associated with the example TCP endpoint provided by the `tcp` CLI.
580      * @note You must establish a TCP connection before you run this command.
581      */
582     else if (aArgs[0] == "run")
583     {
584         VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY);
585         VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY);
586 
587         if (aArgs[1].IsEmpty())
588         {
589             mBenchmarkBytesTotal = OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE;
590         }
591         else
592         {
593             SuccessOrExit(error = aArgs[1].ParseAsUint32(mBenchmarkBytesTotal));
594             VerifyOrExit(mBenchmarkBytesTotal != 0, error = OT_ERROR_INVALID_ARGS);
595         }
596 
597         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
598 
599         mBenchmarkStart       = TimerMilli::GetNow();
600         mBenchmarkBytesUnsent = mBenchmarkBytesTotal;
601 
602         if (mUseCircularSendBuffer)
603         {
604             SuccessOrExit(error = ContinueBenchmarkCircularSend());
605         }
606         else
607         {
608             uint32_t benchmarkLinksLeft =
609                 (mBenchmarkBytesTotal + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes);
610             uint32_t toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), benchmarkLinksLeft);
611 
612             /* We could also point the linked buffers directly to sBenchmarkData. */
613             memset(mSendBufferBytes, 'a', sizeof(mSendBufferBytes));
614 
615             for (uint32_t i = 0; i != toSendOut; i++)
616             {
617                 mBenchmarkLinks[i].mNext   = nullptr;
618                 mBenchmarkLinks[i].mData   = mSendBufferBytes;
619                 mBenchmarkLinks[i].mLength = sizeof(mSendBufferBytes);
620 
621                 if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBufferBytes) != 0)
622                 {
623                     mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBufferBytes);
624                 }
625 
626                 error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i],
627                                              i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME);
628                 VerifyOrExit(error == OT_ERROR_NONE, mBenchmarkBytesTotal = 0);
629             }
630         }
631     }
632     else
633     {
634         error = OT_ERROR_INVALID_ARGS;
635     }
636 
637 exit:
638     return error;
639 }
640 
641 /**
642  * @cli tcp sendend
643  * @code
644  * tcp sendend
645  * Done
646  * @endcode
647  * @par
648  * Sends the "end of stream" signal over the TCP connection
649  * associated with the example TCP endpoint provided by the `tcp` CLI. This
650  * alerts the peer that it will not receive any more data over this TCP connection.
651  * @sa otTcpSendEndOfStream
652  */
Process(Arg aArgs[])653 template <> otError TcpExample::Process<Cmd("sendend")>(Arg aArgs[])
654 {
655     otError error;
656 
657     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
658     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
659 
660     error = otTcpSendEndOfStream(&mEndpoint);
661 
662 exit:
663     return error;
664 }
665 
666 /**
667  * @cli tcp abort
668  * @code
669  * tcp abort
670  * TCP: Connection reset
671  * Done
672  * @endcode
673  * @par
674  * Unceremoniously ends the TCP connection associated with the
675  * example TCP endpoint, transitioning the TCP endpoint to the closed state.
676  * @sa otTcpAbort
677  */
Process(Arg aArgs[])678 template <> otError TcpExample::Process<Cmd("abort")>(Arg aArgs[])
679 {
680     otError error;
681 
682     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
683     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
684 
685     SuccessOrExit(error = otTcpAbort(&mEndpoint));
686     mEndpointConnected         = false;
687     mEndpointConnectedFastOpen = false;
688 
689 exit:
690     return error;
691 }
692 
693 /**
694  * @cli tcp listen
695  * @code
696  * tcp listen :: 30000
697  * Done
698  * @endcode
699  * @cparam tcp listen @ca{ip} @ca{port}
700  * The following parameters are required:
701  * * `ip`: IPv6 address or the unspecified IPv6 address (`::`) of the example
702  *   TCP listener provided by the `tcp` CLI.
703  * * `port`: TCP port of the example TCP listener.
704  *   If no TCP connection is associated with the example TCP endpoint, then any
705  *   incoming connections matching the specified IPv6 address and port are accepted
706  *   and are associated with the example TCP endpoint.
707  * @par
708  * Uses the example TCP listener to listen for incoming connections on the
709  * specified IPv6 address and port. @moreinfo{@tcp}.
710  * @sa otTcpListen
711  */
Process(Arg aArgs[])712 template <> otError TcpExample::Process<Cmd("listen")>(Arg aArgs[])
713 {
714     otError    error;
715     otSockAddr sockaddr;
716 
717     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
718 
719     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
720     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
721     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
722 
723     SuccessOrExit(error = otTcpStopListening(&mListener));
724     error = otTcpListen(&mListener, &sockaddr);
725 
726 exit:
727     return error;
728 }
729 
730 /**
731  * @cli tcp stoplistening
732  * @code
733  * tcp stoplistening
734  * Done
735  * @endcode
736  * @par
737  * Instructs the example TCP listener to stop listening for incoming TCP connections.
738  * @sa otTcpStopListening
739  */
Process(Arg aArgs[])740 template <> otError TcpExample::Process<Cmd("stoplistening")>(Arg aArgs[])
741 {
742     otError error;
743 
744     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
745     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
746 
747     error = otTcpStopListening(&mListener);
748 
749 exit:
750     return error;
751 }
752 
Process(Arg aArgs[])753 otError TcpExample::Process(Arg aArgs[])
754 {
755 #define CmdEntry(aCommandString)                                  \
756     {                                                             \
757         aCommandString, &TcpExample::Process<Cmd(aCommandString)> \
758     }
759 
760     static constexpr Command kCommands[] = {
761         CmdEntry("abort"), CmdEntry("benchmark"), CmdEntry("bind"), CmdEntry("connect"), CmdEntry("deinit"),
762         CmdEntry("init"),  CmdEntry("listen"),    CmdEntry("send"), CmdEntry("sendend"), CmdEntry("stoplistening"),
763     };
764 
765     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
766 
767     otError        error = OT_ERROR_INVALID_COMMAND;
768     const Command *command;
769 
770     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
771     {
772         OutputCommandTable(kCommands);
773         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
774     }
775 
776     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
777     VerifyOrExit(command != nullptr);
778 
779     error = (this->*command->mHandler)(aArgs + 1);
780 
781 exit:
782     return error;
783 }
784 
HandleTcpEstablishedCallback(otTcpEndpoint * aEndpoint)785 void TcpExample::HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint)
786 {
787     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpEstablished(aEndpoint);
788 }
789 
HandleTcpSendDoneCallback(otTcpEndpoint * aEndpoint,otLinkedBuffer * aData)790 void TcpExample::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
791 {
792     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData);
793 }
794 
HandleTcpForwardProgressCallback(otTcpEndpoint * aEndpoint,size_t aInSendBuffer,size_t aBacklog)795 void TcpExample::HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog)
796 {
797     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))
798         ->HandleTcpForwardProgress(aEndpoint, aInSendBuffer, aBacklog);
799 }
800 
HandleTcpReceiveAvailableCallback(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)801 void TcpExample::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint,
802                                                    size_t         aBytesAvailable,
803                                                    bool           aEndOfStream,
804                                                    size_t         aBytesRemaining)
805 {
806     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))
807         ->HandleTcpReceiveAvailable(aEndpoint, aBytesAvailable, aEndOfStream, aBytesRemaining);
808 }
809 
HandleTcpDisconnectedCallback(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)810 void TcpExample::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
811 {
812     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason);
813 }
814 
HandleTcpAcceptReadyCallback(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)815 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReadyCallback(otTcpListener    *aListener,
816                                                                        const otSockAddr *aPeer,
817                                                                        otTcpEndpoint   **aAcceptInto)
818 {
819     return static_cast<TcpExample *>(otTcpListenerGetContext(aListener))
820         ->HandleTcpAcceptReady(aListener, aPeer, aAcceptInto);
821 }
822 
HandleTcpAcceptDoneCallback(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)823 void TcpExample::HandleTcpAcceptDoneCallback(otTcpListener    *aListener,
824                                              otTcpEndpoint    *aEndpoint,
825                                              const otSockAddr *aPeer)
826 {
827     static_cast<TcpExample *>(otTcpListenerGetContext(aListener))->HandleTcpAcceptDone(aListener, aEndpoint, aPeer);
828 }
829 
HandleTcpEstablished(otTcpEndpoint * aEndpoint)830 void TcpExample::HandleTcpEstablished(otTcpEndpoint *aEndpoint)
831 {
832     OT_UNUSED_VARIABLE(aEndpoint);
833     OutputLine("TCP: Connection established");
834 #if OPENTHREAD_CONFIG_TLS_ENABLE
835     if (mUseTls && !mEndpointConnectedFastOpen)
836     {
837         PrepareTlsHandshake();
838         ContinueTlsHandshake();
839     }
840 #endif
841 }
842 
HandleTcpSendDone(otTcpEndpoint * aEndpoint,otLinkedBuffer * aData)843 void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
844 {
845     OT_UNUSED_VARIABLE(aEndpoint);
846     OT_ASSERT(!mUseCircularSendBuffer); // this callback is not used when using the circular send buffer
847 
848     if (mBenchmarkBytesTotal == 0)
849     {
850         // If the benchmark encountered an error, we might end up here. So,
851         // tolerate some benchmark links finishing in this case.
852         if (aData == &mSendLink)
853         {
854             OT_ASSERT(mSendBusy);
855             mSendBusy = false;
856         }
857     }
858     else
859     {
860         OT_ASSERT(aData != &mSendLink);
861         OT_ASSERT(mBenchmarkBytesUnsent >= aData->mLength);
862 
863         mBenchmarkBytesUnsent -= aData->mLength; // could be less than sizeof(mSendBufferBytes) for the first link
864 
865         if (mBenchmarkBytesUnsent >= OT_ARRAY_LENGTH(mBenchmarkLinks) * sizeof(mSendBufferBytes))
866         {
867             aData->mLength = sizeof(mSendBufferBytes);
868 
869             if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE)
870             {
871                 OutputLine("TCP Benchmark Failed");
872                 mBenchmarkBytesTotal = 0;
873             }
874         }
875         else if (mBenchmarkBytesUnsent == 0)
876         {
877             CompleteBenchmark();
878         }
879     }
880 }
881 
HandleTcpForwardProgress(otTcpEndpoint * aEndpoint,size_t aInSendBuffer,size_t aBacklog)882 void TcpExample::HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog)
883 {
884     OT_UNUSED_VARIABLE(aEndpoint);
885     OT_UNUSED_VARIABLE(aBacklog);
886     OT_ASSERT(mUseCircularSendBuffer); // this callback is only used when using the circular send buffer
887 
888     otTcpCircularSendBufferHandleForwardProgress(&mSendBuffer, aInSendBuffer);
889 
890 #if OPENTHREAD_CONFIG_TLS_ENABLE
891     if (mUseTls)
892     {
893         ContinueTlsHandshake();
894     }
895 #endif
896 
897     /* Handle case where we're in a benchmark. */
898     if (mBenchmarkBytesTotal != 0)
899     {
900         if (mBenchmarkBytesUnsent != 0)
901         {
902             /* Continue sending out data if there's data we haven't sent. */
903             IgnoreError(ContinueBenchmarkCircularSend());
904         }
905         else if (aInSendBuffer == 0)
906         {
907             /* Handle case where all data is sent out and the send buffer has drained. */
908             CompleteBenchmark();
909         }
910     }
911 }
912 
HandleTcpReceiveAvailable(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)913 void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint,
914                                            size_t         aBytesAvailable,
915                                            bool           aEndOfStream,
916                                            size_t         aBytesRemaining)
917 {
918     OT_UNUSED_VARIABLE(aBytesRemaining);
919     OT_ASSERT(aEndpoint == &mEndpoint);
920 
921     /* If we get data before the handshake completes, then this is a TFO connection. */
922     if (!mEndpointConnected)
923     {
924         mEndpointConnected         = true;
925         mEndpointConnectedFastOpen = true;
926 
927 #if OPENTHREAD_CONFIG_TLS_ENABLE
928         if (mUseTls)
929         {
930             PrepareTlsHandshake();
931         }
932 #endif
933     }
934 
935 #if OPENTHREAD_CONFIG_TLS_ENABLE
936     if (mUseTls && ContinueTlsHandshake())
937     {
938         ExitNow();
939     }
940 #endif
941 
942     if ((mTlsHandshakeComplete || !mUseTls) && aBytesAvailable > 0)
943     {
944 #if OPENTHREAD_CONFIG_TLS_ENABLE
945         if (mUseTls)
946         {
947             uint8_t buffer[500];
948 
949             for (;;)
950             {
951                 int rv = mbedtls_ssl_read(&mSslContext, buffer, sizeof(buffer));
952 
953                 if (rv < 0)
954                 {
955                     if (rv == MBEDTLS_ERR_SSL_WANT_READ)
956                     {
957                         break;
958                     }
959 
960                     OutputLine("TLS receive failure: %d", rv);
961                 }
962                 else
963                 {
964                     OutputLine("TLS: Received %u bytes: %.*s", static_cast<unsigned>(rv), rv,
965                                reinterpret_cast<const char *>(buffer));
966                 }
967             }
968             OutputLine("(TCP: Received %u bytes)", static_cast<unsigned>(aBytesAvailable));
969         }
970         else
971 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
972         {
973             const otLinkedBuffer *data;
974             size_t                totalReceived = 0;
975 
976             IgnoreError(otTcpReceiveByReference(aEndpoint, &data));
977 
978             for (; data != nullptr; data = data->mNext)
979             {
980                 OutputLine("TCP: Received %u bytes: %.*s", static_cast<unsigned>(data->mLength),
981                            static_cast<unsigned>(data->mLength), reinterpret_cast<const char *>(data->mData));
982                 totalReceived += data->mLength;
983             }
984 
985             OT_ASSERT(aBytesAvailable == totalReceived);
986             IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0));
987         }
988     }
989 
990     if (aEndOfStream)
991     {
992         OutputLine("TCP: Reached end of stream");
993     }
994 
995     ExitNow();
996 
997 exit:
998     return;
999 }
1000 
HandleTcpDisconnected(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)1001 void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
1002 {
1003     static const char *const kReasonStrings[] = {
1004         "Disconnected",            // (0) OT_TCP_DISCONNECTED_REASON_NORMAL
1005         "Connection refused",      // (1) OT_TCP_DISCONNECTED_REASON_REFUSED
1006         "Connection reset",        // (2) OT_TCP_DISCONNECTED_REASON_RESET
1007         "Entered TIME-WAIT state", // (3) OT_TCP_DISCONNECTED_REASON_TIME_WAIT
1008         "Connection timed out",    // (4) OT_TCP_DISCONNECTED_REASON_TIMED_OUT
1009     };
1010 
1011     OT_UNUSED_VARIABLE(aEndpoint);
1012 
1013     static_assert(0 == OT_TCP_DISCONNECTED_REASON_NORMAL, "OT_TCP_DISCONNECTED_REASON_NORMAL value is incorrect");
1014     static_assert(1 == OT_TCP_DISCONNECTED_REASON_REFUSED, "OT_TCP_DISCONNECTED_REASON_REFUSED value is incorrect");
1015     static_assert(2 == OT_TCP_DISCONNECTED_REASON_RESET, "OT_TCP_DISCONNECTED_REASON_RESET value is incorrect");
1016     static_assert(3 == OT_TCP_DISCONNECTED_REASON_TIME_WAIT, "OT_TCP_DISCONNECTED_REASON_TIME_WAIT value is incorrect");
1017     static_assert(4 == OT_TCP_DISCONNECTED_REASON_TIMED_OUT, "OT_TCP_DISCONNECTED_REASON_TIMED_OUT value is incorrect");
1018 
1019     OutputLine("TCP: %s", Stringify(aReason, kReasonStrings));
1020 
1021 #if OPENTHREAD_CONFIG_TLS_ENABLE
1022     if (mUseTls)
1023     {
1024         mbedtls_ssl_session_reset(&mSslContext);
1025     }
1026 #endif
1027 
1028     // We set this to false even for the TIME-WAIT state, so that we can reuse
1029     // the active socket if an incoming connection comes in instead of waiting
1030     // for the 2MSL timeout.
1031     mEndpointConnected         = false;
1032     mEndpointConnectedFastOpen = false;
1033     mSendBusy                  = false;
1034 
1035     // Mark the benchmark as inactive if the connection was disconnected.
1036     mBenchmarkBytesTotal  = 0;
1037     mBenchmarkBytesUnsent = 0;
1038 
1039     otTcpCircularSendBufferForceDiscardAll(&mSendBuffer);
1040 }
1041 
HandleTcpAcceptReady(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)1042 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener    *aListener,
1043                                                                const otSockAddr *aPeer,
1044                                                                otTcpEndpoint   **aAcceptInto)
1045 {
1046     otTcpIncomingConnectionAction action;
1047 
1048     OT_UNUSED_VARIABLE(aListener);
1049 
1050     if (mEndpointConnected)
1051     {
1052         OutputFormat("TCP: Ignoring incoming connection request from ");
1053         OutputSockAddr(*aPeer);
1054         OutputLine(" (active socket is busy)");
1055 
1056         ExitNow(action = OT_TCP_INCOMING_CONNECTION_ACTION_DEFER);
1057     }
1058 
1059     *aAcceptInto = &mEndpoint;
1060     action       = OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT;
1061 
1062 #if OPENTHREAD_CONFIG_TLS_ENABLE
1063     /*
1064      * Natural to wait until the AcceptDone callback but with TFO we could get data before that
1065      * so it doesn't make sense to wait until then.
1066      */
1067     if (mUseTls)
1068     {
1069         int rv;
1070 
1071         rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
1072                                          MBEDTLS_SSL_PRESET_DEFAULT);
1073         if (rv != 0)
1074         {
1075             OutputLine("mbedtls_ssl_config_defaults returned %d", rv);
1076         }
1077 
1078         mbedtls_ssl_conf_ca_chain(&mSslConfig, mSrvCert.next, nullptr);
1079         rv = mbedtls_ssl_conf_own_cert(&mSslConfig, &mSrvCert, &mPKey);
1080 
1081         if (rv != 0)
1082         {
1083             OutputLine("mbedtls_ssl_conf_own_cert returned %d", rv);
1084         }
1085     }
1086 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
1087 
1088 exit:
1089     return action;
1090 }
1091 
HandleTcpAcceptDone(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)1092 void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer)
1093 {
1094     OT_UNUSED_VARIABLE(aListener);
1095     OT_UNUSED_VARIABLE(aEndpoint);
1096 
1097     mEndpointConnected = true;
1098     OutputFormat("Accepted connection from ");
1099     OutputSockAddrLine(*aPeer);
1100 }
1101 
ContinueBenchmarkCircularSend(void)1102 otError TcpExample::ContinueBenchmarkCircularSend(void)
1103 {
1104     otError error = OT_ERROR_NONE;
1105     size_t  freeSpace;
1106 
1107     while (mBenchmarkBytesUnsent != 0 && (freeSpace = otTcpCircularSendBufferGetFreeSpace(&mSendBuffer)) != 0)
1108     {
1109         size_t   toSendThisIteration = OT_MIN(mBenchmarkBytesUnsent, sBenchmarkDataLength);
1110         uint32_t flag                = (toSendThisIteration < freeSpace && toSendThisIteration < mBenchmarkBytesUnsent)
1111                                            ? OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME
1112                                            : 0;
1113         size_t   written             = 0;
1114 
1115 #if OPENTHREAD_CONFIG_TLS_ENABLE
1116         if (mUseTls)
1117         {
1118             int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<const unsigned char *>(sBenchmarkData),
1119                                        toSendThisIteration);
1120 
1121             if (rv > 0)
1122             {
1123                 written = static_cast<size_t>(rv);
1124                 OT_ASSERT(written <= mBenchmarkBytesUnsent);
1125             }
1126             else if (rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ)
1127             {
1128                 ExitNow(error = OT_ERROR_FAILED);
1129             }
1130 
1131             error = OT_ERROR_NONE;
1132         }
1133         else
1134 #endif
1135         {
1136             SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, sBenchmarkData,
1137                                                                toSendThisIteration, &written, flag));
1138         }
1139         mBenchmarkBytesUnsent -= written;
1140     }
1141 
1142 exit:
1143     if (error != OT_ERROR_NONE)
1144     {
1145         OutputLine("TCP Benchmark Failed");
1146         mBenchmarkBytesTotal  = 0;
1147         mBenchmarkBytesUnsent = 0;
1148     }
1149 
1150     return error;
1151 }
1152 
OutputBenchmarkResult(void)1153 void TcpExample::OutputBenchmarkResult(void)
1154 {
1155     uint32_t thousandTimesGoodput =
1156         (1000 * (mBenchmarkLastBytesTotal << 3) + (mBenchmarkTimeUsed >> 1)) / mBenchmarkTimeUsed;
1157 
1158     OutputLine("TCP Benchmark Complete: Transferred %lu bytes in %lu milliseconds", ToUlong(mBenchmarkLastBytesTotal),
1159                ToUlong(mBenchmarkTimeUsed));
1160     OutputLine("TCP Goodput: %lu.%03u kb/s", ToUlong(thousandTimesGoodput / 1000),
1161                static_cast<uint16_t>(thousandTimesGoodput % 1000));
1162 }
1163 
CompleteBenchmark(void)1164 void TcpExample::CompleteBenchmark(void)
1165 {
1166     mBenchmarkTimeUsed       = TimerMilli::GetNow() - mBenchmarkStart;
1167     mBenchmarkLastBytesTotal = mBenchmarkBytesTotal;
1168 
1169     OutputBenchmarkResult();
1170 
1171     mBenchmarkBytesTotal = 0;
1172 }
1173 
1174 #if OPENTHREAD_CONFIG_TLS_ENABLE
PrepareTlsHandshake(void)1175 void TcpExample::PrepareTlsHandshake(void)
1176 {
1177     int rv;
1178 
1179     rv = mbedtls_ssl_set_hostname(&mSslContext, "localhost");
1180 
1181     if (rv != 0)
1182     {
1183         OutputLine("mbedtls_ssl_set_hostname returned %d", rv);
1184     }
1185 
1186     rv = mbedtls_ssl_set_hs_ecjpake_password(&mSslContext, reinterpret_cast<const unsigned char *>(sEcjpakePassword),
1187                                              sEcjpakePasswordLength);
1188     if (rv != 0)
1189     {
1190         OutputLine("mbedtls_ssl_set_hs_ecjpake_password returned %d", rv);
1191     }
1192 
1193     mbedtls_ssl_set_bio(&mSslContext, &mEndpointAndCircularSendBuffer, otTcpMbedTlsSslSendCallback,
1194                         otTcpMbedTlsSslRecvCallback, nullptr);
1195     mTlsHandshakeComplete = false;
1196 }
1197 
ContinueTlsHandshake(void)1198 bool TcpExample::ContinueTlsHandshake(void)
1199 {
1200     bool wasNotAlreadyDone = false;
1201     int  rv;
1202 
1203     if (!mTlsHandshakeComplete)
1204     {
1205         rv = mbedtls_ssl_handshake(&mSslContext);
1206 
1207         if (rv == 0)
1208         {
1209             OutputLine("TLS Handshake Complete");
1210             mTlsHandshakeComplete = true;
1211         }
1212         else if (rv != MBEDTLS_ERR_SSL_WANT_READ && rv != MBEDTLS_ERR_SSL_WANT_WRITE)
1213         {
1214             OutputLine("TLS Handshake Failed: %d", rv);
1215         }
1216 
1217         wasNotAlreadyDone = true;
1218     }
1219 
1220     return wasNotAlreadyDone;
1221 }
1222 #endif // OPENTHREAD_CONFIG_TLS_ENABLE
1223 
1224 } // namespace Cli
1225 } // namespace ot
1226 
1227 #endif // OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
1228