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

libbitcoin / libbitcoin-network / 42256895374

06 Nov 2026 04:55PM UTC coverage: 93.863% (-0.06%) from 93.92%
42256895374

push

github

web-flow
Merge pull request #1010 from evoskuil/master

New options and normalization, io optimize, interface command arg.

112 of 124 new or added lines in 11 files covered. (90.32%)

14 existing lines in 4 files now uncovered.

14576 of 15529 relevant lines covered (93.86%)

2424.16 hits per line

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

83.33
/src/io/socket.cpp
1
/**
2
 * Copyright (c) 2011-2026 libbitcoin developers
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/network/io/socket.hpp>
20

21
#include <atomic>
22
#include <bitcoin/network/define.hpp>
23
#include "backend.hpp"
24
#include "native.hpp"
25

26
namespace libbitcoin {
27
namespace network {
28
namespace io {
29

30
using namespace system;
31
using namespace detail;
32

33
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
34
BC_PUSH_WARNING(NO_REINTERPRET_CAST)
35

36
#if defined(HAVE_WINDOWS)
37
    constexpr int shutdown_receive = SD_RECEIVE;
38
    constexpr int shutdown_send = SD_SEND;
39
    constexpr int shutdown_both = SD_BOTH;
40
    constexpr int send_flags = 0;
41
#else
42
    constexpr int shutdown_receive = SHUT_RD;
43
    constexpr int shutdown_send = SHUT_WR;
44
    constexpr int shutdown_both = SHUT_RDWR;
45
#if defined(MSG_NOSIGNAL)
46
    constexpr int send_flags = MSG_NOSIGNAL;
47
#else
48
    constexpr int send_flags = 0;
49
#endif
50
#endif
51

52
static descriptor_ptr make_descriptor(const executor& executor) NOEXCEPT
1,850 ✔
53
{
54
    return std::make_shared<descriptor>(executor.context().scheduler());
1,850 ✔
55
}
56

57
static detail::backend& backend_of(const executor& executor) NOEXCEPT
81,224 ✔
58
{
59
    return executor.context().scheduler().backend();
81,224 ✔
60
}
61

62
static code native_code() NOEXCEPT
169 ✔
63
{
64
    return to_code(last_error());
169 ✔
65
}
66

67
template <typename Option>
68
static code set_socket_option(native_socket socket, int level, int name,
895 ✔
69
    Option value) NOEXCEPT
70
{
71
    if (::setsockopt(socket, level, name, pointer_cast<const char>(&value),
895 ✔
72
        sizeof(value)) != 0)
73
        return native_code();
×
74

75
    return error::success;
895 ✔
76
}
77

78
// Deliver the result on the executor (never within the initiating call).
79
static void post_result(const executor& executor,
23 ✔
80
    const socket::io_handler& handler, const code& ec) NOEXCEPT
81
{
82
    executor.post([handler, ec]() NOEXCEPT
23 ✔
83
    {
84
        handler(ec, zero);
23 ✔
85
    });
23 ✔
86
}
23 ✔
87

88
template <typename Buffers>
89
static size_t total_size(const Buffers& buffers) NOEXCEPT
83,817 ✔
90
{
91
    size_t total{};
92
    for (const auto& buffer: buffers)
167,822 ✔
93
        total = ceilinged_add(total, buffer.size());
84,005 ✔
94

95
    return total;
83,817 ✔
96
}
97

98
#if defined(HAVE_WINDOWS)
99
template <typename Buffers>
100
static std::vector<WSABUF> to_native(const Buffers& buffers) NOEXCEPT
101
{
102
    std::vector<WSABUF> out{};
103
    out.reserve(buffers.size());
104
    for (const auto& buffer: buffers)
105
        out.push_back(
106
        {
107
            possible_narrow_cast<ULONG>(buffer.size()),
108
            pointer_cast<CHAR>(const_cast<void*>(
109
                static_cast<const void*>(buffer.data())))
110
        });
111

112
    return out;
113
}
114
#else
115
template <typename Buffers>
116
static std::vector<iovec> to_native(const Buffers& buffers) NOEXCEPT
5,883 ✔
117
{
118
    std::vector<iovec> out{};
5,883 ✔
119
    out.reserve(buffers.size());
5,883 ✔
120
    for (const auto& buffer: buffers)
11,766 ✔
121
        out.push_back(
5,883 ✔
122
        {
123
            const_cast<void*>(static_cast<const void*>(buffer.data())),
5,883 ✔
124
            buffer.size()
5,883 ✔
125
        });
126

127
    return out;
5,883 ✔
128
}
×
129
#endif
130

131
// tcp
132
// ----------------------------------------------------------------------------
133

134
tcp::tcp(bool v6) NOEXCEPT
1,027 ✔
135
  : v6_(v6)
1,027 ✔
136
{
137
}
1,027 ✔
138

139
tcp tcp::v4() NOEXCEPT
105 ✔
140
{
141
    return tcp{ false };
105 ✔
142
}
143

144
tcp tcp::v6() NOEXCEPT
×
145
{
146
    return tcp{ true };
×
147
}
148

149
tcp tcp::of(const endpoint& point) NOEXCEPT
922 ✔
150
{
151
    return tcp{ point.address().is_v6() };
922 ✔
152
}
153

154
bool tcp::is_v6() const NOEXCEPT
1,027 ✔
155
{
156
    return v6_;
1,027 ✔
157
}
158

159
// socket
160
// ----------------------------------------------------------------------------
161

162
socket::socket(const executor& executor) NOEXCEPT
1,093 ✔
163
  : executor_(executor), descriptor_(make_descriptor(executor))
1,093 ✔
164
{
165
}
1,093 ✔
166

167
socket::socket(socket&& other) NOEXCEPT
175 ✔
168
  : executor_(other.executor_), descriptor_(std::move(other.descriptor_))
175 ✔
169
{
170
    other.descriptor_ = make_descriptor(other.executor_);
175 ✔
171
}
175 ✔
172

173
socket& socket::operator=(socket&& other) NOEXCEPT
×
174
{
175
    if (this != &other)
×
176
    {
177
        code ignore{};
×
178
        close(ignore);
×
179
        executor_ = other.executor_;
×
180
        descriptor_ = std::move(other.descriptor_);
×
181
        other.descriptor_ = make_descriptor(other.executor_);
×
182
    }
183

184
    return *this;
×
185
}
186

187
socket::~socket() NOEXCEPT
1,268 ✔
188
{
189
    code ignore{};
1,268 ✔
190
    close(ignore);
1,268 ✔
191
}
1,268 ✔
192

193
socket::executor_type socket::get_executor() const NOEXCEPT
59 ✔
194
{
195
    return executor_;
59 ✔
196
}
197

198
bool socket::is_open() const NOEXCEPT
82,178 ✔
199
{
200
    return descriptor_->handle != invalid_socket;
82,178 ✔
201
}
202

203
socket::native_handle_type socket::native_handle() const NOEXCEPT
×
204
{
205
    return static_cast<native_handle_type>(descriptor_->handle);
×
206
}
207

208
void socket::open(const tcp& protocol, code& ec) NOEXCEPT
465 ✔
209
{
210
    if (is_open())
465 ✔
211
    {
212
        ec = error::connect_failed;
×
213
        return;
×
214
    }
215

216
    int native{};
465 ✔
217
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
465 ✔
218
    ec = to_code(native);
465 ✔
219
}
220

221
void socket::bind(const endpoint& point, code& ec) NOEXCEPT
4 ✔
222
{
223
    const auto address = to_socket_address(point);
4 ✔
224
    if (::bind(descriptor_->handle, address.data(), address.size) != 0)
4 ✔
225
    {
226
        ec = native_code();
1 ✔
227
        return;
1 ✔
228
    }
229

230
    descriptor_->bound = true;
3 ✔
231
    ec = error::success;
3 ✔
232
}
233

234
void socket::close(code& ec) NOEXCEPT
2,381 ✔
235
{
236
    if (is_open())
2,381 ✔
237
        backend_of(executor_).close(*descriptor_);
905 ✔
238

239
    ec = error::success;
2,381 ✔
240
}
2,381 ✔
241

242
void socket::close() NOEXCEPT
216 ✔
243
{
244
    code ignore{};
216 ✔
245
    close(ignore);
216 ✔
246
}
216 ✔
247

248
void socket::cancel(code& ec) NOEXCEPT
5 ✔
249
{
250
    if (!is_open())
5 ✔
251
    {
252
        ec = error::bad_stream;
1 ✔
253
        return;
1 ✔
254
    }
255

256
    backend_of(executor_).cancel(*descriptor_);
4 ✔
257
    ec = error::success;
4 ✔
258
}
259

260
void socket::shutdown(shutdown_type what, code& ec) NOEXCEPT
549 ✔
261
{
262
    const auto how = what == shutdown_type::receive ? shutdown_receive :
549 ✔
263
        (what == shutdown_type::send ? shutdown_send : shutdown_both);
264

265
    ec = ::shutdown(descriptor_->handle, how) != 0 ? native_code() :
549 ✔
266
        code{ error::success };
267
}
549 ✔
268

269
void socket::set_option(const no_delay& option, code& ec) NOEXCEPT
348 ✔
270
{
271
    ec = set_socket_option(descriptor_->handle, IPPROTO_TCP, TCP_NODELAY,
348 ✔
272
        option.value ? 1 : 0);
348 ✔
273
}
348 ✔
274

275
void socket::set_option(const keep_alive& option, code& ec) NOEXCEPT
×
276
{
277
    ec = set_socket_option(descriptor_->handle, SOL_SOCKET, SO_KEEPALIVE,
×
278
        option.value ? 1 : 0);
×
279
}
×
280

281
void socket::non_blocking(bool value, code& ec) NOEXCEPT
3 ✔
282
{
283
    // Posix sockets are always nonblocking (blocking calls wait on poll).
284
#if defined(HAVE_WINDOWS)
285
    int native{};
286
    if (!set_blocking(descriptor_->handle, !value, native))
287
    {
288
        ec = to_code(native);
289
        return;
290
    }
291
#endif
292

293
    descriptor_->non_blocking = value;
3 ✔
294
    ec = error::success;
3 ✔
295
}
3 ✔
296

297
bool socket::non_blocking() const NOEXCEPT
1 ✔
298
{
299
    return descriptor_->non_blocking;
1 ✔
300
}
301

302
size_t socket::available(code& ec) const NOEXCEPT
1 ✔
303
{
304
#if defined(HAVE_WINDOWS)
305
    u_long count{};
306
    if (::ioctlsocket(descriptor_->handle, FIONREAD, &count) != 0)
307
#else
308
    int count{};
1 ✔
309
    if (::ioctl(descriptor_->handle, FIONREAD, &count) != 0)
1 ✔
310
#endif
311
    {
312
        ec = native_code();
×
313
        return zero;
×
314
    }
315

316
    ec = error::success;
1 ✔
317
#if defined(HAVE_WINDOWS)
318
    return possible_wide_cast<size_t>(count);
319
#else
320
    return possible_wide_cast<size_t>(sign_cast<unsigned int>(count));
1 ✔
321
#endif
322
}
323

324
endpoint socket::local_endpoint(code& ec) const NOEXCEPT
374 ✔
325
{
326
    socket_address address{};
374 ✔
327
    address.size = sizeof(address.storage);
374 ✔
328
    if (::getsockname(descriptor_->handle, address.data(), &address.size) != 0)
374 ✔
329
    {
330
        ec = native_code();
×
331
        return {};
×
332
    }
333

334
    ec = error::success;
374 ✔
335
    return to_endpoint(address.data());
374 ✔
336
}
337

338
endpoint socket::remote_endpoint(code& ec) const NOEXCEPT
267 ✔
339
{
340
    socket_address address{};
267 ✔
341
    address.size = sizeof(address.storage);
267 ✔
342
    if (::getpeername(descriptor_->handle, address.data(), &address.size) != 0)
267 ✔
343
    {
344
        ec = native_code();
×
345
        return {};
×
346
    }
347

348
    ec = error::success;
267 ✔
349
    return to_endpoint(address.data());
267 ✔
350
}
351

352
endpoint socket::local_endpoint() const NOEXCEPT
6 ✔
353
{
354
    code ignore{};
6 ✔
355
    return local_endpoint(ignore);
6 ✔
356
}
357

358
endpoint socket::remote_endpoint() const NOEXCEPT
3 ✔
359
{
360
    code ignore{};
3 ✔
361
    return remote_endpoint(ignore);
3 ✔
362
}
363

364
bool socket::half_closed() const NOEXCEPT
13 ✔
365
{
366
    if (!is_open())
13 ✔
367
        return false;
368

369
#if defined(HAVE_WINDOWS)
370
    TCP_INFO_v0 info{};
371
    DWORD version{};
372
    DWORD size{};
373
    if (is_zero(::WSAIoctl(descriptor_->handle, SIO_TCP_INFO, &version,
374
        sizeof(version), &info, sizeof(info), &size, nullptr, nullptr)))
375
        return info.State == TCPSTATE_CLOSE_WAIT;
376

377
#elif defined(HAVE_APPLE)
378
    struct tcp_connection_info info{};
379
    socklen_t size = sizeof(info);
380
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP,
381
        TCP_CONNECTION_INFO, &info, &size)))
382
        return info.tcpi_state == TCPS_CLOSE_WAIT;
383

384
#elif defined(HAVE_LINUX)
385
    struct tcp_info info{};
12 ✔
386
    socklen_t size = sizeof(info);
12 ✔
387
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP, TCP_INFO,
12 ✔
388
        &info, &size)))
389
        return info.tcpi_state == TCP_CLOSE_WAIT;
12 ✔
390
#endif
391
    return false;
392
}
393

394
void socket::async_connect(const endpoint& point,
193 ✔
395
    result_handler&& handler) NOEXCEPT
396
{
397
    io_handler completion = [handler = std::move(handler)](const code& ec,
386 ✔
398
        size_t) NOEXCEPT
399
    {
400
        handler(ec);
193 ✔
401
    };
193 ✔
402

403
    if (!is_open())
193 ✔
404
    {
405
        code ec{};
190 ✔
406
        open(tcp::of(point), ec);
190 ✔
407
        if (ec)
190 ✔
408
        {
409
            post_result(executor_, completion, ec);
×
410
            return;
×
411
        }
412
    }
413

414
    const auto op = new operation{ operation::kind::connect, descriptor_,
193 ✔
415
        executor_ };
193 ✔
416
    op->target = to_socket_address(point);
193 ✔
417
    op->handler = std::move(completion);
193 ✔
418
    backend_of(executor_).start(op);
193 ✔
419
}
193 ✔
420

421
void socket::do_read(mutable_buffers&& buffers, io_handler&& handler) NOEXCEPT
76,984 ✔
422
{
423
    if (!is_open())
76,984 ✔
424
    {
425
        post_result(executor_, handler, error::bad_stream);
12 ✔
426
        return;
12 ✔
427
    }
428

429
    const auto op = new operation{ operation::kind::read, descriptor_,
76,972 ✔
430
        executor_ };
76,972 ✔
431
    op->handler = std::move(handler);
76,972 ✔
432
    op->requested = total_size(buffers);
76,972 ✔
433
#if defined(HAVE_WINDOWS)
434
    op->buffers = to_native(buffers);
435
#else
436
    op->in = std::move(buffers);
76,972 ✔
437
#endif
438
    backend_of(executor_).start(op);
76,972 ✔
439
}
440

441
void socket::do_write(const_buffers&& buffers, io_handler&& handler) NOEXCEPT
972 ✔
442
{
443
    if (!is_open())
972 ✔
444
    {
445
        post_result(executor_, handler, error::bad_stream);
10 ✔
446
        return;
10 ✔
447
    }
448

449
    const auto op = new operation{ operation::kind::write, descriptor_,
962 ✔
450
        executor_ };
962 ✔
451
    op->handler = std::move(handler);
962 ✔
452
    op->requested = total_size(buffers);
962 ✔
453
#if defined(HAVE_WINDOWS)
454
    op->buffers = to_native(buffers);
455
#else
456
    op->out = std::move(buffers);
962 ✔
457
#endif
458
    backend_of(executor_).start(op);
962 ✔
459
}
460

461
// Synchronous.
462
// ----------------------------------------------------------------------------
463

464
void socket::connect(const endpoint& point, code& ec) NOEXCEPT
269 ✔
465
{
466
    if (!is_open())
269 ✔
467
    {
468
        open(tcp::of(point), ec);
269 ✔
469
        if (ec)
269 ✔
470
            return;
×
471
    }
472

473
    const auto address = to_socket_address(point);
269 ✔
474
    const auto handle = descriptor_->handle;
269 ✔
475
    if (::connect(handle, address.data(), address.size) == 0)
269 ✔
476
    {
477
        ec = error::success;
×
478
        return;
×
479
    }
480

481
    auto native = last_error();
269 ✔
482
    if (!is_in_progress(native) || descriptor_->non_blocking)
269 ✔
483
    {
484
        ec = to_code(native);
×
485
        return;
×
486
    }
487

488
    if (!wait_ready(handle, true, native))
269 ✔
489
    {
490
        ec = to_code(native);
×
491
        return;
×
492
    }
493

494
    int result{};
269 ✔
495
    socklen_t size = sizeof(result);
269 ✔
496
    if (::getsockopt(handle, SOL_SOCKET, SO_ERROR,
269 ✔
497
        pointer_cast<char>(&result), &size) != 0)
269 ✔
498
        result = last_error();
×
499

500
    ec = to_code(result);
269 ✔
501
}
502

503
size_t socket::do_read_some(const mutable_buffers& buffers, code& ec) NOEXCEPT
5,197 ✔
504
{
505
    const auto requested = total_size(buffers);
5,197 ✔
506
    if (is_zero(requested))
5,197 ✔
507
    {
508
        ec = error::success;
×
509
        return zero;
×
510
    }
511

512
    auto native_buffers = to_native(buffers);
5,197 ✔
513
    while (true)
514
    {
515
#if defined(HAVE_WINDOWS)
516
        DWORD bytes{};
517
        DWORD flags{};
518
        const auto result = ::WSARecv(descriptor_->handle,
519
            native_buffers.data(),
520
            possible_narrow_cast<DWORD>(native_buffers.size()), &bytes,
521
            &flags, nullptr, nullptr);
522

523
        const auto failed = result == SOCKET_ERROR;
524
        const size_t count = bytes;
525
#else
526
        const auto result = ::readv(descriptor_->handle, native_buffers.data(),
5,991 ✔
527
            possible_narrow_sign_cast<int>(native_buffers.size()));
528

529
        const auto failed = result < 0;
530
        const auto count = failed ? zero :
5,991 ✔
531
            possible_narrow_sign_cast<size_t>(result);
5,185 ✔
532
#endif
533
        if (!failed)
5,991 ✔
534
        {
535
            ec = is_zero(count) ? code{ error::peer_disconnect } :
5,185 ✔
536
                code{ error::success };
537

538
            return count;
5,185 ✔
539
        }
540

541
        auto native = last_error();
806 ✔
542
#if !defined(HAVE_WINDOWS)
543
        if (native == EINTR)
806 ✔
544
            continue;
×
545
#endif
546
        if (is_would_block(native) && !descriptor_->non_blocking &&
1,600 ✔
547
            wait_ready(descriptor_->handle, false, native))
794 ✔
548
            continue;
794 ✔
549

550
        ec = to_code(native);
12 ✔
551
        return zero;
552
    }
794 ✔
553
}
5,197 ✔
554

555
size_t socket::do_write_some(const const_buffers& buffers, code& ec) NOEXCEPT
686 ✔
556
{
557
    const auto requested = total_size(buffers);
686 ✔
558
    if (is_zero(requested))
686 ✔
559
    {
560
        ec = error::success;
×
561
        return zero;
×
562
    }
563

564
    auto native_buffers = to_native(buffers);
686 ✔
565
    while (true)
566
    {
567
#if defined(HAVE_WINDOWS)
568
        DWORD bytes{};
569
        const auto result = ::WSASend(descriptor_->handle,
570
            native_buffers.data(),
571
            possible_narrow_cast<DWORD>(native_buffers.size()), &bytes,
572
            send_flags, nullptr, nullptr);
573

574
        const auto failed = result == SOCKET_ERROR;
575
        const size_t count = bytes;
576
#else
577
        msghdr message{};
686 ✔
578
        message.msg_iov = native_buffers.data();
686 ✔
579
        message.msg_iovlen = native_buffers.size();
686 ✔
580
        const auto result = ::sendmsg(descriptor_->handle, &message,
686 ✔
581
            send_flags);
582

583
        const auto failed = result < 0;
584
        const auto count = failed ? zero :
686 ✔
585
            possible_narrow_sign_cast<size_t>(result);
686 ✔
586
#endif
587
        if (!failed)
686 ✔
588
        {
589
            ec = error::success;
686 ✔
590
            return count;
686 ✔
591
        }
592

593
        auto native = last_error();
×
594
#if !defined(HAVE_WINDOWS)
595
        if (native == EINTR)
×
596
            continue;
×
597
#endif
598
        if (is_would_block(native) && !descriptor_->non_blocking &&
×
599
            wait_ready(descriptor_->handle, true, native))
×
600
            continue;
×
601

602
        ec = to_code(native);
×
603
        return zero;
604
    }
×
605
}
686 ✔
606

607
// acceptor
608
// ----------------------------------------------------------------------------
609

610
acceptor::acceptor(const executor& executor) NOEXCEPT
582 ✔
611
  : executor_(executor), descriptor_(make_descriptor(executor))
582 ✔
612
{
613
}
582 ✔
614

615
acceptor::acceptor(const executor& executor, const endpoint& point) NOEXCEPT
94 ✔
616
  : acceptor(executor)
94 ✔
617
{
618
    code ec{};
94 ✔
619
    open(tcp::of(point), ec);
94 ✔
620
    if (!ec) set_option(reuse_address{ true }, ec);
94 ✔
621
    if (!ec) bind(point, ec);
94 ✔
622
    if (!ec) listen(ec);
94 ✔
623
    if (ec) close(ec);
94 ✔
624
}
94 ✔
625

626
acceptor::~acceptor() NOEXCEPT
582 ✔
627
{
628
    code ignore{};
582 ✔
629
    close(ignore);
582 ✔
630
}
582 ✔
631

632
acceptor::executor_type acceptor::get_executor() const NOEXCEPT
×
633
{
634
    return executor_;
×
635
}
636

637
bool acceptor::is_open() const NOEXCEPT
2,015 ✔
638
{
639
    return descriptor_->handle != invalid_socket;
2,015 ✔
640
}
641

642
acceptor::native_handle_type acceptor::native_handle() const NOEXCEPT
×
643
{
644
    return static_cast<native_handle_type>(descriptor_->handle);
×
645
}
646

647
void acceptor::open(const tcp& protocol, code& ec) NOEXCEPT
562 ✔
648
{
649
    if (is_open())
562 ✔
650
    {
651
        ec = error::connect_failed;
×
652
        return;
×
653
    }
654

655
    int native{};
562 ✔
656
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
562 ✔
657
    ec = to_code(native);
562 ✔
658
}
659

660
void acceptor::set_option(const reuse_address& option, code& ec) NOEXCEPT
544 ✔
661
{
662
    ec = set_socket_option(descriptor_->handle, SOL_SOCKET, SO_REUSEADDR,
544 ✔
663
        option.value ? 1 : 0);
544 ✔
664
}
544 ✔
665

666
void acceptor::set_option(const v6_only& option, code& ec) NOEXCEPT
3 ✔
667
{
668
    ec = set_socket_option(descriptor_->handle, IPPROTO_IPV6, IPV6_V6ONLY,
3 ✔
669
        option.value ? 1 : 0);
3 ✔
670
}
3 ✔
671

672
void acceptor::bind(const endpoint& point, code& ec) NOEXCEPT
562 ✔
673
{
674
    const auto address = to_socket_address(point);
562 ✔
675
    if (::bind(descriptor_->handle, address.data(), address.size) != 0)
562 ✔
676
    {
677
        ec = native_code();
6 ✔
678
        return;
6 ✔
679
    }
680

681
    descriptor_->bound = true;
556 ✔
682
    ec = error::success;
556 ✔
683
}
684

685
void acceptor::listen(code& ec) NOEXCEPT
556 ✔
686
{
687
    ec = ::listen(descriptor_->handle, SOMAXCONN) != 0 ? native_code() :
556 ✔
688
        code{ error::success };
689
}
556 ✔
690

691
void acceptor::close(code& ec) NOEXCEPT
823 ✔
692
{
693
    if (is_open())
823 ✔
694
        backend_of(executor_).close(*descriptor_);
562 ✔
695

696
    ec = error::success;
823 ✔
697
}
823 ✔
698

699
void acceptor::close() NOEXCEPT
58 ✔
700
{
701
    code ignore{};
58 ✔
702
    close(ignore);
58 ✔
703
}
58 ✔
704

705
void acceptor::cancel(code& ec) NOEXCEPT
145 ✔
706
{
707
    if (!is_open())
145 ✔
708
    {
709
        ec = error::bad_stream;
7 ✔
710
        return;
7 ✔
711
    }
712

713
    backend_of(executor_).cancel(*descriptor_);
138 ✔
714
    ec = error::success;
138 ✔
715
}
716

717
endpoint acceptor::local_endpoint(code& ec) const NOEXCEPT
325 ✔
718
{
719
    socket_address address{};
325 ✔
720
    address.size = sizeof(address.storage);
325 ✔
721
    if (::getsockname(descriptor_->handle, address.data(), &address.size) != 0)
325 ✔
722
    {
723
        ec = native_code();
×
724
        return {};
×
725
    }
726

727
    ec = error::success;
325 ✔
728
    return to_endpoint(address.data());
325 ✔
729
}
730

731
endpoint acceptor::local_endpoint() const NOEXCEPT
321 ✔
732
{
733
    code ignore{};
321 ✔
734
    return local_endpoint(ignore);
321 ✔
735
}
736

737
void acceptor::async_accept(socket& peer, result_handler&& handler) NOEXCEPT
354 ✔
738
{
739
    socket::io_handler completion = [handler = std::move(handler)](const code& ec,
708 ✔
740
        size_t) NOEXCEPT
741
    {
742
        handler(ec);
354 ✔
743
    };
354 ✔
744

745
    if (!is_open() || peer.is_open())
354 ✔
746
    {
747
        post_result(executor_, completion, is_open() ?
1 ✔
748
            error::connect_failed : error::bad_stream);
749
        return;
750
    }
751

752
    const auto op = new operation{ operation::kind::accept, descriptor_,
353 ✔
753
        executor_ };
353 ✔
754
    op->peer = peer.descriptor_;
353 ✔
755
    op->handler = std::move(completion);
353 ✔
756
    backend_of(executor_).start(op);
353 ✔
757
}
354 ✔
758

759
void acceptor::accept(socket& peer, code& ec) NOEXCEPT
108 ✔
760
{
761
    if (!is_open() || peer.is_open())
108 ✔
762
    {
763
        ec = is_open() ? error::connect_failed : error::bad_stream;
×
764
        return;
×
765
    }
766

767
    while (true)
768
    {
769
        const auto accepted = ::accept(descriptor_->handle, nullptr, nullptr);
210 ✔
770
        if (accepted != invalid_socket)
210 ✔
771
        {
772
            int native{};
108 ✔
773
#if !defined(HAVE_WINDOWS)
774
            ::fcntl(accepted, F_SETFD, FD_CLOEXEC);
108 ✔
775
            if (!set_blocking(accepted, false, native))
108 ✔
776
            {
777
                close_socket(accepted);
×
778
                ec = to_code(native);
×
779
                return;
×
780
            }
781
#endif
782
            if (!backend_of(executor_).adopt(peer.descriptor_, accepted,
216 ✔
783
                descriptor_->v6, native))
108 ✔
784
            {
785
                close_socket(accepted);
×
786
                ec = to_code(native);
×
787
                return;
×
788
            }
789

790
            ec = error::success;
108 ✔
791
            return;
108 ✔
792
        }
793

794
        auto native = last_error();
102 ✔
795
#if !defined(HAVE_WINDOWS)
796
        if (native == EINTR || native == ECONNABORTED)
102 ✔
797
            continue;
×
798
#endif
799
        if (is_would_block(native) && !descriptor_->non_blocking &&
204 ✔
800
            wait_ready(descriptor_->handle, false, native))
102 ✔
801
            continue;
102 ✔
802

803
        ec = to_code(native);
×
804
        return;
805
    }
102 ✔
806
}
807

808
// resolver
809
// ----------------------------------------------------------------------------
810

811
struct resolver::state
812
{
813
    std::atomic<size_t> cancels{};
814
};
815

816
static code resolve_code(int result) NOEXCEPT
128 ✔
817
{
818
    switch (result)
128 ✔
819
    {
820
        case 0:
124 ✔
821
            return error::success;
124 ✔
822
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
823
        case EAI_NODATA:
×
824
#endif
825
        case EAI_FAIL:
826
            return error::operation_failed;
×
827
        default:
4 ✔
828
            return error::resolve_failed;
4 ✔
829
    }
830
}
831

832
resolver::resolver(const executor& executor) NOEXCEPT
274 ✔
833
  : executor_(executor), state_(std::make_shared<state>())
274 ✔
834
{
835
}
274 ✔
836

837
resolver::~resolver() NOEXCEPT
274 ✔
838
{
839
    cancel();
274 ✔
840
}
274 ✔
841

842
resolver::executor_type resolver::get_executor() const NOEXCEPT
×
843
{
844
    return executor_;
×
845
}
846

847
void resolver::async_resolve(const std::string& host,
128 ✔
848
    const std::string& service, resolve_handler&& handler) NOEXCEPT
849
{
850
    auto& scheduler = executor_.context().scheduler();
128 ✔
851
    scheduler.work_started();
128 ✔
852

853
    const auto started = state_->cancels.load();
128 ✔
854
    scheduler.resolve([&scheduler, state = state_, started,
256 ✔
855
        executor = executor_, host, service,
856
        handler = std::move(handler)]() NOEXCEPT
128 ✔
857
    {
858
        addrinfo hints{};
128 ✔
859
        hints.ai_family = AF_UNSPEC;
860
        hints.ai_socktype = SOCK_STREAM;
128 ✔
861
        hints.ai_protocol = IPPROTO_TCP;
128 ✔
862
        hints.ai_flags = AI_ADDRCONFIG;
128 ✔
863

864
        addrinfo* results{};
128 ✔
865
        const auto result = ::getaddrinfo(host.c_str(), service.c_str(),
128 ✔
866
            &hints, &results);
867

868
        endpoints range{};
128 ✔
869
        for (auto it = results; it != nullptr; it = it->ai_next)
252 ✔
870
            if (it->ai_family == AF_INET || it->ai_family == AF_INET6)
124 ✔
871
                range.push_back(to_endpoint(it->ai_addr));
124 ✔
872

873
        if (results != nullptr)
128 ✔
874
            ::freeaddrinfo(results);
124 ✔
875

876
        scheduler.complete([state, started, executor, handler,
256 ✔
877
            ec = resolve_code(result), range = std::move(range)]() NOEXCEPT
128 ✔
878
        {
879
            const auto canceled = state->cancels.load() != started;
128 ✔
880
            const code value = canceled ? error::operation_canceled : ec;
128 ✔
881
            executor.dispatch([handler, value, range]() NOEXCEPT
128 ✔
882
            {
883
                handler(value, value ? endpoints{} : range);
128 ✔
884
            });
128 ✔
885
        });
128 ✔
886
    });
128 ✔
887
}
128 ✔
888

889
void resolver::cancel() NOEXCEPT
316 ✔
890
{
891
    ++state_->cancels;
316 ✔
892
}
316 ✔
893

894
// async_connect (range)
895
// ----------------------------------------------------------------------------
896

897
namespace {
898

899
struct range_connect
900
  : std::enable_shared_from_this<range_connect>
901
{
902
    range_connect(io::socket& socket, const endpoints& range,
122 ✔
903
        connect_handler&& handler) NOEXCEPT
904
      : socket(socket), range(range), handler(std::move(handler))
122 ✔
905
    {
906
    }
122 ✔
907

908
    void next() NOEXCEPT
139 ✔
909
    {
910
        if (index == range.size())
139 ✔
911
        {
912
            const auto ec = last ? last : code{ error::resolve_failed };
17 ✔
913
            socket.get_executor().post([self = shared_from_this(),
17 ✔
914
                ec]() NOEXCEPT
915
            {
916
                self->handler(ec, {});
17 ✔
917
            });
17 ✔
918

919
            return;
920
        }
921

922
        code ignore{};
122 ✔
923
        socket.close(ignore);
122 ✔
924
        socket.async_connect(range.at(index),
122 ✔
925
            [self = shared_from_this()](const code& ec) NOEXCEPT
244 ✔
926
            {
927
                self->handle(ec);
122 ✔
928
            });
122 ✔
929
    }
930

931
    void handle(const code& ec) NOEXCEPT
122 ✔
932
    {
933
        if (!ec || ec == error::operation_canceled)
122 ✔
934
        {
935
            handler(ec, ec ? endpoint{} : range.at(index));
105 ✔
936
            return;
105 ✔
937
        }
938

939
        if (!socket.is_open())
17 ✔
940
        {
UNCOV
941
            handler(error::operation_canceled, {});
×
UNCOV
942
            return;
×
943
        }
944

945
        last = ec;
17 ✔
946
        ++index;
17 ✔
947
        next();
17 ✔
948
    }
949

950
    io::socket& socket;
951
    const endpoints range;
952
    const connect_handler handler;
953
    size_t index{};
954
    code last{};
955
};
956

957
} // namespace
958

959
void async_connect(io::socket& socket, const endpoints& range,
122 ✔
960
    connect_handler&& handler) NOEXCEPT
961
{
962
    std::make_shared<range_connect>(socket, range,
122 ✔
963
        std::move(handler))->next();
122 ✔
964
}
122 ✔
965

966
BC_POP_WARNING()
967
BC_POP_WARNING()
968

969
} // namespace io
970
} // namespace network
971
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc