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

libbitcoin / libbitcoin-network / 42707057166

09 Nov 2026 09:55PM UTC coverage: 93.577% (-0.2%) from 93.746%
42707057166

push

github

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

Add testnet5 network settings.

8 of 8 new or added lines in 2 files covered. (100.0%)

35 existing lines in 5 files now uncovered.

14642 of 15647 relevant lines covered (93.58%)

2258.96 hits per line

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

83.03
/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,890 ✔
53
{
54
    return std::make_shared<descriptor>(executor.context().scheduler());
1,890 ✔
55
}
56

57
static detail::backend& backend_of(const executor& executor) NOEXCEPT
81,288 ✔
58
{
59
    return executor.context().scheduler().backend();
81,288 ✔
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,
621 ✔
69
    Option value) NOEXCEPT
70
{
71
    if (::setsockopt(socket, level, name, pointer_cast<const char>(&value),
621 ✔
72
        sizeof(value)) != 0)
73
        return native_code();
×
74

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

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

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

95
    return total;
83,810 ✔
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*>(buffer.data()))
109
        });
110

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

126
    return out;
5,874 ✔
127
}
×
128
#endif
129

130
// tcp
131
// ----------------------------------------------------------------------------
132

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

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

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

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

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

158
// socket
159
// ----------------------------------------------------------------------------
160

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

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

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

183
    return *this;
×
184
}
185

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

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

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

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

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

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

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

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

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

238
    ec = error::success;
2,439 ✔
239
}
2,439 ✔
240

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

392
#elif defined(HAVE_BSD)
393
    struct tcp_info info{};
394
    socklen_t size = sizeof(info);
395
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP, TCP_INFO,
396
        &info, &size)))
397
        return info.tcpi_state == TCPS_CLOSE_WAIT;
398
#endif
399
    return false;
400
}
401

402
void socket::async_connect(const endpoint& point,
191 ✔
403
    result_handler&& handler) NOEXCEPT
404
{
405
    io_handler completion =
406
        [handler = std::move(handler)](const code& ec, size_t) NOEXCEPT
382 ✔
407
        {
408
            handler(ec);
191 ✔
409
        };
191 ✔
410

411
    if (!is_open())
191 ✔
412
    {
413
        code ec{};
188 ✔
414
        open(tcp::of(point), ec);
188 ✔
415
        if (ec)
188 ✔
416
        {
417
            post_result(executor_, completion, ec);
×
418
            return;
×
419
        }
420
    }
421

422
    constexpr auto kind = operation::kind::connect;
423
    const auto op = new operation{ kind, descriptor_, executor_ };
191 ✔
424
    op->target = to_socket_address(point);
191 ✔
425
    op->handler = std::move(completion);
191 ✔
426
    backend_of(executor_).start(op);
191 ✔
427
}
191 ✔
428

429
void socket::do_read(mutable_buffers&& buffers, io_handler&& handler) NOEXCEPT
76,984 ✔
430
{
431
    if (!is_open())
76,984 ✔
432
    {
433
        post_result(executor_, handler, error::bad_stream);
11 ✔
434
        return;
11 ✔
435
    }
436

437
    constexpr auto kind = operation::kind::read;
438
    const auto op = new operation{ kind, descriptor_, executor_ };
76,973 ✔
439
    op->handler = std::move(handler);
76,973 ✔
440
    op->requested = total_size(buffers);
76,973 ✔
441
#if defined(HAVE_WINDOWS)
442
    op->buffers = to_native(buffers);
443
#else
444
    op->in = std::move(buffers);
76,973 ✔
445
#endif
446
    backend_of(executor_).start(op);
76,973 ✔
447
}
448

449
void socket::do_write(const_buffers&& buffers, io_handler&& handler) NOEXCEPT
973 ✔
450
{
451
    if (!is_open())
973 ✔
452
    {
453
        post_result(executor_, handler, error::bad_stream);
10 ✔
454
        return;
10 ✔
455
    }
456

457
    constexpr auto kind = operation::kind::write;
458
    const auto op = new operation{ kind, descriptor_, executor_ };
963 ✔
459
    op->handler = std::move(handler);
963 ✔
460
    op->requested = total_size(buffers);
963 ✔
461
#if defined(HAVE_WINDOWS)
462
    op->buffers = to_native(buffers);
463
#else
464
    op->out = std::move(buffers);
963 ✔
465
#endif
466
    backend_of(executor_).start(op);
963 ✔
467
}
468

469
// Synchronous.
470
// ----------------------------------------------------------------------------
471

472
void socket::connect(const endpoint& point, code& ec) NOEXCEPT
271 ✔
473
{
474
    if (!is_open())
271 ✔
475
    {
476
        open(tcp::of(point), ec);
271 ✔
477
        if (ec)
271 ✔
478
            return;
×
479
    }
480

481
    const auto address = to_socket_address(point);
271 ✔
482
    const auto handle = descriptor_->handle;
271 ✔
483
    if (is_zero(::connect(handle, address.data(), address.size)))
271 ✔
484
    {
485
        ec = error::success;
×
486
        return;
×
487
    }
488

489
    auto native = last_error();
271 ✔
490
    if (!is_in_progress(native) || descriptor_->non_blocking)
271 ✔
491
    {
492
        ec = to_code(native);
×
493
        return;
×
494
    }
495

496
    if (!wait_ready(handle, true, native))
271 ✔
497
    {
498
        ec = to_code(native);
×
499
        return;
×
500
    }
501

502
    int result{};
271 ✔
503
    socklen_t size = sizeof(result);
271 ✔
504
    const auto value = pointer_cast<char>(&result);
271 ✔
505
    if (!is_zero(::getsockopt(handle, SOL_SOCKET, SO_ERROR, value, &size)))
271 ✔
506
        result = last_error();
×
507

508
    ec = to_code(result);
271 ✔
509
}
510

511
size_t socket::do_read_some(const mutable_buffers& buffers, code& ec) NOEXCEPT
5,188 ✔
512
{
513
    const auto requested = total_size(buffers);
5,188 ✔
514
    if (is_zero(requested))
5,188 ✔
515
    {
516
        ec = error::success;
×
517
        return zero;
×
518
    }
519

520
    auto native_buffers = to_native(buffers);
5,188 ✔
521
    while (true)
522
    {
523
#if defined(HAVE_WINDOWS)
524
        DWORD bytes{};
525
        DWORD flags{};
526
        const auto result = ::WSARecv(descriptor_->handle,
527
            native_buffers.data(),
528
            possible_narrow_cast<DWORD>(native_buffers.size()),
529
            &bytes, &flags, nullptr, nullptr);
530

531
        const auto failed = result == SOCKET_ERROR;
532
        const size_t count = bytes;
533
#else
534
        const auto result = ::readv(descriptor_->handle, native_buffers.data(),
5,980 ✔
535
            possible_narrow_sign_cast<int>(native_buffers.size()));
536

537
        const auto failed = is_negative(result);
5,980 ✔
538
        const auto count = failed ? zero :
5,980 ✔
539
            possible_narrow_sign_cast<size_t>(result);
5,186 ✔
540
#endif
541
        if (!failed)
5,980 ✔
542
        {
543
            ec = is_zero(count) ? error::peer_disconnect : error::success;
10,319 ✔
544
            return count;
5,186 ✔
545
        }
546

547
        auto native = last_error();
794 ✔
548
#if !defined(HAVE_WINDOWS)
549
        if (native == EINTR)
794 ✔
550
            continue;
×
551
#endif
552
        if (is_would_block(native) && !descriptor_->non_blocking &&
1,586 ✔
553
            wait_ready(descriptor_->handle, false, native))
792 ✔
554
            continue;
792 ✔
555

556
        ec = to_code(native);
2 ✔
557
        return zero;
558
    }
792 ✔
559
}
5,188 ✔
560

561
size_t socket::do_write_some(const const_buffers& buffers, code& ec) NOEXCEPT
686 ✔
562
{
563
    const auto requested = total_size(buffers);
686 ✔
564
    if (is_zero(requested))
686 ✔
565
    {
566
        ec = error::success;
×
567
        return zero;
×
568
    }
569

570
    const auto handle = descriptor_->handle;
686 ✔
571
    auto native_buffers = to_native(buffers);
686 ✔
572
    while (true)
573
    {
574
#if defined(HAVE_WINDOWS)
575
        DWORD bytes{};
576
        const auto result = ::WSASend(handle,
577
            native_buffers.data(),
578
            possible_narrow_cast<DWORD>(native_buffers.size()), &bytes,
579
            send_flags, nullptr, nullptr);
580

581
        const auto failed = result == SOCKET_ERROR;
582
        const size_t count = bytes;
583
#else
584
        msghdr message{};
686 ✔
585
        message.msg_iov = native_buffers.data();
686 ✔
586
        message.msg_iovlen = native_buffers.size();
686 ✔
587
        const auto result = ::sendmsg(handle, &message, send_flags);
686 ✔
588

589
        const auto failed = is_negative(result);
686 ✔
590
        const auto count = failed ? zero :
686 ✔
591
            possible_narrow_sign_cast<size_t>(result);
686 ✔
592
#endif
593
        if (!failed)
686 ✔
594
        {
595
            ec = error::success;
686 ✔
596
            return count;
686 ✔
597
        }
598

599
        auto native = last_error();
×
600
#if !defined(HAVE_WINDOWS)
601
        if (native == EINTR)
×
602
            continue;
×
603
#endif
604
        if (is_would_block(native) && !descriptor_->non_blocking &&
×
605
            wait_ready(descriptor_->handle, true, native))
×
606
            continue;
×
607

608
        ec = to_code(native);
×
609
        return zero;
610
    }
×
611
}
686 ✔
612

613
// acceptor
614
// ----------------------------------------------------------------------------
615

616
acceptor::acceptor(const executor& executor) NOEXCEPT
584 ✔
617
  : executor_(executor), descriptor_(make_descriptor(executor))
584 ✔
618
{
619
}
584 ✔
620

621
acceptor::acceptor(const executor& executor, const endpoint& point) NOEXCEPT
94 ✔
622
  : acceptor(executor)
94 ✔
623
{
624
    code ec{};
94 ✔
625
    open(tcp::of(point), ec);
94 ✔
626
    if (!ec) set_option(reuse_address{ true }, ec);
94 ✔
627
    if (!ec) bind(point, ec);
94 ✔
628
    if (!ec) listen(ec);
94 ✔
629
    if (ec) close(ec);
94 ✔
630
}
94 ✔
631

632
acceptor::~acceptor() NOEXCEPT
584 ✔
633
{
634
    code ignore{};
584 ✔
635
    close(ignore);
584 ✔
636
}
584 ✔
637

638
acceptor::executor_type acceptor::get_executor() const NOEXCEPT
×
639
{
640
    return executor_;
×
641
}
642

643
bool acceptor::is_open() const NOEXCEPT
2,017 ✔
644
{
645
    return descriptor_->handle != invalid_socket;
2,017 ✔
646
}
647

648
acceptor::native_handle_type acceptor::native_handle() const NOEXCEPT
×
649
{
650
    return static_cast<native_handle_type>(descriptor_->handle);
×
651
}
652

653
void acceptor::open(const tcp& protocol, code& ec) NOEXCEPT
564 ✔
654
{
655
    if (is_open())
564 ✔
656
    {
657
        ec = error::connect_failed;
×
658
        return;
×
659
    }
660

661
    int native{};
564 ✔
662
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
564 ✔
663
    ec = to_code(native);
564 ✔
664
}
665

666
void acceptor::set_option(const reuse_address& option, code& ec) NOEXCEPT
268 ✔
667
{
668
    ec = set_socket_option(descriptor_->handle, SOL_SOCKET, SO_REUSEADDR,
268 ✔
669
        to_int(option.value));
268 ✔
670
}
268 ✔
671

672
void acceptor::set_option(const v6_only& option, code& ec) NOEXCEPT
3 ✔
673
{
674
    ec = set_socket_option(descriptor_->handle, IPPROTO_IPV6, IPV6_V6ONLY,
3 ✔
675
        to_int(option.value));
3 ✔
676
}
3 ✔
677

678
void acceptor::bind(const endpoint& point, code& ec) NOEXCEPT
564 ✔
679
{
680
    const auto address = to_socket_address(point);
564 ✔
681
    if (!is_zero(::bind(descriptor_->handle, address.data(), address.size)))
564 ✔
682
    {
683
        ec = native_code();
6 ✔
684
        return;
6 ✔
685
    }
686

687
    descriptor_->bound = true;
558 ✔
688
    ec = error::success;
558 ✔
689
}
690

691
void acceptor::listen(code& ec) NOEXCEPT
558 ✔
692
{
693
    ec = !is_zero(::listen(descriptor_->handle, SOMAXCONN)) ? native_code() :
558 ✔
694
        code{ error::success };
695
}
558 ✔
696

697
void acceptor::close(code& ec) NOEXCEPT
821 ✔
698
{
699
    if (is_open())
821 ✔
700
        backend_of(executor_).close(*descriptor_);
564 ✔
701

702
    ec = error::success;
821 ✔
703
}
821 ✔
704

705
void acceptor::close() NOEXCEPT
55 ✔
706
{
707
    code ignore{};
55 ✔
708
    close(ignore);
55 ✔
709
}
55 ✔
710

711
void acceptor::cancel(code& ec) NOEXCEPT
145 ✔
712
{
713
    if (!is_open())
145 ✔
714
    {
715
        ec = error::bad_stream;
7 ✔
716
        return;
7 ✔
717
    }
718

719
    backend_of(executor_).cancel(*descriptor_);
138 ✔
720
    ec = error::success;
138 ✔
721
}
722

723
endpoint acceptor::local_endpoint(code& ec) const NOEXCEPT
469 ✔
724
{
725
    socket_address address{};
469 ✔
726
    address.size = sizeof(address.storage);
469 ✔
727
    const auto handle = descriptor_->handle;
469 ✔
728
    if (!is_zero(::getsockname(handle, address.data(), &address.size)))
469 ✔
729
    {
730
        ec = native_code();
×
731
        return {};
×
732
    }
733

734
    ec = error::success;
469 ✔
735
    return to_endpoint(address.data());
469 ✔
736
}
737

738
endpoint acceptor::local_endpoint() const NOEXCEPT
465 ✔
739
{
740
    code ignore{};
465 ✔
741
    return local_endpoint(ignore);
465 ✔
742
}
743

744
void acceptor::async_accept(socket& peer, result_handler&& handler) NOEXCEPT
356 ✔
745
{
746
    socket::io_handler completion =
747
        [handler = std::move(handler)](const code& ec, size_t) NOEXCEPT
712 ✔
748
        {
749
            handler(ec);
356 ✔
750
        };
356 ✔
751

752
    if (!is_open() || peer.is_open())
356 ✔
753
    {
754
        const auto ec = is_open() ? error::connect_failed : error::bad_stream;
1 ✔
755
        post_result(executor_, completion, ec);
1 ✔
756
        return;
757
    }
758

759
    constexpr auto kind = operation::kind::accept;
760
    const auto op = new operation{ kind, descriptor_, executor_ };
355 ✔
761
    op->peer = peer.descriptor_;
355 ✔
762
    op->handler = std::move(completion);
355 ✔
763
    backend_of(executor_).start(op);
355 ✔
764
}
356 ✔
765

766
void acceptor::accept(socket& peer, code& ec) NOEXCEPT
108 ✔
767
{
768
    if (!is_open() || peer.is_open())
108 ✔
769
    {
770
        ec = is_open() ? error::connect_failed : error::bad_stream;
×
771
        return;
×
772
    }
773

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

797
            ec = error::success;
108 ✔
798
            return;
108 ✔
799
        }
800

801
        auto native = last_error();
102 ✔
802
#if !defined(HAVE_WINDOWS)
803
        if (native == EINTR || native == ECONNABORTED)
102 ✔
804
            continue;
×
805
#endif
806
        if (is_would_block(native) && !descriptor_->non_blocking &&
204 ✔
807
            wait_ready(descriptor_->handle, false, native))
102 ✔
808
            continue;
102 ✔
809

810
        ec = to_code(native);
×
811
        return;
812
    }
102 ✔
813
}
814

815
// resolver
816
// ----------------------------------------------------------------------------
817

818
struct resolver::state
819
{
820
    std::atomic<size_t> cancels{};
821
};
822

823
static code resolve_code(int result) NOEXCEPT
128 ✔
824
{
825
    switch (result)
128 ✔
826
    {
827
        case 0:
124 ✔
828
            return error::success;
124 ✔
829
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
830
        case EAI_NODATA:
×
831
#endif
832
        case EAI_FAIL:
833
            return error::operation_failed;
×
834
        default:
4 ✔
835
            return error::resolve_failed;
4 ✔
836
    }
837
}
838

839
resolver::resolver(const executor& executor) NOEXCEPT
274 ✔
840
  : executor_(executor), state_(std::make_shared<state>())
274 ✔
841
{
842
}
274 ✔
843

844
resolver::~resolver() NOEXCEPT
274 ✔
845
{
846
    cancel();
274 ✔
847
}
274 ✔
848

849
resolver::executor_type resolver::get_executor() const NOEXCEPT
×
850
{
851
    return executor_;
×
852
}
853

854
void resolver::async_resolve(const std::string& host,
128 ✔
855
    const std::string& service, resolve_handler&& handler) NOEXCEPT
856
{
857
    auto& scheduler = executor_.context().scheduler();
128 ✔
858
    scheduler.work_started();
128 ✔
859

860
    const auto started = state_->cancels.load();
128 ✔
861
    scheduler.resolve([&scheduler, state = state_, started,
256 ✔
862
        executor = executor_, host, service,
863
        handler = std::move(handler)]() NOEXCEPT
128 ✔
864
    {
865
        addrinfo hints{};
128 ✔
866
        hints.ai_family = AF_UNSPEC;
867
        hints.ai_socktype = SOCK_STREAM;
128 ✔
868
        hints.ai_protocol = IPPROTO_TCP;
128 ✔
869
        hints.ai_flags = AI_ADDRCONFIG;
128 ✔
870

871
        addrinfo* results{};
128 ✔
872
        const auto result = ::getaddrinfo(host.c_str(), service.c_str(),
128 ✔
873
            &hints, &results);
874

875
        endpoints range{};
128 ✔
876
        for (auto it = results; it != nullptr; it = it->ai_next)
252 ✔
877
            if (it->ai_family == AF_INET || it->ai_family == AF_INET6)
124 ✔
878
                range.push_back(to_endpoint(it->ai_addr));
124 ✔
879

880
        if (results != nullptr)
128 ✔
881
            ::freeaddrinfo(results);
124 ✔
882

883
        scheduler.complete([state, started, executor, handler,
256 ✔
884
            ec = resolve_code(result), range = std::move(range)]() NOEXCEPT
128 ✔
885
        {
886
            const auto canceled = state->cancels.load() != started;
128 ✔
887
            const code value = canceled ? error::operation_canceled : ec;
128 ✔
888
            executor.dispatch([handler, value, range]() NOEXCEPT
128 ✔
889
            {
890
                handler(value, value ? endpoints{} : range);
128 ✔
891
            });
128 ✔
892
        });
128 ✔
893
    });
128 ✔
894
}
128 ✔
895

896
void resolver::cancel() NOEXCEPT
316 ✔
897
{
898
    ++state_->cancels;
316 ✔
899
}
316 ✔
900

901
// async_connect (range)
902
// ----------------------------------------------------------------------------
903

904
namespace {
905

906
struct range_connect
907
  : std::enable_shared_from_this<range_connect>
908
{
909
    range_connect(io::socket& socket, const endpoints& range,
120 ✔
910
        connect_handler&& handler) NOEXCEPT
911
      : socket(socket), range(range), handler(std::move(handler))
120 ✔
912
    {
913
    }
120 ✔
914

915
    void next() NOEXCEPT
137 ✔
916
    {
917
        if (index == range.size())
137 ✔
918
        {
919
            const auto ec = last ? last : code{ error::resolve_failed };
17 ✔
920
            socket.get_executor().post(
51 ✔
921
                [self = shared_from_this(), ec]() NOEXCEPT
34 ✔
922
                {
923
                    self->handler(ec, {});
17 ✔
924
                });
17 ✔
925

926
            return;
927
        }
928

929
        code ignore{};
120 ✔
930
        socket.close(ignore);
120 ✔
931
        socket.async_connect(range.at(index),
120 ✔
932
            [self = shared_from_this()](const code& ec) NOEXCEPT
240 ✔
933
            {
934
                self->handle(ec);
120 ✔
935
            });
120 ✔
936
    }
937

938
    void handle(const code& ec) NOEXCEPT
120 ✔
939
    {
940
        if (!ec || ec == error::operation_canceled)
120 ✔
941
        {
942
            handler(ec, ec ? endpoint{} : range.at(index));
103 ✔
943
            return;
103 ✔
944
        }
945

946
        if (!socket.is_open())
17 ✔
947
        {
948
            handler(error::operation_canceled, {});
×
949
            return;
×
950
        }
951

952
        last = ec;
17 ✔
953
        ++index;
17 ✔
954
        next();
17 ✔
955
    }
956

957
    io::socket& socket;
958
    const endpoints range;
959
    const connect_handler handler;
960
    size_t index{};
961
    code last{};
962
};
963

964
} // namespace
965

966
void async_connect(io::socket& socket, const endpoints& range,
120 ✔
967
    connect_handler&& handler) NOEXCEPT
968
{
969
    std::make_shared<range_connect>(socket, range,
120 ✔
970
        std::move(handler))->next();
120 ✔
971
}
120 ✔
972

973
BC_POP_WARNING()
974
BC_POP_WARNING()
975

976
} // namespace io
977
} // namespace network
978
} // 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