Coverage Report

Created: 2025-05-12 06:47

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