mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 13:49:07 +00:00
added anyone proxy support
This commit is contained in:
parent
2015d454eb
commit
2647f75ed6
@ -14,6 +14,7 @@ import (
|
||||
"git.debros.io/DeBros/network/pkg/constants"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"git.debros.io/DeBros/network/pkg/anyoneproxy"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -21,6 +22,7 @@ var (
|
||||
timeout = 30 * time.Second
|
||||
format = "table"
|
||||
useProduction = false
|
||||
disableAnon = false
|
||||
)
|
||||
|
||||
// version metadata populated via -ldflags at build time
|
||||
@ -42,6 +44,9 @@ func main() {
|
||||
// Parse global flags
|
||||
parseGlobalFlags(args)
|
||||
|
||||
// Apply disable flag early so all network operations honor it
|
||||
anyoneproxy.SetDisabled(disableAnon)
|
||||
|
||||
switch command {
|
||||
case "version":
|
||||
fmt.Printf("network-cli %s", version)
|
||||
@ -105,6 +110,8 @@ func parseGlobalFlags(args []string) {
|
||||
}
|
||||
case "--production":
|
||||
useProduction = true
|
||||
case "--disable-anonrc":
|
||||
disableAnon = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
295
cmd/node/main.go
295
cmd/node/main.go
@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
@ -13,28 +13,33 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"git.debros.io/DeBros/network/pkg/anyoneproxy"
|
||||
"git.debros.io/DeBros/network/pkg/client"
|
||||
"git.debros.io/DeBros/network/pkg/config"
|
||||
"git.debros.io/DeBros/network/pkg/constants"
|
||||
"git.debros.io/DeBros/network/pkg/logging"
|
||||
"git.debros.io/DeBros/network/pkg/node"
|
||||
"git.debros.io/DeBros/network/pkg/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
dataDir = flag.String("data", "", "Data directory (auto-detected if not provided)")
|
||||
nodeID = flag.String("id", "", "Node identifier (for running multiple local nodes)")
|
||||
bootstrap = flag.String("bootstrap", "", "Bootstrap peer address (for manual override)")
|
||||
role = flag.String("role", "auto", "Node role: auto|bootstrap|node (auto detects based on config)")
|
||||
p2pPort = flag.Int("p2p-port", 4001, "LibP2P listen port")
|
||||
rqlHTTP = flag.Int("rqlite-http-port", 5001, "RQLite HTTP API port")
|
||||
rqlRaft = flag.Int("rqlite-raft-port", 7001, "RQLite Raft port")
|
||||
advertise = flag.String("advertise", "auto", "Advertise mode: auto|localhost|ip")
|
||||
devLocal = flag.Bool("dev-local", false, "Enable development localhost defaults for the client library (sets NETWORK_DEV_LOCAL=1)")
|
||||
help = flag.Bool("help", false, "Show help")
|
||||
dataDir = flag.String("data", "", "Data directory (auto-detected if not provided)")
|
||||
nodeID = flag.String("id", "", "Node identifier (for running multiple local nodes)")
|
||||
bootstrap = flag.String("bootstrap", "", "Bootstrap peer address (for manual override)")
|
||||
role = flag.String("role", "auto", "Node role: auto|bootstrap|node (auto detects based on config)")
|
||||
p2pPort = flag.Int("p2p-port", 4001, "LibP2P listen port")
|
||||
rqlHTTP = flag.Int("rqlite-http-port", 5001, "RQLite HTTP API port")
|
||||
rqlRaft = flag.Int("rqlite-raft-port", 7001, "RQLite Raft port")
|
||||
advertise = flag.String("advertise", "auto", "Advertise mode: auto|localhost|ip")
|
||||
devLocal = flag.Bool("dev-local", false, "Enable development localhost defaults for the client library (sets NETWORK_DEV_LOCAL=1)")
|
||||
disableAnon = flag.Bool("disable-anonrc", false, "Disable Anyone proxy routing (defaults to enabled on 127.0.0.1:9050)")
|
||||
help = flag.Bool("help", false, "Show help")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
// Apply proxy disable flag early
|
||||
anyoneproxy.SetDisabled(*disableAnon)
|
||||
|
||||
if *help {
|
||||
flag.Usage()
|
||||
return
|
||||
@ -193,84 +198,84 @@ func isBootstrapNode() bool {
|
||||
|
||||
// getPreferredLocalIP returns a non-loopback IPv4 address of this machine
|
||||
func getPreferredLocalIP() (string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if (iface.Flags&net.FlagUp) == 0 || (iface.Flags&net.FlagLoopback) != 0 {
|
||||
continue
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no non-loopback IPv4 found")
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if (iface.Flags&net.FlagUp) == 0 || (iface.Flags&net.FlagLoopback) != 0 {
|
||||
continue
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no non-loopback IPv4 found")
|
||||
}
|
||||
|
||||
// isLocalIP checks if the given IP address belongs to this machine
|
||||
func isLocalIP(ip string) bool {
|
||||
if ip == "127.0.0.1" || strings.EqualFold(ip, "localhost") {
|
||||
return true
|
||||
}
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if (iface.Flags&net.FlagUp) == 0 {
|
||||
continue
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var a net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
a = v.IP
|
||||
case *net.IPAddr:
|
||||
a = v.IP
|
||||
}
|
||||
if a != nil && a.String() == ip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
if ip == "127.0.0.1" || strings.EqualFold(ip, "localhost") {
|
||||
return true
|
||||
}
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if (iface.Flags & net.FlagUp) == 0 {
|
||||
continue
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var a net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
a = v.IP
|
||||
case *net.IPAddr:
|
||||
a = v.IP
|
||||
}
|
||||
if a != nil && a.String() == ip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseHostFromMultiaddr extracts the host from a multiaddr
|
||||
func parseHostFromMultiaddr(multiaddr string) string {
|
||||
// Simple parsing for /ip4/host/tcp/port/p2p/peerid format
|
||||
parts := strings.Split(multiaddr, "/")
|
||||
// Simple parsing for /ip4/host/tcp/port/p2p/peerid format
|
||||
parts := strings.Split(multiaddr, "/")
|
||||
|
||||
// Look for ip4/ip6/dns host in the multiaddr
|
||||
for i, part := range parts {
|
||||
if (part == "ip4" || part == "ip6" || part == "dns" || part == "dns4" || part == "dns6") && i+1 < len(parts) {
|
||||
return parts[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
// Look for ip4/ip6/dns host in the multiaddr
|
||||
for i, part := range parts {
|
||||
if (part == "ip4" || part == "ip6" || part == "dns" || part == "dns4" || part == "dns6") && i+1 < len(parts) {
|
||||
return parts[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func startNode(ctx context.Context, cfg *config.Config, port int, isBootstrap bool, logger *logging.StandardLogger) error {
|
||||
@ -309,73 +314,73 @@ func startNode(ctx context.Context, cfg *config.Config, port int, isBootstrap bo
|
||||
|
||||
// runNetworkDiagnostics performs network connectivity tests
|
||||
func runNetworkDiagnostics(target string, logger *logging.StandardLogger) {
|
||||
// If target has scheme, treat as HTTP URL. Otherwise treat as host:port raft.
|
||||
var host, port string
|
||||
if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
|
||||
url := strings.TrimPrefix(strings.TrimPrefix(target, "http://"), "https://")
|
||||
parts := strings.Split(url, ":")
|
||||
if len(parts) == 2 {
|
||||
host, port = parts[0], parts[1]
|
||||
}
|
||||
} else {
|
||||
parts := strings.Split(target, ":")
|
||||
if len(parts) == 2 {
|
||||
host, port = parts[0], parts[1]
|
||||
}
|
||||
}
|
||||
if host == "" || port == "" {
|
||||
logger.Printf("Cannot parse host:port from %s", target)
|
||||
return
|
||||
}
|
||||
// If target has scheme, treat as HTTP URL. Otherwise treat as host:port raft.
|
||||
var host, port string
|
||||
if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
|
||||
url := strings.TrimPrefix(strings.TrimPrefix(target, "http://"), "https://")
|
||||
parts := strings.Split(url, ":")
|
||||
if len(parts) == 2 {
|
||||
host, port = parts[0], parts[1]
|
||||
}
|
||||
} else {
|
||||
parts := strings.Split(target, ":")
|
||||
if len(parts) == 2 {
|
||||
host, port = parts[0], parts[1]
|
||||
}
|
||||
}
|
||||
if host == "" || port == "" {
|
||||
logger.Printf("Cannot parse host:port from %s", target)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Printf("Testing TCP connectivity to %s:%s", host, port)
|
||||
if output, err := exec.Command("timeout", "5", "nc", "-z", "-v", host, port).CombinedOutput(); err == nil {
|
||||
logger.Printf("✅ Port %s:%s is reachable", host, port)
|
||||
logger.Printf("netcat output: %s", strings.TrimSpace(string(output)))
|
||||
} else {
|
||||
logger.Printf("❌ Port %s:%s is NOT reachable", host, port)
|
||||
logger.Printf("netcat error: %v", err)
|
||||
logger.Printf("netcat output: %s", strings.TrimSpace(string(output)))
|
||||
}
|
||||
logger.Printf("Testing TCP connectivity to %s:%s", host, port)
|
||||
if output, err := exec.Command("timeout", "5", "nc", "-z", "-v", host, port).CombinedOutput(); err == nil {
|
||||
logger.Printf("✅ Port %s:%s is reachable", host, port)
|
||||
logger.Printf("netcat output: %s", strings.TrimSpace(string(output)))
|
||||
} else {
|
||||
logger.Printf("❌ Port %s:%s is NOT reachable", host, port)
|
||||
logger.Printf("netcat error: %v", err)
|
||||
logger.Printf("netcat output: %s", strings.TrimSpace(string(output)))
|
||||
}
|
||||
|
||||
// Also probe HTTP status on port 5001 of the same host, which is the default HTTP API
|
||||
httpURL := fmt.Sprintf("http://%s:%d/status", host, 5001)
|
||||
if output, err := exec.Command("timeout", "5", "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", httpURL).Output(); err == nil {
|
||||
httpCode := strings.TrimSpace(string(output))
|
||||
if httpCode == "200" {
|
||||
logger.Printf("✅ HTTP service on %s is responding correctly (status: %s)", httpURL, httpCode)
|
||||
} else {
|
||||
logger.Printf("⚠️ HTTP service on %s responded with status: %s", httpURL, httpCode)
|
||||
}
|
||||
} else {
|
||||
logger.Printf("❌ HTTP request to %s failed: %v", httpURL, err)
|
||||
}
|
||||
// Also probe HTTP status on port 5001 of the same host, which is the default HTTP API
|
||||
httpURL := fmt.Sprintf("http://%s:%d/status", host, 5001)
|
||||
if output, err := exec.Command("timeout", "5", "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", httpURL).Output(); err == nil {
|
||||
httpCode := strings.TrimSpace(string(output))
|
||||
if httpCode == "200" {
|
||||
logger.Printf("✅ HTTP service on %s is responding correctly (status: %s)", httpURL, httpCode)
|
||||
} else {
|
||||
logger.Printf("⚠️ HTTP service on %s responded with status: %s", httpURL, httpCode)
|
||||
}
|
||||
} else {
|
||||
logger.Printf("❌ HTTP request to %s failed: %v", httpURL, err)
|
||||
}
|
||||
|
||||
// Ping test
|
||||
if output, err := exec.Command("ping", "-c", "3", "-W", "2", host).Output(); err == nil {
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "packet loss") {
|
||||
logger.Printf("🏓 Ping result: %s", strings.TrimSpace(line))
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.Printf("❌ Ping test failed: %v", err)
|
||||
}
|
||||
// Ping test
|
||||
if output, err := exec.Command("ping", "-c", "3", "-W", "2", host).Output(); err == nil {
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "packet loss") {
|
||||
logger.Printf("🏓 Ping result: %s", strings.TrimSpace(line))
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.Printf("❌ Ping test failed: %v", err)
|
||||
}
|
||||
|
||||
// DNS resolution
|
||||
if output, err := exec.Command("nslookup", host).Output(); err == nil {
|
||||
logger.Printf("🔍 DNS resolution successful")
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Address:") && !strings.Contains(line, "#53") {
|
||||
logger.Printf("DNS result: %s", strings.TrimSpace(line))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.Printf("❌ DNS resolution failed: %v", err)
|
||||
}
|
||||
// DNS resolution
|
||||
if output, err := exec.Command("nslookup", host).Output(); err == nil {
|
||||
logger.Printf("🔍 DNS resolution successful")
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Address:") && !strings.Contains(line, "#53") {
|
||||
logger.Printf("DNS result: %s", strings.TrimSpace(line))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.Printf("❌ DNS resolution failed: %v", err)
|
||||
}
|
||||
|
||||
logger.Printf("=== END DIAGNOSTICS ===")
|
||||
logger.Printf("=== END DIAGNOSTICS ===")
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -5,13 +5,13 @@ go 1.23.8
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/libp2p/go-libp2p v0.41.1
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.33.1
|
||||
github.com/libp2p/go-libp2p-pubsub v0.14.2
|
||||
github.com/multiformats/go-multiaddr v0.15.0
|
||||
github.com/rqlite/gorqlite v0.0.0-20250609141355-ac86a4a1c9a8
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/net v0.42.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -59,7 +59,6 @@ require (
|
||||
github.com/libp2p/go-netroute v0.2.2 // indirect
|
||||
github.com/libp2p/go-reuseport v0.4.0 // indirect
|
||||
github.com/libp2p/go-yamux/v5 v5.0.0 // indirect
|
||||
github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.66 // indirect
|
||||
@ -123,7 +122,6 @@ require (
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
|
10
go.sum
10
go.sum
@ -132,8 +132,6 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
@ -186,8 +184,6 @@ github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQsc
|
||||
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
|
||||
github.com/libp2p/go-yamux/v5 v5.0.0 h1:2djUh96d3Jiac/JpGkKs4TO49YhsfLopAoryfPmf+Po=
|
||||
github.com/libp2p/go-yamux/v5 v5.0.0/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU=
|
||||
github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q=
|
||||
github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
||||
@ -196,7 +192,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
|
||||
@ -450,7 +445,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
@ -488,9 +482,6 @@ golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -512,7 +503,6 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
|
160
pkg/anyoneproxy/socks.go
Normal file
160
pkg/anyoneproxy/socks.go
Normal file
@ -0,0 +1,160 @@
|
||||
package anyoneproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
goproxy "golang.org/x/net/proxy"
|
||||
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// disabled controls runtime disabling via flags. Default is false (proxy enabled).
|
||||
var disabled bool
|
||||
|
||||
// SetDisabled allows binaries to disable Anyone routing via a flag (e.g. --disable-anonrc).
|
||||
func SetDisabled(v bool) { disabled = v }
|
||||
|
||||
// Enabled reports whether Anyone proxy routing is active.
|
||||
// Defaults to true, using SOCKS5 at 127.0.0.1:9050, unless explicitly disabled
|
||||
// via SetDisabled(true) or environment variable ANYONE_DISABLE=1.
|
||||
// ANYONE_SOCKS5 may override the proxy address.
|
||||
func Enabled() bool {
|
||||
if disabled {
|
||||
return false
|
||||
}
|
||||
if os.Getenv("ANYONE_DISABLE") == "1" {
|
||||
return false
|
||||
}
|
||||
// If explicitly enabled via env or custom addr provided, also true.
|
||||
if os.Getenv("ANYONE_PROXY_ENABLED") == "1" {
|
||||
return true
|
||||
}
|
||||
if os.Getenv("ANYONE_SOCKS5") != "" {
|
||||
return true
|
||||
}
|
||||
// Default: enabled
|
||||
return true
|
||||
}
|
||||
|
||||
// socksAddr returns the SOCKS5 address to use for proxying (host:port).
|
||||
func socksAddr() string {
|
||||
if v := os.Getenv("ANYONE_SOCKS5"); v != "" {
|
||||
return v
|
||||
}
|
||||
return "127.0.0.1:9050"
|
||||
}
|
||||
|
||||
// socksContextDialer implements tcp.ContextDialer over a SOCKS5 proxy.
|
||||
type socksContextDialer struct{ addr string }
|
||||
|
||||
func (d *socksContextDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
// Derive timeout from context deadline if present
|
||||
var timeout time.Duration
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
timeout = time.Until(deadline)
|
||||
if timeout <= 0 {
|
||||
return nil, context.DeadlineExceeded
|
||||
}
|
||||
}
|
||||
base := &net.Dialer{Timeout: timeout}
|
||||
// Create a SOCKS5 dialer using the base dialer
|
||||
socksDialer, err := goproxy.SOCKS5("tcp", d.addr, nil, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return socksDialer.Dial(network, address)
|
||||
}
|
||||
|
||||
// DialerForAddr returns a tcp.DialerForAddr that routes through the Anyone SOCKS5 proxy.
|
||||
// It automatically BYPASSES the proxy for loopback, private, and link-local addresses
|
||||
// to allow local/dev networking (e.g. 127.0.0.1, 10.0.0.0/8, 192.168.0.0/16, fc00::/7, fe80::/10).
|
||||
func DialerForAddr() tcp.DialerForAddr {
|
||||
return func(raddr ma.Multiaddr) (tcp.ContextDialer, error) {
|
||||
// Prefer direct dialing for local/private targets
|
||||
if ip4, err := raddr.ValueForProtocol(ma.P_IP4); err == nil {
|
||||
if ip := net.ParseIP(ip4); ip != nil {
|
||||
if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
return &net.Dialer{}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if ip6, err := raddr.ValueForProtocol(ma.P_IP6); err == nil {
|
||||
if ip := net.ParseIP(ip6); ip != nil {
|
||||
if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
return &net.Dialer{}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if host, err := raddr.ValueForProtocol(ma.P_DNS); err == nil {
|
||||
if host == "localhost" {
|
||||
return &net.Dialer{}, nil
|
||||
}
|
||||
}
|
||||
if host, err := raddr.ValueForProtocol(ma.P_DNS4); err == nil {
|
||||
if host == "localhost" {
|
||||
return &net.Dialer{}, nil
|
||||
}
|
||||
}
|
||||
if host, err := raddr.ValueForProtocol(ma.P_DNS6); err == nil {
|
||||
if host == "localhost" {
|
||||
return &net.Dialer{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Default: use SOCKS dialer
|
||||
return &socksContextDialer{addr: socksAddr()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPClient returns an *http.Client that routes all TCP connections via the Anyone SOCKS5 proxy.
|
||||
// If Anyone proxy is not enabled, it returns http.DefaultClient.
|
||||
func NewHTTPClient() *http.Client {
|
||||
if !Enabled() {
|
||||
return http.DefaultClient
|
||||
}
|
||||
tr := &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// Bypass proxy for localhost/private IPs
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
host = addr
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
d := &net.Dialer{}
|
||||
return d.DialContext(ctx, network, addr)
|
||||
}
|
||||
}
|
||||
if host == "localhost" {
|
||||
d := &net.Dialer{}
|
||||
return d.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
d := &socksContextDialer{addr: socksAddr()}
|
||||
return d.DialContext(ctx, network, addr)
|
||||
},
|
||||
}
|
||||
return &http.Client{Transport: tr}
|
||||
}
|
||||
|
||||
// Address returns the SOCKS5 address used for Anyone routing.
|
||||
func Address() string { return socksAddr() }
|
||||
|
||||
// Running returns true if Anyone proxy is enabled and reachable at Address().
|
||||
// It attempts a short TCP dial and returns false on failure.
|
||||
func Running() bool {
|
||||
if !Enabled() {
|
||||
return false
|
||||
}
|
||||
conn, err := net.DialTimeout("tcp", socksAddr(), 200*time.Millisecond)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = conn.Close()
|
||||
return true
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"git.debros.io/DeBros/network/pkg/discovery"
|
||||
"git.debros.io/DeBros/network/pkg/pubsub"
|
||||
"git.debros.io/DeBros/network/pkg/storage"
|
||||
"git.debros.io/DeBros/network/pkg/anyoneproxy"
|
||||
)
|
||||
|
||||
// Client implements the NetworkClient interface
|
||||
@ -123,14 +124,23 @@ func (c *Client) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create LibP2P host
|
||||
h, err := libp2p.New(
|
||||
// Create LibP2P host with optional Anyone proxy for TCP and optional QUIC disable
|
||||
var opts []libp2p.Option
|
||||
opts = append(opts,
|
||||
libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"), // Random port
|
||||
libp2p.Security(noise.ID, noise.New),
|
||||
libp2p.Transport(tcp.NewTCPTransport),
|
||||
libp2p.Transport(libp2pquic.NewTransport),
|
||||
libp2p.DefaultMuxers,
|
||||
)
|
||||
if anyoneproxy.Enabled() {
|
||||
opts = append(opts, libp2p.Transport(tcp.NewTCPTransport, tcp.WithDialerForAddr(anyoneproxy.DialerForAddr())))
|
||||
} else {
|
||||
opts = append(opts, libp2p.Transport(tcp.NewTCPTransport))
|
||||
}
|
||||
// Enable QUIC only when not proxying. When proxy is enabled, prefer TCP via SOCKS5.
|
||||
if !anyoneproxy.Enabled() {
|
||||
opts = append(opts, libp2p.Transport(libp2pquic.NewTransport))
|
||||
}
|
||||
h, err := libp2p.New(opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create libp2p host: %w", err)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/rqlite/gorqlite"
|
||||
"git.debros.io/DeBros/network/pkg/anyoneproxy"
|
||||
)
|
||||
|
||||
// DatabaseClientImpl implements DatabaseClient
|
||||
@ -250,7 +251,15 @@ func (d *DatabaseClientImpl) connectToAvailableNode() (*gorqlite.Connection, err
|
||||
var lastErr error
|
||||
|
||||
for _, rqliteURL := range rqliteNodes {
|
||||
conn, err := gorqlite.Open(rqliteURL)
|
||||
var conn *gorqlite.Connection
|
||||
var err error
|
||||
// If Anyone proxy is enabled, build a proxy-aware HTTP client
|
||||
if anyoneproxy.Enabled() {
|
||||
httpClient := anyoneproxy.NewHTTPClient()
|
||||
conn, err = gorqlite.OpenWithClient(rqliteURL, httpClient)
|
||||
} else {
|
||||
conn, err = gorqlite.Open(rqliteURL)
|
||||
}
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
noise "github.com/libp2p/go-libp2p/p2p/security/noise"
|
||||
libp2pquic "github.com/libp2p/go-libp2p/p2p/transport/quic"
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
|
||||
@ -22,6 +23,7 @@ import (
|
||||
|
||||
"git.debros.io/DeBros/network/pkg/config"
|
||||
"git.debros.io/DeBros/network/pkg/database"
|
||||
"git.debros.io/DeBros/network/pkg/anyoneproxy"
|
||||
"git.debros.io/DeBros/network/pkg/logging"
|
||||
"git.debros.io/DeBros/network/pkg/storage"
|
||||
)
|
||||
@ -132,15 +134,44 @@ func (n *Node) startLibP2P() error {
|
||||
return fmt.Errorf("failed to load identity: %w", err)
|
||||
}
|
||||
|
||||
// Log Anyone proxy status before constructing host
|
||||
n.logger.ComponentInfo(logging.ComponentLibP2P, "Anyone proxy status",
|
||||
zap.Bool("proxy_enabled", anyoneproxy.Enabled()),
|
||||
zap.String("proxy_addr", anyoneproxy.Address()),
|
||||
zap.Bool("proxy_running", anyoneproxy.Running()),
|
||||
)
|
||||
|
||||
if anyoneproxy.Enabled() && !anyoneproxy.Running() {
|
||||
n.logger.Warn("Anyone proxy is enabled but not reachable",
|
||||
zap.String("addr", anyoneproxy.Address()))
|
||||
}
|
||||
|
||||
// Create LibP2P host with persistent identity
|
||||
h, err := libp2p.New(
|
||||
// Build options allowing conditional proxying via Anyone SOCKS5 and optional QUIC disable
|
||||
var opts []libp2p.Option
|
||||
opts = append(opts,
|
||||
libp2p.Identity(identity),
|
||||
libp2p.ListenAddrs(listenAddrs...),
|
||||
libp2p.Security(noise.ID, noise.New),
|
||||
libp2p.Transport(tcp.NewTCPTransport),
|
||||
libp2p.Transport(libp2pquic.NewTransport),
|
||||
libp2p.DefaultMuxers,
|
||||
)
|
||||
|
||||
// TCP transport with optional SOCKS5 dialer override
|
||||
if anyoneproxy.Enabled() {
|
||||
opts = append(opts, libp2p.Transport(tcp.NewTCPTransport, tcp.WithDialerForAddr(anyoneproxy.DialerForAddr())))
|
||||
} else {
|
||||
opts = append(opts, libp2p.Transport(tcp.NewTCPTransport))
|
||||
}
|
||||
|
||||
// QUIC transport: disabled when proxy is enabled (default),
|
||||
// enabled only when not proxying.
|
||||
if !anyoneproxy.Enabled() {
|
||||
opts = append(opts, libp2p.Transport(libp2pquic.NewTransport))
|
||||
} else {
|
||||
n.logger.ComponentDebug(logging.ComponentLibP2P, "QUIC disabled due to proxy being enabled")
|
||||
}
|
||||
|
||||
h, err := libp2p.New(opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -148,12 +179,31 @@ func (n *Node) startLibP2P() error {
|
||||
n.host = h
|
||||
|
||||
// Create DHT for peer discovery - Use server mode for better peer discovery
|
||||
kademliaDHT, err := dht.New(context.Background(), h, dht.Mode(dht.ModeServer))
|
||||
// Use configured protocol prefix to ensure we discover peers on the correct DHT namespace
|
||||
dhtPrefix := n.config.Discovery.DHTPrefix
|
||||
if strings.TrimSpace(dhtPrefix) == "" {
|
||||
dhtPrefix = "/network/kad/1.0.0"
|
||||
}
|
||||
n.logger.ComponentInfo(logging.ComponentDHT, "Using DHT protocol prefix", zap.String("prefix", dhtPrefix))
|
||||
kademliaDHT, err := dht.New(
|
||||
context.Background(),
|
||||
h,
|
||||
dht.Mode(dht.ModeServer),
|
||||
dht.ProtocolPrefix(protocol.ID(dhtPrefix)),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create DHT: %w", err)
|
||||
}
|
||||
n.dht = kademliaDHT
|
||||
|
||||
// Log configured bootstrap peers
|
||||
if len(n.config.Discovery.BootstrapPeers) > 0 {
|
||||
n.logger.ComponentInfo(logging.ComponentDHT, "Configured bootstrap peers",
|
||||
zap.Strings("peers", n.config.Discovery.BootstrapPeers))
|
||||
} else {
|
||||
n.logger.ComponentDebug(logging.ComponentDHT, "No bootstrap peers configured")
|
||||
}
|
||||
|
||||
// Connect to LibP2P bootstrap peers if configured
|
||||
if err := n.connectToBootstrapPeers(); err != nil {
|
||||
n.logger.Warn("Failed to connect to bootstrap peers", zap.Error(err))
|
||||
@ -308,6 +358,13 @@ func (n *Node) connectToBootstrapPeer(ctx context.Context, addr string) error {
|
||||
return fmt.Errorf("failed to extract peer info: %w", err)
|
||||
}
|
||||
|
||||
// Log resolved peer info prior to connect
|
||||
n.logger.ComponentDebug(logging.ComponentDHT, "Resolved bootstrap peer",
|
||||
zap.String("peer_id", peerInfo.ID.String()),
|
||||
zap.String("addr", addr),
|
||||
zap.Int("addr_count", len(peerInfo.Addrs)),
|
||||
)
|
||||
|
||||
// Connect to the peer
|
||||
if err := n.host.Connect(ctx, *peerInfo); err != nil {
|
||||
return fmt.Errorf("failed to connect to peer: %w", err)
|
||||
|
Loading…
x
Reference in New Issue
Block a user