mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
New flag Firewall in the Features field to inform uncontacted peers that a Traverse message might be required to establish a connection. Close #61
This flag can be activated via the config setting LocalFirewall. The Features field exists in Announcement/Response messages and was now added to peer records.
This commit is contained in:
@@ -105,7 +105,7 @@ func (peer *rootPeer) contact() {
|
||||
|
||||
for _, address := range peer.addresses {
|
||||
// Port internal is always set to 0 for root peers. It disables NAT detection and will not send out a Traverse message.
|
||||
contactArbitraryPeer(peer.publicKey, address, 0)
|
||||
contactArbitraryPeer(peer.publicKey, address, 0, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ func (nets *Networks) autoMulticastBroadcast() {
|
||||
}
|
||||
|
||||
// contactArbitraryPeer contacts a new arbitrary peer for the first time.
|
||||
func contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, receiverPortInternal uint16) (contacted bool) {
|
||||
func contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, receiverPortInternal uint16, receiverFirewall bool) (contacted bool) {
|
||||
findSelf := ShouldSendFindSelf()
|
||||
_, blockchainHeight, blockchainVersion := UserBlockchain.Header()
|
||||
packets := protocol.EncodeAnnouncement(true, findSelf, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent)
|
||||
@@ -216,7 +216,7 @@ func contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, rece
|
||||
|
||||
Filters.MessageOutAnnouncement(publicKey, nil, raw, findSelf, nil, nil, nil)
|
||||
|
||||
networks.sendAllNetworks(publicKey, raw, address, receiverPortInternal, nil, &bootstrapFindSelf{})
|
||||
networks.sendAllNetworks(publicKey, raw, address, receiverPortInternal, receiverFirewall, nil, &bootstrapFindSelf{})
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *protocol.MessageResponse
|
||||
}
|
||||
|
||||
// Initiate contact. Once a response comes back, the peer will be actually added to the peer list.
|
||||
contactArbitraryPeer(closePeer.PublicKey, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal)
|
||||
contactArbitraryPeer(closePeer.PublicKey, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, closePeer.Features&(1<<protocol.FeatureFirewall) > 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result
|
||||
result = &protocol.PeerRecord{
|
||||
PublicKey: peer.PublicKey,
|
||||
NodeID: peer.NodeID,
|
||||
Features: peer.Features,
|
||||
}
|
||||
|
||||
if connectionIPv4 != nil {
|
||||
|
||||
@@ -29,8 +29,9 @@ SeedList:
|
||||
Address: ["194.233.66.99:112","[2407:3640:2057:5241::1]:112"]
|
||||
|
||||
# Connection settings
|
||||
EnableUPnP: true # Enables support for UPnP.
|
||||
EnableUPnP: true # Enables support for UPnP.
|
||||
LocalFirewall: false # Indicates that a local firewall may drop unsolicited incoming packets.
|
||||
|
||||
# PortForward specifies an external port that was manually forwarded by the user. All listening IPs must have that same port number forwarded!
|
||||
# If this setting is invalid, it will prohibit other peers from connecting. If set, it automatically disables UPnP.
|
||||
PortForward: 0 # Default not set.
|
||||
PortForward: 0 # Default not set.
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
// Version is the current core library version
|
||||
const Version = "Alpha 4/15.11.2021"
|
||||
const Version = "Alpha 4/20.11.2021"
|
||||
|
||||
var config struct {
|
||||
// Locations of important files and folders
|
||||
@@ -39,7 +39,8 @@ var config struct {
|
||||
SeedListVersion int `yaml:"SeedListVersion"`
|
||||
|
||||
// Connection settings
|
||||
EnableUPnP bool `yaml:"EnableUPnP"` // Enables support for UPnP.
|
||||
EnableUPnP bool `yaml:"EnableUPnP"` // Enables support for UPnP.
|
||||
LocalFirewall bool `yaml:"LocalFirewall"` // Indicates that a local firewall may drop unsolicited incoming packets.
|
||||
|
||||
// PortForward specifies an external port that was manually forwarded by the user. All listening IPs must have that same port number forwarded!
|
||||
// If this setting is invalid, it will prohibit other peers from connecting. If set, it automatically disables UPnP.
|
||||
|
||||
@@ -29,7 +29,8 @@ type Connection struct {
|
||||
Expires time.Time // Inactive connections only: Expiry date. If it does not become active by that date, it will be considered expired and removed.
|
||||
Status int // 0 = Active established connection, 1 = Inactive, 2 = Removed, 3 = Redundant
|
||||
RoundTripTime time.Duration // Full round-trip time of last reply.
|
||||
traversePeer *PeerInfo // Temporary peer that may act as proxy for a Traverse message used for the first packet. This is used to establish this Connection to a peer that is behind a NAT.
|
||||
Firewall bool // Whether the remote peer indicates a potential firewall. This means a Traverse message shall be sent to establish a connection.
|
||||
traversePeer *PeerInfo // Temporary peer that may act as proxy for a Traverse message used for the first packet. This is used to establish this Connection to a peer that is behind a NAT or firewall.
|
||||
}
|
||||
|
||||
// Connection status
|
||||
@@ -325,9 +326,15 @@ func (peer *PeerInfo) IsPortForward() (result bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFirewallReported checks if the peer reported to be behind a firewall
|
||||
func (peer *PeerInfo) IsFirewallReported() (result bool) {
|
||||
return peer.Features&(1<<protocol.FeatureFirewall) > 0
|
||||
}
|
||||
|
||||
// ---- sending code ----
|
||||
|
||||
// send sends the packet to the peer on the connection
|
||||
// isFirstPacket indicates whether this is the first packet to an uncontacted peer.
|
||||
func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, isFirstPacket bool) (err error) {
|
||||
if c == nil {
|
||||
return errors.New("invalid connection")
|
||||
@@ -347,8 +354,8 @@ func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.P
|
||||
|
||||
err = c.Network.send(c.Address.IP, c.Address.Port, raw)
|
||||
|
||||
// Send Traverse message if the peer is behind a NAT and this is the first message. Only for Announcement.
|
||||
if err == nil && isFirstPacket && c.IsBehindNAT() && c.traversePeer != nil && packet.Command == protocol.CommandAnnouncement {
|
||||
// Send Traverse message if the peer is behind a NAT or firewall and this is the first message. Only for Announcement.
|
||||
if err == nil && isFirstPacket && (c.IsBehindNAT() || c.Firewall) && c.traversePeer != nil && packet.Command == protocol.CommandAnnouncement {
|
||||
c.traversePeer.sendTraverse(packet, receiverPublicKey)
|
||||
}
|
||||
|
||||
@@ -359,7 +366,7 @@ func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.P
|
||||
func (peer *PeerInfo) send(packet *protocol.PacketRaw) (err error) {
|
||||
if peer.isVirtual { // special case for peers that were not contacted before
|
||||
for _, address := range peer.targetAddresses {
|
||||
networks.sendAllNetworks(peer.PublicKey, packet, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, peer.traversePeer, nil)
|
||||
networks.sendAllNetworks(peer.PublicKey, packet, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, peer.Features&(1<<protocol.FeatureFirewall) > 0, peer.traversePeer, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -414,8 +421,8 @@ func (peer *PeerInfo) sendConnection(packet *protocol.PacketRaw, connection *Con
|
||||
}
|
||||
|
||||
// sendAllNetworks sends a raw packet via all networks. It assigns a new sequence for each sent packet.
|
||||
// receiverPortInternal is important for NAT detection and sending the traverse message.
|
||||
func (nets *Networks) sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet *protocol.PacketRaw, remote *net.UDPAddr, receiverPortInternal uint16, traversePeer *PeerInfo, sequenceData interface{}) (err error) {
|
||||
// receiverPortInternal is important for NAT detection and sending the traverse message. Firewall indicates whether the remote peer was reported to be behind a firewall.
|
||||
func (nets *Networks) sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet *protocol.PacketRaw, remote *net.UDPAddr, receiverPortInternal uint16, receiverFirewall bool, traversePeer *PeerInfo, sequenceData interface{}) (err error) {
|
||||
nets.RLock()
|
||||
defer nets.RUnlock()
|
||||
|
||||
@@ -436,7 +443,7 @@ func (nets *Networks) sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet
|
||||
if sequenceData != nil {
|
||||
packet.Sequence = nets.Sequences.ArbitrarySequence(receiverPublicKey, sequenceData).SequenceNumber
|
||||
}
|
||||
err = (&Connection{Network: network, Address: remote, PortInternal: receiverPortInternal, traversePeer: traversePeer}).send(packet, receiverPublicKey, isFirstPacket)
|
||||
err = (&Connection{Network: network, Address: remote, PortInternal: receiverPortInternal, traversePeer: traversePeer, Firewall: receiverFirewall}).send(packet, receiverPublicKey, isFirstPacket)
|
||||
isFirstPacket = false
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -26,6 +26,12 @@ func (peer *PeerInfo) pingConnection(connection *Connection) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ping sends a ping. This function exists only for debugging purposes, it should not be used normally.
|
||||
// This ping is not used for uptime detection and the LastPingOut time in connections is not set.
|
||||
func (peer *PeerInfo) Ping() {
|
||||
peer.send(&protocol.PacketRaw{Command: protocol.CommandPing, Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber})
|
||||
}
|
||||
|
||||
// Chat sends a text message
|
||||
func (peer *PeerInfo) Chat(text string) {
|
||||
peer.send(&protocol.PacketRaw{Command: protocol.CommandChat, Payload: []byte(text)})
|
||||
|
||||
@@ -171,6 +171,7 @@ func (nets *Networks) packetWorker() {
|
||||
// Update known internal/external port and User Agent
|
||||
connection.PortInternal = announce.PortInternal
|
||||
connection.PortExternal = announce.PortExternal
|
||||
connection.Firewall = announce.Features&(1<<protocol.FeatureFirewall) > 0
|
||||
if len(announce.UserAgent) > 0 {
|
||||
peer.UserAgent = announce.UserAgent
|
||||
}
|
||||
@@ -199,6 +200,7 @@ func (nets *Networks) packetWorker() {
|
||||
// Update known internal/external port and User Agent
|
||||
connection.PortInternal = response.PortInternal
|
||||
connection.PortExternal = response.PortExternal
|
||||
connection.Firewall = response.Features&(1<<protocol.FeatureFirewall) > 0
|
||||
if len(response.UserAgent) > 0 {
|
||||
peer.UserAgent = response.UserAgent
|
||||
}
|
||||
@@ -359,5 +361,8 @@ func FeatureSupport() (feature byte) {
|
||||
if networks.countListen6 > 0 {
|
||||
feature |= 1 << protocol.FeatureIPv6Listen
|
||||
}
|
||||
if networks.localFirewall {
|
||||
feature |= 1 << protocol.FeatureFirewall
|
||||
}
|
||||
return feature
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ type Networks struct {
|
||||
|
||||
// ipListen keeps a simple list of IPs listened to. This allows quickly identifying if an IP matches with a listened one.
|
||||
ipListen *ipList
|
||||
|
||||
// localFirewall indicates if a local firewall may drop unsolicited incoming packets
|
||||
localFirewall bool
|
||||
}
|
||||
|
||||
// ReplyTimeout is the round-trip timeout for message sequences.
|
||||
@@ -47,4 +50,10 @@ func initMessageSequence() {
|
||||
networks.Sequences = protocol.NewSequenceManager(ReplyTimeout)
|
||||
|
||||
networks.ipListen = NewIPList()
|
||||
|
||||
// There is currently no suitable live firewall detection code. Instead, there is the config flag.
|
||||
// Windows: If the user runs as non-admin, it can be assumed that the Windows Firewall creates a rule to drop unsolicited incoming packets.
|
||||
// Changing the Windows Firewall (via netsh or otherwise) requires elevated admin rights.
|
||||
// This flag will be passed on to other peers to indicate that uncontacted peers shall use the Traverse message for establishing connections.
|
||||
networks.localFirewall = config.LocalFirewall
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ type PeerInfo struct {
|
||||
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.
|
||||
Features uint8 // Feature bit array. 0 = IPv4_LISTEN, 1 = IPv6_LISTEN
|
||||
Features uint8 // Feature bit array. 0 = IPv4_LISTEN, 1 = IPv6_LISTEN, 1 = FIREWALL
|
||||
isVirtual bool // Whether it is a virtual peer for establishing a connection.
|
||||
targetAddresses []*peerAddress // Virtual peer: Addresses to send any replies.
|
||||
traversePeer *PeerInfo // Virtual peer: Same field as in connection.
|
||||
@@ -222,7 +222,7 @@ func records2Nodes(records []protocol.PeerRecord, peerSource *PeerInfo) (nodes [
|
||||
continue
|
||||
}
|
||||
|
||||
peer = &PeerInfo{PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource}
|
||||
peer = &PeerInfo{PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource, Features: record.Features}
|
||||
}
|
||||
|
||||
nodes = append(nodes, &dht.Node{ID: peer.NodeID, LastSeen: record.LastContactT, Info: peer})
|
||||
@@ -239,6 +239,7 @@ func selfPeerRecord() (result protocol.PeerRecord) {
|
||||
//IP: network.address.IP,
|
||||
//Port: uint16(network.address.Port),
|
||||
LastContact: 0,
|
||||
Features: FeatureSupport(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
type MessageAnnouncement struct {
|
||||
*MessageRaw // Underlying raw message
|
||||
Protocol uint8 // Protocol version supported (low 4 bits).
|
||||
Features uint8 // Feature support (high 4 bits). Future use.
|
||||
Features uint8 // Feature support
|
||||
Actions uint8 // Action bit array. See ActionX
|
||||
BlockchainHeight uint32 // Blockchain height
|
||||
BlockchainVersion uint64 // Blockchain version
|
||||
@@ -44,6 +44,7 @@ type InfoStore struct {
|
||||
const (
|
||||
FeatureIPv4Listen = 0 // Sender listens on IPv4
|
||||
FeatureIPv6Listen = 1 // Sender listens on IPv6
|
||||
FeatureFirewall = 2 // Sender indicates a potential firewall. This informs uncontacted peers that a Traverse message might be required to establish a connection.
|
||||
)
|
||||
|
||||
// Actions between peers, sent via Announcement message. They correspond to the bit array index.
|
||||
|
||||
@@ -47,6 +47,7 @@ type PeerRecord struct {
|
||||
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
|
||||
Features uint8 // Feature support. Same as in Announcement/Response message.
|
||||
}
|
||||
|
||||
// Hash2Peer links a hash to peers who are known to store the data and to peers who are considered close to the hash
|
||||
@@ -208,7 +209,8 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int,
|
||||
|
||||
peer.LastContact = binary.LittleEndian.Uint32(data[index+65 : index+65+4])
|
||||
peer.LastContactT = time.Now().Add(-time.Second * time.Duration(peer.LastContact))
|
||||
reason := data[index+69]
|
||||
peer.Features = data[index+69] & 0x7F
|
||||
reason := data[index+69] >> 7
|
||||
|
||||
var err error
|
||||
if peer.PublicKey, err = btcec.ParsePubKey(peerIDcompressed, btcec.S256()); err != nil {
|
||||
@@ -421,7 +423,7 @@ createPacketLoop:
|
||||
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
|
||||
raw[69] = peer.Features | reason<<7
|
||||
|
||||
// IPv4
|
||||
copy(raw[33:33+4], peer.IPv4.To4())
|
||||
|
||||
Reference in New Issue
Block a user