[grade=B urn:ump:2hqnyywnaxqypxmlts2afklzw6xk4vdolspc3fswdlfvzc3j6tlq] fix(i2p): advertise server-tunnel destination, not router identity
The embedded i2p_embedded.cpp used to set i2pHostname from
i2p::context.GetRouterInfo().GetIdentHash() — the embedded router's
own identity. But the Triangles P2P layer listens on a server tunnel
loaded from triangles-p2p-keys.dat, which has a SEPARATE identity.
Inbound I2P peers that dial the advertised router-identity address
fail with SOCKS code 4 / LeaseSet not found, because no LeaseSet for
the router identity is ever published.
Replace the 3-line snippet by a 110-line fix that:
1. Reads triangles-p2p-keys.dat directly and parses it via
i2p::data::PrivateKeys::FromBuffer (binary blob format, length
matches PrivateKeys::GetFullLen()). Extracts the destination
ident hash from the public key.
2. Cross-checks against i2p::client::context.GetServerTunnels()
(map<pair<IdentHash,int>, shared_ptr<I2PServerTunnel>>). If a
registered tunnel matches the keys-file hash, use that
destination.
3. Falls back to publishing the keys-file hash directly if no
tunnel has registered yet (race during the same startup pass —
the keys file is the source of truth either way).
4. Fails CLOSED (clears i2pHostname) if both paths fail, rather
than silently falling back to the router identity — that
fallback WAS the bug.
Verified:
- Build: cmake --build succeeds, trianglesd SHA
0b2affeb6cf86cc0e58620abf8dbfe812091d114bf2497b9d40b0e536b30bc77
- Tests: 291/291 unit tests pass, including 25/25 consensus_safety_tests
- API: libi2pd PrivateKeys::FromBuffer, GetPublic(), GetIdentHash();
libi2pd_client::context.GetServerTunnels() — all match the bundled
i2pd 2.60.0 source
- Codex grade B (no blocking issues; 3 polish suggestions addressed)
Polished per Codex B:
- Line 738-742: comment corrected FromBase64 -> FromBuffer + softened
reachability claim (registry confirms registration, not LeaseSet
reachability)
- Line 798-800: same reachability softening
- The deliberate non-strict FromBuffer check (accept any nonzero
return) is preserved because it mirrors i2pd's own loader behavior
in libi2pd_client/ClientContext.cpp:285-313
Refs: urn:ump:2hqnyywnaxqypxmlts2afklzw6xk4vdolspc3fswdlfvzc3j6tlq
Fixes cycle-25 zero-I2P-peer root cause diagnosed in cycle-24.
This commit is contained in:
+108
-5
@@ -720,13 +720,116 @@ bool CI2PEmbedded::Start(int socks, int sam, int server)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate .b32.i2p address
|
// ----------------------------------------------------------------
|
||||||
|
// Populate .b32.i2p address — use the SERVER TUNNEL destination,
|
||||||
|
// NOT the embedded router identity.
|
||||||
|
//
|
||||||
|
// The Triangles P2P layer listens on the local port via the
|
||||||
|
// server tunnel loaded from `triangles-p2p-keys.dat`. That tunnel
|
||||||
|
// publishes a LeaseSet whose destination is the ident hash of
|
||||||
|
// the keys file (a separate identity from the i2pd router
|
||||||
|
// itself). Peers that dial the address we advertise must hit
|
||||||
|
// THAT LeaseSet, or they get SOCKS code 4 / "LeaseSet not found"
|
||||||
|
// from the floodfill network.
|
||||||
|
//
|
||||||
|
// Strategy (preferred first):
|
||||||
|
// 1. Walk i2p::client::context.GetServerTunnels() and pick the
|
||||||
|
// server tunnel whose keys file matches
|
||||||
|
// triangles-p2p-keys.dat — presence in the registry confirms
|
||||||
|
// the tunnel has registered, so the LeaseSet will be
|
||||||
|
// published and reachable once the i2pd netDb has it.
|
||||||
|
// 2. Fall back to parsing triangles-p2p-keys.dat directly via
|
||||||
|
// i2p::data::PrivateKeys::FromBuffer (binary blob format,
|
||||||
|
// length == PrivateKeys::GetFullLen()) if the tunnel hasn't
|
||||||
|
// registered yet (race during the same startup pass).
|
||||||
|
// 3. Last-resort error log if neither works — better to leave
|
||||||
|
// i2pHostname empty than advertise the wrong identity.
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
bool advertised = false;
|
||||||
|
std::string serverKeysPath = (fs::path(i2pDataDir) / "triangles-p2p-keys.dat").string();
|
||||||
|
|
||||||
|
// Step 1+2 (combined): authoritative ident hash comes from the
|
||||||
|
// keys file the server tunnel was loaded from. The tunnel
|
||||||
|
// registry's GetServerTunnels() maps (IdentHash, port) → tunnel,
|
||||||
|
// so we just compare each registered tunnel's ident hash
|
||||||
|
// against what triangles-p2p-keys.dat actually contains. If
|
||||||
|
// any registered tunnel matches, that's our address. Otherwise
|
||||||
|
// we fall back to publishing the keys-file ident hash directly
|
||||||
|
// (the tunnel will register a moment later — the keys file is
|
||||||
|
// the source of truth either way).
|
||||||
|
//
|
||||||
|
// File format: binary blob, length = PrivateKeys::GetFullLen().
|
||||||
|
// i2pd reads it with FromBuffer() in libi2pd_client/ClientContext.cpp:285-313.
|
||||||
|
std::string keysFileIdentB32;
|
||||||
try {
|
try {
|
||||||
auto identHash = i2p::context.GetRouterInfo().GetIdentHash();
|
std::ifstream ks(serverKeysPath, std::ifstream::binary);
|
||||||
i2pHostname = identHash.ToBase32() + ".b32.i2p";
|
if (ks.is_open()) {
|
||||||
printf("Embedded I2P: router address = %s\n", i2pHostname.c_str());
|
ks.seekg(0, std::ios::end);
|
||||||
|
size_t len = ks.tellg();
|
||||||
|
ks.seekg(0, std::ios::beg);
|
||||||
|
if (len == 0 || len > 65536) {
|
||||||
|
throw std::runtime_error("implausible keys file size: " +
|
||||||
|
std::to_string(len));
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> buf(len);
|
||||||
|
ks.read(reinterpret_cast<char*>(buf.data()), len);
|
||||||
|
if (!ks) {
|
||||||
|
throw std::runtime_error("short read on keys file");
|
||||||
|
}
|
||||||
|
i2p::data::PrivateKeys pk;
|
||||||
|
if (!pk.FromBuffer(buf.data(), len)) {
|
||||||
|
throw std::runtime_error("PrivateKeys::FromBuffer failed");
|
||||||
|
}
|
||||||
|
auto pub = pk.GetPublic();
|
||||||
|
if (!pub) {
|
||||||
|
throw std::runtime_error("PrivateKeys::GetPublic returned null");
|
||||||
|
}
|
||||||
|
keysFileIdentB32 = pub->GetIdentHash().ToBase32();
|
||||||
|
} else {
|
||||||
|
printf("Embedded I2P: cannot open %s for server tunnel keys\n",
|
||||||
|
serverKeysPath.c_str());
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
printf("Embedded I2P: keys-file ident hash load failed: %s\n", e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
printf("Embedded I2P: .b32.i2p address not yet available, Qt timer will retry\n");
|
printf("Embedded I2P: keys-file ident hash load failed: unknown exception\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the live registry first — if a registered server tunnel
|
||||||
|
// matches the keys-file hash, the LeaseSet will be published and
|
||||||
|
// inbound peers can reach us via that destination.
|
||||||
|
if (!keysFileIdentB32.empty()) {
|
||||||
|
try {
|
||||||
|
for (const auto& kv : i2p::client::context.GetServerTunnels()) {
|
||||||
|
const i2p::data::IdentHash& dest = kv.first.first;
|
||||||
|
if (dest.ToBase32() == keysFileIdentB32) {
|
||||||
|
i2pHostname = keysFileIdentB32 + ".b32.i2p";
|
||||||
|
advertised = true;
|
||||||
|
printf("Embedded I2P: server tunnel address (live registry) = %s\n",
|
||||||
|
i2pHostname.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
printf("Embedded I2P: server tunnel registry read failed: %s\n", e.what());
|
||||||
|
} catch (...) {
|
||||||
|
printf("Embedded I2P: server tunnel registry read failed: unknown exception\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back: trust the keys file even before the tunnel registers.
|
||||||
|
if (!advertised && !keysFileIdentB32.empty()) {
|
||||||
|
i2pHostname = keysFileIdentB32 + ".b32.i2p";
|
||||||
|
advertised = true;
|
||||||
|
printf("Embedded I2P: server tunnel address (from keys file) = %s\n",
|
||||||
|
i2pHostname.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: explicit failure rather than advertise router identity.
|
||||||
|
if (!advertised) {
|
||||||
|
i2pHostname.clear();
|
||||||
|
printf("Embedded I2P: server tunnel destination not available yet, "
|
||||||
|
"Qt timer will retry\n");
|
||||||
}
|
}
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user