upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-02 09:03:01 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-02 09:03:01 +0000
commit7c70eb4498e88725241493033edfe89554ff1b18 (patch)
treea053678bf029d17c3a0b193fbaaf1abaf3778aff /src
parente4f898c4a6b9900553c64b9804889b5fdae3e55f (diff)
tunnel: report which stage failed instead of guessing the cause
Diffstat (limited to 'src')
-rw-r--r--src/btfrv.cpp16
-rw-r--r--src/btfrv.h19
-rw-r--r--src/btftunnel.cpp37
-rw-r--r--src/btftunnel.h25
-rw-r--r--src/main.cpp11
5 files changed, 93 insertions, 15 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp
index 133eb89..864fe3d 100644
--- a/src/btfrv.cpp
+++ b/src/btfrv.cpp
@@ -334,17 +334,29 @@ bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs)
334} 334}
335 335
336RvSocket RvClientConnect(const char* relay_host, unsigned short port, 336RvSocket RvClientConnect(const char* relay_host, unsigned short port,
337 const unsigned char target_pubkey[32]) 337 const unsigned char target_pubkey[32],
338 RvClientFail* pFail)
338{ 339{
340 if (pFail) *pFail = RVC_FAIL_DIAL;
339 if (!RvInit()) return RV_INVALID; 341 if (!RvInit()) return RV_INVALID;
340 SOCKET s = ConnectTo(relay_host, port); 342 SOCKET s = ConnectTo(relay_host, port);
341 if (s == INVALID_SOCKET) return RV_INVALID; 343 if (s == INVALID_SOCKET) return RV_INVALID;
344 if (pFail) *pFail = RVC_FAIL_PAIR;
342 unsigned char hdr[33]; 345 unsigned char hdr[33];
343 hdr[0] = (unsigned char)'C'; 346 hdr[0] = (unsigned char)'C';
344 memcpy(hdr + 1, target_pubkey, 32); 347 memcpy(hdr + 1, target_pubkey, 32);
345 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } 348 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; }
346 unsigned char paired = 0; 349 unsigned char paired = 0;
347 if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } 350 if (!ReadN(s, &paired, 1)) { CLOSESOCK(s); return RV_INVALID; }
351 if (paired != 0x01)
352 {
353 // The relay answered and declined: HandleIncoming writes 0x00 and
354 // closes when no service is parked under this pubkey right now.
355 if (pFail) *pFail = RVC_FAIL_REFUSED;
356 CLOSESOCK(s);
357 return RV_INVALID;
358 }
359 if (pFail) *pFail = RVC_FAIL_NONE;
348 return (RvSocket)s; 360 return (RvSocket)s;
349} 361}
350 362
diff --git a/src/btfrv.h b/src/btfrv.h
index 9cd7476..1e85cb0 100644
--- a/src/btfrv.h
+++ b/src/btfrv.h
@@ -57,10 +57,25 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port,
57bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs = 0); 57bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs = 0);
58 58
59// ---- client side (the node dialing a .btf address) ---- 59// ---- client side (the node dialing a .btf address) ----
60// Where a failed RvClientConnect stopped. RV_INVALID alone collapses three
61// different situations -- relay unreachable, relay reachable but nobody
62// registered, connection died mid-pairing -- and they point at different
63// components. The caller can only report the right one if this side says
64// which it saw.
65enum RvClientFail
66{
67 RVC_FAIL_NONE = 0,
68 RVC_FAIL_DIAL, // TCP connect to the relay itself failed
69 RVC_FAIL_REFUSED, // relay answered 0x00: no service registered under this pubkey
70 RVC_FAIL_PAIR, // relay connection died before pairing completed
71};
72
60// Connect through the relay to the service registered under `target_pubkey`. 73// Connect through the relay to the service registered under `target_pubkey`.
61// Returns the paired socket, or RV_INVALID on error. 74// Returns the paired socket, or RV_INVALID on error -- with `pFail`, if
75// given, naming the stage that failed.
62RvSocket RvClientConnect(const char* relay_host, unsigned short port, 76RvSocket RvClientConnect(const char* relay_host, unsigned short port,
63 const unsigned char target_pubkey[32]); 77 const unsigned char target_pubkey[32],
78 RvClientFail* pFail = 0);
64 79
65// Blocking read/write of exactly n bytes over a rendezvous socket. Return false 80// Blocking read/write of exactly n bytes over a rendezvous socket. Return false
66// on error/EOF. 81// on error/EOF.
diff --git a/src/btftunnel.cpp b/src/btftunnel.cpp
index 63d178b..408476e 100644
--- a/src/btftunnel.cpp
+++ b/src/btftunnel.cpp
@@ -136,18 +136,47 @@ static btf_socket_t StartTunnel(RvSocket rv, const unsigned char shared[32])
136 return appEnd; 136 return appEnd;
137} 137}
138 138
139const char* TunnelFailText(TunnelFail f)
140{
141 switch (f)
142 {
143 case TUNNEL_FAIL_NONE: return "no failure";
144 case TUNNEL_FAIL_INIT: return "local socket/crypto init failed";
145 case TUNNEL_FAIL_DIAL: return "meeting node unreachable (tcp dial failed)";
146 case TUNNEL_FAIL_REFUSED: return "relay reachable, but no service registered there for this address";
147 case TUNNEL_FAIL_PAIR: return "relay connection died before pairing completed";
148 case TUNNEL_FAIL_HANDSHAKE: return "end-to-end handshake failed";
149 case TUNNEL_FAIL_LOCAL: return "local tunnel setup failed";
150 }
151 return "unknown failure";
152}
153
139btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port, 154btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port,
140 const unsigned char target_pubkey[32], 155 const unsigned char target_pubkey[32],
141 const unsigned char service_enc_pub[32]) 156 const unsigned char service_enc_pub[32],
157 TunnelFail* pFail)
142{ 158{
159 if (pFail) *pFail = TUNNEL_FAIL_INIT;
143 if (!TunnelInit()) return INVALID_SOCKET; 160 if (!TunnelInit()) return INVALID_SOCKET;
144 RvSocket rv = RvClientConnect(meeting_host, port, target_pubkey); 161 RvClientFail rvFail = RVC_FAIL_NONE;
145 if (rv == RV_INVALID) return INVALID_SOCKET; 162 RvSocket rv = RvClientConnect(meeting_host, port, target_pubkey, &rvFail);
163 if (rv == RV_INVALID)
164 {
165 if (pFail)
166 *pFail = rvFail == RVC_FAIL_DIAL ? TUNNEL_FAIL_DIAL
167 : rvFail == RVC_FAIL_REFUSED ? TUNNEL_FAIL_REFUSED
168 : TUNNEL_FAIL_PAIR;
169 return INVALID_SOCKET;
170 }
146 171
147 unsigned char eph_pub[32], shared[32]; 172 unsigned char eph_pub[32], shared[32];
173 if (pFail) *pFail = TUNNEL_FAIL_HANDSHAKE;
148 if (!ChanClientHandshake(service_enc_pub, eph_pub, shared)) { RvClose(rv); return INVALID_SOCKET; } 174 if (!ChanClientHandshake(service_enc_pub, eph_pub, shared)) { RvClose(rv); return INVALID_SOCKET; }
149 if (!RvWriteN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; } 175 if (!RvWriteN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; }
150 return StartTunnel(rv, shared); 176 if (pFail) *pFail = TUNNEL_FAIL_LOCAL;
177 btf_socket_t app = StartTunnel(rv, shared);
178 if (app != INVALID_SOCKET && pFail) *pFail = TUNNEL_FAIL_NONE;
179 return app;
151} 180}
152 181
153btf_socket_t BtfServiceWrap(RvSocket rv, const unsigned char my_enc_sk[32]) 182btf_socket_t BtfServiceWrap(RvSocket rv, const unsigned char my_enc_sk[32])
diff --git a/src/btftunnel.h b/src/btftunnel.h
index 24a7509..3cc87cd 100644
--- a/src/btftunnel.h
+++ b/src/btftunnel.h
@@ -36,12 +36,33 @@ namespace btf
36// Initialize (sockets + libsodium). Idempotent. 36// Initialize (sockets + libsodium). Idempotent.
37bool TunnelInit(); 37bool TunnelInit();
38 38
39// Which stage of BtfClientTunnel failed. INVALID_SOCKET alone says only "no
40// tunnel"; a caller that then guesses at causes in its log line sends whoever
41// reads it to the wrong component (issue #17: "relay unreachable or pool not
42// listening" blamed two healthy parts). The stage is what this side actually
43// knows -- report that.
44enum TunnelFail
45{
46 TUNNEL_FAIL_NONE = 0,
47 TUNNEL_FAIL_INIT, // local socket/crypto init failed
48 TUNNEL_FAIL_DIAL, // TCP connect to the meeting node failed
49 TUNNEL_FAIL_REFUSED, // relay reachable, but no service registered under the pubkey
50 TUNNEL_FAIL_PAIR, // relay connection died before pairing completed
51 TUNNEL_FAIL_HANDSHAKE, // end-to-end channel handshake failed
52 TUNNEL_FAIL_LOCAL, // local pump plumbing (socket pair / threads) failed
53};
54
55// Short description of a TunnelFail for log lines.
56const char* TunnelFailText(TunnelFail f);
57
39// Client: dial the service registered under `target_pubkey` at the meeting node 58// Client: dial the service registered under `target_pubkey` at the meeting node
40// (host:port), box to its static x25519 `service_enc_pub`, and return a local 59// (host:port), box to its static x25519 `service_enc_pub`, and return a local
41// plaintext SOCKET tunneled end-to-end to the service. INVALID_SOCKET on error. 60// plaintext SOCKET tunneled end-to-end to the service. INVALID_SOCKET on error
61// -- with `pFail`, if given, naming the stage that failed.
42btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port, 62btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port,
43 const unsigned char target_pubkey[32], 63 const unsigned char target_pubkey[32],
44 const unsigned char service_enc_pub[32]); 64 const unsigned char service_enc_pub[32],
65 TunnelFail* pFail = 0);
45 66
46// Service: wrap an already-paired rendezvous socket (RvServiceRegister followed 67// Service: wrap an already-paired rendezvous socket (RvServiceRegister followed
47// by RvServiceWaitPaired -- the pairing byte MUST already be consumed) with 68// by RvServiceWaitPaired -- the pairing byte MUST already be consumed) with
diff --git a/src/main.cpp b/src/main.cpp
index 7b3e555..c0a8825 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3758,13 +3758,14 @@ static bool PoolParticipantMiner()
3758 // Step 2b: open encrypted tunnel to pool 3758 // Step 2b: open encrypted tunnel to pool
3759 // ------------------------------------------------------------------ 3759 // ------------------------------------------------------------------
3760 SetParticipantMiningStatus("opening tunnel"); 3760 SetParticipantMiningStatus("opening tunnel");
3761 btf::TunnelFail tunnelFail = btf::TUNNEL_FAIL_NONE;
3761 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, 3762 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port,
3762 target_pubkey, service_enc_pub); 3763 target_pubkey, service_enc_pub, &tunnelFail);
3763 if (s == INVALID_SOCKET) { 3764 if (s == INVALID_SOCKET) {
3764 LogPrint("worker", "[worker] tunnel connect FAILED via %s" 3765 LogPrint("worker", "[worker] tunnel connect FAILED via %s: %s\n",
3765 " (relay unreachable or pool not listening)\n", 3766 meetingHostPort.c_str(), btf::TunnelFailText(tunnelFail));
3766 meetingHostPort.c_str()); 3767 SetParticipantMiningStatus(std::string("tunnel connect failed: ")
3767 SetParticipantMiningStatus("tunnel connect failed"); 3768 + btf::TunnelFailText(tunnelFail));
3768 return false; 3769 return false;
3769 } 3770 }
3770 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str()); 3771 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str());