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-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
committerbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
commit5ce650382352c1503f40abf08129ada69402af55 (patch)
tree6a18198901f1e2c4933f1ac53c9365eec512bf63 /src/btftunnel.cpp
parentf34113291da7e5e3dc7d2468d4f9c212c05c584b (diff)
public commit
Diffstat (limited to 'src/btftunnel.cpp')
-rw-r--r--src/btftunnel.cpp162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/btftunnel.cpp b/src/btftunnel.cpp
new file mode 100644
index 0000000..63d178b
--- /dev/null
+++ b/src/btftunnel.cpp
@@ -0,0 +1,162 @@
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 "sockcount.h"
27#include <cstring>
28#include <vector>
29#include <thread>
30
31namespace btf
32{
33
34bool TunnelInit()
35{
36 return RvInit() && ChanInit();
37}
38
39// Create a connected loopback socket pair (no socketpair() on Windows).
40// Returns app end and pump end; both are ordinary full-duplex TCP sockets.
41static bool MakeLocalPair(btf_socket_t& appEnd, btf_socket_t& pumpEnd)
42{
43 SOCKET listener = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_PAIR_LISTEN);
44 if (listener == INVALID_SOCKET) return false;
45
46 struct sockaddr_in addr;
47 memset(&addr, 0, sizeof(addr));
48 addr.sin_family = AF_INET;
49 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
50 addr.sin_port = 0; // ephemeral
51 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0) { BtfCloseSocket(listener); return false; }
52 if (listen(listener, 1) != 0) { BtfCloseSocket(listener); return false; }
53
54#ifdef _WIN32
55 int len = sizeof(addr);
56#else
57 socklen_t len = sizeof(addr);
58#endif
59 if (getsockname(listener, (struct sockaddr*)&addr, &len) != 0) { BtfCloseSocket(listener); return false; }
60
61 SOCKET a = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_PAIR_APP);
62 if (a == INVALID_SOCKET) { BtfCloseSocket(listener); return false; }
63 if (connect(a, (struct sockaddr*)&addr, sizeof(addr)) != 0) { BtfCloseSocket(a); BtfCloseSocket(listener); return false; }
64
65 SOCKET b = BtfSocketTag(accept(listener, NULL, NULL), SOCK_PAIR_PUMP);
66 BtfCloseSocket(listener);
67 if (b == INVALID_SOCKET) { BtfCloseSocket(a); return false; }
68
69 appEnd = a;
70 pumpEnd = b;
71 return true;
72}
73
74static bool SockReadN(SOCKET s, void* buf, int n)
75{
76 char* p = (char*)buf; int off = 0;
77 while (off < n) { int r = recv(s, p + off, n - off, 0); if (r <= 0) return false; off += r; }
78 return true;
79}
80static bool SockWriteN(SOCKET s, const void* buf, int n)
81{
82 const char* p = (const char*)buf; int off = 0;
83 while (off < n) { int r = send(s, p + off, n - off, 0); if (r <= 0) return false; off += r; }
84 return true;
85}
86
87// Outbound pump: plaintext from the app -> echan frame -> rendezvous socket.
88static void PumpOut(SOCKET pumpEnd, RvSocket rv, std::vector<unsigned char> key)
89{
90 char buf[16 * 1024];
91 for (;;)
92 {
93 int r = recv(pumpEnd, buf, sizeof(buf), 0);
94 if (r <= 0) break;
95 std::vector<unsigned char> frame = ChanEncryptFrame(key.data(), (const unsigned char*)buf, r);
96 if (!RvWriteN(rv, frame.data(), (int)frame.size())) break;
97 }
98 // Tear down: unblock the inbound pump and the app.
99 shutdown((SOCKET)rv, SD_BOTH);
100 shutdown(pumpEnd, SD_BOTH);
101}
102
103// Inbound pump: echan frames from the rendezvous socket -> plaintext to the app.
104static void PumpIn(SOCKET pumpEnd, RvSocket rv, std::vector<unsigned char> key)
105{
106 for (;;)
107 {
108 unsigned char len[4];
109 if (!RvReadN(rv, len, 4)) break;
110 size_t n = ((size_t)len[0]<<24)|((size_t)len[1]<<16)|((size_t)len[2]<<8)|len[3];
111 if (n < (size_t)(CHAN_NONCEBYTES + CHAN_MACBYTES) || n > (1u<<20)) break;
112 std::vector<unsigned char> body(n);
113 if (!RvReadN(rv, body.data(), (int)n)) break;
114 std::vector<unsigned char> pt;
115 if (!ChanDecryptBody(key.data(), body.data(), (int)n, pt)) break; // auth fail -> drop
116 if (!pt.empty() && !SockWriteN(pumpEnd, pt.data(), (int)pt.size())) break;
117 }
118 shutdown(pumpEnd, SD_BOTH);
119 shutdown((SOCKET)rv, SD_BOTH);
120}
121
122// Start the two pumps and hand back the app-facing socket. When both pumps
123// finish, close the pump end and the rendezvous socket.
124static btf_socket_t StartTunnel(RvSocket rv, const unsigned char shared[32])
125{
126 btf_socket_t appEnd, pumpEnd;
127 if (!MakeLocalPair(appEnd, pumpEnd)) { RvClose(rv); return INVALID_SOCKET; }
128
129 std::vector<unsigned char> key(shared, shared + 32);
130 std::thread(PumpOut, pumpEnd, rv, key).detach();
131 std::thread([pumpEnd, rv, key]() {
132 PumpIn(pumpEnd, rv, key);
133 BtfCloseSocket(pumpEnd);
134 RvClose(rv);
135 }).detach();
136 return appEnd;
137}
138
139btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port,
140 const unsigned char target_pubkey[32],
141 const unsigned char service_enc_pub[32])
142{
143 if (!TunnelInit()) return INVALID_SOCKET;
144 RvSocket rv = RvClientConnect(meeting_host, port, target_pubkey);
145 if (rv == RV_INVALID) return INVALID_SOCKET;
146
147 unsigned char eph_pub[32], shared[32];
148 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; }
150 return StartTunnel(rv, shared);
151}
152
153btf_socket_t BtfServiceWrap(RvSocket rv, const unsigned char my_enc_sk[32])
154{
155 if (!TunnelInit()) { RvClose(rv); return INVALID_SOCKET; }
156 unsigned char eph_pub[32], shared[32];
157 if (!RvReadN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; }
158 if (!ChanEndpointHandshake(eph_pub, my_enc_sk, shared)) { RvClose(rv); return INVALID_SOCKET; }
159 return StartTunnel(rv, shared);
160}
161
162} // namespace btf