Core modules (M1-M6): - pkg/protocol: message format, encoding, NAT type enums - pkg/config: server/client config structs, env vars, validation - pkg/auth: CRC64 token, TOTP gen/verify, one-time relay tokens - pkg/nat: UDP/TCP STUN client and server - pkg/signal: WSS message dispatch, sync request/response - pkg/punch: UDP/TCP hole punching + priority chain - pkg/mux: stream multiplexer (7B frame: StreamID+Flags+Len) - pkg/tunnel: mux-based port forwarding with stats - pkg/relay: relay manager with TOTP auth + session bridging - internal/server: signaling server (login/heartbeat/report/coordinator) - internal/client: client (NAT detect/login/punch/relay/reconnect) - cmd/inp2ps + cmd/inp2pc: main entrypoints with graceful shutdown All tests pass: 16 tests across 5 packages Code: 3559 lines core + 861 lines tests = 19 source files
190 lines
4.0 KiB
Go
190 lines
4.0 KiB
Go
package relay
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/openp2p-cn/inp2p/pkg/auth"
|
|
)
|
|
|
|
func TestRelayBridge(t *testing.T) {
|
|
token := auth.MakeToken("test", "pass")
|
|
mgr := NewManager(29700, true, false, 10, token)
|
|
if err := mgr.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer mgr.Stop()
|
|
|
|
sessionID := "test-session-1"
|
|
totp := auth.GenTOTP(token, time.Now().Unix())
|
|
|
|
var wg sync.WaitGroup
|
|
var connA, connB net.Conn
|
|
var errA, errB error
|
|
|
|
// Peer A connects as "from"
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
connA, errA = ConnectToRelay(
|
|
fmt.Sprintf("127.0.0.1:%d", 29700),
|
|
sessionID, "from", "nodeA", totp,
|
|
)
|
|
}()
|
|
|
|
// Peer B connects as "to" after a short delay
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
time.Sleep(200 * time.Millisecond)
|
|
connB, errB = ConnectToRelay(
|
|
fmt.Sprintf("127.0.0.1:%d", 29700),
|
|
sessionID, "to", "nodeB", totp,
|
|
)
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
if errA != nil {
|
|
t.Fatalf("connA error: %v", errA)
|
|
}
|
|
if errB != nil {
|
|
t.Fatalf("connB error: %v", errB)
|
|
}
|
|
defer connA.Close()
|
|
defer connB.Close()
|
|
|
|
// Test data flow: A → B
|
|
msg := []byte("hello through relay")
|
|
connA.Write(msg)
|
|
|
|
buf := make([]byte, 256)
|
|
connB.SetReadDeadline(time.Now().Add(3 * time.Second))
|
|
n, err := connB.Read(buf)
|
|
if err != nil {
|
|
t.Fatalf("read from B: %v", err)
|
|
}
|
|
if string(buf[:n]) != string(msg) {
|
|
t.Errorf("got %q, want %q", buf[:n], msg)
|
|
}
|
|
|
|
// Test data flow: B → A
|
|
reply := []byte("relay pong")
|
|
connB.Write(reply)
|
|
|
|
connA.SetReadDeadline(time.Now().Add(3 * time.Second))
|
|
n, err = connA.Read(buf)
|
|
if err != nil {
|
|
t.Fatalf("read from A: %v", err)
|
|
}
|
|
if string(buf[:n]) != string(reply) {
|
|
t.Errorf("got %q, want %q", buf[:n], reply)
|
|
}
|
|
|
|
// Verify session count
|
|
if mgr.ActiveSessions() != 1 {
|
|
t.Errorf("active sessions: got %d want 1", mgr.ActiveSessions())
|
|
}
|
|
|
|
t.Logf("✅ Relay bridge OK: A↔B bidirectional, %d active sessions", mgr.ActiveSessions())
|
|
}
|
|
|
|
func TestRelayLargeData(t *testing.T) {
|
|
token := auth.MakeToken("test", "pass")
|
|
mgr := NewManager(29701, true, false, 10, token)
|
|
if err := mgr.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer mgr.Stop()
|
|
|
|
sessionID := "test-large-data"
|
|
totp := auth.GenTOTP(token, time.Now().Unix())
|
|
|
|
var wg sync.WaitGroup
|
|
var connA, connB net.Conn
|
|
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
var err error
|
|
connA, err = ConnectToRelay("127.0.0.1:29701", sessionID, "from", "bigA", totp)
|
|
if err != nil {
|
|
t.Errorf("connA: %v", err)
|
|
}
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
time.Sleep(100 * time.Millisecond)
|
|
var err error
|
|
connB, err = ConnectToRelay("127.0.0.1:29701", sessionID, "to", "bigB", totp)
|
|
if err != nil {
|
|
t.Errorf("connB: %v", err)
|
|
}
|
|
}()
|
|
wg.Wait()
|
|
|
|
if connA == nil || connB == nil {
|
|
t.Fatal("connection failed")
|
|
}
|
|
defer connA.Close()
|
|
defer connB.Close()
|
|
|
|
// Send 1MB through relay
|
|
const dataSize = 1024 * 1024
|
|
data := make([]byte, dataSize)
|
|
for i := range data {
|
|
data[i] = byte(i % 256)
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
connA.Write(data)
|
|
}()
|
|
|
|
// Read exact amount on B side
|
|
received := make([]byte, dataSize)
|
|
total := 0
|
|
connB.SetReadDeadline(time.Now().Add(10 * time.Second))
|
|
for total < dataSize {
|
|
n, err := connB.Read(received[total:])
|
|
if err != nil {
|
|
t.Fatalf("read at %d: %v", total, err)
|
|
}
|
|
total += n
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
if len(received) != len(data) {
|
|
t.Fatalf("size mismatch: got %d want %d", len(received), len(data))
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
if received[i] != data[i] {
|
|
t.Fatalf("data mismatch at byte %d", i)
|
|
break
|
|
}
|
|
}
|
|
t.Logf("✅ 1MB relay transfer OK")
|
|
}
|
|
|
|
func TestRelayAuthDenied(t *testing.T) {
|
|
token := auth.MakeToken("real", "token")
|
|
mgr := NewManager(29702, true, false, 10, token)
|
|
if err := mgr.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer mgr.Stop()
|
|
|
|
// Use wrong TOTP
|
|
wrongToken := auth.GenTOTP(auth.MakeToken("wrong", "creds"), time.Now().Unix())
|
|
_, err := ConnectToRelay("127.0.0.1:29702", "bad-session", "from", "badNode", wrongToken)
|
|
if err == nil {
|
|
t.Fatal("expected auth denied, got success")
|
|
}
|
|
t.Logf("✅ Auth denied correctly: %v", err)
|
|
}
|