diff --git a/Commands.go b/Commands.go index 1c34466..8d18f34 100644 --- a/Commands.go +++ b/Commands.go @@ -10,6 +10,8 @@ import ( "bytes" "fmt" "time" + + "github.com/PeernetOfficial/core/dht" ) // respondClosesContactsCount is the number of closest contact to respond. @@ -31,19 +33,29 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement) { fmt.Printf("Incoming secondary announcement from %s\n", msg.connection.Address.String()) } + // Filter function to only share peers that are "connectable" to the remote one. It checks IPv4, IPv6, and local connection. + filterFunc := func(allowLocal, allowIPv4, allowIPv6 bool) dht.NodeFilterFunc { + return func(node *dht.Node) (accept bool) { + return node.Info.(*PeerInfo).IsConnectable(allowLocal, allowIPv4, allowIPv6) + } + } + + allowIPv4 := msg.Features&(1< 0 + allowIPv6 := msg.Features&(1< 0 + var hash2Peers []Hash2Peer - // Requesting peers close to the sender? FIND_SELF + // FIND_SELF: Requesting peers close to the sender? if msg.Actions&(1< 0 { - for _, node := range nodesDHT.GetClosestContacts(respondClosesContactsCount, peer.NodeID, peer.NodeID) { - // do not respond the caller's own peer - if info := node.Info.(*PeerInfo).peer2Info(msg.connection.IsLocal()); info != nil { + // do not respond the caller's own peer (add to ignore list) + for _, node := range nodesDHT.GetClosestContacts(respondClosesContactsCount, peer.NodeID, filterFunc(msg.connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) { + if info := node.Info.(*PeerInfo).peer2Info(msg.connection.IsLocal(), allowIPv4, allowIPv6); info != nil { hash2Peers = append(hash2Peers, Hash2Peer{ID: KeyHash{node.ID}, Closest: []InfoPeer{*info}}) } } } - // Find a different peer? + // FIND_PEER: Find a different peer? Note that in this case no IPv4/IPv6 connectivity check is performed. if msg.Actions&(1< 0 { // TODO } @@ -62,8 +74,8 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement) { peer.sendResponse(added, hash2Peers, nil, nil) } -func (peer *PeerInfo) peer2Info(allowLocal bool) (result *InfoPeer) { - if connection := peer.GetConnection2Share(allowLocal); connection != nil { +func (peer *PeerInfo) peer2Info(allowLocal, allowIPv4, allowIPv6 bool) (result *InfoPeer) { + if connection := peer.GetConnection2Share(allowLocal, allowIPv4, allowIPv6); connection != nil { return &InfoPeer{ PublicKey: peer.PublicKey, NodeID: peer.NodeID, diff --git a/Config.go b/Config.go index 8fb60ba..cda328d 100644 --- a/Config.go +++ b/Config.go @@ -16,7 +16,7 @@ import ( ) // Version is the current core library version -const Version = "0.1" +const Version = "0.2" var config struct { LogFile string `yaml:"LogFile"` // Log file diff --git a/Connection.go b/Connection.go index 420d7c7..f16fc59 100644 --- a/Connection.go +++ b/Connection.go @@ -56,18 +56,36 @@ func (peer *PeerInfo) GetConnections(active bool) (connections []*Connection) { return peer.connectionInactive } -// GetConnection2Share returns a connection to share. Nil if none. -// allowLocal specifies whether it is OK to return local IPs. -func (peer *PeerInfo) GetConnection2Share(allowLocal bool) (connections *Connection) { +// IsConnectable checks if the peer is connectable to the given IP parameters. +func (peer *PeerInfo) IsConnectable(allowLocal, allowIPv4, allowIPv6 bool) bool { peer.RLock() defer peer.RUnlock() - if peer.connectionLatest != nil && !(!allowLocal && peer.connectionLatest.IsLocal()) { + // Only 1 active connection must be allowed for being connectable. + for _, connection := range peer.connectionActive { + if IsIPv4(connection.Address.IP) && allowIPv4 || IsIPv6(connection.Address.IP) && allowIPv6 { + if !(!allowLocal && connection.IsLocal()) { + return true + } + } + } + + return false +} + +// GetConnection2Share returns a connection to share. Nil if none. +// allowLocal specifies whether it is OK to return local IPs. +func (peer *PeerInfo) GetConnection2Share(allowLocal, allowIPv4, allowIPv6 bool) (connections *Connection) { + peer.RLock() + defer peer.RUnlock() + + if peer.connectionLatest != nil && !(!allowLocal && peer.connectionLatest.IsLocal()) && + (IsIPv4(peer.connectionLatest.Address.IP) && allowIPv4 || IsIPv6(peer.connectionLatest.Address.IP) && allowIPv6) { return peer.connectionLatest } for _, connection := range peer.connectionActive { - if !(!allowLocal && connection.IsLocal()) { + if (IsIPv4(connection.Address.IP) && allowIPv4 || IsIPv6(connection.Address.IP) && allowIPv6) && !(!allowLocal && connection.IsLocal()) { return connection } } diff --git a/Message Encoding.go b/Message Encoding.go index 940f3fc..711d161 100644 --- a/Message Encoding.go +++ b/Message Encoding.go @@ -22,9 +22,6 @@ import ( // ProtocolVersion is the current protocol version const ProtocolVersion = 0 -// FeatureSupport is for future use -var FeatureSupport = 0 - // UserAgent should be set by the caller var UserAgent = "Peernet Core/0.1" @@ -53,6 +50,12 @@ const ( ActionInfoStore = 3 // INFO_STORE Sender indicates storing provided data ) +// Features are sent as bit array in the Announcement message. +const ( + FeatureIPv4Listen = 0 // Sender listens on IPv4 + FeatureIPv6Listen = 1 // Sender listens on IPv6 +) + // MessageRaw is a high-level message between peers that has not been decoded type MessageRaw struct { PacketRaw @@ -258,8 +261,8 @@ func msgDecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) { return nil, errors.New("response: invalid minimum length") } - result.Protocol = msg.Payload[0] & 0x0F // Protocol version support is stored in the first 4 bits - result.Features = msg.Payload[1] // Feature support + result.Protocol = msg.Payload[0] & 0x0F // Protocol version support is stored in the first 4 bits + result.Features = decodeFeatureSupport(msg.Payload[1]) // Feature support result.Actions = msg.Payload[2] result.BlockchainHeight = binary.LittleEndian.Uint32(msg.Payload[3:7]) result.BlockchainVersion = binary.LittleEndian.Uint64(msg.Payload[7:15]) @@ -418,6 +421,15 @@ func decodeEmbeddedFile(data []byte, count int) (filesEmbed []EmbeddedFileData, return filesEmbed, read, true } +func decodeFeatureSupport(feature byte) byte { + // Compatibility with Alpha 1: Set both IPv4/IPv6 bits if none is set. + if feature&(1< udpMaxPacketSize-packetLengthMin } +func selfFeatureSupport() (feature byte) { + // networksMutex not needed here + if len(networks4) > 0 { + feature |= 1 << FeatureIPv4Listen + } + if len(networks6) > 0 { + feature |= 1 << FeatureIPv6Listen + } + return feature +} + // msgEncodeAnnouncement encodes an announcement message. It may return multiple messages if the input does not fit into one. // findPeer is a list of node IDs (blake3 hash of peer ID compressed form) // findValue is a list of hashes @@ -438,7 +461,7 @@ createPacketLoop: packetSize := announcementPayloadHeaderSize raw[0] = byte(ProtocolVersion) // Protocol - raw[1] = byte(FeatureSupport) // Feature support + raw[1] = selfFeatureSupport() // Feature support //raw[2] = Actions // Action bit array binary.LittleEndian.PutUint32(raw[3:7], BlockchainHeight) binary.LittleEndian.PutUint64(raw[7:15], BlockchainVersion) @@ -574,7 +597,7 @@ createPacketLoop: packetSize := announcementPayloadHeaderSize raw[0] = byte(ProtocolVersion) // Protocol - raw[1] = byte(FeatureSupport) // Feature support + raw[1] = selfFeatureSupport() // Feature support //raw[2] = Actions // Action bit array binary.LittleEndian.PutUint32(raw[3:7], BlockchainHeight) binary.LittleEndian.PutUint64(raw[7:15], BlockchainVersion) diff --git a/go.mod b/go.mod index d7c487e..2808d80 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/PeernetOfficial/core go 1.16 require ( - github.com/PeernetOfficial/core/dht v0.0.0-20210316211915-8b329f7258d1 + github.com/PeernetOfficial/core/dht v0.0.0-20210405124533-d83123bf46c9 github.com/PeernetOfficial/core/reuseport v0.0.0-20210316211915-8b329f7258d1 github.com/btcsuite/btcd v0.21.0-beta golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b diff --git a/go.sum b/go.sum index 0079476..be36f40 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/PeernetOfficial/core/dht v0.0.0-20210316211915-8b329f7258d1 h1:kbSPQTir7JrBAR7OL6TZIwWsRuqroafeXnFpKXOyEvk= -github.com/PeernetOfficial/core/dht v0.0.0-20210316211915-8b329f7258d1/go.mod h1:sb2H21VIVTohCXVrDFo/Skl2tN8Yzba+MXSS6NgCYXw= +github.com/PeernetOfficial/core/dht v0.0.0-20210405124533-d83123bf46c9 h1:J3SCdaAy8XnuwgwdqYimNzyt4HFQv+EKCPYbItoky8c= +github.com/PeernetOfficial/core/dht v0.0.0-20210405124533-d83123bf46c9/go.mod h1:sb2H21VIVTohCXVrDFo/Skl2tN8Yzba+MXSS6NgCYXw= github.com/PeernetOfficial/core/reuseport v0.0.0-20210316211915-8b329f7258d1 h1:m1dEg5E8ZwT3l/F6xiDrU2Q/ZJ9BeVFAZt/OUBxtr4M= github.com/PeernetOfficial/core/reuseport v0.0.0-20210316211915-8b329f7258d1/go.mod h1:j5lvi5jDpdDyuQrsUkCv4kkibVhPWj87sMol41sXIxc= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=