- Document all 4 platforms with status and architecture details - Add API reference table - Add platform setup sections (iOS, macOS, Android, Linux) - Document build commands for all platforms - Add binary size table - Note Android Tailscale tunnel limitation and path forward - Add Linux test file Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
117 lines
2.9 KiB
Go
117 lines
2.9 KiB
Go
// Linux test for tsnet — loads the shared library via CGo and tests the tunnel.
|
|
//
|
|
// Run from macOS via Docker:
|
|
// docker run --rm --platform linux/amd64 \
|
|
// -v /path/to/flutter-tsnet:/src -w /src/test \
|
|
// golang:latest go run linux_test.go \
|
|
// -authkey=tskey-auth-xxx \
|
|
// -heater-ip=100.x.x.x
|
|
package main
|
|
|
|
/*
|
|
#cgo LDFLAGS: -L${SRCDIR}/../linux -ltailscale -lresolv -lpthread
|
|
#include <stdlib.h>
|
|
|
|
extern char* TailscaleStart(char*, char*, char*);
|
|
extern char* TailscaleStartProxy(char*, int, int*);
|
|
extern char* TailscaleStopProxy();
|
|
extern char* TailscaleStop();
|
|
extern char* TailscaleStatus(char**);
|
|
extern char* TailscaleIP(char**);
|
|
extern void TailscaleFreeString(char*);
|
|
*/
|
|
import "C"
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
func goString(cs *C.char) string {
|
|
if cs == nil {
|
|
return ""
|
|
}
|
|
s := C.GoString(cs)
|
|
C.TailscaleFreeString(cs)
|
|
return s
|
|
}
|
|
|
|
func main() {
|
|
authKey := flag.String("authkey", "", "Tailscale auth key")
|
|
heaterIP := flag.String("heater-ip", "", "Heater's Tailscale IP")
|
|
heaterPort := flag.Int("heater-port", 5050, "Heater's port")
|
|
flag.Parse()
|
|
|
|
if *authKey == "" || *heaterIP == "" {
|
|
fmt.Fprintf(os.Stderr, "Usage: go run linux_test.go -authkey=tskey-auth-xxx -heater-ip=100.x.x.x\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// State dir
|
|
stateDir := "/tmp/tsnet-linux-test"
|
|
os.MkdirAll(stateDir, 0o700)
|
|
|
|
// 1. Start
|
|
fmt.Println("[1/4] Starting tsnet...")
|
|
start := time.Now()
|
|
dirC := C.CString(stateDir)
|
|
keyC := C.CString(*authKey)
|
|
hostC := C.CString("tsnet-linux-test")
|
|
err := goString(C.TailscaleStart(dirC, keyC, hostC))
|
|
C.free(unsafe.Pointer(dirC))
|
|
C.free(unsafe.Pointer(keyC))
|
|
C.free(unsafe.Pointer(hostC))
|
|
if err != "" {
|
|
fmt.Printf("[1/4] FAIL: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("[1/4] Started in %dms\n", time.Since(start).Milliseconds())
|
|
|
|
// 2. Get IP (with retry)
|
|
fmt.Println("[2/4] Getting Tailscale IP...")
|
|
var ip string
|
|
for i := 0; i < 30; i++ {
|
|
var errOut *C.char
|
|
ipC := C.TailscaleIP(&errOut)
|
|
e := goString(errOut)
|
|
if e == "" {
|
|
ip = goString(ipC)
|
|
break
|
|
}
|
|
C.TailscaleFreeString(ipC)
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|
|
fmt.Printf("[2/4] Our IP: %s\n", ip)
|
|
|
|
// 3. Start proxy
|
|
fmt.Println("[3/4] Starting proxy...")
|
|
ipC := C.CString(*heaterIP)
|
|
var localPort C.int
|
|
errStr := goString(C.TailscaleStartProxy(ipC, C.int(*heaterPort), &localPort))
|
|
C.free(unsafe.Pointer(ipC))
|
|
if errStr != "" {
|
|
fmt.Printf("[3/4] FAIL: %s\n", errStr)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("[3/4] Proxy: localhost:%d → %s:%d\n", int(localPort), *heaterIP, *heaterPort)
|
|
|
|
// 4. Test TCP
|
|
fmt.Println("[4/4] Testing TCP connection...")
|
|
conn, connErr := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", int(localPort)), 10*time.Second)
|
|
if connErr != nil {
|
|
fmt.Printf("[4/4] TCP FAILED: %v\n", connErr)
|
|
} else {
|
|
fmt.Println("[4/4] TCP SUCCESS")
|
|
conn.Close()
|
|
}
|
|
|
|
// Cleanup
|
|
C.TailscaleStopProxy()
|
|
C.TailscaleStop()
|
|
|
|
fmt.Println("\nALL TESTS PASSED")
|
|
}
|