• 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

94.82
/src/net/socket_connect.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/net/socket.hpp>
20

21
#include <bitcoin/network/config/config.hpp>
22
#include <bitcoin/network/define.hpp>
23
#include <bitcoin/network/log/log.hpp>
24

25
namespace libbitcoin {
26
namespace network {
27

28
using namespace std::placeholders;
29

30
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
31

32
// Accept.
33
// ----------------------------------------------------------------------------
34
// Closure of the acceptor, not the socket, releases the async_accept handler.
35
// The socket is not guarded during async_accept. This is required so the
36
// acceptor may be guarded from its own strand while preserving hiding of
37
// socket internals. This makes concurrent calls unsafe, however only the
38
// acceptor (a socket factory) requires access to the socket at this time.
39
// network::acceptor both invokes this call in the network strand and
40
// initializes the io::acceptor with the network strand. So the call to
41
// acceptor.async_accept invokes its handler on that strand as well.
42

43
void socket::accept(io::acceptor& acceptor,
285 ✔
44
    result_handler&& handler) NOEXCEPT
45
{
46
    BC_ASSERT_MSG(!get_base().is_open(), "accept on open socket");
285 ✔
47
    // Dispatches on the acceptor's strand (which should be network).
48
    acceptor.async_accept(get_base(),
285 ✔
49
        std::bind(&socket::handle_accept,
570 ✔
50
            shared_from_this(), _1, handler));
570 ✔
51
}
285 ✔
52

53
void socket::handle_accept(code ec,
285 ✔
54
    const result_handler& handler) NOEXCEPT
55
{
56
    // This is running in the acceptor (not socket) execution context.
57
    // socket_ and endpoint_ are not guarded here, see comments on accept.
58

59
    if (ec == error::operation_canceled)
285 ✔
60
    {
61
        handler(error::operation_canceled);
18 ✔
62
        return;
101 ✔
63
    }
64

65
    if (ec)
267 ✔
66
    {
67
        if (ec == error::unknown) logx("accept", ec);
1 ✔
68
        handler(ec);
1 ✔
69
        return;
1 ✔
70
    }
71

72
    endpoint_ = { get_base().remote_endpoint(ec) };
266 ✔
73
    address_ = endpoint_;
266 ✔
74

75
    if (ec)
266 ✔
76
    {
77
        if (ec == error::unknown) logx("remote", ec);
×
78
        handler(ec);
×
79
        return;
×
80
    }
81

82
    code ignore{};
266 ✔
83
    binding_ = { get_base().local_endpoint(ignore) };
266 ✔
84

85
    // Defer handshake to the acceptor when connection is proxied.
86
    if (proxied_)
266 ✔
87
    {
88
        handler(error::success);
82 ✔
89
        return;
82 ✔
90
    }
91

92
    // Not in socket strand.
93
    do_handshake(handler);
184 ✔
94
}
95

96
// Connect.
97
// ----------------------------------------------------------------------------
98

99
void socket::connect(const io::endpoints& range,
122 ✔
100
    result_handler&& handler) NOEXCEPT
101
{
102
    io::post(strand_,
122 ✔
103
        std::bind(&socket::do_connect,
244 ✔
104
            shared_from_this(), range, std::move(handler)));
244 ✔
105
}
122 ✔
106

107
void socket::do_connect(const io::endpoints& range,
122 ✔
108
    const result_handler& handler) NOEXCEPT
109
{
110
    BC_ASSERT(stranded());
122 ✔
111
    BC_ASSERT_MSG(!websocket(), "socket is upgraded");
122 ✔
112
    BC_ASSERT_MSG(!get_base().is_open(), "connect on open socket");
122 ✔
113

114
    if (!bind_.address().is_unspecified())
122 ✔
115
    {
116
        do_connect_bound(range, zero, handler);
4 ✔
117
        return;
4 ✔
118
    }
119

120
    // Establishes a socket connection by trying each endpoint in sequence.
121
    io::async_connect(get_base(), range,
118 ✔
122
        std::bind(&socket::handle_connect,
236 ✔
123
            shared_from_this(), _1, _2, handler));
236 ✔
124
}
125

126
// The range connect closes the socket before each attempt, discarding the
127
// binding, so bound endpoints (of the bound family) are attempted singly.
128
void socket::do_connect_bound(const io::endpoints& range, size_t index,
5 ✔
129
    const result_handler& handler) NOEXCEPT
130
{
131
    BC_ASSERT(stranded());
5 ✔
132

133
    index = next_bound(range, index);
5 ✔
134
    if (index == range.size())
5 ✔
135
    {
136
        handler(error::resolve_failed);
1 ✔
137
        return;
2 ✔
138
    }
139

140
    const auto peer = *std::next(range.begin(), index);
4 ✔
141
    auto& base = get_base();
4 ✔
142
    code ec{};
4 ✔
143
    base.close(ec);
4 ✔
144
    base.open(io::tcp::of(peer), ec);
4 ✔
145

146
    if (!ec)
4 ✔
147
        base.bind(bind_, ec);
4 ✔
148

149
    if (ec)
4 ✔
150
    {
151
        if (ec == error::unknown) logx("bind", ec);
1 ✔
152
        handler(ec);
1 ✔
153
        return;
1 ✔
154
    }
155

156
    base.async_connect(peer,
3 ✔
157
        std::bind(&socket::handle_connect_bound,
6 ✔
158
            shared_from_this(), _1, range, add1(index), peer, handler));
6 ✔
159
}
160

161
void socket::handle_connect_bound(const code& ec,
3 ✔
162
    const io::endpoints& range, size_t next, const io::endpoint& peer,
163
    const result_handler& handler) NOEXCEPT
164
{
165
    BC_ASSERT(stranded());
3 ✔
166

167
    if (ec && ec != error::operation_canceled && !stopped_.load() &&
3 ✔
168
        next_bound(range, next) != range.size())
1 ✔
169
    {
170
        do_connect_bound(range, next, handler);
1 ✔
171
        return;
1 ✔
172
    }
173

174
    handle_connect(ec, peer, handler);
2 ✔
175
}
176

177
// private
178
size_t socket::next_bound(const io::endpoints& range,
6 ✔
179
    size_t index) const NOEXCEPT
180
{
181
    const auto v4 = bind_.address().is_v4();
6 ✔
182
    for (auto it = std::next(range.begin(), index); it != range.end();
13 ✔
183
        ++it, ++index)
1 ✔
184
        if (it->address().is_v4() == v4)
6 ✔
185
            break;
186

187
    return index;
6 ✔
188
}
189

190
void socket::handle_connect(const code& ec, const io::endpoint& peer,
120 ✔
191
    const result_handler& handler) NOEXCEPT
192
{
193
    BC_ASSERT(stranded());
120 ✔
194

195
    // The endpoint is the intended target, the address is the resolved peer.
196
    // An address dial retains its metadata, a proxied peer is the local
197
    // binding of the proxy server, and is set by its handshake.
198
    if (!proxied_ && !address_)
120 ✔
199
        address_ = { peer };
91 ✔
200

201
    if (ec == error::operation_canceled)
120 ✔
202
    {
UNCOV
203
        handler(error::operation_canceled);
×
204
        return;
38 ✔
205
    }
206

207
    if (ec)
120 ✔
208
    {
209
        if (ec == error::unknown) logx("connect", ec);
16 ✔
210
        handler(ec);
16 ✔
211
        return;
16 ✔
212
    }
213

214
    code ignore{};
104 ✔
215
    binding_ = { get_base().local_endpoint(ignore) };
104 ✔
216

217
    // Defer handshake to the connector when connection is proxied.
218
    if (proxied_)
104 ✔
219
    {
220
        handler(error::success);
22 ✔
221
        return;
22 ✔
222
    }
223

224
    // In socket strand.
225
    do_handshake(handler);
82 ✔
226
}
227

228
// Handshake (accept & connect).
229
// ----------------------------------------------------------------------------
230

231
void socket::handshake(result_handler&& handler) NOEXCEPT
84 ✔
232
{
233
    io::dispatch(strand_,
84 ✔
234
        std::bind(&socket::do_handshake,
168 ✔
235
            shared_from_this(), std::move(handler)));
168 ✔
236
}
84 ✔
237

238
// Invokes handler on acceptor (network) or connector (socket) strand.
239
void socket::do_handshake(const result_handler& handler) NOEXCEPT
350 ✔
240
{
241
    ////BC_ASSERT(stranded());
242

243
    code ignore{};
350 ✔
244
    get_base().set_option(io::no_delay{ true }, ignore);
350 ✔
245

246
    if (!inbound_ || is_zero(timer_->timeout().count()) ||
350 ✔
247
        std::holds_alternative<std::monostate>(context_))
83 ✔
248
    {
249
        do_upgrade(handler);
348 ✔
250
        return;
348 ✔
251
    }
252

253
    timer_->start(std::bind(&socket::handle_handshake_timer,
2 ✔
254
        shared_from_this(), _1));
4 ✔
255

256
    do_upgrade(std::bind(&socket::handle_handshaked,
2 ✔
257
        shared_from_this(), _1, handler));
4 ✔
258
}
259

260
// private
261
void socket::handle_handshaked(const code& ec,
2 ✔
262
    const result_handler& handler) NOEXCEPT
263
{
264
    handshaked_ = true;
2 ✔
265
    timer_->stop();
2 ✔
266
    handler(ec);
2 ✔
267
}
2 ✔
268

269
// private
270
// Socket stop cancels the pending handshake, which completes the handler.
271
void socket::handle_handshake_timer(const code& ec) NOEXCEPT
2 ✔
272
{
273
    if (!ec && !handshaked_)
2 ✔
274
        stop();
2 ✔
275
}
2 ✔
276

277
// private
278
void socket::do_upgrade(const result_handler& handler) NOEXCEPT
350 ✔
279
{
280
    if (std::holds_alternative<cref<p2ps::context>>(context_))
350 ✔
281
    {
282
        // The accepted peer is detected as v1 or v2 before upgrade.
283
        if (inbound_)
7 ✔
284
        {
285
            io::async_read(get_base(),
12 ✔
286
                detection_.prepare(p2ps::stream::detection_size),
12 ✔
287
                std::bind(&socket::handle_detection,
12 ✔
288
                    shared_from_this(), _1, handler));
12 ✔
289
            return;
6 ✔
290
        }
291

292
        // Extract to temporary to avoid dangling reference after destruction.
293
        auto socket = std::move(get_base());
1 ✔
294

295
        // P2PS (bip324) context is applied to the socket.
296
        socket_.emplace<p2ps::stream>(std::move(socket),
1 ✔
297
            std::get<cref<p2ps::context>>(context_).get());
1 ✔
298

299
        // Posts handler to socket strand.
300
        get_p2ps().async_handshake(
2 ✔
301
            std::bind(&socket::handle_encrypted_handshake,
2 ✔
302
                shared_from_this(), _1, handler));
2 ✔
303
        return;
304
    }
1 ✔
305

306
    if (std::holds_alternative<cref<zmtp::context>>(context_))
343 ✔
307
    {
308
        // Extract to temporary to avoid dangling reference after destruction.
309
        auto socket = std::move(get_base());
43 ✔
310

311
        // ZMTP (native) context is applied to the socket.
312
        auto& stream = socket_.emplace<zmtp::stream>(std::move(socket),
43 ✔
313
            std::get<cref<zmtp::context>>(context_).get(), role_);
43 ✔
314

315
        // Posts handler to socket strand.
316
        stream.async_handshake(inbound_,
43 ✔
317
            std::bind(&socket::handle_publisher_handshake,
86 ✔
318
                shared_from_this(), _1, handler));
86 ✔
319
        return;
320
    }
43 ✔
321

322
    if (std::holds_alternative<cref<tls::context>>(context_))
300 ✔
323
    {
324
        // The client role is not implemented.
325
        if (!inbound_)
11 ✔
326
        {
327
            handler(error::tls_alert_handshake_failure);
×
328
            return;
×
329
        }
330

331
        // Extract to temporary to avoid dangling reference after destruction.
332
        auto socket = std::move(get_base());
11 ✔
333

334
        // TLS context is applied to the socket.
335
        auto& stream = socket_.emplace<tls::stream>(std::move(socket),
11 ✔
336
            std::get<cref<tls::context>>(context_).get());
11 ✔
337

338
        // Posts handler to socket strand.
339
        stream.async_handshake(
11 ✔
340
            std::bind(&socket::handle_handshake,
22 ✔
341
                shared_from_this(), _1, handler));
22 ✔
342
        return;
343
    }
11 ✔
344

345
    handler(error::success);
289 ✔
346
}
347

348
// Invoked on the socket strand.
349
void socket::handle_detection(const code& ec,
6 ✔
350
    const result_handler& handler) NOEXCEPT
351
{
352
    BC_ASSERT(stranded());
6 ✔
353

354
    if (ec)
6 ✔
355
    {
356
        if (ec == error::unknown) logx("detection", ec);
1 ✔
357
        handler(ec);
1 ✔
358
        return;
3 ✔
359
    }
360

361
    using namespace system;
362
    constexpr auto size = p2ps::stream::detection_size;
363

364
    detection_.commit(size);
5 ✔
365
    const auto& context = std::get<cref<p2ps::context>>(context_).get();
5 ✔
366
    const auto data = pointer_cast<const uint8_t>(detection_.data().data());
5 ✔
367
    const std::span<const uint8_t> prefix{ data, size };
5 ✔
368

369
    // A v1 peer is served without upgrade, the buffer retains the prefix.
370
    if (p2ps::stream::detected_v1(prefix, context.identifier))
5 ✔
371
    {
372
        handler(error::success);
2 ✔
373
        return;
2 ✔
374
    }
375

376
    // The prefix is consumed by the upgrade (a partial peer key).
377
    data_chunk key{ prefix.begin(), prefix.end() };
3 ✔
378
    detection_.consume(size);
3 ✔
379

380
    // Extract to temporary to avoid dangling reference after destruction.
381
    auto socket = std::move(get_base());
3 ✔
382

383
    // P2PS (bip324) context is applied to the socket.
384
    socket_.emplace<p2ps::stream>(std::move(socket), context);
3 ✔
385

386
    // Posts handler to socket strand.
387
    get_p2ps().async_handshake(std::move(key),
6 ✔
388
        std::bind(&socket::handle_encrypted_handshake,
6 ✔
389
            shared_from_this(), _1, handler));
6 ✔
390
}
3 ✔
391

392
void socket::handle_encrypted_handshake(const code& ec,
4 ✔
393
    const result_handler& handler) NOEXCEPT
394
{
395
    BC_ASSERT(stranded());
4 ✔
396

397
    if (ec == error::operation_canceled)
4 ✔
398
    {
399
        handler(error::operation_canceled);
×
400
        return;
×
401
    }
402

403
    if (ec == error::unknown) logx("encrypted handshake", ec);
4 ✔
404
    handler(ec);
4 ✔
405
}
406

407
void socket::handle_publisher_handshake(const code& ec,
43 ✔
408
    const result_handler& handler) NOEXCEPT
409
{
410
    BC_ASSERT(stranded());
43 ✔
411

412
    if (ec == error::operation_canceled)
43 ✔
413
    {
414
        handler(error::operation_canceled);
×
415
        return;
×
416
    }
417

418
    if (ec == error::unknown) logx("publisher handshake", ec);
43 ✔
419
    handler(ec);
43 ✔
420
}
421

422
void socket::handle_handshake(const code& ec,
11 ✔
423
    const result_handler& handler) NOEXCEPT
424
{
425
    BC_ASSERT(stranded());
11 ✔
426

427
    if (ec == error::operation_canceled)
11 ✔
428
    {
429
        handler(error::operation_canceled);
1 ✔
430
        return;
1 ✔
431
    }
432

433
    if (ec == error::unknown) logx("handshake", ec);
10 ✔
434
    handler(ec);
10 ✔
435
}
436

437
BC_POP_WARNING()
438

439
} // namespace network
440
} // 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