• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 2590

29 Aug 2024 08:02AM UTC coverage: 91.13% (-0.01%) from 91.143%
2590

push

Evergreen

web-flow
RCORE-2242: Include pathname in exception thrown in File::rw_lock (#8000)

* Include pathname in exception thrown in File::rw_lock
* Strengthen check on synced file name size

102810 of 181608 branches covered (56.61%)

14 of 15 new or added lines in 2 files covered. (93.33%)

88 existing lines in 9 files now uncovered.

217393 of 238553 relevant lines covered (91.13%)

6073251.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.59
/test/test_util_network_ssl.cpp
1
#include <thread>
2

3
#include <realm/sync/network/network_ssl.hpp>
4
#include <realm/util/future.hpp>
5

6
#include "test.hpp"
7
#include "util/semaphore.hpp"
8

9
#ifdef _WIN32
10
#include <wincrypt.h>
11
#endif
12

13
using namespace realm;
14
using namespace realm::sync;
15
using namespace realm::test_util;
16
using namespace realm::util;
17

18
// Test independence and thread-safety
19
// -----------------------------------
20
//
21
// All tests must be thread safe and independent of each other. This
22
// is required because it allows for both shuffling of the execution
23
// order and for parallelized testing.
24
//
25
// In particular, avoid using std::rand() since it is not guaranteed
26
// to be thread safe. Instead use the API offered in
27
// `test/util/random.hpp`.
28
//
29
// All files created in tests must use the TEST_PATH macro (or one of
30
// its friends) to obtain a suitable file system path. See
31
// `test/util/test_path.hpp`.
32
//
33
//
34
// Debugging and the ONLY() macro
35
// ------------------------------
36
//
37
// A simple way of disabling all tests except one called `Foo`, is to
38
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
39
// test suite. Note that you can also use filtering by setting the
40
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
41
// this.
42
//
43
// Another way to debug a particular test, is to copy that test into
44
// `experiments/testcase.cpp` and then run `sh build.sh
45
// check-testcase` (or one of its friends) from the command line.
46

47
#if !REALM_MOBILE
48
namespace {
49

50
network::Endpoint bind_acceptor(network::Acceptor& acceptor)
51
{
41✔
52
    network::Endpoint ep; // Wildcard
41✔
53
    acceptor.open(ep.protocol());
41✔
54
    acceptor.bind(ep);
41✔
55
    ep = acceptor.local_endpoint(); // Get actual bound endpoint
41✔
56
    acceptor.listen();
41✔
57
    return ep;
41✔
58
}
41✔
59

60
void connect_sockets(network::Socket& server_socket, network::Socket& client_socket)
61
{
41✔
62
    network::Service& server_service = server_socket.get_service();
41✔
63
    network::Service& client_service = client_socket.get_service();
41✔
64
    network::Acceptor acceptor(server_service);
41✔
65
    network::Endpoint ep = bind_acceptor(acceptor);
41✔
66
    bool accept_occurred = false, connect_occurred = false;
41✔
67
    auto accept_handler = [&](std::error_code ec) {
41✔
68
        REALM_ASSERT(!ec);
41✔
69
        accept_occurred = true;
41✔
70
    };
41✔
71
    auto connect_handler = [&](std::error_code ec) {
41✔
72
        REALM_ASSERT(!ec);
41✔
73
        connect_occurred = true;
41✔
74
    };
41✔
75
    server_service.post([&](Status status) {
41✔
76
        if (!status.is_ok())
41✔
77
            return;
×
78
        acceptor.async_accept(server_socket, std::move(accept_handler));
41✔
79
    });
41✔
80
    client_service.post([&](Status status) {
41✔
81
        if (!status.is_ok())
41✔
82
            return;
×
83
        client_socket.async_connect(ep, std::move(connect_handler));
41✔
84
    });
41✔
85
    if (&server_service == &client_service) {
41✔
86
        server_service.run();
20✔
87
    }
20✔
88
    else {
21✔
89
        std::thread thread{[&] {
21✔
90
            server_service.run();
21✔
91
        }};
21✔
92
        client_service.run();
21✔
93
        thread.join();
21✔
94
    }
21✔
95
    REALM_ASSERT(accept_occurred);
41✔
96
    REALM_ASSERT(connect_occurred);
41✔
97
}
41✔
98

99
void configure_server_ssl_context_for_test(network::ssl::Context& ssl_context)
100
{
32✔
101
    ssl_context.use_certificate_chain_file(get_test_resource_path() + "test_util_network_ssl_ca.pem");
32✔
102
    ssl_context.use_private_key_file(get_test_resource_path() + "test_util_network_ssl_key.pem");
32✔
103
}
32✔
104

105
void connect_ssl_streams(network::ssl::Stream& server_stream, network::ssl::Stream& client_stream)
106
{
24✔
107
    network::Socket& server_socket = server_stream.lowest_layer();
24✔
108
    network::Socket& client_socket = client_stream.lowest_layer();
24✔
109
    connect_sockets(server_socket, client_socket);
24✔
110
    network::Service& server_service = server_socket.get_service();
24✔
111
    network::Service& client_service = client_socket.get_service();
24✔
112
    bool server_handshake_occurred = false, client_handshake_occurred = false;
24✔
113
    auto server_handshake_handler = [&](std::error_code ec) {
24✔
114
        REALM_ASSERT(!ec);
24✔
115
        server_handshake_occurred = true;
24✔
116
    };
24✔
117
    auto client_handshake_handler = [&](std::error_code ec) {
24✔
118
        REALM_ASSERT(!ec);
24✔
119
        client_handshake_occurred = true;
24✔
120
    };
24✔
121
    server_service.post([&](Status status) {
24✔
122
        if (!status.is_ok())
24✔
123
            return;
×
124
        server_stream.async_handshake(std::move(server_handshake_handler));
24✔
125
    });
24✔
126
    client_service.post([&](Status status) {
24✔
127
        if (!status.is_ok())
24✔
128
            return;
×
129
        client_stream.async_handshake(std::move(client_handshake_handler));
24✔
130
    });
24✔
131
    if (&server_service == &client_service) {
24✔
132
        server_service.run();
16✔
133
    }
16✔
134
    else {
8✔
135
        std::thread thread{[&] {
8✔
136
            server_service.run();
8✔
137
        }};
8✔
138
        client_service.run();
8✔
139
        thread.join();
8✔
140
    }
8✔
141
    REALM_ASSERT(server_handshake_occurred);
24✔
142
    REALM_ASSERT(client_handshake_occurred);
24✔
143
}
24✔
144

145

146
class PingPongDelayFixture {
147
public:
148
    PingPongDelayFixture(network::Service& service)
149
        : PingPongDelayFixture{service, service}
150
    {
×
151
    }
×
152

153
    PingPongDelayFixture(network::Service& server_service, network::Service& client_service)
154
        : m_server_socket{server_service}
155
        , m_client_socket{client_service}
156
    {
×
157
        connect_sockets(m_server_socket, m_client_socket);
×
158
    }
×
159

160
    // Must be called by thread associated with `server_service`
161
    void start_server()
162
    {
×
163
        initiate_server_read();
×
164
    }
×
165

166
    // Must be called by thread associated with `server_service`
167
    void stop_server()
168
    {
×
169
        m_server_socket.cancel();
×
170
    }
×
171

172
    // Must be called by thread associated with `client_service`
173
    void delay_client(UniqueFunction<void()> handler, int n = 512)
174
    {
×
175
        m_handler = std::move(handler);
×
176
        m_num = n;
×
177
        initiate_client_write();
×
178
    }
×
179

180
private:
181
    network::Socket m_server_socket, m_client_socket;
182
    char m_server_char = 0, m_client_char = 0;
183
    int m_num;
184
    UniqueFunction<void()> m_handler;
185

186
    void initiate_server_read()
187
    {
×
188
        auto handler = [this](std::error_code ec, size_t) {
×
189
            if (ec != error::operation_aborted)
×
190
                handle_server_read(ec);
×
191
        };
×
192
        m_server_socket.async_read(&m_server_char, 1, std::move(handler));
×
193
    }
×
194

195
    void handle_server_read(std::error_code ec)
196
    {
×
197
        if (ec)
×
198
            throw std::system_error(ec);
×
199
        initiate_server_write();
×
200
    }
×
201

202
    void initiate_server_write()
203
    {
×
204
        auto handler = [this](std::error_code ec, size_t) {
×
205
            if (ec != error::operation_aborted)
×
206
                handle_server_write(ec);
×
207
        };
×
208
        m_server_socket.async_write(&m_server_char, 1, std::move(handler));
×
209
    }
×
210

211
    void handle_server_write(std::error_code ec)
212
    {
×
213
        if (ec)
×
214
            throw std::system_error(ec);
×
215
        initiate_server_read();
×
216
    }
×
217

218
    void initiate_client_write()
219
    {
×
220
        if (m_num <= 0) {
×
221
            UniqueFunction<void()> handler = std::move(m_handler);
×
222
            m_handler = nullptr;
×
223
            handler();
×
224
            return;
×
225
        }
×
226
        --m_num;
×
227

×
228
        auto handler = [this](std::error_code ec, size_t) {
×
229
            if (ec != error::operation_aborted)
×
230
                handle_client_write(ec);
×
231
        };
×
232
        m_client_socket.async_write(&m_client_char, 1, std::move(handler));
×
233
    }
×
234

235
    void handle_client_write(std::error_code ec)
236
    {
×
237
        if (ec)
×
238
            throw std::system_error(ec);
×
239
        initiate_client_read();
×
240
    }
×
241

242
    void initiate_client_read()
243
    {
×
244
        auto handler = [this](std::error_code ec, size_t) {
×
245
            if (ec != error::operation_aborted)
×
246
                handle_client_read(ec);
×
247
        };
×
248
        m_client_socket.async_read(&m_client_char, 1, std::move(handler));
×
249
    }
×
250

251
    void handle_client_read(std::error_code ec)
252
    {
×
253
        if (ec)
×
254
            throw std::system_error(ec);
×
255
        initiate_client_write();
×
256
    }
×
257
};
258

259
} // unnamed namespace
260

261

262
TEST(Util_Network_SSL_Handshake)
263
{
2✔
264
    network::Service service_1, service_2;
2✔
265
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
266
    network::ssl::Context ssl_context_1;
2✔
267
    network::ssl::Context ssl_context_2;
2✔
268
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
269
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
270
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
271
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
272
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
273
    connect_sockets(socket_1, socket_2);
2✔
274

275
    auto connector = [&] {
2✔
276
        std::error_code ec;
2✔
277
        ssl_stream_2.handshake(ec);
2✔
278
        CHECK_EQUAL(std::error_code(), ec);
2✔
279
    };
2✔
280
    auto acceptor = [&] {
2✔
281
        std::error_code ec;
2✔
282
        ssl_stream_1.handshake(ec);
2✔
283
        CHECK_EQUAL(std::error_code(), ec);
2✔
284
    };
2✔
285

286
    std::thread thread_1(std::move(connector));
2✔
287
    std::thread thread_2(std::move(acceptor));
2✔
288
    thread_1.join();
2✔
289
    thread_2.join();
2✔
290
}
2✔
291

292

293
TEST(Util_Network_SSL_AsyncHandshake)
294
{
2✔
295
    network::Service service;
2✔
296
    network::Socket socket_1{service}, socket_2{service};
2✔
297
    network::ssl::Context ssl_context_1;
2✔
298
    network::ssl::Context ssl_context_2;
2✔
299
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
300
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
301
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
302
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
303
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
304
    connect_sockets(socket_1, socket_2);
2✔
305

306
    bool connect_completed = false;
2✔
307
    auto connect_handler = [&](std::error_code ec) {
2✔
308
        CHECK_EQUAL(std::error_code(), ec);
2✔
309
        connect_completed = true;
2✔
310
    };
2✔
311
    bool accept_completed = false;
2✔
312
    auto accept_handler = [&](std::error_code ec) {
2✔
313
        CHECK_EQUAL(std::error_code(), ec);
2✔
314
        accept_completed = true;
2✔
315
    };
2✔
316

317
    ssl_stream_1.async_handshake(std::move(accept_handler));
2✔
318
    ssl_stream_2.async_handshake(std::move(connect_handler));
2✔
319
    service.run();
2✔
320
    CHECK(connect_completed);
2✔
321
    CHECK(accept_completed);
2✔
322
}
2✔
323

324

325
TEST(Util_Network_SSL_ReadWriteShutdown)
326
{
2✔
327
    network::Service service_1, service_2;
2✔
328
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
329
    network::ssl::Context ssl_context_1;
2✔
330
    network::ssl::Context ssl_context_2;
2✔
331
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
332
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
333
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
334
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
335
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
336
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
337

338
    const char* message = "hello";
2✔
339
    char buffer[256];
2✔
340

341
    auto writer = [&] {
2✔
342
        std::size_t n = ssl_stream_1.write(message, std::strlen(message));
2✔
343
        CHECK_EQUAL(std::strlen(message), n);
2✔
344
        ssl_stream_1.shutdown();
2✔
345
    };
2✔
346
    auto reader = [&] {
2✔
347
        std::error_code ec;
2✔
348
        std::size_t n = ssl_stream_2.read(buffer, sizeof buffer, ec);
2✔
349
        if (CHECK_EQUAL(MiscExtErrors::end_of_input, ec)) {
2✔
350
            if (CHECK_EQUAL(std::strlen(message), n))
2✔
351
                CHECK(std::equal(buffer, buffer + n, message));
2✔
352
        }
2✔
353
    };
2✔
354

355
    std::thread thread_1(std::move(writer));
2✔
356
    std::thread thread_2(std::move(reader));
2✔
357
    thread_1.join();
2✔
358
    thread_2.join();
2✔
359
}
2✔
360

361

362
TEST(Util_Network_SSL_AsyncReadWriteShutdown)
363
{
2✔
364
    network::Service service;
2✔
365
    network::Socket socket_1{service}, socket_2{service};
2✔
366
    network::ssl::Context ssl_context_1;
2✔
367
    network::ssl::Context ssl_context_2;
2✔
368
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
369
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
370
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
371
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
372
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
373
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
374

375
    const char* message = "hello";
2✔
376
    char buffer[256];
2✔
377

378
    bool shutdown_completed = false;
2✔
379
    auto shutdown_handler = [&](std::error_code ec) {
2✔
380
        CHECK_EQUAL(std::error_code(), ec);
2✔
381
        shutdown_completed = true;
2✔
382
    };
2✔
383
    auto write_handler = [&](std::error_code ec, std::size_t n) {
2✔
384
        CHECK_EQUAL(std::error_code(), ec);
2✔
385
        CHECK_EQUAL(std::strlen(message), n);
2✔
386
        ssl_stream_1.async_shutdown(std::move(shutdown_handler));
2✔
387
    };
2✔
388
    bool read_completed = false;
2✔
389
    auto read_handler = [&](std::error_code ec, std::size_t n) {
2✔
390
        CHECK_EQUAL(MiscExtErrors::end_of_input, ec);
2✔
391
        if (CHECK_EQUAL(std::strlen(message), n))
2✔
392
            CHECK(std::equal(buffer, buffer + n, message));
2✔
393
        read_completed = true;
2✔
394
    };
2✔
395

396
    ssl_stream_1.async_write(message, std::strlen(message), std::move(write_handler));
2✔
397
    ssl_stream_2.async_read(buffer, sizeof buffer, std::move(read_handler));
2✔
398
    service.run();
2✔
399
    CHECK(shutdown_completed);
2✔
400
    CHECK(read_completed);
2✔
401
}
2✔
402

403

404
TEST(Util_Network_SSL_PrematureEndOfInputOnHandshakeRead)
405
{
2✔
406
    network::Service service_1, service_2;
2✔
407
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
408
    network::ssl::Context ssl_context_1;
2✔
409
    network::ssl::Context ssl_context_2;
2✔
410
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
411
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
412
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
413
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
414
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
415
    connect_sockets(socket_1, socket_2);
2✔
416

417
    socket_1.shutdown(network::Socket::shutdown_send);
2✔
418

419
    // Use a separate thread to consume data written by Stream::handshake(),
420
    // such that we can be sure not to block.
421
    auto consumer = [&] {
2✔
422
        constexpr std::size_t size = 4096;
2✔
423
        std::unique_ptr<char[]> buffer(new char[size]);
2✔
424
        std::error_code ec;
2✔
425
        do {
4✔
426
            socket_1.read_some(buffer.get(), size, ec);
4✔
427
        } while (!ec);
4✔
428
        REALM_ASSERT(ec == MiscExtErrors::end_of_input);
2✔
429
    };
2✔
430

431
    std::thread thread(std::move(consumer));
2✔
432

433
#if REALM_HAVE_OPENSSL
1✔
434
    CHECK_SYSTEM_ERROR(ssl_stream_2.handshake(), MiscExtErrors::premature_end_of_input);
1✔
435
#elif REALM_HAVE_SECURE_TRANSPORT
436
    // We replace the CHECK_SYSTEM_ERROR check for "premature end of input"
437
    // with a check for any error code, Mac OS occasionally reports another
438
    // system error. We can revisit the details of the error code later. The
439
    // detailed check is disabled for now to reduce the number of failed unit
440
    // test runs.
441
    CHECK_THROW(ssl_stream_2.handshake(), std::system_error);
1✔
442
#endif
1✔
443

444
    socket_2.close();
2✔
445
    thread.join();
2✔
446
}
2✔
447

448

449
#ifndef _WIN32 // FIXME: winsock doesn't have EPIPE, what's the equivalent?
450
TEST(Util_Network_SSL_BrokenPipeOnHandshakeWrite)
451
{
2✔
452
    network::Service service;
2✔
453
    network::Socket socket_1{service}, socket_2{service};
2✔
454
    network::ssl::Context ssl_context_1;
2✔
455
    network::ssl::Context ssl_context_2;
2✔
456
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
457
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
458
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
459
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
460
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
461
    connect_sockets(socket_1, socket_2);
2✔
462

463
    socket_1.close();
2✔
464

465
    // Fill the kernel level write buffer, to provoke error::broken_pipe.
466
    constexpr std::size_t size = 4096;
2✔
467
    std::unique_ptr<char[]> buffer(new char[size]);
2✔
468
    std::fill(buffer.get(), buffer.get() + size, 0);
2✔
469
    std::error_code ec;
2✔
470
    do {
16✔
471
        socket_2.write_some(buffer.get(), size, ec);
16✔
472
    } while (!ec);
16✔
473
#if REALM_PLATFORM_APPLE
1✔
474
    // Which error we get from writing to a closed socket seems to depend on
475
    // some asynchronous kernel state. If it notices that the socket is closed
476
    // before sending any data we get EPIPE, and if it's after trying to send
477
    // data it's ECONNRESET. EHOSTDOWN is not documented as an error code from
478
    // send() and may be worth investigating once the macOS 12 XNU source is
479
    // released.
480
    REALM_ASSERT(ec == error::broken_pipe || ec == error::connection_reset || ec.value() == EHOSTDOWN);
1!
481
#else
482
    REALM_ASSERT(ec == error::broken_pipe);
1✔
483
#endif
1✔
484

485
    CHECK_SYSTEM_ERROR(ssl_stream_2.handshake(), error::broken_pipe);
2✔
486
}
2✔
487
#endif
488

489

490
TEST(Util_Network_SSL_EndOfInputOnRead)
491
{
2✔
492
    network::Service service_1, service_2;
2✔
493
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
494
    network::ssl::Context ssl_context_1;
2✔
495
    network::ssl::Context ssl_context_2;
2✔
496
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
497
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
498
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
499
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
500
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
501
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
502
    ssl_stream_2.shutdown();
2✔
503
    socket_2.shutdown(network::Socket::shutdown_send);
2✔
504
    char ch;
2✔
505
    CHECK_SYSTEM_ERROR(ssl_stream_1.read_some(&ch, 1), MiscExtErrors::end_of_input);
2✔
506
}
2✔
507

508

509
TEST(Util_Network_SSL_PrematureEndOfInputOnRead)
510
{
2✔
511
    network::Service service_1, service_2;
2✔
512
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
513
    network::ssl::Context ssl_context_1;
2✔
514
    network::ssl::Context ssl_context_2;
2✔
515
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
516
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
517
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
518
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
519
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
520
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
521

522
    socket_2.shutdown(network::Socket::shutdown_send);
2✔
523
    char ch;
2✔
524
    CHECK_SYSTEM_ERROR(ssl_stream_1.read_some(&ch, 1), MiscExtErrors::premature_end_of_input);
2✔
525
}
2✔
526

527

528
#ifndef _WIN32 // FIXME: winsock doesn't have EPIPE, what's the equivalent?
529
TEST(Util_Network_SSL_BrokenPipeOnWrite)
530
{
2✔
531
    network::Service service;
2✔
532
    network::Socket socket_1{service}, socket_2{service};
2✔
533
    network::ssl::Context ssl_context_1;
2✔
534
    network::ssl::Context ssl_context_2;
2✔
535
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
536
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
537
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
538
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
539
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
540
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
541

542
    socket_1.close();
2✔
543

544
    // Fill the kernel level write buffer, to provoke error::broken_pipe.
545
    constexpr std::size_t size = 4096;
2✔
546
    std::unique_ptr<char[]> buffer(new char[size]);
2✔
547
    std::fill(buffer.get(), buffer.get() + size, 0);
2✔
548
    std::error_code ec;
2✔
549
    do {
11✔
550
        socket_2.write_some(buffer.get(), size, ec);
11✔
551
    } while (!ec);
11✔
552
#if REALM_PLATFORM_APPLE
1✔
553
    REALM_ASSERT(ec == error::broken_pipe || ec == error::connection_reset || ec.value() == EHOSTDOWN);
1!
554
#else
555
    REALM_ASSERT(ec == error::broken_pipe);
1✔
556
#endif
1✔
557

558
    char ch = 0;
2✔
559
    CHECK_SYSTEM_ERROR(ssl_stream_2.write(&ch, 1), error::broken_pipe);
2✔
560
}
2✔
561

562

563
TEST(Util_Network_SSL_BrokenPipeOnShutdown)
564
{
2✔
565
    network::Service service;
2✔
566
    network::Socket socket_1{service}, socket_2{service};
2✔
567
    network::ssl::Context ssl_context_1;
2✔
568
    network::ssl::Context ssl_context_2;
2✔
569
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
570
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
571
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
572
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
573
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
574
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
575

576
    socket_1.close();
2✔
577

578
    // Fill the kernel level write buffer, to provoke error::broken_pipe.
579
    constexpr std::size_t size = 4096;
2✔
580
    std::unique_ptr<char[]> buffer(new char[size]);
2✔
581
    std::fill(buffer.get(), buffer.get() + size, 0);
2✔
582
    std::error_code ec;
2✔
583
    do {
10✔
584
        socket_2.write_some(buffer.get(), size, ec);
10✔
585
    } while (!ec);
10✔
586
#if REALM_PLATFORM_APPLE
1✔
587
    REALM_ASSERT(ec == error::broken_pipe || ec == error::connection_reset || ec.value() == EHOSTDOWN);
1!
588
#else
589
    REALM_ASSERT(ec == error::broken_pipe);
1✔
590
#endif
1✔
591

592
    CHECK_SYSTEM_ERROR(ssl_stream_2.shutdown(), error::broken_pipe);
2✔
593
}
2✔
594
#endif
595

596

597
TEST(Util_Network_SSL_ShutdownBeforeCloseNotifyReceived)
598
{
2✔
599
    network::Service service;
2✔
600
    network::Socket socket_1{service}, socket_2{service};
2✔
601
    network::ssl::Context ssl_context_1;
2✔
602
    network::ssl::Context ssl_context_2;
2✔
603
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
604
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
605
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
606
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
607
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
608
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
609

610
    // Shut down peer 1's writing side before it has received a shutdown alert
611
    // from peer 2.
612
    ssl_stream_1.shutdown();
2✔
613
}
2✔
614

615

616
TEST(Util_Network_SSL_ShutdownAfterCloseNotifyReceived)
617
{
2✔
618
    network::Service service;
2✔
619
    network::Socket socket_1{service}, socket_2{service};
2✔
620
    network::ssl::Context ssl_context_1;
2✔
621
    network::ssl::Context ssl_context_2;
2✔
622
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
623
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
624
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
625
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
626
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
627
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
628

629
    // Make sure peer 2 gets an SSL shutdown alert.
630
    ssl_stream_1.shutdown();
2✔
631
    socket_1.shutdown(network::Socket::shutdown_send);
2✔
632

633
    // Make sure peer 2 received the shutdown alert from peer 1 before peer 2
634
    // writes.
635
    char ch;
2✔
636
    CHECK_SYSTEM_ERROR(ssl_stream_2.read_some(&ch, 1), MiscExtErrors::end_of_input);
2✔
637

638
    // Check that peer 2 can stil permform a shutdown operation.
639
    ssl_stream_2.shutdown();
2✔
640
}
2✔
641

642

643
TEST(Util_Network_SSL_WriteAfterCloseNotifyReceived)
644
{
2✔
645
    network::Service service;
2✔
646
    network::Socket socket_1{service}, socket_2{service};
2✔
647
    network::ssl::Context ssl_context_1;
2✔
648
    network::ssl::Context ssl_context_2;
2✔
649
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
650
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
651
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
652
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
653
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
654
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
655

656
    // Shut down peer 1's writing side, such that peer 2 gets an SSL shutdown
657
    // alert.
658
    ssl_stream_1.shutdown();
2✔
659
    socket_1.shutdown(network::Socket::shutdown_send);
2✔
660

661
    // Make sure peer 2 received the shutdown alert from peer 1 before peer 2
662
    // writes.
663
    char ch;
2✔
664
    CHECK_SYSTEM_ERROR(ssl_stream_2.read_some(&ch, 1), MiscExtErrors::end_of_input);
2✔
665

666
    // Make peer 2 Write a message, which must fail....????
667
    const char* message = "hello";
2✔
668
    CHECK_SYSTEM_ERROR(ssl_stream_2.write(message, std::strlen(message)), error::broken_pipe);
2✔
669
}
2✔
670

671
TEST(Util_Network_SSL_BasicSendAndReceive)
672
{
2✔
673
    network::Service service;
2✔
674
    network::Socket socket_1{service}, socket_2{service};
2✔
675
    network::ssl::Context ssl_context_1;
2✔
676
    network::ssl::Context ssl_context_2;
2✔
677
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
678
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
679
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
680
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
681
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
682
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
683

684
    // Make peer 2 Write a message.
685
    const char* message = "hello";
2✔
686
    ssl_stream_2.write(message, std::strlen(message));
2✔
687
    ssl_stream_2.shutdown();
2✔
688
    socket_2.shutdown(network::Socket::shutdown_send);
2✔
689

690
    // Check that peer 1 received the message correctly.
691
    char buffer[256];
2✔
692
    std::error_code ec;
2✔
693
    std::size_t n = ssl_stream_1.read(buffer, sizeof buffer, ec);
2✔
694
    CHECK_EQUAL(MiscExtErrors::end_of_input, ec);
2✔
695
    if (CHECK_EQUAL(std::strlen(message), n))
2✔
696
        CHECK(std::equal(buffer, buffer + n, message));
2✔
697
}
2✔
698

699

700
#if REALM_HAVE_SECURE_TRANSPORT
701

702
template <typename ReadHandler, typename ReadError>
703
void run_ssl_nonzero_length_test(test_util::unit_test::TestContext& test_context, ReadHandler&& read_handler,
704
                                 ReadError&& read_error_callback)
705
{
2✔
706
    network::Service service;
2✔
707
    network::DeadlineTimer run_timer{service};
2✔
708
    network::Socket socket_1{service};
2✔
709
    network::Socket socket_2{service};
2✔
710
    network::ssl::Context ssl_context_1;
2✔
711
    network::ssl::Context ssl_context_2;
2✔
712
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
713
    network::ssl::Stream ssl_stream_1 = {socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
714
    network::ssl::Stream ssl_stream_2 = {socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
715
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
716
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
717
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
718
    network::ReadAheadBuffer rab;
2✔
719

720
    char buffer[50];
2✔
721

722
    auto service_thread = std::thread([&test_context, &service, &run_timer]() {
2✔
723
        run_timer.async_wait(std::chrono::seconds(10), [&test_context](Status status) {
2✔
724
            if (!status.is_ok())
×
725
                return;
726
            test_context.logger->info("run_ssl_nonzero_length_test: service timed out");
727
            abort(); // fail the test if the timer expires
728
        });
729
        service.run();
2✔
730
    });
2✔
731

732
    auto [r_promise, read_future] = util::make_promise_future<void>();
2✔
733
    auto async_read_handler = [&test_context, read_promise = std::move(r_promise),
2✔
734
                               handler = std::move(read_handler)](std::error_code ec, std::size_t n) mutable {
2✔
735
        CHECK_NOT_EQUAL(n, 50);
2✔
736
        handler(test_context, ec, n);
2✔
737
        read_promise.emplace_value();
2✔
738
    };
2✔
739

740
    // Set the error before the read
741
    service.post([&](Status status) {
2✔
742
        if (!status.is_ok())
2✔
743
            return;
744

745
        read_error_callback(test_context, ssl_stream_2);
2✔
746
        ssl_stream_2.async_read(buffer, 50, rab, std::move(async_read_handler));
2✔
747
    });
2✔
748
    read_future.get();
2✔
749

750
    // Shut down stream
751
    auto [s_promise, shutdown_future] = util::make_promise_future<void>();
2✔
752
    auto shutdown_handler = [&test_context, shutdown_promise = std::move(s_promise)](std::error_code ec) mutable {
2✔
753
        CHECK_EQUAL(std::error_code(), ec);
2✔
754
        shutdown_promise.emplace_value();
2✔
755
    };
2✔
756
    service.post([&](Status status) {
2✔
757
        if (!status.is_ok())
2✔
758
            return;
759

760
        ssl_stream_1.async_shutdown(std::move(shutdown_handler));
2✔
761
    });
2✔
762
    shutdown_future.get();
2✔
763

764
    // Stop the service thread after shutdown is complete
765
    service.stop();
2✔
766
    service_thread.join();
2✔
767
}
2✔
768

769
TEST(Util_Network_SSL_Nonzero_Length_Error)
770
{
1✔
771
    using MockSSLError = network::ssl::Stream::MockSSLError;
1✔
772
    auto&& read_handler = [](test_util::unit_test::TestContext& test_context, std::error_code ec, std::size_t n) {
1✔
773
        CHECK_EQUAL(util::MiscExtErrors::premature_end_of_input, ec);
1✔
774
        test_context.logger->info("Util_Network_SSL_Nonzero_Length_Error: n: %1", n);
1✔
775
        CHECK_EQUAL(0, n);
1✔
776
    };
1✔
777
    auto&& read_error_callback = [](test_util::unit_test::TestContext&, network::ssl::Stream& ssl_stream_2) {
1✔
778
        ssl_stream_2.set_mock_ssl_perform_error(
1✔
779
            std::make_unique<MockSSLError>(MockSSLError::Operation::read, static_cast<int>(errSSLClosedAbort), 0));
1✔
780
    };
1✔
781
    run_ssl_nonzero_length_test(test_context, std::move(read_handler), std::move(read_error_callback));
1✔
782
}
1✔
783

784

785
TEST(Util_Network_SSL_Nonzero_Length_EndOfInput)
786
{
1✔
787
    auto&& read_handler = [](test_util::unit_test::TestContext& test_context, std::error_code ec, std::size_t n) {
1✔
788
        CHECK_EQUAL(util::MiscExtErrors::end_of_input, ec);
1✔
789
        test_context.logger->info("Util_Network_SSL_Nonzero_Length_EndOfInput: n: %1", n);
1✔
790
        CHECK_EQUAL(6, n);
1✔
791
    };
1✔
792
    auto&& read_error_callback = [](test_util::unit_test::TestContext&, network::ssl::Stream& ssl_stream_2) {
1✔
793
        using MockSSLError = network::ssl::Stream::MockSSLError;
1✔
794
        ssl_stream_2.set_mock_ssl_perform_error(
1✔
795
            std::make_unique<MockSSLError>(MockSSLError::Operation::read, static_cast<int>(errSSLClosedGraceful), 6));
1✔
796
    };
1✔
797
    run_ssl_nonzero_length_test(test_context, std::move(read_handler), std::move(read_error_callback));
1✔
798
}
1✔
799
#endif
800

801

802
TEST(Util_Network_SSL_StressTest)
803
{
2✔
804
    network::Service service_1, service_2;
2✔
805
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
806
    network::ssl::Context ssl_context_1;
2✔
807
    network::ssl::Context ssl_context_2;
2✔
808
    configure_server_ssl_context_for_test(ssl_context_1);
2✔
809
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
810
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
811
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
812
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
813
    connect_ssl_streams(ssl_stream_1, ssl_stream_2);
2✔
814

815
    constexpr size_t original_size = 0x100000; // 1MiB
2✔
816
    std::unique_ptr<char[]> original_1, original_2;
2✔
817
    original_1.reset(new char[original_size]);
2✔
818
    original_2.reset(new char[original_size]);
2✔
819
    {
2✔
820
        std::mt19937_64 prng{std::random_device()()};
2✔
821
        using lim = std::numeric_limits<char>;
2✔
822
        std::uniform_int_distribution<short> dist{short(lim::min()), short(lim::max())};
2✔
823
        log("Initializing...");
2✔
824
        for (size_t i = 0; i < original_size; ++i)
2,097,154✔
825
            original_1[i] = char(dist(prng));
2,097,152✔
826
        for (size_t i = 0; i < original_size; ++i)
2,097,154✔
827
            original_2[i] = char(dist(prng));
2,097,152✔
828
        log("Initialized");
2✔
829
    }
2✔
830

831
    struct Stats {
2✔
832
        std::uint_fast64_t num_cancellations = 0;
2✔
833
        std::uint_fast64_t num_reads = 0, num_canceled_reads = 0;
2✔
834
        std::uint_fast64_t num_writes = 0, num_canceled_writes = 0;
2✔
835
    };
2✔
836

837
#ifdef _WIN32
838
    // With 512, it would take 9 minutes in 32-bit Debug mode on Windows
839
    constexpr int num_cycles = 32;
840
#else
841
    constexpr int num_cycles = 512;
2✔
842
#endif
2✔
843
    auto thread = [&](int id, network::ssl::Stream& ssl_stream, const char* read_original, const char* write_original,
2✔
844
                      Stats& stats) {
4✔
845
        std::unique_ptr<char[]> read_buffer{new char[original_size]};
4✔
846
        std::mt19937_64 prng{std::random_device()()};
4✔
847
        // Using range 1B -> 32KiB because that undershoots and overshoots in
848
        // equal amounts with respect to the SSL frame size of 16KiB.
849
        std::uniform_int_distribution<size_t> read_write_size_dist(1, 32 * 1024);
4✔
850
        std::uniform_int_distribution<int> delayed_read_write_dist(0, 49);
4✔
851
        network::Service& service = ssl_stream.lowest_layer().get_service();
4✔
852
        network::DeadlineTimer cancellation_timer{service};
4✔
853
        network::DeadlineTimer read_timer{service};
4✔
854
        network::DeadlineTimer write_timer{service};
4✔
855
        bool read_done = false, write_done = false;
4✔
856
        UniqueFunction<void()> shedule_cancellation = [&] {
4✔
857
            cancellation_timer.async_wait(std::chrono::microseconds(10), [&](Status status) {
×
858
                REALM_ASSERT(status.is_ok() || status == ErrorCodes::OperationAborted);
×
859
                if (status == ErrorCodes::OperationAborted)
×
860
                    return;
×
861
                if (read_done && write_done)
×
862
                    return;
×
863
                ssl_stream.lowest_layer().cancel();
×
864
                ++stats.num_cancellations;
×
865
                shedule_cancellation();
×
866
            });
×
867
        };
×
868
        //        shedule_cancellation();
869
        char* read_begin = read_buffer.get();
4✔
870
        char* read_end = read_buffer.get() + original_size;
4✔
871
        int num_read_cycles = 0;
4✔
872
        UniqueFunction<void()> read = [&] {
237,085✔
873
            if (read_begin == read_end) {
237,085✔
874
                log("<R%1>", id);
2,048✔
875
                CHECK(std::equal(read_original, read_original + original_size, read_buffer.get()));
2,048✔
876
                ++num_read_cycles;
2,048✔
877
                if (num_read_cycles == num_cycles) {
2,048✔
878
                    log("End of read %1", id);
4✔
879
                    read_done = true;
4✔
880
                    if (write_done)
4✔
881
                        cancellation_timer.cancel();
4✔
882
                    return;
4✔
883
                }
4✔
884
                read_begin = read_buffer.get();
2,044✔
885
                read_end = read_buffer.get() + original_size;
2,044✔
886
            }
2,044✔
887
            auto handler = [&](std::error_code ec, size_t n) {
237,207✔
888
                REALM_ASSERT(!ec || ec == error::operation_aborted);
237,194!
889
                ++stats.num_reads;
237,194✔
890
                if (ec == error::operation_aborted) {
237,194✔
891
                    ++stats.num_canceled_reads;
×
892
                }
×
893
                else {
237,194✔
894
                    read_begin += n;
237,194✔
895
                }
237,194✔
896
                if (delayed_read_write_dist(prng) == 0) {
237,194✔
897
                    read_timer.async_wait(std::chrono::microseconds(100), [&](Status status) {
4,838✔
898
                        REALM_ASSERT(status.is_ok());
4,836✔
899
                        read();
4,836✔
900
                    });
4,836✔
901
                }
4,838✔
902
                else {
232,356✔
903
                    read();
232,356✔
904
                }
232,356✔
905
            };
237,194✔
906
            char* buffer = read_begin;
237,081✔
907
            size_t size = read_write_size_dist(prng);
237,081✔
908
            size_t max_size = read_end - read_begin;
237,081✔
909
            if (size > max_size)
237,081✔
910
                size = max_size;
3,670✔
911
            ssl_stream.async_read_some(buffer, size, std::move(handler));
237,081✔
912
        };
237,081✔
913
        read();
4✔
914
        const char* write_begin = write_original;
4✔
915
        const char* write_end = write_original + original_size;
4✔
916
        int num_write_cycles = 0;
4✔
917
        UniqueFunction<void()> write = [&] {
153,928✔
918
            if (write_begin == write_end) {
153,928✔
919
                log("<W%1>", id);
2,048✔
920
                ++num_write_cycles;
2,048✔
921
                if (num_write_cycles == num_cycles) {
2,048✔
922
                    log("End of write %1", id);
4✔
923
                    write_done = true;
4✔
924
                    if (read_done)
4✔
UNCOV
925
                        cancellation_timer.cancel();
×
926
                    return;
4✔
927
                }
4✔
928
                write_begin = write_original;
2,044✔
929
                write_end = write_original + original_size;
2,044✔
930
            }
2,044✔
931
            auto handler = [&](std::error_code ec, size_t n) {
153,958✔
932
                REALM_ASSERT(!ec || ec == error::operation_aborted);
153,958!
933
                ++stats.num_writes;
153,958✔
934
                if (ec == error::operation_aborted) {
153,958✔
935
                    ++stats.num_canceled_writes;
×
936
                }
×
937
                else {
153,958✔
938
                    write_begin += n;
153,958✔
939
                }
153,958✔
940
                if (delayed_read_write_dist(prng) == 0) {
153,958✔
941
                    write_timer.async_wait(std::chrono::microseconds(100), [&](Status status) {
3,020✔
942
                        REALM_ASSERT(status.is_ok());
3,020✔
943
                        write();
3,020✔
944
                    });
3,020✔
945
                }
3,020✔
946
                else {
150,938✔
947
                    write();
150,938✔
948
                }
150,938✔
949
            };
153,958✔
950
            const char* data = write_begin;
153,924✔
951
            size_t size = read_write_size_dist(prng);
153,924✔
952
            size_t max_size = write_end - write_begin;
153,924✔
953
            if (size > max_size)
153,924✔
954
                size = max_size;
2,371✔
955
            ssl_stream.async_write_some(data, size, std::move(handler));
153,924✔
956
        };
153,924✔
957
        write();
4✔
958
        service.run();
4✔
959
    };
4✔
960

961
    Stats stats_1, stats_2;
2✔
962
    std::thread thread_1{[&] {
2✔
963
        thread(1, ssl_stream_1, original_1.get(), original_2.get(), stats_1);
2✔
964
    }};
2✔
965
    std::thread thread_2{[&] {
2✔
966
        thread(2, ssl_stream_2, original_2.get(), original_1.get(), stats_2);
2✔
967
    }};
2✔
968
    thread_1.join();
2✔
969
    thread_2.join();
2✔
970

971
    ssl_stream_1.shutdown();
2✔
972
    ssl_stream_2.shutdown();
2✔
973

974
    char ch;
2✔
975
    CHECK_SYSTEM_ERROR(ssl_stream_1.read_some(&ch, 1), MiscExtErrors::end_of_input);
2✔
976
    CHECK_SYSTEM_ERROR(ssl_stream_2.read_some(&ch, 1), MiscExtErrors::end_of_input);
2✔
977

978
    log("Cancellations: %1, %2", stats_1.num_cancellations, stats_2.num_cancellations);
2✔
979
    log("Reads:  %1 (%2 canceled), %3 (%4 canceled)", stats_1.num_reads, stats_1.num_canceled_reads,
2✔
980
        stats_2.num_reads, stats_2.num_canceled_reads);
2✔
981
    log("Writes: %1 (%2 canceled), %3 (%4 canceled)", stats_1.num_writes, stats_1.num_canceled_writes,
2✔
982
        stats_2.num_writes, stats_2.num_canceled_writes);
2✔
983
}
2✔
984

985
// The host name is contained in both the
986
// Common Name and the Subject Alternartive Name
987
// section of the server certificate.
988
TEST(Util_Network_SSL_Certificate_CN_SAN)
989
{
2✔
990
    network::Service service_1, service_2;
2✔
991
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
992
    network::ssl::Context ssl_context_1;
2✔
993
    network::ssl::Context ssl_context_2;
2✔
994

995
    std::string ca_dir = get_test_resource_path();
2✔
996

997
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
2✔
998
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
2✔
999
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
2✔
1000

1001
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
1002
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
1003
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
1004
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
1005

1006
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
2✔
1007

1008
    // We expect success because the certificate is signed for www.example.com
1009
    // in both Common Name and SAN.
1010
    ssl_stream_2.set_host_name("www.example.com");
2✔
1011

1012
    connect_sockets(socket_1, socket_2);
2✔
1013

1014
    auto connector = [&] {
2✔
1015
        std::error_code ec;
2✔
1016
        ssl_stream_2.handshake(ec);
2✔
1017
        CHECK_EQUAL(std::error_code(), ec);
2✔
1018
    };
2✔
1019
    auto acceptor = [&] {
2✔
1020
        std::error_code ec;
2✔
1021
        ssl_stream_1.handshake(ec);
2✔
1022
        CHECK_EQUAL(std::error_code(), ec);
2✔
1023
    };
2✔
1024

1025
    std::thread thread_1(std::move(connector));
2✔
1026
    std::thread thread_2(std::move(acceptor));
2✔
1027
    thread_1.join();
2✔
1028
    thread_2.join();
2✔
1029
}
2✔
1030

1031
// The host name is only contained in the
1032
// Subject Alternative Name section of the certificate.
1033
TEST(Util_Network_SSL_Certificate_SAN)
1034
{
2✔
1035
    network::Service service_1, service_2;
2✔
1036
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
1037
    network::ssl::Context ssl_context_1;
2✔
1038
    network::ssl::Context ssl_context_2;
2✔
1039

1040
    std::string ca_dir = get_test_resource_path();
2✔
1041

1042
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
2✔
1043
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
2✔
1044
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
2✔
1045

1046
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
1047
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
1048
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
1049
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
1050

1051
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
2✔
1052

1053
    ssl_stream_2.set_host_name("support.example.com");
2✔
1054

1055
    connect_sockets(socket_1, socket_2);
2✔
1056

1057
    auto connector = [&] {
2✔
1058
        std::error_code ec;
2✔
1059
        ssl_stream_2.handshake(ec);
2✔
1060
        CHECK_EQUAL(std::error_code(), ec);
2✔
1061
    };
2✔
1062
    auto acceptor = [&] {
2✔
1063
        std::error_code ec;
2✔
1064
        ssl_stream_1.handshake(ec);
2✔
1065
        CHECK_EQUAL(std::error_code(), ec);
2✔
1066
    };
2✔
1067

1068
    std::thread thread_1(std::move(connector));
2✔
1069
    std::thread thread_2(std::move(acceptor));
2✔
1070
    thread_1.join();
2✔
1071
    thread_2.join();
2✔
1072
}
2✔
1073

1074

1075
// FIXME: Verification of peer against Common Name is no longer supported in
1076
// Catalina (macOS).
1077
#if REALM_HAVE_OPENSSL || !REALM_HAVE_SECURE_TRANSPORT
1078

1079
// The host name www.example.com is contained in Common Name but not in SAN.
1080
TEST(Util_Network_SSL_Certificate_CN)
1081
{
1✔
1082
    network::Service service_1, service_2;
1✔
1083
    network::Socket socket_1{service_1}, socket_2{service_2};
1✔
1084
    network::ssl::Context ssl_context_1;
1✔
1085
    network::ssl::Context ssl_context_2;
1✔
1086

1087
    std::string ca_dir = get_test_resource_path();
1✔
1088

1089
    ssl_context_1.use_certificate_chain_file(ca_dir + "ip-chain.crt.pem");
1✔
1090
    ssl_context_1.use_private_key_file(ca_dir + "ip-server.key.pem");
1✔
1091
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
1✔
1092

1093
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1✔
1094
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1✔
1095
    ssl_stream_1.set_logger(test_context.logger.get());
1✔
1096
    ssl_stream_2.set_logger(test_context.logger.get());
1✔
1097

1098
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1✔
1099

1100
    ssl_stream_2.set_host_name("www.example.com");
1✔
1101

1102
    connect_sockets(socket_1, socket_2);
1✔
1103

1104
    auto connector = [&] {
1✔
1105
        std::error_code ec;
1✔
1106
        ssl_stream_2.handshake(ec);
1✔
1107
        CHECK_EQUAL(std::error_code(), ec);
1✔
1108
    };
1✔
1109
    auto acceptor = [&] {
1✔
1110
        std::error_code ec;
1✔
1111
        ssl_stream_1.handshake(ec);
1✔
1112
        CHECK_EQUAL(std::error_code(), ec);
1✔
1113
    };
1✔
1114

1115
    std::thread thread_1(std::move(connector));
1✔
1116
    std::thread thread_2(std::move(acceptor));
1✔
1117
    thread_1.join();
1✔
1118
    thread_2.join();
1✔
1119
}
1✔
1120

1121
#endif // REALM_HAVE_OPENSSL || !REALM_HAVE_SECURE_TRANSPORT
1122

1123
// The ip address is contained in the IP SAN section
1124
// of the certificate. For OpenSSL, we expect failure because we only
1125
// check for DNS. For Secure Transport we get success because the
1126
// ip section is checked. This discrepancy could be resolved in the
1127
// future if deemed important.
1128
TEST(Util_Network_SSL_Certificate_IP)
1129
{
2✔
1130
    network::Service service_1, service_2;
2✔
1131
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
1132
    network::ssl::Context ssl_context_1;
2✔
1133
    network::ssl::Context ssl_context_2;
2✔
1134

1135
    std::string ca_dir = get_test_resource_path();
2✔
1136

1137
    ssl_context_1.use_certificate_chain_file(ca_dir + "ip-chain.crt.pem");
2✔
1138
    ssl_context_1.use_private_key_file(ca_dir + "ip-server.key.pem");
2✔
1139
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
2✔
1140

1141
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
1142
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
1143
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
1144
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
1145

1146
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
2✔
1147

1148
    ssl_stream_2.set_host_name("127.0.0.1");
2✔
1149

1150
    connect_sockets(socket_1, socket_2);
2✔
1151

1152
    auto connector = [&] {
2✔
1153
        std::error_code ec;
2✔
1154
        ssl_stream_2.handshake(ec);
2✔
1155
#if REALM_HAVE_OPENSSL
1✔
1156
        CHECK_NOT_EQUAL(std::error_code(), ec);
1✔
1157
#elif REALM_HAVE_SECURE_TRANSPORT
1158
        CHECK_EQUAL(std::error_code(), ec);
1✔
1159
#endif
1✔
1160
    };
2✔
1161
    auto acceptor = [&] {
2✔
1162
        std::error_code ec;
2✔
1163
        ssl_stream_1.handshake(ec);
2✔
1164
#if REALM_HAVE_OPENSSL
1✔
1165
        CHECK_NOT_EQUAL(std::error_code(), ec);
1✔
1166
#elif REALM_HAVE_SECURE_TRANSPORT
1167
        CHECK_EQUAL(std::error_code(), ec);
1✔
1168
#endif
1✔
1169
    };
2✔
1170

1171
    std::thread thread_1(std::move(connector));
2✔
1172
    std::thread thread_2(std::move(acceptor));
2✔
1173
    thread_1.join();
2✔
1174
    thread_2.join();
2✔
1175
}
2✔
1176

1177
// The certificate contains incorrect host names.
1178
// We expect the handshake to fail.
1179
TEST(Util_Network_SSL_Certificate_Failure)
1180
{
2✔
1181
    network::Service service_1, service_2;
2✔
1182
    network::Socket socket_1{service_1}, socket_2{service_2};
2✔
1183
    network::ssl::Context ssl_context_1;
2✔
1184
    network::ssl::Context ssl_context_2;
2✔
1185

1186
    std::string ca_dir = get_test_resource_path();
2✔
1187

1188
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
2✔
1189
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
2✔
1190
    ssl_context_2.use_verify_file(ca_dir + "crt.pem");
2✔
1191

1192
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
2✔
1193
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
2✔
1194
    ssl_stream_1.set_logger(test_context.logger.get());
2✔
1195
    ssl_stream_2.set_logger(test_context.logger.get());
2✔
1196

1197
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
2✔
1198

1199
    // We expect failure because the certificate is signed for www.example.com
1200
    ssl_stream_2.set_host_name("www.another-example.com");
2✔
1201

1202
    connect_sockets(socket_1, socket_2);
2✔
1203

1204
    auto connector = [&] {
2✔
1205
        std::error_code ec;
2✔
1206
        ssl_stream_2.handshake(ec);
2✔
1207
        // Refine this
1208
        CHECK_NOT_EQUAL(std::error_code(), ec);
2✔
1209
    };
2✔
1210
    auto acceptor = [&] {
2✔
1211
        std::error_code ec;
2✔
1212
        ssl_stream_1.handshake(ec);
2✔
1213
        // Refine this
1214
        CHECK_NOT_EQUAL(std::error_code(), ec);
2✔
1215
    };
2✔
1216

1217
    std::thread thread_1(std::move(connector));
2✔
1218
    std::thread thread_2(std::move(acceptor));
2✔
1219
    thread_1.join();
2✔
1220
    thread_2.join();
2✔
1221
}
2✔
1222

1223
#ifdef _WIN32
1224

1225
// Adding a trusted root certificate causes a blocking popup to appear so only run this test
1226
// if in an interactive session with a debugger attached, otherwise CI machines will block.
1227
// TODO: maybe use the CI environment variable for the condition?
1228
TEST_IF(Util_Network_SSL_Certificate_From_Windows_Cert_Store, IsDebuggerPresent())
1229
{
1230
    std::string ca_dir = get_test_resource_path();
1231

1232
    BIO* file = BIO_new_file((ca_dir + "crt.pem").c_str(), "rb");
1233
    X509* cert = PEM_read_bio_X509(file, NULL, 0, NULL);
1234
    BIO_free(file);
1235

1236
    BIO* mem = BIO_new(BIO_s_mem());
1237
    int size = i2d_X509_bio(mem, cert);
1238
    BUF_MEM* buffer;
1239
    BIO_get_mem_ptr(mem, &buffer);
1240

1241
    PCCERT_CONTEXT cert_context;
1242
    HCERTSTORE store = CertOpenSystemStoreA(NULL, "ROOT");
1243
    CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(buffer->data),
1244
                                     static_cast<DWORD>(buffer->length), CERT_STORE_ADD_USE_EXISTING, &cert_context);
1245
    BIO_free(mem);
1246

1247
    network::Service service_1, service_2;
1248
    network::Socket socket_1{service_1}, socket_2{service_2};
1249
    network::ssl::Context ssl_context_1;
1250
    network::ssl::Context ssl_context_2;
1251

1252
    ssl_context_1.use_certificate_chain_file(ca_dir + "dns-chain.crt.pem");
1253
    ssl_context_1.use_private_key_file(ca_dir + "dns-checked-server.key.pem");
1254
    ssl_context_2
1255
        .use_default_verify(); // this will import the Windows certificates in the context's certificate store
1256

1257
    network::ssl::Stream ssl_stream_1{socket_1, ssl_context_1, network::ssl::Stream::server};
1258
    network::ssl::Stream ssl_stream_2{socket_2, ssl_context_2, network::ssl::Stream::client};
1259
    ssl_stream_1.set_logger(test_context.logger.get());
1260
    ssl_stream_2.set_logger(test_context.logger.get());
1261

1262
    ssl_stream_2.set_verify_mode(network::ssl::VerifyMode::peer);
1263

1264
    // We expect success because the certificate is signed for www.example.com
1265
    // in both Common Name and SAN.
1266
    ssl_stream_2.set_host_name("www.example.com");
1267

1268
    connect_sockets(socket_1, socket_2);
1269

1270
    auto connector = [&] {
1271
        std::error_code ec;
1272
        ssl_stream_2.handshake(ec);
1273
        CHECK_EQUAL(std::error_code(), ec);
1274
    };
1275
    auto acceptor = [&] {
1276
        std::error_code ec;
1277
        ssl_stream_1.handshake(ec);
1278
        CHECK_EQUAL(std::error_code(), ec);
1279
    };
1280

1281
    std::thread thread_1(std::move(connector));
1282
    std::thread thread_2(std::move(acceptor));
1283
    thread_1.join();
1284
    thread_2.join();
1285

1286
    CertDeleteCertificateFromStore(cert_context);
1287
    CertCloseStore(store, 0);
1288
}
1289

1290
#endif // _WIN32
1291

1292
#endif // !REALM_MOBILE
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc