fuzz coverage

Coverage Report

Created: 2025-06-01 19:34

/Users/eugenesiegel/btc/bitcoin/src/i2p.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2020-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <chainparams.h>
6
#include <common/args.h>
7
#include <compat/compat.h>
8
#include <compat/endian.h>
9
#include <crypto/sha256.h>
10
#include <i2p.h>
11
#include <logging.h>
12
#include <netaddress.h>
13
#include <netbase.h>
14
#include <random.h>
15
#include <script/parsing.h>
16
#include <sync.h>
17
#include <tinyformat.h>
18
#include <util/fs.h>
19
#include <util/readwritefile.h>
20
#include <util/sock.h>
21
#include <util/strencodings.h>
22
#include <util/threadinterrupt.h>
23
24
#include <chrono>
25
#include <memory>
26
#include <ranges>
27
#include <stdexcept>
28
#include <string>
29
30
using util::Split;
31
32
namespace i2p {
33
34
/**
35
 * Swap Standard Base64 <-> I2P Base64.
36
 * Standard Base64 uses `+` and `/` as last two characters of its alphabet.
37
 * I2P Base64 uses `-` and `~` respectively.
38
 * So it is easy to detect in which one is the input and convert to the other.
39
 * @param[in] from Input to convert.
40
 * @return converted `from`
41
 */
42
static std::string SwapBase64(const std::string& from)
43
0
{
44
0
    std::string to;
45
0
    to.resize(from.size());
46
0
    for (size_t i = 0; i < from.size(); ++i) {
47
0
        switch (from[i]) {
48
0
        case '-':
49
0
            to[i] = '+';
50
0
            break;
51
0
        case '~':
52
0
            to[i] = '/';
53
0
            break;
54
0
        case '+':
55
0
            to[i] = '-';
56
0
            break;
57
0
        case '/':
58
0
            to[i] = '~';
59
0
            break;
60
0
        default:
61
0
            to[i] = from[i];
62
0
            break;
63
0
        }
64
0
    }
65
0
    return to;
66
0
}
67
68
/**
69
 * Decode an I2P-style Base64 string.
70
 * @param[in] i2p_b64 I2P-style Base64 string.
71
 * @return decoded `i2p_b64`
72
 * @throw std::runtime_error if decoding fails
73
 */
74
static Binary DecodeI2PBase64(const std::string& i2p_b64)
75
0
{
76
0
    const std::string& std_b64 = SwapBase64(i2p_b64);
77
0
    auto decoded = DecodeBase64(std_b64);
78
0
    if (!decoded) {
79
0
        throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
Line
Count
Source
1172
0
#define strprintf tfm::format
80
0
    }
81
0
    return std::move(*decoded);
82
0
}
83
84
/**
85
 * Derive the .b32.i2p address of an I2P destination (binary).
86
 * @param[in] dest I2P destination.
87
 * @return the address that corresponds to `dest`
88
 * @throw std::runtime_error if conversion fails
89
 */
90
static CNetAddr DestBinToAddr(const Binary& dest)
91
0
{
92
0
    CSHA256 hasher;
93
0
    hasher.Write(dest.data(), dest.size());
94
0
    unsigned char hash[CSHA256::OUTPUT_SIZE];
95
0
    hasher.Finalize(hash);
96
97
0
    CNetAddr addr;
98
0
    const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
99
0
    if (!addr.SetSpecial(addr_str)) {
100
0
        throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str));
Line
Count
Source
1172
0
#define strprintf tfm::format
101
0
    }
102
103
0
    return addr;
104
0
}
105
106
/**
107
 * Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
108
 * @param[in] dest I2P destination.
109
 * @return the address that corresponds to `dest`
110
 * @throw std::runtime_error if conversion fails
111
 */
112
static CNetAddr DestB64ToAddr(const std::string& dest)
113
0
{
114
0
    const Binary& decoded = DecodeI2PBase64(dest);
115
0
    return DestBinToAddr(decoded);
116
0
}
117
118
namespace sam {
119
120
Session::Session(const fs::path& private_key_file,
121
                 const Proxy& control_host,
122
                 CThreadInterrupt* interrupt)
123
0
    : m_private_key_file{private_key_file},
124
0
      m_control_host{control_host},
125
0
      m_interrupt{interrupt},
126
0
      m_transient{false}
127
0
{
128
0
}
129
130
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
131
0
    : m_control_host{control_host},
132
0
      m_interrupt{interrupt},
133
0
      m_transient{true}
134
0
{
135
0
}
136
137
Session::~Session()
138
0
{
139
0
    LOCK(m_mutex);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
140
0
    Disconnect();
141
0
}
142
143
bool Session::Listen(Connection& conn)
144
0
{
145
0
    try {
146
0
        LOCK(m_mutex);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
147
0
        CreateIfNotCreatedAlready();
148
0
        conn.me = m_my_addr;
149
0
        conn.sock = StreamAccept();
150
0
        return true;
151
0
    } catch (const std::runtime_error& e) {
152
0
        LogPrintLevel(BCLog::I2P, BCLog::Level::Error, "Couldn't listen: %s\n", e.what());
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
153
0
        CheckControlSock();
154
0
    }
155
0
    return false;
156
0
}
157
158
bool Session::Accept(Connection& conn)
159
0
{
160
0
    AssertLockNotHeld(m_mutex);
Line
Count
Source
147
0
#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
161
162
0
    std::string errmsg;
163
0
    bool disconnect{false};
164
165
0
    while (!*m_interrupt) {
166
0
        Sock::Event occurred;
167
0
        if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
168
0
            errmsg = "wait on socket failed";
169
0
            break;
170
0
        }
171
172
0
        if (occurred == 0) {
173
            // Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
174
0
            continue;
175
0
        }
176
177
0
        std::string peer_dest;
178
0
        try {
179
0
            peer_dest = conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
180
0
        } catch (const std::runtime_error& e) {
181
0
            errmsg = e.what();
182
0
            break;
183
0
        }
184
185
0
        CNetAddr peer_addr;
186
0
        try {
187
0
            peer_addr = DestB64ToAddr(peer_dest);
188
0
        } catch (const std::runtime_error& e) {
189
            // The I2P router is expected to send the Base64 of the connecting peer,
190
            // but it may happen that something like this is sent instead:
191
            // STREAM STATUS RESULT=I2P_ERROR MESSAGE="Session was closed"
192
            // In that case consider the session damaged and close it right away,
193
            // even if the control socket is alive.
194
0
            if (peer_dest.find("RESULT=I2P_ERROR") != std::string::npos) {
195
0
                errmsg = strprintf("unexpected reply that hints the session is unusable: %s", peer_dest);
Line
Count
Source
1172
0
#define strprintf tfm::format
196
0
                disconnect = true;
197
0
            } else {
198
0
                errmsg = e.what();
199
0
            }
200
0
            break;
201
0
        }
202
203
0
        conn.peer = CService(peer_addr, I2P_SAM31_PORT);
204
205
0
        return true;
206
0
    }
207
208
0
    if (*m_interrupt) {
209
0
        LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
210
0
    } else {
211
0
        LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
212
0
    }
213
0
    if (disconnect) {
214
0
        LOCK(m_mutex);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
215
0
        Disconnect();
216
0
    } else {
217
0
        CheckControlSock();
218
0
    }
219
0
    return false;
220
0
}
221
222
bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
223
0
{
224
    // Refuse connecting to arbitrary ports. We don't specify any destination port to the SAM proxy
225
    // when connecting (SAM 3.1 does not use ports) and it forces/defaults it to I2P_SAM31_PORT.
226
0
    if (to.GetPort() != I2P_SAM31_PORT) {
227
0
        LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error connecting to %s, connection refused due to arbitrary port %s\n", to.ToStringAddrPort(), to.GetPort());
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
228
0
        proxy_error = false;
229
0
        return false;
230
0
    }
231
232
0
    proxy_error = true;
233
234
0
    std::string session_id;
235
0
    std::unique_ptr<Sock> sock;
236
0
    conn.peer = to;
237
238
0
    try {
239
0
        {
240
0
            LOCK(m_mutex);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
241
0
            CreateIfNotCreatedAlready();
242
0
            session_id = m_session_id;
243
0
            conn.me = m_my_addr;
244
0
            sock = Hello();
245
0
        }
246
247
0
        const Reply& lookup_reply =
248
0
            SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringAddr()));
Line
Count
Source
1172
0
#define strprintf tfm::format
249
250
0
        const std::string& dest = lookup_reply.Get("VALUE");
251
252
0
        const Reply& connect_reply = SendRequestAndGetReply(
253
0
            *sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
Line
Count
Source
1172
0
#define strprintf tfm::format
254
0
            false);
255
256
0
        const std::string& result = connect_reply.Get("RESULT");
257
258
0
        if (result == "OK") {
259
0
            conn.sock = std::move(sock);
260
0
            return true;
261
0
        }
262
263
0
        if (result == "INVALID_ID") {
264
0
            LOCK(m_mutex);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
265
0
            Disconnect();
266
0
            throw std::runtime_error("Invalid session id");
267
0
        }
268
269
0
        if (result == "CANT_REACH_PEER" || result == "TIMEOUT") {
270
0
            proxy_error = false;
271
0
        }
272
273
0
        throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
Line
Count
Source
1172
0
#define strprintf tfm::format
274
0
    } catch (const std::runtime_error& e) {
275
0
        LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error connecting to %s: %s\n", to.ToStringAddrPort(), e.what());
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
276
0
        CheckControlSock();
277
0
        return false;
278
0
    }
279
0
}
280
281
// Private methods
282
283
std::string Session::Reply::Get(const std::string& key) const
284
0
{
285
0
    const auto& pos = keys.find(key);
286
0
    if (pos == keys.end() || !pos->second.has_value()) {
287
0
        throw std::runtime_error(
288
0
            strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
Line
Count
Source
1172
0
#define strprintf tfm::format
289
0
    }
290
0
    return pos->second.value();
291
0
}
292
293
Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
294
                                               const std::string& request,
295
                                               bool check_result_ok) const
296
0
{
297
0
    sock.SendComplete(request + "\n", MAX_WAIT_FOR_IO, *m_interrupt);
298
299
0
    Reply reply;
300
301
    // Don't log the full "SESSION CREATE ..." because it contains our private key.
302
0
    reply.request = request.starts_with("SESSION CREATE") ? "SESSION CREATE ..." : request;
303
304
    // It could take a few minutes for the I2P router to reply as it is querying the I2P network
305
    // (when doing name lookup, for example). Notice: `RecvUntilTerminator()` is checking
306
    // `m_interrupt` more often, so we would not be stuck here for long if `m_interrupt` is
307
    // signaled.
308
0
    static constexpr auto recv_timeout = 3min;
309
310
0
    reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt, MAX_MSG_SIZE);
311
312
0
    for (const auto& kv : Split(reply.full, ' ')) {
313
0
        const auto pos{std::ranges::find(kv, '=')};
314
0
        if (pos != kv.end()) {
315
0
            reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
316
0
        } else {
317
0
            reply.keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
318
0
        }
319
0
    }
320
321
0
    if (check_result_ok && reply.Get("RESULT") != "OK") {
322
0
        throw std::runtime_error(
323
0
            strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
Line
Count
Source
1172
0
#define strprintf tfm::format
324
0
    }
325
326
0
    return reply;
327
0
}
328
329
std::unique_ptr<Sock> Session::Hello() const
330
0
{
331
0
    auto sock = m_control_host.Connect();
332
333
0
    if (!sock) {
334
0
        throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
Line
Count
Source
1172
0
#define strprintf tfm::format
335
0
    }
336
337
0
    SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
338
339
0
    return sock;
340
0
}
341
342
void Session::CheckControlSock()
343
0
{
344
0
    LOCK(m_mutex);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
345
346
0
    std::string errmsg;
347
0
    if (m_control_sock && !m_control_sock->IsConnected(errmsg)) {
348
0
        LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Control socket error: %s\n", errmsg);
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
349
0
        Disconnect();
350
0
    }
351
0
}
352
353
void Session::DestGenerate(const Sock& sock)
354
0
{
355
    // https://geti2p.net/spec/common-structures#key-certificates
356
    // "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
357
    // Use "7" because i2pd <2.24.0 does not recognize the textual form.
358
    // If SIGNATURE_TYPE is not specified, then the default one is DSA_SHA1.
359
0
    const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);
360
361
0
    m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
362
0
}
363
364
void Session::GenerateAndSavePrivateKey(const Sock& sock)
365
0
{
366
0
    DestGenerate(sock);
367
368
    // umask is set to 0077 in common/system.cpp, which is ok.
369
0
    if (!WriteBinaryFile(m_private_key_file,
370
0
                         std::string(m_private_key.begin(), m_private_key.end()))) {
371
0
        throw std::runtime_error(
372
0
            strprintf("Cannot save I2P private key to %s", fs::quoted(fs::PathToString(m_private_key_file))));
Line
Count
Source
1172
0
#define strprintf tfm::format
373
0
    }
374
0
}
375
376
Binary Session::MyDestination() const
377
0
{
378
    // From https://geti2p.net/spec/common-structures#destination:
379
    // "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
380
    // non-zero"
381
0
    static constexpr size_t DEST_LEN_BASE = 387;
382
0
    static constexpr size_t CERT_LEN_POS = 385;
383
384
0
    uint16_t cert_len;
385
386
0
    if (m_private_key.size() < CERT_LEN_POS + sizeof(cert_len)) {
387
0
        throw std::runtime_error(strprintf("The private key is too short (%d < %d)",
Line
Count
Source
1172
0
#define strprintf tfm::format
388
0
                                           m_private_key.size(),
389
0
                                           CERT_LEN_POS + sizeof(cert_len)));
390
0
    }
391
392
0
    memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
393
0
    cert_len = be16toh_internal(cert_len);
394
395
0
    const size_t dest_len = DEST_LEN_BASE + cert_len;
396
397
0
    if (dest_len > m_private_key.size()) {
398
0
        throw std::runtime_error(strprintf("Certificate length (%d) designates that the private key should "
Line
Count
Source
1172
0
#define strprintf tfm::format
399
0
                                           "be %d bytes, but it is only %d bytes",
400
0
                                           cert_len,
401
0
                                           dest_len,
402
0
                                           m_private_key.size()));
403
0
    }
404
405
0
    return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
406
0
}
407
408
void Session::CreateIfNotCreatedAlready()
409
0
{
410
0
    std::string errmsg;
411
0
    if (m_control_sock && m_control_sock->IsConnected(errmsg)) {
412
0
        return;
413
0
    }
414
415
0
    const auto session_type = m_transient ? "transient" : "persistent";
416
0
    const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs
417
418
0
    LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Creating %s SAM session %s with %s\n", session_type, session_id, m_control_host.ToString());
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
419
420
0
    auto sock = Hello();
421
422
0
    if (m_transient) {
423
        // The destination (private key) is generated upon session creation and returned
424
        // in the reply in DESTINATION=.
425
0
        const Reply& reply = SendRequestAndGetReply(
426
0
            *sock,
427
0
            strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7 "
Line
Count
Source
1172
0
#define strprintf tfm::format
428
0
                      "i2cp.leaseSetEncType=4,0 inbound.quantity=1 outbound.quantity=1",
429
0
                      session_id));
430
431
0
        m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
432
0
    } else {
433
        // Read our persistent destination (private key) from disk or generate
434
        // one and save it to disk. Then use it when creating the session.
435
0
        const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
436
0
        if (read_ok) {
437
0
            m_private_key.assign(data.begin(), data.end());
438
0
        } else {
439
0
            GenerateAndSavePrivateKey(*sock);
440
0
        }
441
442
0
        const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
443
444
0
        SendRequestAndGetReply(*sock,
445
0
                               strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s "
Line
Count
Source
1172
0
#define strprintf tfm::format
446
0
                                         "i2cp.leaseSetEncType=4,0 inbound.quantity=3 outbound.quantity=3",
447
0
                                         session_id,
448
0
                                         private_key_b64));
449
0
    }
450
451
0
    m_my_addr = CService(DestBinToAddr(MyDestination()), I2P_SAM31_PORT);
452
0
    m_session_id = session_id;
453
0
    m_control_sock = std::move(sock);
454
455
0
    LogPrintLevel(BCLog::I2P, BCLog::Level::Info, "%s SAM session %s created, my address=%s\n",
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
456
0
        Capitalize(session_type),
457
0
        m_session_id,
458
0
        m_my_addr.ToStringAddrPort());
459
0
}
460
461
std::unique_ptr<Sock> Session::StreamAccept()
462
0
{
463
0
    auto sock = Hello();
464
465
0
    const Reply& reply = SendRequestAndGetReply(
466
0
        *sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);
Line
Count
Source
1172
0
#define strprintf tfm::format
467
468
0
    const std::string& result = reply.Get("RESULT");
469
470
0
    if (result == "OK") {
471
0
        return sock;
472
0
    }
473
474
0
    if (result == "INVALID_ID") {
475
        // If our session id is invalid, then force session re-creation on next usage.
476
0
        Disconnect();
477
0
    }
478
479
0
    throw std::runtime_error(strprintf("\"%s\"", reply.full));
Line
Count
Source
1172
0
#define strprintf tfm::format
480
0
}
481
482
void Session::Disconnect()
483
0
{
484
0
    if (m_control_sock) {
485
0
        if (m_session_id.empty()) {
486
0
            LogPrintLevel(BCLog::I2P, BCLog::Level::Info, "Destroying incomplete SAM session\n");
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
487
0
        } else {
488
0
            LogPrintLevel(BCLog::I2P, BCLog::Level::Info, "Destroying SAM session %s\n", m_session_id);
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
489
0
        }
490
0
        m_control_sock.reset();
491
0
    }
492
0
    m_session_id.clear();
493
0
}
494
} // namespace sam
495
} // namespace i2p