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

libbitcoin / libbitcoin-network / 43516148593

13 Nov 2026 10:01PM UTC coverage: 93.776% (-0.2%) from 93.947%
43516148593

push

github

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

Test only POLLRDHUP in half_closed fallback.

0 of 1 new or added line in 1 file covered. (0.0%)

36 existing lines in 6 files now uncovered.

14856 of 15842 relevant lines covered (93.78%)

2336.82 hits per line

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

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

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

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

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

75
    return error::success;
649 ✔
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
84,557 ✔
90
{
91
    size_t total{};
92
    for (const auto& buffer: buffers)
169,369 ✔
93
        total = ceilinged_add(total, buffer.size());
84,812 ✔
94

95
    return total;
84,557 ✔
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
6,517 ✔
116
{
117
    std::vector<iovec> out{};
6,517 ✔
118
    out.reserve(buffers.size());
6,517 ✔
119
    for (const auto& buffer: buffers)
13,034 ✔
120
        out.push_back(
6,517 ✔
121
        {
122
            const_cast<void*>(static_cast<const void*>(buffer.data())),
6,517 ✔
123
            buffer.size()
6,517 ✔
124
        });
125

126
    return out;
6,517 ✔
127
}
×
128
#endif
129

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

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

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

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

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

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

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

161
socket::socket(const executor& executor) NOEXCEPT
1,189 ✔
162
  : executor_(executor), descriptor_(make_descriptor(executor))
1,189 ✔
163
{
164
}
1,189 ✔
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,364 ✔
187
{
188
    code ignore{};
1,364 ✔
189
    close(ignore);
1,364 ✔
190
}
1,364 ✔
191

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

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

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

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

215
    int native{};
522 ✔
216
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
522 ✔
217
    ec = to_code(native);
522 ✔
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,578 ✔
234
{
235
    if (is_open())
2,578 ✔
236
        backend_of(executor_).close(*descriptor_);
993 ✔
237

238
    ec = error::success;
2,578 ✔
239
}
2,578 ✔
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
601 ✔
260
{
261
    const auto how = what == shutdown_type::receive ? shutdown_receive :
601 ✔
262
        (what == shutdown_type::send ? shutdown_send : shutdown_both);
263

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

268
void socket::set_option(const no_delay& option, code& ec) NOEXCEPT
378 ✔
269
{
270
    ec = set_socket_option(descriptor_->handle, IPPROTO_TCP, TCP_NODELAY,
378 ✔
271
        to_int(option.value));
378 ✔
272
}
378 ✔
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
432 ✔
324
{
325
    socket_address address{};
432 ✔
326
    address.size = sizeof(address.storage);
432 ✔
327
    const auto handle = descriptor_->handle;
432 ✔
328
    if (!is_zero(::getsockname(handle, address.data(), &address.size)))
432 ✔
329
    {
330
        ec = native_code();
×
331
        return {};
×
332
    }
333

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

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

349
    ec = error::success;
298 ✔
350
    return to_endpoint(address.data());
298 ✔
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
14 ✔
366
{
367
    if (!is_open())
14 ✔
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
            info.State == TCPSTATE_CLOSED;
378

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

387
#elif defined(HAVE_LINUX)
388
    constexpr auto minimum = possible_narrow_cast<socklen_t>(
389
        offsetof(tcp_info, tcpi_rcv_mss) + sizeof(tcp_info::tcpi_rcv_mss));
390

391
    struct tcp_info info{};
13 ✔
392
    socklen_t size = sizeof(info);
13 ✔
393
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP, TCP_INFO,
13 ✔
394
        &info, &size)) && size >= minimum)
13 ✔
395
        return info.tcpi_state == TCP_CLOSE_WAIT ||
13 ✔
396
            (info.tcpi_state == TCP_CLOSE && !is_zero(info.tcpi_rcv_mss));
2 ✔
397

398
    pollfd peer{ descriptor_->handle, POLLRDHUP, 0 };
×
399
    if (is_one(::poll(&peer, 1, 0)))
×
NEW
400
        return is_nonzero(peer.revents & POLLRDHUP);
×
401

402
#elif defined(HAVE_BSD)
403
    struct tcp_info info{};
404
    socklen_t size = sizeof(info);
405
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP, TCP_INFO,
406
        &info, &size)))
407
        return info.tcpi_state == TCPS_CLOSE_WAIT;
408
#endif
409
    return false;
410
}
411

412
void socket::async_connect(const endpoint& point,
191 ✔
413
    result_handler&& handler) NOEXCEPT
414
{
415
    io_handler completion =
416
        [handler = std::move(handler)](const code& ec, size_t) NOEXCEPT
382 ✔
417
        {
418
            handler(ec);
191 ✔
419
        };
191 ✔
420

421
    if (!is_open())
191 ✔
422
    {
423
        code ec{};
188 ✔
424
        open(tcp::of(point), ec);
188 ✔
425
        if (ec)
188 ✔
426
        {
427
            post_result(executor_, completion, ec);
×
428
            return;
×
429
        }
430
    }
431

432
    constexpr auto kind = operation::kind::connect;
433
    const auto op = new operation{ kind, descriptor_, executor_ };
191 ✔
434
    op->target = to_socket_address(point);
191 ✔
435
    op->handler = std::move(completion);
191 ✔
436
    backend_of(executor_).start(op);
191 ✔
437
}
191 ✔
438

439
void socket::do_read(mutable_buffers&& buffers, io_handler&& handler) NOEXCEPT
77,026 ✔
440
{
441
    if (!is_open())
77,026 ✔
442
    {
443
        post_result(executor_, handler, error::bad_stream);
11 ✔
444
        return;
11 ✔
445
    }
446

447
    constexpr auto kind = operation::kind::read;
448
    const auto op = new operation{ kind, descriptor_, executor_ };
77,015 ✔
449
    op->handler = std::move(handler);
77,015 ✔
450
    op->requested = total_size(buffers);
77,015 ✔
451
#if defined(HAVE_WINDOWS)
452
    op->buffers = to_native(buffers);
453
#else
454
    op->in = std::move(buffers);
77,015 ✔
455
#endif
456
    backend_of(executor_).start(op);
77,015 ✔
457
}
458

459
void socket::do_write(const_buffers&& buffers, io_handler&& handler) NOEXCEPT
1,035 ✔
460
{
461
    if (!is_open())
1,035 ✔
462
    {
463
        post_result(executor_, handler, error::bad_stream);
10 ✔
464
        return;
10 ✔
465
    }
466

467
    constexpr auto kind = operation::kind::write;
468
    const auto op = new operation{ kind, descriptor_, executor_ };
1,025 ✔
469
    op->handler = std::move(handler);
1,025 ✔
470
    op->requested = total_size(buffers);
1,025 ✔
471
#if defined(HAVE_WINDOWS)
472
    op->buffers = to_native(buffers);
473
#else
474
    op->out = std::move(buffers);
1,025 ✔
475
#endif
476
    backend_of(executor_).start(op);
1,025 ✔
477
}
478

479
// Synchronous.
480
// ----------------------------------------------------------------------------
481

482
void socket::connect(const endpoint& point, code& ec) NOEXCEPT
300 ✔
483
{
484
    if (!is_open())
300 ✔
485
    {
486
        open(tcp::of(point), ec);
300 ✔
487
        if (ec)
300 ✔
488
            return;
×
489
    }
490

491
    const auto address = to_socket_address(point);
300 ✔
492
    const auto handle = descriptor_->handle;
300 ✔
493
    if (is_zero(::connect(handle, address.data(), address.size)))
300 ✔
494
    {
495
        ec = error::success;
×
496
        return;
×
497
    }
498

499
    auto native = last_error();
300 ✔
500
    if (!is_in_progress(native) || descriptor_->non_blocking)
300 ✔
501
    {
502
        ec = to_code(native);
×
503
        return;
×
504
    }
505

506
    if (!wait_ready(handle, true, native))
300 ✔
507
    {
508
        ec = to_code(native);
×
509
        return;
×
510
    }
511

512
    int result{};
300 ✔
513
    socklen_t size = sizeof(result);
300 ✔
514
    const auto value = pointer_cast<char>(&result);
300 ✔
515
    if (!is_zero(::getsockopt(handle, SOL_SOCKET, SO_ERROR, value, &size)))
300 ✔
516
        result = last_error();
×
517

518
    ec = to_code(result);
300 ✔
519
}
520

521
size_t socket::do_read_some(const mutable_buffers& buffers, code& ec) NOEXCEPT
5,793 ✔
522
{
523
    const auto requested = total_size(buffers);
5,793 ✔
524
    if (is_zero(requested))
5,793 ✔
525
    {
526
        ec = error::success;
×
527
        return zero;
×
528
    }
529

530
    auto native_buffers = to_native(buffers);
5,793 ✔
531
    while (true)
532
    {
533
#if defined(HAVE_WINDOWS)
534
        DWORD bytes{};
535
        DWORD flags{};
536
        const auto result = ::WSARecv(descriptor_->handle,
537
            native_buffers.data(),
538
            possible_narrow_cast<DWORD>(native_buffers.size()),
539
            &bytes, &flags, nullptr, nullptr);
540

541
        const auto failed = result == SOCKET_ERROR;
542
        const size_t count = bytes;
543
#else
544
        const auto result = ::readv(descriptor_->handle, native_buffers.data(),
6,644 ✔
545
            possible_narrow_sign_cast<int>(native_buffers.size()));
546

547
        const auto failed = is_negative(result);
6,644 ✔
548
        const auto count = failed ? zero :
6,644 ✔
549
            possible_narrow_sign_cast<size_t>(result);
5,791 ✔
550
#endif
551
        if (!failed)
6,644 ✔
552
        {
553
            ec = is_zero(count) ? error::peer_disconnect : error::success;
11,522 ✔
554
            return count;
5,791 ✔
555
        }
556

557
        auto native = last_error();
853 ✔
558
#if !defined(HAVE_WINDOWS)
559
        if (native == EINTR)
853 ✔
560
            continue;
×
561
#endif
562
        if (is_would_block(native) && !descriptor_->non_blocking &&
1,704 ✔
563
            wait_ready(descriptor_->handle, false, native))
851 ✔
564
            continue;
851 ✔
565

566
        ec = to_code(native);
2 ✔
567
        return zero;
568
    }
851 ✔
569
}
5,793 ✔
570

571
size_t socket::do_write_some(const const_buffers& buffers, code& ec) NOEXCEPT
724 ✔
572
{
573
    const auto requested = total_size(buffers);
724 ✔
574
    if (is_zero(requested))
724 ✔
575
    {
576
        ec = error::success;
×
577
        return zero;
×
578
    }
579

580
    const auto handle = descriptor_->handle;
724 ✔
581
    auto native_buffers = to_native(buffers);
724 ✔
582
    while (true)
583
    {
584
#if defined(HAVE_WINDOWS)
585
        DWORD bytes{};
586
        const auto result = ::WSASend(handle,
587
            native_buffers.data(),
588
            possible_narrow_cast<DWORD>(native_buffers.size()), &bytes,
589
            send_flags, nullptr, nullptr);
590

591
        const auto failed = result == SOCKET_ERROR;
592
        const size_t count = bytes;
593
#else
594
        msghdr message{};
724 ✔
595
        message.msg_iov = native_buffers.data();
724 ✔
596
        message.msg_iovlen = native_buffers.size();
724 ✔
597
        const auto result = ::sendmsg(handle, &message, send_flags);
724 ✔
598

599
        const auto failed = is_negative(result);
724 ✔
600
        const auto count = failed ? zero :
724 ✔
601
            possible_narrow_sign_cast<size_t>(result);
724 ✔
602
#endif
603
        if (!failed)
724 ✔
604
        {
605
            ec = error::success;
724 ✔
606
            return count;
724 ✔
607
        }
608

609
        auto native = last_error();
×
610
#if !defined(HAVE_WINDOWS)
611
        if (native == EINTR)
×
612
            continue;
×
613
#endif
614
        if (is_would_block(native) && !descriptor_->non_blocking &&
×
615
            wait_ready(descriptor_->handle, true, native))
×
616
            continue;
×
617

618
        ec = to_code(native);
×
619
        return zero;
620
    }
×
621
}
724 ✔
622

623
// acceptor
624
// ----------------------------------------------------------------------------
625

626
acceptor::acceptor(const executor& executor) NOEXCEPT
613 ✔
627
  : executor_(executor), descriptor_(make_descriptor(executor))
613 ✔
628
{
629
}
613 ✔
630

631
acceptor::acceptor(const executor& executor, const endpoint& point) NOEXCEPT
94 ✔
632
  : acceptor(executor)
94 ✔
633
{
634
    code ec{};
94 ✔
635
    open(tcp::of(point), ec);
94 ✔
636
    if (!ec) set_option(reuse_address{ true }, ec);
94 ✔
637
    if (!ec) bind(point, ec);
94 ✔
638
    if (!ec) listen(ec);
94 ✔
639
    if (ec) close(ec);
94 ✔
640
}
94 ✔
641

642
acceptor::~acceptor() NOEXCEPT
613 ✔
643
{
644
    code ignore{};
613 ✔
645
    close(ignore);
613 ✔
646
}
613 ✔
647

648
acceptor::executor_type acceptor::get_executor() const NOEXCEPT
×
649
{
650
    return executor_;
×
651
}
652

653
bool acceptor::is_open() const NOEXCEPT
2,104 ✔
654
{
655
    return descriptor_->handle != invalid_socket;
2,104 ✔
656
}
657

658
acceptor::native_handle_type acceptor::native_handle() const NOEXCEPT
×
659
{
660
    return static_cast<native_handle_type>(descriptor_->handle);
×
661
}
662

663
void acceptor::open(const tcp& protocol, code& ec) NOEXCEPT
593 ✔
664
{
665
    if (is_open())
593 ✔
666
    {
667
        ec = error::connect_failed;
×
668
        return;
×
669
    }
670

671
    int native{};
593 ✔
672
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
593 ✔
673
    ec = to_code(native);
593 ✔
674
}
675

676
void acceptor::set_option(const reuse_address& option, code& ec) NOEXCEPT
268 ✔
677
{
678
    ec = set_socket_option(descriptor_->handle, SOL_SOCKET, SO_REUSEADDR,
268 ✔
679
        to_int(option.value));
268 ✔
680
}
268 ✔
681

682
void acceptor::set_option(const v6_only& option, code& ec) NOEXCEPT
3 ✔
683
{
684
    ec = set_socket_option(descriptor_->handle, IPPROTO_IPV6, IPV6_V6ONLY,
3 ✔
685
        to_int(option.value));
3 ✔
686
}
3 ✔
687

688
void acceptor::bind(const endpoint& point, code& ec) NOEXCEPT
593 ✔
689
{
690
    const auto address = to_socket_address(point);
593 ✔
691
    if (!is_zero(::bind(descriptor_->handle, address.data(), address.size)))
593 ✔
692
    {
693
        ec = native_code();
6 ✔
694
        return;
6 ✔
695
    }
696

697
    descriptor_->bound = true;
587 ✔
698
    ec = error::success;
587 ✔
699
}
700

701
void acceptor::listen(code& ec) NOEXCEPT
587 ✔
702
{
703
    ec = !is_zero(::listen(descriptor_->handle, SOMAXCONN)) ? native_code() :
587 ✔
704
        code{ error::success };
705
}
587 ✔
706

707
void acceptor::close(code& ec) NOEXCEPT
850 ✔
708
{
709
    if (is_open())
850 ✔
710
        backend_of(executor_).close(*descriptor_);
593 ✔
711

712
    ec = error::success;
850 ✔
713
}
850 ✔
714

715
void acceptor::close() NOEXCEPT
55 ✔
716
{
717
    code ignore{};
55 ✔
718
    close(ignore);
55 ✔
719
}
55 ✔
720

721
void acceptor::cancel(code& ec) NOEXCEPT
145 ✔
722
{
723
    if (!is_open())
145 ✔
724
    {
725
        ec = error::bad_stream;
7 ✔
726
        return;
7 ✔
727
    }
728

729
    backend_of(executor_).cancel(*descriptor_);
138 ✔
730
    ec = error::success;
138 ✔
731
}
732

733
endpoint acceptor::local_endpoint(code& ec) const NOEXCEPT
498 ✔
734
{
735
    socket_address address{};
498 ✔
736
    address.size = sizeof(address.storage);
498 ✔
737
    const auto handle = descriptor_->handle;
498 ✔
738
    if (!is_zero(::getsockname(handle, address.data(), &address.size)))
498 ✔
739
    {
740
        ec = native_code();
×
741
        return {};
×
742
    }
743

744
    ec = error::success;
498 ✔
745
    return to_endpoint(address.data());
498 ✔
746
}
747

748
endpoint acceptor::local_endpoint() const NOEXCEPT
493 ✔
749
{
750
    code ignore{};
493 ✔
751
    return local_endpoint(ignore);
493 ✔
752
}
753

754
void acceptor::async_accept(socket& peer, result_handler&& handler) NOEXCEPT
384 ✔
755
{
756
    socket::io_handler completion =
757
        [handler = std::move(handler)](const code& ec, size_t) NOEXCEPT
768 ✔
758
        {
759
            handler(ec);
384 ✔
760
        };
384 ✔
761

762
    if (!is_open() || peer.is_open())
384 ✔
763
    {
764
        const auto ec = is_open() ? error::connect_failed : error::bad_stream;
1 ✔
765
        post_result(executor_, completion, ec);
1 ✔
766
        return;
767
    }
768

769
    constexpr auto kind = operation::kind::accept;
770
    const auto op = new operation{ kind, descriptor_, executor_ };
383 ✔
771
    op->peer = peer.descriptor_;
383 ✔
772
    op->handler = std::move(completion);
383 ✔
773
    backend_of(executor_).start(op);
383 ✔
774
}
384 ✔
775

776
void acceptor::accept(socket& peer, code& ec) NOEXCEPT
109 ✔
777
{
778
    if (!is_open() || peer.is_open())
109 ✔
779
    {
780
        ec = is_open() ? error::connect_failed : error::bad_stream;
×
781
        return;
×
782
    }
783

784
    while (true)
785
    {
786
        const auto accepted = ::accept(descriptor_->handle, nullptr, nullptr);
211 ✔
787
        if (accepted != invalid_socket)
211 ✔
788
        {
789
            int native{};
109 ✔
790
#if !defined(HAVE_WINDOWS)
791
            ::fcntl(accepted, F_SETFD, FD_CLOEXEC);
109 ✔
792
            if (!set_blocking(accepted, false, native))
109 ✔
793
            {
794
                close_socket(accepted);
×
795
                ec = to_code(native);
×
796
                return;
×
797
            }
798
#endif
799
            if (!backend_of(executor_).adopt(peer.descriptor_, accepted,
218 ✔
800
                descriptor_->v6, native))
109 ✔
801
            {
802
                close_socket(accepted);
×
803
                ec = to_code(native);
×
804
                return;
×
805
            }
806

807
            ec = error::success;
109 ✔
808
            return;
109 ✔
809
        }
810

811
        auto native = last_error();
102 ✔
812
#if !defined(HAVE_WINDOWS)
813
        if (native == EINTR || native == ECONNABORTED)
102 ✔
814
            continue;
×
815
#endif
816
        if (is_would_block(native) && !descriptor_->non_blocking &&
204 ✔
817
            wait_ready(descriptor_->handle, false, native))
102 ✔
818
            continue;
102 ✔
819

820
        ec = to_code(native);
×
821
        return;
822
    }
102 ✔
823
}
824

825
// resolver
826
// ----------------------------------------------------------------------------
827

828
struct resolver::state
829
{
830
    std::atomic<size_t> cancels{};
831
};
832

833
static code resolve_code(int result) NOEXCEPT
128 ✔
834
{
835
    switch (result)
128 ✔
836
    {
837
        case 0:
124 ✔
838
            return error::success;
124 ✔
839
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
840
        case EAI_NODATA:
×
841
#endif
842
        case EAI_FAIL:
843
            return error::operation_failed;
×
844
        default:
4 ✔
845
            return error::resolve_failed;
4 ✔
846
    }
847
}
848

849
resolver::resolver(const executor& executor) NOEXCEPT
274 ✔
850
  : executor_(executor), state_(std::make_shared<state>())
274 ✔
851
{
852
}
274 ✔
853

854
resolver::~resolver() NOEXCEPT
274 ✔
855
{
856
    cancel();
274 ✔
857
}
274 ✔
858

859
resolver::executor_type resolver::get_executor() const NOEXCEPT
×
860
{
861
    return executor_;
×
862
}
863

864
void resolver::async_resolve(const std::string& host,
128 ✔
865
    const std::string& service, resolve_handler&& handler) NOEXCEPT
866
{
867
    auto& scheduler = executor_.context().scheduler();
128 ✔
868
    scheduler.work_started();
128 ✔
869

870
    const auto started = state_->cancels.load();
128 ✔
871
    scheduler.resolve([&scheduler, state = state_, started,
256 ✔
872
        executor = executor_, host, service,
873
        handler = std::move(handler)]() NOEXCEPT
128 ✔
874
    {
875
        addrinfo hints{};
128 ✔
876
        hints.ai_family = AF_UNSPEC;
877
        hints.ai_socktype = SOCK_STREAM;
128 ✔
878
        hints.ai_protocol = IPPROTO_TCP;
128 ✔
879
        hints.ai_flags = AI_ADDRCONFIG;
128 ✔
880

881
        addrinfo* results{};
128 ✔
882
        const auto result = ::getaddrinfo(host.c_str(), service.c_str(),
128 ✔
883
            &hints, &results);
884

885
        endpoints range{};
128 ✔
886
        for (auto it = results; it != nullptr; it = it->ai_next)
252 ✔
887
            if (it->ai_family == AF_INET || it->ai_family == AF_INET6)
124 ✔
888
                range.push_back(to_endpoint(it->ai_addr));
124 ✔
889

890
        if (results != nullptr)
128 ✔
891
            ::freeaddrinfo(results);
124 ✔
892

893
        scheduler.complete([state, started, executor, handler,
256 ✔
894
            ec = resolve_code(result), range = std::move(range)]() NOEXCEPT
128 ✔
895
        {
896
            const auto canceled = state->cancels.load() != started;
128 ✔
897
            const code value = canceled ? error::operation_canceled : ec;
128 ✔
898
            executor.dispatch([handler, value, range]() NOEXCEPT
128 ✔
899
            {
900
                handler(value, value ? endpoints{} : range);
128 ✔
901
            });
128 ✔
902
        });
128 ✔
903
    });
128 ✔
904
}
128 ✔
905

906
void resolver::cancel() NOEXCEPT
316 ✔
907
{
908
    ++state_->cancels;
316 ✔
909
}
316 ✔
910

911
// async_connect (range)
912
// ----------------------------------------------------------------------------
913

914
namespace {
915

916
struct range_connect
917
  : std::enable_shared_from_this<range_connect>
918
{
919
    range_connect(io::socket& socket, const endpoints& range,
120 ✔
920
        connect_handler&& handler) NOEXCEPT
921
      : socket(socket), range(range), handler(std::move(handler))
120 ✔
922
    {
923
    }
120 ✔
924

925
    void next() NOEXCEPT
135 ✔
926
    {
927
        if (index == range.size())
135 ✔
928
        {
929
            const auto ec = last ? last : code{ error::resolve_failed };
15 ✔
930
            socket.get_executor().post(
45 ✔
931
                [self = shared_from_this(), ec]() NOEXCEPT
30 ✔
932
                {
933
                    self->handler(ec, {});
15 ✔
934
                });
15 ✔
935

936
            return;
937
        }
938

939
        code ignore{};
120 ✔
940
        socket.close(ignore);
120 ✔
941
        socket.async_connect(range.at(index),
120 ✔
942
            [self = shared_from_this()](const code& ec) NOEXCEPT
240 ✔
943
            {
944
                self->handle(ec);
120 ✔
945
            });
120 ✔
946
    }
947

948
    void handle(const code& ec) NOEXCEPT
120 ✔
949
    {
950
        if (!ec || ec == error::operation_canceled)
120 ✔
951
        {
952
            handler(ec, ec ? endpoint{} : range.at(index));
103 ✔
953
            return;
103 ✔
954
        }
955

956
        if (!socket.is_open())
17 ✔
957
        {
958
            handler(error::operation_canceled, {});
2 ✔
959
            return;
2 ✔
960
        }
961

962
        last = ec;
15 ✔
963
        ++index;
15 ✔
964
        next();
15 ✔
965
    }
966

967
    io::socket& socket;
968
    const endpoints range;
969
    const connect_handler handler;
970
    size_t index{};
971
    code last{};
972
};
973

974
} // namespace
975

976
void async_connect(io::socket& socket, const endpoints& range,
120 ✔
977
    connect_handler&& handler) NOEXCEPT
978
{
979
    std::make_shared<range_connect>(socket, range,
120 ✔
980
        std::move(handler))->next();
120 ✔
981
}
120 ✔
982

983
BC_POP_WARNING()
984
BC_POP_WARNING()
985

986
} // namespace io
987
} // namespace network
988
} // 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