Add new fields Port Internal and Port External to the exchanged peer record according to the latest Whitepaper 0.7.3.

This allows peers to detect each other's NAT and port forwarding status.
This commit is contained in:
Kleissner
2021-05-08 13:03:45 +02:00
parent 3a502a1ecc
commit 530cd541c1
3 changed files with 39 additions and 29 deletions

View File

@@ -16,7 +16,7 @@ import (
)
// respondClosesContactsCount is the number of closest contact to respond.
// Each peer record will take 55 bytes. Overhead is 77 + 20 payload header + UA length + 6 + 34 = 137 bytes without UA.
// Each peer record will take 60 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.
const respondClosesContactsCount = 5
@@ -100,10 +100,12 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement) {
func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result *PeerRecord) {
if connection := peer.GetConnection2Share(allowLocal, allowIPv4, allowIPv6); connection != nil {
return &PeerRecord{
PublicKey: peer.PublicKey,
NodeID: peer.NodeID,
IP: connection.Address.IP,
Port: uint16(connection.Address.Port),
PublicKey: peer.PublicKey,
NodeID: peer.NodeID,
IP: connection.Address.IP,
Port: uint16(connection.Address.Port),
PortReportedInternal: connection.PortInternal,
PortReportedExternal: connection.PortExternal,
}
}

View File

@@ -104,11 +104,14 @@ type InfoStore struct {
// PeerRecord informs about a peer
type PeerRecord struct {
PublicKey *btcec.PublicKey // Public Key
NodeID []byte // Kademlia Node ID
IP net.IP // IP
Port uint16 // Port
LastContact uint32 // Last contact in seconds
PublicKey *btcec.PublicKey // Public Key
NodeID []byte // Kademlia Node ID
IP net.IP // IP address
Port 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.
PortReportedExternal 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
@@ -351,6 +354,9 @@ func msgDecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) {
return
}
// Length of peer record in bytes
const peerRecordSize = 60
// 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) {
index := 0
@@ -370,7 +376,7 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int,
// Response contains peer records
for m := 0; m < int(countField); m++ {
if read += 56; len(data) < read {
if read += peerRecordSize; len(data) < read {
return nil, 0, false
}
@@ -387,8 +393,11 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int,
}
peer.Port = binary.LittleEndian.Uint16(data[index+49 : index+49+2])
peer.LastContact = binary.LittleEndian.Uint32(data[index+51 : index+51+4])
reason := data[index+55]
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))
reason := data[index+59]
var err error
if peer.PublicKey, err = btcec.ParsePubKey(peerIDcompressed, btcec.S256()); err != nil {
@@ -403,7 +412,7 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int,
hash2Peer.Storing = append(hash2Peer.Storing, peer)
}
index += 56
index += peerRecordSize
}
hash2Peers = append(hash2Peers, hash2Peer)
@@ -654,7 +663,7 @@ createPacketLoop:
// Encode the peer response data for FIND_SELF, FIND_PEER and FIND_VALUE requests.
if len(hash2Peers) > 0 {
for n, hash2Peer := range hash2Peers {
if isPacketSizeExceed(packetSize, 34+56) { // check if minimum length is available in packet
if isPacketSizeExceed(packetSize, 34+peerRecordSize) { // check if minimum length is available in packet
packetsRaw = append(packetsRaw, raw[:packetSize])
hash2Peers = hash2Peers[n:]
continue createPacketLoop
@@ -668,7 +677,7 @@ createPacketLoop:
count2 := uint16(0)
for m, peer := range hash2Peer.Storing {
if isPacketSizeExceed(packetSize, 56) { // 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])
hash2Peers = hash2Peers[n:]
hash2Peer.Storing = hash2Peer.Storing[m:]
@@ -679,10 +688,12 @@ createPacketLoop:
copy(raw[index:index+33], peer.PublicKey.SerializeCompressed())
copy(raw[index+33:index+33+16], peer.IP.To16())
binary.LittleEndian.PutUint16(raw[index+49:index+51], peer.Port)
binary.LittleEndian.PutUint32(raw[index+51:index+55], peer.LastContact)
raw[index+55] = 1
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 += 56
packetSize += peerRecordSize
binary.LittleEndian.PutUint16(raw[count2Index+0:count2Index+2], uint16(m+1))
count2++
}
@@ -690,7 +701,7 @@ createPacketLoop:
hash2Peer.Storing = nil
for m, peer := range hash2Peer.Closest {
if isPacketSizeExceed(packetSize, 56) { // 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])
hash2Peers = hash2Peers[n:]
hash2Peer.Closest = hash2Peer.Closest[m:]
@@ -701,10 +712,12 @@ createPacketLoop:
copy(raw[index:index+33], peer.PublicKey.SerializeCompressed())
copy(raw[index+33:index+33+16], peer.IP.To16())
binary.LittleEndian.PutUint16(raw[index+49:index+51], peer.Port)
binary.LittleEndian.PutUint32(raw[index+51:index+55], peer.LastContact)
raw[index+55] = 0
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 += 56
packetSize += peerRecordSize
count2++
binary.LittleEndian.PutUint16(raw[count2Index+0:count2Index+2], count2)
}
@@ -809,8 +822,3 @@ func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []Ha
return err
}
// lastContact2Time translates a last contact time in seconds to a timestamp
func lastContact2Time(LastContact uint32) time.Time {
return time.Now().Add(-time.Second * time.Duration(LastContact))
}

View File

@@ -205,7 +205,7 @@ func record2Peer(record PeerRecord, network *Network) (peerN *PeerInfo) {
func records2Nodes(records []PeerRecord, network *Network) (nodes []*dht.Node) {
for _, record := range records {
peer := record2Peer(record, network)
nodes = append(nodes, &dht.Node{ID: peer.NodeID, LastSeen: lastContact2Time(record.LastContact), Info: peer})
nodes = append(nodes, &dht.Node{ID: peer.NodeID, LastSeen: record.LastContactT, Info: peer})
}
return