diff --git a/.gitignore b/.gitignore index 2887eff..8cc36df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .history/ +.idea/ p2p_poc peer_cache.json diff --git a/client.go b/client.go index c251dc0..f214467 100644 --- a/client.go +++ b/client.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "log" + "net" "os" "sync" "time" @@ -70,8 +71,8 @@ func NewClient(config Config) (P2PClient, error) { hostOpts = append(hostOpts, libp2p.Identity(config.PrivateKey)) // Configure announce addresses if provided (useful for K8s) + var announceAddrs []multiaddr.Multiaddr if len(config.AnnounceAddrs) > 0 { - var announceAddrs []multiaddr.Multiaddr for _, addrStr := range config.AnnounceAddrs { maddr, err := multiaddr.NewMultiaddr(addrStr) if err != nil { @@ -87,17 +88,37 @@ func NewClient(config Config) (P2PClient, error) { logger.Infof("Using custom announce addresses: %v", config.AnnounceAddrs) } - // Create libp2p host + // Simple address factory to only use custom announce addresses if provided + // Otherwise let libp2p/AutoNAT handle address detection automatically + var addressFactory func([]multiaddr.Multiaddr) []multiaddr.Multiaddr + if len(announceAddrs) > 0 { + addressFactory = func([]multiaddr.Multiaddr) []multiaddr.Multiaddr { + return announceAddrs + } + logger.Infof("Using only custom announce addresses for advertising") + } + + // Get bootstrap peers from DHT library - these can act as relays + bootstrapPeers := dht.GetDefaultBootstrapPeerAddrInfos() + + // Create libp2p host with NAT traversal options hostOpts = append(hostOpts, libp2p.ListenAddrStrings( - "/ip4/0.0.0.0/tcp/0", - "/ip6/::/tcp/0", + fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", config.Port), // Listen on all interfaces + fmt.Sprintf("/ip6/::/tcp/%d", config.Port), ), - libp2p.EnableNATService(), - libp2p.EnableHolePunching(), - libp2p.EnableRelay(), + libp2p.NATPortMap(), // Try UPnP/NAT-PMP for automatic port forwarding + libp2p.EnableNATService(), // AutoNAT to detect if we're reachable + libp2p.EnableHolePunching(), // DCUtR protocol for NAT hole punching + libp2p.EnableRelay(), // Act as relay for others + libp2p.EnableAutoRelayWithStaticRelays(bootstrapPeers), // Use bootstrap nodes as potential relays ) + // Only apply address factory if custom announce addresses are provided + if addressFactory != nil { + hostOpts = append(hostOpts, libp2p.AddrsFactory(addressFactory)) + } + h, err := libp2p.New(hostOpts...) if err != nil { cancel() @@ -108,7 +129,6 @@ func NewClient(config Config) (P2PClient, error) { logger.Infof("Listening on: %v", h.Addrs()) // Set up DHT with bootstrap peers - bootstrapPeers := dht.GetDefaultBootstrapPeerAddrInfos() kadDHT, err := dht.New(ctx, h, dht.Mode(dht.ModeServer), dht.BootstrapPeers(bootstrapPeers...)) if err != nil { h.Close() @@ -634,3 +654,51 @@ func PrivateKeyFromHex(keyHex string) (crypto.PrivKey, error) { return priv, nil } + +// Function to check if an IP address is private +func isPrivateIP(addr multiaddr.Multiaddr) bool { + ipStr, err := extractIPFromMultiaddr(addr) + if err != nil { + return false + } + // Check for IPv6 loopback + if ipStr == "::1" { + return true + } + + ip := net.ParseIP(ipStr) + if ip == nil || ip.To4() == nil { + return false + } + + // Define private IPv4 and IPv6 ranges + privateRanges := []*net.IPNet{ + // IPv4 + {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)}, + {IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)}, + {IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)}, + {IP: net.ParseIP("127.0.0.0"), Mask: net.CIDRMask(8, 32)}, + // IPv6 + {IP: net.ParseIP("fc00::"), Mask: net.CIDRMask(7, 128)}, // Unique local address + {IP: net.ParseIP("fe80::"), Mask: net.CIDRMask(10, 128)}, // Link-local unicast + } + + // Check if the IP falls into any of the private ranges or is loopback (::1) + for _, r := range privateRanges { + if r.Contains(ip) { + return true + } + } + + return false +} + +// Function to extract IP information from a Multiaddr (supports IPv4 and IPv6) +func extractIPFromMultiaddr(addr multiaddr.Multiaddr) (string, error) { + ip, err := addr.ValueForProtocol(multiaddr.P_IP4) + if err == nil && ip != "" { + return ip, nil + } + return addr.ValueForProtocol(multiaddr.P_IP6) +} + diff --git a/config.go b/config.go index 992524f..1c87d73 100644 --- a/config.go +++ b/config.go @@ -53,4 +53,10 @@ type Config struct { // Example: []string{"/ip4/203.0.113.1/tcp/4001"} // If not provided, libp2p will automatically detect and announce local addresses. AnnounceAddrs []string + + // AllowPrivateIPs indicates whether to allow connections to peers with private IP addresses. + AllowPrivateIPs bool + + // Port is the network port to listen on for incoming connections. If zero, a random port will be chosen. + Port int } diff --git a/example/go.mod b/example/go.mod index 5bdd7a7..e36f2a0 100644 --- a/example/go.mod +++ b/example/go.mod @@ -3,8 +3,9 @@ module github.com/ordishs/p2p_poc/example go 1.25.1 require ( - github.com/ordishs/gocore v1.0.81 github.com/bsv-blockchain/go-p2p-message-bus v0.0.0 + github.com/libp2p/go-libp2p v0.43.0 + github.com/ordishs/gocore v1.0.81 ) require ( @@ -39,7 +40,6 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect - github.com/libp2p/go-libp2p v0.43.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-libp2p-kad-dht v0.34.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect diff --git a/example/main.go b/example/main.go index b855704..b3dd8e4 100644 --- a/example/main.go +++ b/example/main.go @@ -10,15 +10,16 @@ import ( "syscall" "time" + p2p "github.com/bsv-blockchain/go-p2p-message-bus" "github.com/libp2p/go-libp2p/core/crypto" "github.com/ordishs/gocore" - p2p "github.com/bsv-blockchain/go-p2p-message-bus" ) func main() { name := flag.String("name", "", "Your node name") privateKey := flag.String("key", "", "Private key hex (will generate if not provided)") topics := flag.String("topics", "broadcast_p2p_poc", "Comma-separated list of topics to subscribe to") + port := flag.Int("port", 0, "port to listen on (0 for random)") noBroadcast := flag.Bool("no-broadcast", false, "Disable message broadcasting") flag.Parse() @@ -62,10 +63,12 @@ func main() { // Create P2P client client, err := p2p.NewClient(p2p.Config{ - Name: *name, - Logger: logger, - PrivateKey: privKey, - PeerCacheFile: "peer_cache.json", // Enable peer persistence + Name: *name, + Logger: logger, + PrivateKey: privKey, + Port: *port, + AllowPrivateIPs: false, + PeerCacheFile: "peer_cache.json", // Enable peer persistence }) if err != nil { logger.Fatalf("Failed to create P2P client: %v", err) diff --git a/go.mod b/go.mod index 65228bb..f3cff00 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/koron/go-ssdp v0.0.6 // indirect @@ -50,7 +51,9 @@ require ( github.com/libp2p/go-yamux/v5 v5.0.1 // 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-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/miekg/dns v1.1.68 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect @@ -66,6 +69,7 @@ require ( github.com/multiformats/go-multistream v0.6.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/ordishs/gocore v1.0.81 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect diff --git a/go.sum b/go.sum index c843827..a96ab76 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,8 @@ 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= @@ -164,9 +166,14 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm 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= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 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.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= @@ -213,6 +220,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/ordishs/gocore v1.0.81 h1:5egJfLOVrdnK6es2ED1SbXVfEazGSMkpvo1bvvKO8Nc= +github.com/ordishs/gocore v1.0.81/go.mod h1:8/PDn0aIq7/AQcrGBXHE1rkw8bkd33bwgCo2SXDq09s= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= @@ -437,6 +446,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=