added anyone proxy support

This commit is contained in:
anonpenguin 2025-08-10 16:34:11 +03:00
parent 2015d454eb
commit 2647f75ed6
8 changed files with 403 additions and 167 deletions

View File

@ -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
}
}
}

View File

@ -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
View File

@ -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
View File

@ -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
View 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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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)