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

realm / realm-core / 2555

09 Aug 2024 06:49PM UTC coverage: 91.123% (+0.04%) from 91.087%
2555

push

Evergreen

web-flow
Actually check for unuplaoded changes in no_pending_local_changes() (#7967)

We can have local changesets stored which have already been uploaded and
acknoledged by the server, so checking all of the changesets is incorrect. We
need to instead only check changesets for versions after the current position
of the upload cursor.

102818 of 181588 branches covered (56.62%)

37 of 38 new or added lines in 2 files covered. (97.37%)

42 existing lines in 9 files now uncovered.

217381 of 238557 relevant lines covered (91.12%)

5684689.85 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 {
54✔
471
        socket_2.write_some(buffer.get(), size, ec);
54✔
472
    } while (!ec);
54✔
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 {
28✔
550
        socket_2.write_some(buffer.get(), size, ec);
28✔
551
    } while (!ec);
28✔
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 {
14✔
584
        socket_2.write_some(buffer.get(), size, ec);
14✔
585
    } while (!ec);
14✔
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 = [&] {
236,365✔
873
            if (read_begin == read_end) {
236,365✔
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) {
236,561✔
888
                REALM_ASSERT(!ec || ec == error::operation_aborted);
236,552!
889
                ++stats.num_reads;
236,552✔
890
                if (ec == error::operation_aborted) {
236,552✔
891
                    ++stats.num_canceled_reads;
×
892
                }
×
893
                else {
236,552✔
894
                    read_begin += n;
236,552✔
895
                }
236,552✔
896
                if (delayed_read_write_dist(prng) == 0) {
236,552✔
897
                    read_timer.async_wait(std::chrono::microseconds(100), [&](Status status) {
4,771✔
898
                        REALM_ASSERT(status.is_ok());
4,771✔
899
                        read();
4,771✔
900
                    });
4,771✔
901
                }
4,771✔
902
                else {
231,781✔
903
                    read();
231,781✔
904
                }
231,781✔
905
            };
236,552✔
906
            char* buffer = read_begin;
236,361✔
907
            size_t size = read_write_size_dist(prng);
236,361✔
908
            size_t max_size = read_end - read_begin;
236,361✔
909
            if (size > max_size)
236,361✔
910
                size = max_size;
3,655✔
911
            ssl_stream.async_read_some(buffer, size, std::move(handler));
236,361✔
912
        };
236,361✔
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,934✔
918
            if (write_begin == write_end) {
153,934✔
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,957✔
932
                REALM_ASSERT(!ec || ec == error::operation_aborted);
153,954!
933
                ++stats.num_writes;
153,954✔
934
                if (ec == error::operation_aborted) {
153,954✔
935
                    ++stats.num_canceled_writes;
×
936
                }
×
937
                else {
153,954✔
938
                    write_begin += n;
153,954✔
939
                }
153,954✔
940
                if (delayed_read_write_dist(prng) == 0) {
153,954✔
941
                    write_timer.async_wait(std::chrono::microseconds(100), [&](Status status) {
3,097✔
942
                        REALM_ASSERT(status.is_ok());
3,097✔
943
                        write();
3,097✔
944
                    });
3,097✔
945
                }
3,097✔
946
                else {
150,857✔
947
                    write();
150,857✔
948
                }
150,857✔
949
            };
153,954✔
950
            const char* data = write_begin;
153,930✔
951
            size_t size = read_write_size_dist(prng);
153,930✔
952
            size_t max_size = write_end - write_begin;
153,930✔
953
            if (size > max_size)
153,930✔
954
                size = max_size;
2,390✔
955
            ssl_stream.async_write_some(data, size, std::move(handler));
153,930✔
956
        };
153,930✔
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