mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
Add reported internal and external port to Connection. New function peer.IsBehindNAT.
PeerlistAdd is now called on every incoming message. User Agent fixes.
This commit is contained in:
36
Commands.go
36
Commands.go
@@ -16,18 +16,12 @@ import (
|
||||
)
|
||||
|
||||
// respondClosesContactsCount is the number of closest contact to respond.
|
||||
// Each peer record will take 55 bytes. Overhead is 77 + 15 payload header + UA length + 6 + 34 = 132 bytes without UA.
|
||||
// Each peer record will take 55 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
|
||||
|
||||
// cmdAnouncement handles an incoming announcement
|
||||
func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement) {
|
||||
var added bool
|
||||
if peer == nil {
|
||||
peer, added = PeerlistAdd(msg.SenderPublicKey, msg.connection)
|
||||
fmt.Printf("Incoming initial 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) {
|
||||
@@ -99,7 +93,8 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement) {
|
||||
peer.announcementStore(msg.InfoStoreFiles)
|
||||
}
|
||||
|
||||
peer.sendResponse(msg.Sequence, added, hash2Peers, filesEmbed, hashesNotFound)
|
||||
sendUA := msg.UserAgent != "" // Send user agent if one was provided. Per protocol the first announcement message must have the User Agent set.
|
||||
peer.sendResponse(msg.Sequence, sendUA, hash2Peers, filesEmbed, hashesNotFound)
|
||||
}
|
||||
|
||||
func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result *PeerRecord) {
|
||||
@@ -117,11 +112,6 @@ func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result
|
||||
|
||||
// cmdResponse handles the response to the announcement
|
||||
func (peer *PeerInfo) cmdResponse(msg *MessageResponse) {
|
||||
if peer == nil {
|
||||
peer, _ = PeerlistAdd(msg.SenderPublicKey, msg.connection)
|
||||
fmt.Printf("Incoming initial response from %s\n", msg.connection.Address.String())
|
||||
}
|
||||
|
||||
// The sequence data is used to correlate this response with the announcement.
|
||||
if msg.sequence == nil || msg.sequence.data == nil {
|
||||
// If there is no sequence data but there were results returned, it means we received unsolicited response data. It will be rejected.
|
||||
@@ -176,18 +166,18 @@ func (peer *PeerInfo) cmdResponse(msg *MessageResponse) {
|
||||
|
||||
// cmdPing handles an incoming ping message
|
||||
func (peer *PeerInfo) cmdPing(msg *MessageRaw) {
|
||||
if peer == nil {
|
||||
// Unexpected incoming ping, reply with announcement message. For security reasons the remote peer is not asked for FIND_SELF.
|
||||
peer, _ = PeerlistAdd(msg.SenderPublicKey, msg.connection)
|
||||
// If PortInternal is 0, it means no incoming announcement or response message was received on that connection.
|
||||
// This means the ping is unexpected. In that case for security reasons the remote peer is not asked for FIND_SELF.
|
||||
if msg.connection.PortInternal == 0 {
|
||||
peer.sendAnnouncement(true, false, nil, nil, nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
peer.send(&PacketRaw{Command: CommandPong, Sequence: msg.Sequence})
|
||||
//fmt.Printf("Incoming ping from %s on %s\n", msg.connection.Address.String(), msg.connection.Address.String())
|
||||
}
|
||||
|
||||
// cmdPong handles an incoming pong message
|
||||
func (peer *PeerInfo) cmdPong(msg *MessageRaw) {
|
||||
//fmt.Printf("Incoming pong from %s on %s\n", msg.connection.Address.String(), msg.connection.Address.String())
|
||||
}
|
||||
|
||||
// cmdChat handles a chat message [debug]
|
||||
@@ -205,15 +195,7 @@ func (peer *PeerInfo) cmdLocalDiscovery(msg *MessageAnnouncement) {
|
||||
// return
|
||||
//}
|
||||
|
||||
if peer == nil {
|
||||
peer, _ = PeerlistAdd(msg.SenderPublicKey, msg.connection)
|
||||
|
||||
fmt.Printf("Incoming initial local discovery from %s\n", msg.connection.Address.String())
|
||||
//} else {
|
||||
// fmt.Printf("Incoming secondary local discovery from %s\n", msg.connection.Address.String())
|
||||
}
|
||||
|
||||
peer.sendAnnouncement(true, true, nil, nil, nil, &bootstrapFindSelf{})
|
||||
peer.sendAnnouncement(true, ShouldSendFindSelf(), nil, nil, nil, &bootstrapFindSelf{})
|
||||
}
|
||||
|
||||
// pingTime is the time in seconds to send out ping messages
|
||||
|
||||
@@ -19,8 +19,10 @@ import (
|
||||
// Connection is an established connection between a remote IP address and a local network adapter.
|
||||
// New connections may only be created in case of successful INCOMING packets.
|
||||
type Connection struct {
|
||||
Network *Network // network which received the packet
|
||||
Address *net.UDPAddr // address of the sender or receiver
|
||||
Network *Network // Network which received the packet.
|
||||
Address *net.UDPAddr // Address of the remote peer.
|
||||
PortInternal uint16 // Internal listening port reported by remote peer. 0 if no Announcement/Response message was yet received.
|
||||
PortExternal uint16 // External listening port reported by remote peer. 0 if not known by the peer.
|
||||
LastPacketIn time.Time // Last time an incoming packet was received.
|
||||
LastPacketOut time.Time // Last time an outgoing packet was attempted to send.
|
||||
LastPingOut time.Time // Last ping out.
|
||||
@@ -110,7 +112,7 @@ func (peer *PeerInfo) registerConnection(incoming *Connection) (result *Connecti
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
|
||||
// first check if already an active connection
|
||||
// first check if already an active connection to the same IP
|
||||
for _, connection := range peer.connectionActive {
|
||||
if connection.Equal(incoming) {
|
||||
// Connection already established. Verify port and update if necessary.
|
||||
@@ -242,6 +244,30 @@ func (peer *PeerInfo) GetRTT() (rtt time.Duration) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsBehindNAT checks if the peer is behind NAT
|
||||
func (peer *PeerInfo) IsBehindNAT() (result bool) {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
|
||||
// Default is no. Only if a public network reports different connected port vs internal one, NAT is assumed.
|
||||
// This also assumes that all 3rd party clients bind their connection to the outgoing port.
|
||||
// PortInternal is 0 if no Announcement or Response message was received.
|
||||
|
||||
for _, connection := range peer.connectionActive {
|
||||
if connection.PortInternal > 0 && connection.Address.Port != int(connection.PortInternal) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, connection := range peer.connectionInactive {
|
||||
if connection.PortInternal > 0 && connection.Address.Port != int(connection.PortInternal) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ---- sending code ----
|
||||
|
||||
// setAnnouncementPorts sets the fields Internal Port and External Port according to the connection details.
|
||||
|
||||
@@ -495,12 +495,12 @@ createPacketLoop:
|
||||
|
||||
// only on initial announcement the User Agent must be provided according to the protocol spec
|
||||
if sendUA {
|
||||
if len(UserAgent) > 255 {
|
||||
UserAgent = UserAgent[:255]
|
||||
}
|
||||
userAgentB := []byte(UserAgent)
|
||||
if len(userAgentB) > 255 {
|
||||
userAgentB = userAgentB[:255]
|
||||
}
|
||||
|
||||
raw[15] = byte(len(userAgentB))
|
||||
raw[19] = byte(len(userAgentB))
|
||||
copy(raw[announcementPayloadHeaderSize:announcementPayloadHeaderSize+len(userAgentB)], userAgentB)
|
||||
packetSize += len(userAgentB)
|
||||
}
|
||||
@@ -637,12 +637,12 @@ createPacketLoop:
|
||||
|
||||
// only on initial response the User Agent must be provided according to the protocol spec
|
||||
if sendUA {
|
||||
if len(UserAgent) > 255 {
|
||||
UserAgent = UserAgent[:255]
|
||||
}
|
||||
userAgentB := []byte(UserAgent)
|
||||
if len(userAgentB) > 255 {
|
||||
userAgentB = userAgentB[:255]
|
||||
}
|
||||
|
||||
raw[15] = byte(len(userAgentB))
|
||||
raw[19] = byte(len(userAgentB))
|
||||
copy(raw[announcementPayloadHeaderSize:announcementPayloadHeaderSize+len(userAgentB)], userAgentB)
|
||||
packetSize += len(userAgentB)
|
||||
}
|
||||
|
||||
26
Network.go
26
Network.go
@@ -147,13 +147,13 @@ func packetWorker(packets <-chan networkWire) {
|
||||
|
||||
connection := &Connection{Network: packet.network, Address: packet.sender, Status: ConnectionActive}
|
||||
|
||||
peer := PeerlistLookup(senderPublicKey)
|
||||
if peer != nil {
|
||||
// Existing peers: Update statistics and network address if new
|
||||
atomic.AddUint64(&peer.StatsPacketReceived, 1)
|
||||
// A peer structure will always be returned, even if the peer won't be added to the peer list.
|
||||
peer, added := PeerlistAdd(senderPublicKey, connection)
|
||||
if !added {
|
||||
connection = peer.registerConnection(connection)
|
||||
}
|
||||
|
||||
atomic.AddUint64(&peer.StatsPacketReceived, 1)
|
||||
connection.LastPacketIn = time.Now()
|
||||
|
||||
// process the packet
|
||||
@@ -162,6 +162,13 @@ func packetWorker(packets <-chan networkWire) {
|
||||
switch decoded.Command {
|
||||
case CommandAnnouncement: // Announce
|
||||
if announce, _ := msgDecodeAnnouncement(raw); announce != nil {
|
||||
// Update known internal/external port and User Agent
|
||||
connection.PortInternal = announce.PortInternal
|
||||
connection.PortExternal = announce.PortExternal
|
||||
if len(announce.UserAgent) > 0 {
|
||||
peer.UserAgent = announce.UserAgent
|
||||
}
|
||||
|
||||
peer.cmdAnouncement(announce)
|
||||
}
|
||||
|
||||
@@ -175,11 +182,22 @@ func packetWorker(packets <-chan networkWire) {
|
||||
connection.RoundTripTime = rtt
|
||||
}
|
||||
|
||||
// Update known internal/external port and User Agent
|
||||
connection.PortInternal = response.PortInternal
|
||||
connection.PortExternal = response.PortExternal
|
||||
if len(response.UserAgent) > 0 {
|
||||
peer.UserAgent = response.UserAgent
|
||||
}
|
||||
|
||||
peer.cmdResponse(response)
|
||||
}
|
||||
|
||||
case CommandLocalDiscovery: // Local discovery, sent via IPv4 broadcast and IPv6 multicast
|
||||
if announce, _ := msgDecodeAnnouncement(raw); announce != nil {
|
||||
if len(announce.UserAgent) > 0 {
|
||||
peer.UserAgent = announce.UserAgent
|
||||
}
|
||||
|
||||
peer.cmdLocalDiscovery(announce)
|
||||
}
|
||||
|
||||
|
||||
@@ -89,12 +89,14 @@ type PeerInfo struct {
|
||||
sync.RWMutex // Mutex for access to list of connections.
|
||||
messageSequence uint32 // Sequence number. Increased with every message.
|
||||
IsRootPeer bool // Whether the peer is a trusted root peer.
|
||||
UserAgent string // User Agent reported by remote peer. Empty if no Announcement/Response message was yet received.
|
||||
|
||||
// statistics
|
||||
StatsPacketSent uint64 // Count of packets sent
|
||||
StatsPacketReceived uint64 // Count of packets received
|
||||
}
|
||||
|
||||
// peerList keeps track of all peers
|
||||
var peerList map[[btcec.PubKeyBytesLenCompressed]byte]*PeerInfo
|
||||
var peerlistMutex sync.RWMutex
|
||||
|
||||
@@ -121,6 +123,8 @@ func PeerlistAdd(PublicKey *btcec.PublicKey, connections ...*Connection) (peer *
|
||||
// add to Kademlia
|
||||
nodesDHT.AddNode(&dht.Node{ID: peer.NodeID, Info: peer})
|
||||
|
||||
// TODO: If the node isn't added to Kademlia, it should be either added temporarily to the peerList with an expiration, or to a temp list, or not at all.
|
||||
|
||||
// send to all channels non-blocking
|
||||
for _, monitor := range peerMonitor {
|
||||
select {
|
||||
|
||||
Reference in New Issue
Block a user