mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
Add new fields Port Internal and Port External to the Announcement/Response message according to the latest Whitepaper 0.7.3.
These fields will be used for NAT detection and UPnP support.
This commit is contained in:
@@ -7,6 +7,7 @@ Author: Peter Kleissner
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
@@ -243,6 +244,28 @@ func (peer *PeerInfo) GetRTT() (rtt time.Duration) {
|
||||
|
||||
// ---- sending code ----
|
||||
|
||||
// setAnnouncementPorts 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.
|
||||
func setAnnouncementPorts(packet *PacketRaw, n *Network) {
|
||||
if packet.Command != CommandAnnouncement && packet.Command != CommandResponse { // only for Announcement and Response messages
|
||||
return
|
||||
}
|
||||
|
||||
// The internal port is set to where the network listens on.
|
||||
// Datacenter: This should usually be the same as the outgoing port.
|
||||
// NAT: The internal port will be different than the outgoing one.
|
||||
portI := uint16(n.address.Port)
|
||||
|
||||
// External port: This is usually unknown, except in these 2 cases:
|
||||
// UPnP: The port is forwarded automatically.
|
||||
// Manual override in config: The user can specify a (global) incoming port that must be open on all listening IPs.
|
||||
// This external port will be then passed onto other peers who will use it to connect.
|
||||
portE := n.externalPort
|
||||
|
||||
binary.LittleEndian.PutUint16(packet.Payload[15:17], portI)
|
||||
binary.LittleEndian.PutUint16(packet.Payload[17:19], portE)
|
||||
}
|
||||
|
||||
// send sends a raw packet to the peer. Only uses active connections.
|
||||
func (peer *PeerInfo) send(packet *PacketRaw) (err error) {
|
||||
if len(peer.connectionActive) == 0 {
|
||||
@@ -251,11 +274,7 @@ func (peer *PeerInfo) send(packet *PacketRaw) (err error) {
|
||||
|
||||
packet.Protocol = ProtocolVersion
|
||||
|
||||
raw, err := PacketEncrypt(peerPrivateKey, peer.PublicKey, packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// always count as one sent packet even if sent via broadcast
|
||||
atomic.AddUint64(&peer.StatsPacketSent, 1)
|
||||
|
||||
// Send out the wire. Use connectionLatest if available.
|
||||
@@ -263,6 +282,13 @@ func (peer *PeerInfo) send(packet *PacketRaw) (err error) {
|
||||
// Windows: This works great in case the adapter gets disabled, however, does not detect if the network cable is unplugged.
|
||||
c := peer.connectionLatest
|
||||
if c != nil {
|
||||
setAnnouncementPorts(packet, c.Network)
|
||||
|
||||
raw, err := PacketEncrypt(peerPrivateKey, peer.PublicKey, packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.LastPacketOut = time.Now()
|
||||
|
||||
if err = c.Network.send(c.Address.IP, c.Address.Port, raw); err == nil {
|
||||
@@ -281,6 +307,13 @@ func (peer *PeerInfo) send(packet *PacketRaw) (err error) {
|
||||
// The receiver is responsible for incoming deduplication of packets.
|
||||
activeConnections := peer.GetConnections(true)
|
||||
for _, c := range activeConnections {
|
||||
setAnnouncementPorts(packet, c.Network)
|
||||
|
||||
raw, err := PacketEncrypt(peerPrivateKey, peer.PublicKey, packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.LastPacketOut = time.Now()
|
||||
c.Network.send(c.Address.IP, c.Address.Port, raw)
|
||||
}
|
||||
@@ -312,39 +345,27 @@ func sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet *PacketRaw, remo
|
||||
networksMutex.RLock()
|
||||
defer networksMutex.RUnlock()
|
||||
|
||||
networksTarget := networks4
|
||||
if IsIPv6(remote.IP.To16()) {
|
||||
for _, network := range networks6 {
|
||||
// Do not mix link-local unicast targets with non link-local networks (only when iface is known, i.e. not catch all local)
|
||||
if network.iface != nil && remote.IP.IsLinkLocalUnicast() != network.address.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
networksTarget = networks6
|
||||
}
|
||||
|
||||
packet.Sequence = msgArbitrarySequence(receiverPublicKey, sequenceData).sequence
|
||||
if raw, err = PacketEncrypt(peerPrivateKey, receiverPublicKey, packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = network.send(remote.IP, remote.Port, raw)
|
||||
if err == nil {
|
||||
successCount++
|
||||
}
|
||||
for _, network := range networksTarget {
|
||||
// Do not mix link-local unicast targets with non link-local networks (only when iface is known, i.e. not catch all local)
|
||||
if network.iface != nil && remote.IP.IsLinkLocalUnicast() != network.address.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
for _, network := range networks4 {
|
||||
// Do not mix link-local unicast targets with non link-local networks (only when iface is known, i.e. not catch all local)
|
||||
if network.iface != nil && remote.IP.IsLinkLocalUnicast() != network.address.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
|
||||
packet.Sequence = msgArbitrarySequence(receiverPublicKey, sequenceData).sequence
|
||||
if raw, err = PacketEncrypt(peerPrivateKey, receiverPublicKey, packet); err != nil {
|
||||
return err
|
||||
}
|
||||
setAnnouncementPorts(packet, network)
|
||||
|
||||
err = network.send(remote.IP, remote.Port, raw)
|
||||
if err == nil {
|
||||
successCount++
|
||||
}
|
||||
packet.Sequence = msgArbitrarySequence(receiverPublicKey, sequenceData).sequence
|
||||
if raw, err = PacketEncrypt(peerPrivateKey, receiverPublicKey, packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = network.send(remote.IP, remote.Port, raw)
|
||||
if err == nil {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ const (
|
||||
CommandPing = 2 // Keep-alive message (no payload).
|
||||
CommandPong = 3 // Response to ping (no payload).
|
||||
CommandLocalDiscovery = 4 // Local discovery
|
||||
CommandTraverse = 5 // Help establish a connection between 2 remote peers
|
||||
|
||||
// Blockchain
|
||||
CommandGet = 6 // Request blocks for specified peer.
|
||||
@@ -78,6 +79,8 @@ type MessageAnnouncement struct {
|
||||
Actions uint8 // Action bit array. See ActionX
|
||||
BlockchainHeight uint32 // Blockchain height
|
||||
BlockchainVersion uint64 // Blockchain version
|
||||
PortInternal uint16 // Internal port. Can be used to detect NATs.
|
||||
PortExternal uint16 // External port if known. 0 if not. Can be used for UPnP support.
|
||||
UserAgent string // User Agent. Format "Software/Version". Required in the initial announcement/bootstrap. UTF-8 encoded. Max length is 255 bytes.
|
||||
FindPeerKeys []KeyHash // FIND_PEER data
|
||||
FindDataKeys []KeyHash // FIND_VALUE data
|
||||
@@ -130,6 +133,8 @@ type MessageResponse struct {
|
||||
Actions uint8 // Action bit array. See ActionX
|
||||
BlockchainHeight uint32 // Blockchain height
|
||||
BlockchainVersion uint64 // Blockchain version
|
||||
PortInternal uint16 // Internal port. Can be used to detect NATs.
|
||||
PortExternal uint16 // External port if known. 0 if not. Can be used for UPnP support.
|
||||
UserAgent string // User Agent. Format "Software/Version". Required in the initial announcement/bootstrap. UTF-8 encoded. Max length is 255 bytes.
|
||||
Hash2Peers []Hash2Peer // List of peers that know the requested hashes or at least are close to it
|
||||
FilesEmbed []EmbeddedFileData // Files that were embedded in the response
|
||||
@@ -139,7 +144,7 @@ type MessageResponse struct {
|
||||
// ---- message decoding ----
|
||||
|
||||
// Minimum length of Announcement payload header without User Agent
|
||||
const announcementPayloadHeaderSize = 16
|
||||
const announcementPayloadHeaderSize = 20
|
||||
|
||||
// msgDecodeAnnouncement decodes the incoming announcement message. Returns nil if invalid.
|
||||
func msgDecodeAnnouncement(msg *MessageRaw) (result *MessageAnnouncement, err error) {
|
||||
@@ -156,8 +161,10 @@ func msgDecodeAnnouncement(msg *MessageRaw) (result *MessageAnnouncement, err er
|
||||
result.Actions = msg.Payload[2]
|
||||
result.BlockchainHeight = binary.LittleEndian.Uint32(msg.Payload[3:7])
|
||||
result.BlockchainVersion = binary.LittleEndian.Uint64(msg.Payload[7:15])
|
||||
result.PortInternal = binary.LittleEndian.Uint16(msg.Payload[15:17])
|
||||
result.PortExternal = binary.LittleEndian.Uint16(msg.Payload[17:19])
|
||||
|
||||
userAgentLength := int(msg.Payload[15])
|
||||
userAgentLength := int(msg.Payload[19])
|
||||
if userAgentLength > 0 {
|
||||
if userAgentLength > len(msg.Payload)-announcementPayloadHeaderSize {
|
||||
return nil, errors.New("announcement: user agent overflow")
|
||||
@@ -274,8 +281,10 @@ func msgDecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) {
|
||||
result.Actions = msg.Payload[2]
|
||||
result.BlockchainHeight = binary.LittleEndian.Uint32(msg.Payload[3:7])
|
||||
result.BlockchainVersion = binary.LittleEndian.Uint64(msg.Payload[7:15])
|
||||
result.PortInternal = binary.LittleEndian.Uint16(msg.Payload[15:17])
|
||||
result.PortExternal = binary.LittleEndian.Uint16(msg.Payload[17:19])
|
||||
|
||||
userAgentLength := int(msg.Payload[15])
|
||||
userAgentLength := int(msg.Payload[19])
|
||||
read := announcementPayloadHeaderSize
|
||||
|
||||
if userAgentLength > 0 {
|
||||
|
||||
@@ -26,6 +26,7 @@ type Network struct {
|
||||
multicastSocket net.PacketConn // Multicast socket, IPv6 only.
|
||||
broadcastSocket net.PacketConn // Broadcast socket, IPv4 only.
|
||||
broadcastIPv4 []net.IP // Broadcast IPs, IPv4 only.
|
||||
externalPort uint16 // External port. 0 if not known.
|
||||
isTerminated bool // If true, the network was signaled for termination
|
||||
terminateSignal chan interface{} // gets closed on termination signal, can be used in select via "case _ = <- network.terminateSignal:"
|
||||
sync.RWMutex // for sychronized closing
|
||||
@@ -218,8 +219,8 @@ func GetNetworks(networkType int) (networks []*Network) {
|
||||
}
|
||||
|
||||
// GetListen returns connectivity information
|
||||
func (network *Network) GetListen() (listen *net.UDPAddr, multicastIPv6 net.IP, broadcastIPv4 []net.IP) {
|
||||
return network.address, network.multicastIP, network.broadcastIPv4
|
||||
func (network *Network) GetListen() (listen *net.UDPAddr, multicastIPv6 net.IP, broadcastIPv4 []net.IP, externalPort uint16) {
|
||||
return network.address, network.multicastIP, network.broadcastIPv4, network.externalPort
|
||||
}
|
||||
|
||||
// GetAdapterName returns the adapter name, if available
|
||||
|
||||
Reference in New Issue
Block a user