upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/btftunnel.cpp
diff options
context:
space:
mode:
authorBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
committerBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
commit51d617e70f90a972185917ea0096c1ecef2bfa8b (patch)
tree9fbbcd867de9de9d0776afdd02f594ba312c46eb /src/btftunnel.cpp
Bitflash: fair-launch release
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
Diffstat (limited to 'src/btftunnel.cpp')
-rw-r--r--src/btftunnel.cpp161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/btftunnel.cpp b/src/btftunnel.cpp
new file mode 100644
index 0000000..65c4b89
--- /dev/null
+++ b/src/btftunnel.cpp
@@ -0,0 +1,161 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3//
4// Transparent anonymous tunnel. See btftunnel.h.
5
6#include "btftunnel.h"
7#include "btfchan.h"
8
9#ifdef _WIN32
10#include <winsock2.h>
11#include <ws2tcpip.h>
12#else
13#include <sys/socket.h>
14#include <netinet/in.h>
15#include <arpa/inet.h>
16#include <unistd.h>
17typedef int SOCKET;
18#ifndef INVALID_SOCKET
19#define INVALID_SOCKET (-1)
20#endif
21#define closesocket(s) ::close(s)
22#ifndef SD_BOTH
23#define SD_BOTH SHUT_RDWR
24#endif
25#endif
26#include <cstring>
27#include <vector>
28#include <thread>
29
30namespace btf
31{
32
33bool TunnelInit()
34{
35 return RvInit() && ChanInit();
36}
37
38// Create a connected loopback socket pair (no socketpair() on Windows).
39// Returns app end and pump end; both are ordinary full-duplex TCP sockets.
40static bool MakeLocalPair(btf_socket_t& appEnd, btf_socket_t& pumpEnd)
41{
42 SOCKET listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
43 if (listener == INVALID_SOCKET) return false;
44
45 struct sockaddr_in addr;
46 memset(&addr, 0, sizeof(addr));
47 addr.sin_family = AF_INET;
48 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
49 addr.sin_port = 0; // ephemeral
50 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0) { closesocket(listener); return false; }
51 if (listen(listener, 1) != 0) { closesocket(listener); return false; }
52
53#ifdef _WIN32
54 int len = sizeof(addr);
55#else
56 socklen_t len = sizeof(addr);
57#endif
58 if (getsockname(listener, (struct sockaddr*)&addr, &len) != 0) { closesocket(listener); return false; }
59
60 SOCKET a = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
61 if (a == INVALID_SOCKET) { closesocket(listener); return false; }
62 if (connect(a, (struct sockaddr*)&addr, sizeof(addr)) != 0) { closesocket(a); closesocket(listener); return false; }
63
64 SOCKET b = accept(listener, NULL, NULL);
65 closesocket(listener);
66 if (b == INVALID_SOCKET) { closesocket(a); return false; }
67
68 appEnd = a;
69 pumpEnd = b;
70 return true;
71}
72
73static bool SockReadN(SOCKET s, void* buf, int n)
74{
75 char* p = (char*)buf; int off = 0;
76 while (off < n) { int r = recv(s, p + off, n - off, 0); if (r <= 0) return false; off += r; }
77 return true;
78}
79static bool SockWriteN(SOCKET s, const void* buf, int n)
80{
81 const char* p = (const char*)buf; int off = 0;
82 while (off < n) { int r = send(s, p + off, n - off, 0); if (r <= 0) return false; off += r; }
83 return true;
84}
85
86// Outbound pump: plaintext from the app -> echan frame -> rendezvous socket.
87static void PumpOut(SOCKET pumpEnd, RvSocket rv, std::vector<unsigned char> key)
88{
89 char buf[16 * 1024];
90 for (;;)
91 {
92 int r = recv(pumpEnd, buf, sizeof(buf), 0);
93 if (r <= 0) break;
94 std::vector<unsigned char> frame = ChanEncryptFrame(key.data(), (const unsigned char*)buf, r);
95 if (!RvWriteN(rv, frame.data(), (int)frame.size())) break;
96 }
97 // Tear down: unblock the inbound pump and the app.
98 shutdown((SOCKET)rv, SD_BOTH);
99 shutdown(pumpEnd, SD_BOTH);
100}
101
102// Inbound pump: echan frames from the rendezvous socket -> plaintext to the app.
103static void PumpIn(SOCKET pumpEnd, RvSocket rv, std::vector<unsigned char> key)
104{
105 for (;;)
106 {
107 unsigned char len[4];
108 if (!RvReadN(rv, len, 4)) break;
109 size_t n = ((size_t)len[0]<<24)|((size_t)len[1]<<16)|((size_t)len[2]<<8)|len[3];
110 if (n < (size_t)(CHAN_NONCEBYTES + CHAN_MACBYTES) || n > (1u<<20)) break;
111 std::vector<unsigned char> body(n);
112 if (!RvReadN(rv, body.data(), (int)n)) break;
113 std::vector<unsigned char> pt;
114 if (!ChanDecryptBody(key.data(), body.data(), (int)n, pt)) break; // auth fail -> drop
115 if (!pt.empty() && !SockWriteN(pumpEnd, pt.data(), (int)pt.size())) break;
116 }
117 shutdown(pumpEnd, SD_BOTH);
118 shutdown((SOCKET)rv, SD_BOTH);
119}
120
121// Start the two pumps and hand back the app-facing socket. When both pumps
122// finish, close the pump end and the rendezvous socket.
123static btf_socket_t StartTunnel(RvSocket rv, const unsigned char shared[32])
124{
125 btf_socket_t appEnd, pumpEnd;
126 if (!MakeLocalPair(appEnd, pumpEnd)) { RvClose(rv); return INVALID_SOCKET; }
127
128 std::vector<unsigned char> key(shared, shared + 32);
129 std::thread(PumpOut, pumpEnd, rv, key).detach();
130 std::thread([pumpEnd, rv, key]() {
131 PumpIn(pumpEnd, rv, key);
132 closesocket(pumpEnd);
133 RvClose(rv);
134 }).detach();
135 return appEnd;
136}
137
138btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port,
139 const unsigned char target_pubkey[32],
140 const unsigned char service_enc_pub[32])
141{
142 if (!TunnelInit()) return INVALID_SOCKET;
143 RvSocket rv = RvClientConnect(meeting_host, port, target_pubkey);
144 if (rv == RV_INVALID) return INVALID_SOCKET;
145
146 unsigned char eph_pub[32], shared[32];
147 if (!ChanClientHandshake(service_enc_pub, eph_pub, shared)) { RvClose(rv); return INVALID_SOCKET; }
148 if (!RvWriteN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; }
149 return StartTunnel(rv, shared);
150}
151
152btf_socket_t BtfServiceWrap(RvSocket rv, const unsigned char my_enc_sk[32])
153{
154 if (!TunnelInit()) { RvClose(rv); return INVALID_SOCKET; }
155 unsigned char eph_pub[32], shared[32];
156 if (!RvReadN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; }
157 if (!ChanEndpointHandshake(eph_pub, my_enc_sk, shared)) { RvClose(rv); return INVALID_SOCKET; }
158 return StartTunnel(rv, shared);
159}
160
161} // namespace btf