mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
Include both IPv4/IPv6 addresses in peer records in Response message. This makes peer list sharing more efficient and speeds up discovery. Close #24
This commit is contained in:
49
Bootstrap.go
49
Bootstrap.go
@@ -231,16 +231,12 @@ func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *MessageResponse, closest
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the self-reported external port if available.
|
for _, address := range closePeer.ToAddresses() {
|
||||||
port := closePeer.Port
|
// Initiate contact. Once a response comes back, the peer will be actually added to the peer list.
|
||||||
if closePeer.PortReportedExternal > 0 {
|
if contactArbitraryPeer(closePeer.PublicKey, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal) {
|
||||||
port = closePeer.PortReportedExternal
|
// Blacklist the target Peer ID, IP:Port for contact in the next 10 minutes.
|
||||||
}
|
// TODO
|
||||||
|
}
|
||||||
// Initiate contact. Once a response comes back, the peer will be actually added to the peer list.
|
|
||||||
if contactArbitraryPeer(closePeer.PublicKey, &net.UDPAddr{IP: closePeer.IP, Port: int(port)}, closePeer.PortReportedInternal) {
|
|
||||||
// Blacklist the target Peer ID, IP:Port for contact in the next 10 minutes.
|
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,8 +249,16 @@ func ShouldSendFindSelf() bool {
|
|||||||
|
|
||||||
// IsBadQuality checks if the returned peer record is bad quality and should be discarded
|
// IsBadQuality checks if the returned peer record is bad quality and should be discarded
|
||||||
func (record *PeerRecord) IsBadQuality() bool {
|
func (record *PeerRecord) IsBadQuality() bool {
|
||||||
|
isIPv4 := record.IPv4 != nil && !record.IPv4.IsUnspecified()
|
||||||
|
isIPv6 := record.IPv6 != nil && !record.IPv6.IsUnspecified()
|
||||||
|
|
||||||
|
// At least one IP must be provided.
|
||||||
|
if !isIPv4 && !isIPv6 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Internal port must be provided. Otherwise the external port is likely not provided either, and checking the NAT and port forwarded status is not possible.
|
// Internal port must be provided. Otherwise the external port is likely not provided either, and checking the NAT and port forwarded status is not possible.
|
||||||
if record.PortReportedInternal == 0 {
|
if isIPv4 && record.IPv4PortReportedInternal == 0 || isIPv6 && record.IPv6PortReportedInternal == 0 {
|
||||||
//fmt.Printf("IsBadQuality port internal not available for target %s port %d, peer %s\n", record.IP.String(), record.Port, hex.EncodeToString(record.PublicKey.SerializeCompressed()))
|
//fmt.Printf("IsBadQuality port internal not available for target %s port %d, peer %s\n", record.IP.String(), record.Port, hex.EncodeToString(record.PublicKey.SerializeCompressed()))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -267,3 +271,26 @@ func (record *PeerRecord) IsBadQuality() bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToAddresses returns the addresses in a usable way
|
||||||
|
func (record *PeerRecord) ToAddresses() (addresses []*peerAddress) {
|
||||||
|
// IPv4
|
||||||
|
ipv4Port := record.IPv4Port
|
||||||
|
if record.IPv4PortReportedExternal > 0 { // Use the external port if available
|
||||||
|
ipv4Port = record.IPv4PortReportedExternal
|
||||||
|
}
|
||||||
|
if record.IPv4 != nil && !record.IPv4.IsUnspecified() {
|
||||||
|
addresses = append(addresses, &peerAddress{IP: record.IPv4, Port: ipv4Port, PortInternal: record.IPv4PortReportedInternal})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6
|
||||||
|
ipv6Port := record.IPv6Port
|
||||||
|
if record.IPv6PortReportedExternal > 0 { // Use the external port if available
|
||||||
|
ipv6Port = record.IPv6PortReportedExternal
|
||||||
|
}
|
||||||
|
if record.IPv6 != nil && !record.IPv6.IsUnspecified() {
|
||||||
|
addresses = append(addresses, &peerAddress{IP: record.IPv6, Port: ipv6Port, PortInternal: record.IPv6PortReportedInternal})
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses
|
||||||
|
}
|
||||||
|
|||||||
36
Commands.go
36
Commands.go
@@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// respondClosesContactsCount is the number of closest contact to respond.
|
// respondClosesContactsCount is the number of closest contact to respond.
|
||||||
// Each peer record will take 60 bytes. Overhead is 77 + 20 payload header + UA length + 6 + 34 = 137 bytes without UA.
|
// Each peer record will take 70 bytes. Overhead is 77 + 20 payload header + UA length + 6 + 34 = 137 bytes without UA.
|
||||||
// It makes sense to stay below 508 bytes (no fragmentation). Reporting back 5 contacts for FIND_SELF requests should do the magic.
|
// It makes sense to stay below 508 bytes (no fragmentation). Reporting back 5 contacts for FIND_SELF requests should do the magic.
|
||||||
const respondClosesContactsCount = 5
|
const respondClosesContactsCount = 5
|
||||||
|
|
||||||
@@ -98,18 +98,32 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result *PeerRecord) {
|
func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result *PeerRecord) {
|
||||||
if connection := peer.GetConnection2Share(allowLocal, allowIPv4, allowIPv6); connection != nil {
|
connectionIPv4 := peer.GetConnection2Share(allowLocal, allowIPv4, false)
|
||||||
return &PeerRecord{
|
connectionIPv6 := peer.GetConnection2Share(allowLocal, false, allowIPv6)
|
||||||
PublicKey: peer.PublicKey,
|
if connectionIPv4 == nil && connectionIPv6 == nil {
|
||||||
NodeID: peer.NodeID,
|
return nil
|
||||||
IP: connection.Address.IP,
|
|
||||||
Port: uint16(connection.Address.Port),
|
|
||||||
PortReportedInternal: connection.PortInternal,
|
|
||||||
PortReportedExternal: connection.PortExternal,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
result = &PeerRecord{
|
||||||
|
PublicKey: peer.PublicKey,
|
||||||
|
NodeID: peer.NodeID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if connectionIPv4 != nil {
|
||||||
|
result.IPv4 = connectionIPv4.Address.IP
|
||||||
|
result.IPv4Port = uint16(connectionIPv4.Address.Port)
|
||||||
|
result.IPv4PortReportedInternal = connectionIPv4.PortInternal
|
||||||
|
result.IPv4PortReportedExternal = connectionIPv4.PortExternal
|
||||||
|
}
|
||||||
|
|
||||||
|
if connectionIPv6 != nil {
|
||||||
|
result.IPv6 = connectionIPv6.Address.IP
|
||||||
|
result.IPv6Port = uint16(connectionIPv6.Address.Port)
|
||||||
|
result.IPv6PortReportedInternal = connectionIPv6.PortInternal
|
||||||
|
result.IPv6PortReportedExternal = connectionIPv6.PortExternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// cmdResponse handles the response to the announcement
|
// cmdResponse handles the response to the announcement
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Version is the current core library version
|
// Version is the current core library version
|
||||||
const Version = "Alpha 2"
|
const Version = "Alpha 2/20.05.2021"
|
||||||
|
|
||||||
var config struct {
|
var config struct {
|
||||||
LogFile string `yaml:"LogFile"` // Log file
|
LogFile string `yaml:"LogFile"` // Log file
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ func (peer *PeerInfo) IsConnectable(allowLocal, allowIPv4, allowIPv6 bool) bool
|
|||||||
// GetConnection2Share returns a connection to share. Nil if none.
|
// GetConnection2Share returns a connection to share. Nil if none.
|
||||||
// allowLocal specifies whether it is OK to return local IPs.
|
// allowLocal specifies whether it is OK to return local IPs.
|
||||||
func (peer *PeerInfo) GetConnection2Share(allowLocal, allowIPv4, allowIPv6 bool) (connection *Connection) {
|
func (peer *PeerInfo) GetConnection2Share(allowLocal, allowIPv4, allowIPv6 bool) (connection *Connection) {
|
||||||
|
if !allowLocal && !allowIPv4 && !allowIPv6 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
peer.RLock()
|
peer.RLock()
|
||||||
defer peer.RUnlock()
|
defer peer.RUnlock()
|
||||||
|
|
||||||
|
|||||||
@@ -104,14 +104,18 @@ type InfoStore struct {
|
|||||||
|
|
||||||
// PeerRecord informs about a peer
|
// PeerRecord informs about a peer
|
||||||
type PeerRecord struct {
|
type PeerRecord struct {
|
||||||
PublicKey *btcec.PublicKey // Public Key
|
PublicKey *btcec.PublicKey // Public Key
|
||||||
NodeID []byte // Kademlia Node ID
|
NodeID []byte // Kademlia Node ID
|
||||||
IP net.IP // IP address
|
IPv4 net.IP // IPv4 address. 0 if not set.
|
||||||
Port uint16 // Port (actual one used for connection)
|
IPv4Port uint16 // Port (actual one used for connection)
|
||||||
PortReportedInternal uint16 // Internal port as reported by that peer. This can be used to identify whether the peer is potentially behind a NAT.
|
IPv4PortReportedInternal uint16 // Internal port as reported by that peer. This can be used to identify whether the peer is potentially behind a NAT.
|
||||||
PortReportedExternal uint16 // External port as reported by that peer. This is used in case of port forwarding (manual or automated).
|
IPv4PortReportedExternal uint16 // External port as reported by that peer. This is used in case of port forwarding (manual or automated).
|
||||||
LastContact uint32 // Last contact in seconds
|
IPv6 net.IP // IPv6 address. 0 if not set.
|
||||||
LastContactT time.Time // Last contact time translated from seconds
|
IPv6Port uint16 // Port (actual one used for connection)
|
||||||
|
IPv6PortReportedInternal uint16 // Internal port as reported by that peer. This can be used to identify whether the peer is potentially behind a NAT.
|
||||||
|
IPv6PortReportedExternal uint16 // External port as reported by that peer. This is used in case of port forwarding (manual or automated).
|
||||||
|
LastContact uint32 // Last contact in seconds
|
||||||
|
LastContactT time.Time // Last contact time translated from seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash2Peer links a hash to peers who are known to store the data and to peers who are considered close to the hash
|
// Hash2Peer links a hash to peers who are known to store the data and to peers who are considered close to the hash
|
||||||
@@ -372,7 +376,7 @@ func msgDecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Length of peer record in bytes
|
// Length of peer record in bytes
|
||||||
const peerRecordSize = 60
|
const peerRecordSize = 70
|
||||||
|
|
||||||
// decodePeerRecord decodes the response data for FIND_SELF, FIND_PEER and FIND_VALUE messages
|
// decodePeerRecord decodes the response data for FIND_SELF, FIND_PEER and FIND_VALUE messages
|
||||||
func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int, valid bool) {
|
func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int, valid bool) {
|
||||||
@@ -402,19 +406,31 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int,
|
|||||||
peerIDcompressed := make([]byte, 33)
|
peerIDcompressed := make([]byte, 33)
|
||||||
copy(peerIDcompressed[:], data[index:index+33])
|
copy(peerIDcompressed[:], data[index:index+33])
|
||||||
|
|
||||||
ipB := make([]byte, 16)
|
// IPv4
|
||||||
copy(ipB[:], data[index+33:index+33+16])
|
ipv4B := make([]byte, 4)
|
||||||
peer.IP = ipB
|
copy(ipv4B[:], data[index+33:index+33+4])
|
||||||
if peer.IP.To4() != nil { // If IPv4, convert to native 4-byte representation
|
|
||||||
peer.IP = peer.IP.To4()
|
peer.IPv4 = ipv4B
|
||||||
|
peer.IPv4Port = binary.LittleEndian.Uint16(data[index+37 : index+37+2])
|
||||||
|
peer.IPv4PortReportedInternal = binary.LittleEndian.Uint16(data[index+39 : index+39+2])
|
||||||
|
peer.IPv4PortReportedExternal = binary.LittleEndian.Uint16(data[index+41 : index+41+2])
|
||||||
|
|
||||||
|
// IPv6
|
||||||
|
ipv6B := make([]byte, 16)
|
||||||
|
copy(ipv6B[:], data[index+43:index+43+16])
|
||||||
|
|
||||||
|
peer.IPv6 = ipv6B
|
||||||
|
peer.IPv6Port = binary.LittleEndian.Uint16(data[index+59 : index+59+2])
|
||||||
|
peer.IPv6PortReportedInternal = binary.LittleEndian.Uint16(data[index+61 : index+61+2])
|
||||||
|
peer.IPv6PortReportedExternal = binary.LittleEndian.Uint16(data[index+63 : index+63+2])
|
||||||
|
|
||||||
|
if peer.IPv6.To4() != nil { // IPv6 address mismatch
|
||||||
|
return nil, 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.Port = binary.LittleEndian.Uint16(data[index+49 : index+49+2])
|
peer.LastContact = binary.LittleEndian.Uint32(data[index+65 : index+65+4])
|
||||||
peer.PortReportedInternal = binary.LittleEndian.Uint16(data[index+51 : index+51+2])
|
|
||||||
peer.PortReportedExternal = binary.LittleEndian.Uint16(data[index+53 : index+53+2])
|
|
||||||
peer.LastContact = binary.LittleEndian.Uint32(data[index+55 : index+55+4])
|
|
||||||
peer.LastContactT = time.Now().Add(-time.Second * time.Duration(peer.LastContact))
|
peer.LastContactT = time.Now().Add(-time.Second * time.Duration(peer.LastContact))
|
||||||
reason := data[index+59]
|
reason := data[index+69]
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if peer.PublicKey, err = btcec.ParsePubKey(peerIDcompressed, btcec.S256()); err != nil {
|
if peer.PublicKey, err = btcec.ParsePubKey(peerIDcompressed, btcec.S256()); err != nil {
|
||||||
@@ -692,7 +708,7 @@ createPacketLoop:
|
|||||||
packetSize += 34
|
packetSize += 34
|
||||||
count2 := uint16(0)
|
count2 := uint16(0)
|
||||||
|
|
||||||
for m, peer := range hash2Peer.Storing {
|
for m := range hash2Peer.Storing {
|
||||||
if isPacketSizeExceed(packetSize, peerRecordSize) { // check if minimum length is available in packet
|
if isPacketSizeExceed(packetSize, peerRecordSize) { // check if minimum length is available in packet
|
||||||
packetsRaw = append(packetsRaw, raw[:packetSize])
|
packetsRaw = append(packetsRaw, raw[:packetSize])
|
||||||
hash2Peers = hash2Peers[n:]
|
hash2Peers = hash2Peers[n:]
|
||||||
@@ -701,13 +717,7 @@ createPacketLoop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
index := packetSize
|
index := packetSize
|
||||||
copy(raw[index:index+33], peer.PublicKey.SerializeCompressed())
|
encodePeerRecord(raw[index:index+peerRecordSize], &hash2Peer.Storing[m], 1)
|
||||||
copy(raw[index+33:index+33+16], peer.IP.To16())
|
|
||||||
binary.LittleEndian.PutUint16(raw[index+49:index+51], peer.Port)
|
|
||||||
binary.LittleEndian.PutUint16(raw[index+51:index+53], peer.PortReportedInternal)
|
|
||||||
binary.LittleEndian.PutUint16(raw[index+53:index+55], peer.PortReportedExternal)
|
|
||||||
binary.LittleEndian.PutUint32(raw[index+55:index+59], peer.LastContact)
|
|
||||||
raw[index+59] = 1
|
|
||||||
|
|
||||||
packetSize += peerRecordSize
|
packetSize += peerRecordSize
|
||||||
binary.LittleEndian.PutUint16(raw[count2Index+0:count2Index+2], uint16(m+1))
|
binary.LittleEndian.PutUint16(raw[count2Index+0:count2Index+2], uint16(m+1))
|
||||||
@@ -716,7 +726,7 @@ createPacketLoop:
|
|||||||
|
|
||||||
hash2Peer.Storing = nil
|
hash2Peer.Storing = nil
|
||||||
|
|
||||||
for m, peer := range hash2Peer.Closest {
|
for m := range hash2Peer.Closest {
|
||||||
if isPacketSizeExceed(packetSize, peerRecordSize) { // check if minimum length is available in packet
|
if isPacketSizeExceed(packetSize, peerRecordSize) { // check if minimum length is available in packet
|
||||||
packetsRaw = append(packetsRaw, raw[:packetSize])
|
packetsRaw = append(packetsRaw, raw[:packetSize])
|
||||||
hash2Peers = hash2Peers[n:]
|
hash2Peers = hash2Peers[n:]
|
||||||
@@ -725,13 +735,7 @@ createPacketLoop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
index := packetSize
|
index := packetSize
|
||||||
copy(raw[index:index+33], peer.PublicKey.SerializeCompressed())
|
encodePeerRecord(raw[index:index+peerRecordSize], &hash2Peer.Closest[m], 0)
|
||||||
copy(raw[index+33:index+33+16], peer.IP.To16())
|
|
||||||
binary.LittleEndian.PutUint16(raw[index+49:index+51], peer.Port)
|
|
||||||
binary.LittleEndian.PutUint16(raw[index+51:index+53], peer.PortReportedInternal)
|
|
||||||
binary.LittleEndian.PutUint16(raw[index+53:index+55], peer.PortReportedExternal)
|
|
||||||
binary.LittleEndian.PutUint32(raw[index+55:index+59], peer.LastContact)
|
|
||||||
raw[index+59] = 0
|
|
||||||
|
|
||||||
packetSize += peerRecordSize
|
packetSize += peerRecordSize
|
||||||
count2++
|
count2++
|
||||||
@@ -799,6 +803,25 @@ createPacketLoop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodePeerRecord encodes a single peer record and stores it into raw
|
||||||
|
func encodePeerRecord(raw []byte, peer *PeerRecord, reason uint8) {
|
||||||
|
copy(raw[0:0+33], peer.PublicKey.SerializeCompressed())
|
||||||
|
binary.LittleEndian.PutUint32(raw[65:65+4], peer.LastContact)
|
||||||
|
raw[69] = reason
|
||||||
|
|
||||||
|
// IPv4
|
||||||
|
copy(raw[33:33+4], peer.IPv4.To4())
|
||||||
|
binary.LittleEndian.PutUint16(raw[37:37+2], peer.IPv4Port)
|
||||||
|
binary.LittleEndian.PutUint16(raw[39:39+2], peer.IPv4PortReportedInternal)
|
||||||
|
binary.LittleEndian.PutUint16(raw[41:41+2], peer.IPv4PortReportedExternal)
|
||||||
|
|
||||||
|
// IPv6
|
||||||
|
copy(raw[43:43+16], peer.IPv6.To16())
|
||||||
|
binary.LittleEndian.PutUint16(raw[59:59+2], peer.IPv6Port)
|
||||||
|
binary.LittleEndian.PutUint16(raw[61:61+2], peer.IPv6PortReportedInternal)
|
||||||
|
binary.LittleEndian.PutUint16(raw[63:63+2], peer.IPv6PortReportedExternal)
|
||||||
|
}
|
||||||
|
|
||||||
// setSelfReportedPorts sets the fields Internal Port and External Port according to the connection details.
|
// setSelfReportedPorts sets the fields Internal Port and External Port according to the connection details.
|
||||||
// This is important for the remote peer to make smart decisions whether this peer is behind a NAT/firewall and supports port forwarding/UPnP.
|
// This is important for the remote peer to make smart decisions whether this peer is behind a NAT/firewall and supports port forwarding/UPnP.
|
||||||
func (packet *PacketRaw) setSelfReportedPorts(n *Network) {
|
func (packet *PacketRaw) setSelfReportedPorts(n *Network) {
|
||||||
|
|||||||
13
Peer ID.go
13
Peer ID.go
@@ -212,16 +212,11 @@ func records2Nodes(records []PeerRecord, peerSource *PeerInfo) (nodes []*dht.Nod
|
|||||||
peer = peerSource
|
peer = peerSource
|
||||||
} else if peer = PeerlistLookup(record.PublicKey); peer == nil {
|
} else if peer = PeerlistLookup(record.PublicKey); peer == nil {
|
||||||
// Create temporary peer which is not added to the global list and not added to Kademlia.
|
// Create temporary peer which is not added to the global list and not added to Kademlia.
|
||||||
var addresses []*peerAddress
|
|
||||||
|
|
||||||
port := record.Port
|
|
||||||
if record.PortReportedExternal > 0 { // Use the external port if available
|
|
||||||
port = record.PortReportedExternal
|
|
||||||
}
|
|
||||||
|
|
||||||
addresses = append(addresses, &peerAddress{IP: record.IP, Port: port, PortInternal: record.PortReportedInternal})
|
|
||||||
|
|
||||||
// traversePeer is set to the peer who provided the node information.
|
// traversePeer is set to the peer who provided the node information.
|
||||||
|
addresses := record.ToAddresses()
|
||||||
|
if len(addresses) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
peer = &PeerInfo{PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: publicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource}
|
peer = &PeerInfo{PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: publicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user