diff --git a/Bootstrap.go b/Bootstrap.go index 6230592..894f5d0 100644 --- a/Bootstrap.go +++ b/Bootstrap.go @@ -208,7 +208,7 @@ func (nets *Networks) autoMulticastBroadcast() { func contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, receiverPortInternal uint16) (contacted bool) { findSelf := ShouldSendFindSelf() _, blockchainHeight, blockchainVersion := UserBlockchain.Header() - packets := EncodeAnnouncement(true, findSelf, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion) + packets := protocol.EncodeAnnouncement(true, findSelf, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent) if len(packets) == 0 { return false } @@ -230,13 +230,13 @@ type bootstrapFindSelf struct { const bootstrapAcceptContacts = 5 // cmdResponseBootstrapFindSelf processes FIND_SELF responses -func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *MessageResponse, closest []PeerRecord) { +func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *protocol.MessageResponse, closest []protocol.PeerRecord) { if len(closest) > bootstrapAcceptContacts { closest = closest[:bootstrapAcceptContacts] } for _, closePeer := range closest { - if IsReturnedPeerBadQuality(&closePeer) { + if isReturnedPeerBadQuality(&closePeer) { continue } @@ -246,12 +246,12 @@ func (peer *PeerInfo) cmdResponseBootstrapFindSelf(msg *MessageResponse, closest } // Check if the reported peer was recently contacted (in connection with the origin peer) for bootstrapping. This makes sure inactive peers are not contacted over and over again. - recent, blacklisted := closePeer.IsRecent(peer.NodeID) + recent, blacklisted := isReturnedPeerRecent(&closePeer, peer.NodeID) if blacklisted { continue } - for _, address := range closePeer.ToAddresses() { + for _, address := range peerRecordToAddresses(&closePeer) { // Check if the specific IP:Port was already contacted in the last 5-10 minutes. if recent.IsAddressContacted(address) { continue @@ -269,8 +269,8 @@ func ShouldSendFindSelf() bool { return true } -// IsReturnedPeerBadQuality checks if the returned peer record is bad quality and should be discarded -func IsReturnedPeerBadQuality(record *PeerRecord) bool { +// isReturnedPeerBadQuality checks if the returned peer record is bad quality and should be discarded +func isReturnedPeerBadQuality(record *protocol.PeerRecord) bool { isIPv4 := record.IPv4 != nil && !record.IPv4.IsUnspecified() isIPv6 := record.IPv6 != nil && !record.IPv6.IsUnspecified() @@ -294,8 +294,8 @@ func IsReturnedPeerBadQuality(record *PeerRecord) bool { return false } -// ToAddresses returns the addresses in a usable way -func (record *PeerRecord) ToAddresses() (addresses []*peerAddress) { +// peerRecordToAddresses returns the addresses in a usable way +func peerRecordToAddresses(record *protocol.PeerRecord) (addresses []*peerAddress) { // IPv4 ipv4Port := record.IPv4Port if record.IPv4PortReportedExternal > 0 { // Use the external port if available @@ -352,8 +352,8 @@ func resetRecentContacts() { } } -// IsRecent checks if the peer is blacklisted related to the origin peer due to recent contact. It will create a "recent contact" if none exists. -func (record *PeerRecord) IsRecent(originNodeID []byte) (recent *recentContactInfo, blacklisted bool) { +// isReturnedPeerRecent checks if the peer is blacklisted related to the origin peer due to recent contact. It will create a "recent contact" if none exists. +func isReturnedPeerRecent(record *protocol.PeerRecord, originNodeID []byte) (recent *recentContactInfo, blacklisted bool) { key := publicKey2Compressed(record.PublicKey) recentContactsMutex.Lock() diff --git a/Command Traverse.go b/Command Traverse.go index 1fd9bd3..e2f10f9 100644 --- a/Command Traverse.go +++ b/Command Traverse.go @@ -15,7 +15,7 @@ import ( ) // cmdTraverseForward handles an incoming traverse message that should be forwarded to another peer -func (peer *PeerInfo) cmdTraverseForward(msg *MessageTraverse) { +func (peer *PeerInfo) cmdTraverseForward(msg *protocol.MessageTraverse) { // Verify the signature. This makes sure that a fowarded message cannot be replayed by others. if !msg.SignerPublicKey.IsEqual(peer.PublicKey) || !msg.SignerPublicKey.IsEqual(msg.SenderPublicKey) { return @@ -35,8 +35,8 @@ func (peer *PeerInfo) cmdTraverseForward(msg *MessageTraverse) { } // Get the right IP:Port of the original sender to share to the target peer. - allowIPv4 := peerTarget.Features&(1< 0 - allowIPv6 := peerTarget.Features&(1< 0 + allowIPv4 := peerTarget.Features&(1< 0 + allowIPv6 := peerTarget.Features&(1< 0 connectionIPv4 := peer.GetConnection2Share(false, allowIPv4, false) connectionIPv6 := peer.GetConnection2Share(false, false, allowIPv6) @@ -58,14 +58,14 @@ func (peer *PeerInfo) cmdTraverseForward(msg *MessageTraverse) { PortIPv6ReportedExternal = connectionIPv6.PortExternal } - if err := msgEncodeTraverseSetAddress(msg.Payload, IPv4, PortIPv4, PortIPv4ReportedExternal, IPv6, PortIPv6, PortIPv6ReportedExternal); err != nil { + if err := protocol.EncodeTraverseSetAddress(msg.Payload, IPv4, PortIPv4, PortIPv4ReportedExternal, IPv6, PortIPv6, PortIPv6ReportedExternal); err != nil { return } peerTarget.send(&protocol.PacketRaw{Command: protocol.CommandTraverse, Payload: msg.Payload}) } -func (peer *PeerInfo) cmdTraverseReceive(msg *MessageTraverse) { +func (peer *PeerInfo) cmdTraverseReceive(msg *protocol.MessageTraverse) { if msg.Expires.Before(time.Now()) { return } @@ -114,13 +114,13 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *MessageTraverse) { } // process the packet and create a virtual peer - raw := &MessageRaw{SenderPublicKey: senderPublicKey, PacketRaw: *decoded} + raw := &protocol.MessageRaw{SenderPublicKey: senderPublicKey, PacketRaw: *decoded} peerV := &PeerInfo{PublicKey: senderPublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(senderPublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses} // process it! switch decoded.Command { case protocol.CommandAnnouncement: // Announce - if announce, _ := msgDecodeAnnouncement(raw); announce != nil { + if announce, _ := protocol.DecodeAnnouncement(raw); announce != nil { if len(announce.UserAgent) > 0 { peerV.UserAgent = announce.UserAgent } diff --git a/Commands.go b/Commands.go index 477a843..3f3763c 100644 --- a/Commands.go +++ b/Commands.go @@ -20,7 +20,7 @@ import ( const respondClosesContactsCount = 5 // cmdAnouncement handles an incoming announcement. Connection may be nil for traverse relayed messages. -func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement, connection *Connection) { +func (peer *PeerInfo) cmdAnouncement(msg *protocol.MessageAnnouncement, connection *Connection) { // 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) { @@ -28,18 +28,18 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement, connection *Conne } } - allowIPv4 := msg.Features&(1< 0 - allowIPv6 := msg.Features&(1< 0 + allowIPv4 := msg.Features&(1< 0 + allowIPv6 := msg.Features&(1< 0 - var hash2Peers []Hash2Peer + var hash2Peers []protocol.Hash2Peer var hashesNotFound [][]byte - var filesEmbed []EmbeddedFileData + var filesEmbed []protocol.EmbeddedFileData // FIND_SELF: Requesting peers close to the sender? - if msg.Actions&(1< 0 { - Filters.IncomingRequest(peer, ActionFindSelf, peer.NodeID, nil) + if msg.Actions&(1< 0 { + Filters.IncomingRequest(peer, protocol.ActionFindSelf, peer.NodeID, nil) - selfD := Hash2Peer{ID: KeyHash{peer.NodeID}} + selfD := protocol.Hash2Peer{ID: protocol.KeyHash{Hash: peer.NodeID}} // do not respond the caller's own peer (add to ignore list) for _, node := range nodesDHT.GetClosestContacts(respondClosesContactsCount, peer.NodeID, filterFunc(connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) { @@ -56,11 +56,11 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement, connection *Conne } // FIND_PEER: Find a different peer? - if msg.Actions&(1< 0 && len(msg.FindPeerKeys) > 0 { + if msg.Actions&(1< 0 && len(msg.FindPeerKeys) > 0 { for _, findPeer := range msg.FindPeerKeys { - Filters.IncomingRequest(peer, ActionFindPeer, findPeer.Hash, nil) + Filters.IncomingRequest(peer, protocol.ActionFindPeer, findPeer.Hash, nil) - details := Hash2Peer{ID: findPeer} + details := protocol.Hash2Peer{ID: findPeer} // Same as before, put self as ignoredNodes. for _, node := range nodesDHT.GetClosestContacts(respondClosesContactsCount, findPeer.Hash, filterFunc(connection.IsLocal(), allowIPv4, allowIPv6), peer.NodeID) { @@ -78,16 +78,16 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement, connection *Conne } // Find a value? - if msg.Actions&(1< 0 { + if msg.Actions&(1< 0 { for _, findHash := range msg.FindDataKeys { - Filters.IncomingRequest(peer, ActionFindValue, findHash.Hash, nil) + Filters.IncomingRequest(peer, protocol.ActionFindValue, findHash.Hash, nil) stored, data := announcementGetData(findHash.Hash) if stored && len(data) > 0 { - filesEmbed = append(filesEmbed, EmbeddedFileData{ID: findHash, Data: data}) + filesEmbed = append(filesEmbed, protocol.EmbeddedFileData{ID: findHash, Data: data}) } else if stored { selfRecord := selfPeerRecord() - hash2Peers = append(hash2Peers, Hash2Peer{ID: findHash, Storing: []PeerRecord{selfRecord}}) + hash2Peers = append(hash2Peers, protocol.Hash2Peer{ID: findHash, Storing: []protocol.PeerRecord{selfRecord}}) } else { hashesNotFound = append(hashesNotFound, findHash.Hash) } @@ -95,9 +95,9 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement, connection *Conne } // Information about files stored by the sender? - if msg.Actions&(1< 0 && len(msg.InfoStoreFiles) > 0 { + if msg.Actions&(1< 0 && len(msg.InfoStoreFiles) > 0 { for n := range msg.InfoStoreFiles { - Filters.IncomingRequest(peer, ActionInfoStore, msg.InfoStoreFiles[n].ID.Hash, &msg.InfoStoreFiles[n]) + Filters.IncomingRequest(peer, protocol.ActionInfoStore, msg.InfoStoreFiles[n].ID.Hash, &msg.InfoStoreFiles[n]) } peer.announcementStore(msg.InfoStoreFiles) @@ -107,14 +107,14 @@ func (peer *PeerInfo) cmdAnouncement(msg *MessageAnnouncement, connection *Conne peer.sendResponse(msg.Sequence, sendUA, hash2Peers, filesEmbed, hashesNotFound) } -func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result *PeerRecord) { +func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result *protocol.PeerRecord) { connectionIPv4 := peer.GetConnection2Share(allowLocal, allowIPv4, false) connectionIPv6 := peer.GetConnection2Share(allowLocal, false, allowIPv6) if connectionIPv4 == nil && connectionIPv6 == nil { return nil } - result = &PeerRecord{ + result = &protocol.PeerRecord{ PublicKey: peer.PublicKey, NodeID: peer.NodeID, } @@ -137,7 +137,7 @@ func (peer *PeerInfo) peer2Record(allowLocal, allowIPv4, allowIPv6 bool) (result } // cmdResponse handles the response to the announcement -func (peer *PeerInfo) cmdResponse(msg *MessageResponse, connection *Connection) { +func (peer *PeerInfo) cmdResponse(msg *protocol.MessageResponse, connection *Connection) { // The sequence data is used to correlate this response with the announcement. if msg.SequenceInfo == nil || msg.SequenceInfo.Data == nil { // If there is no sequence data but there were results returned, it means we received unsolicited response data. It will be rejected. @@ -191,7 +191,7 @@ func (peer *PeerInfo) cmdResponse(msg *MessageResponse, connection *Connection) } // cmdPing handles an incoming ping message -func (peer *PeerInfo) cmdPing(msg *MessageRaw, connection *Connection) { +func (peer *PeerInfo) cmdPing(msg *protocol.MessageRaw, connection *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 connection.PortInternal == 0 { @@ -207,16 +207,16 @@ func (peer *PeerInfo) cmdPing(msg *MessageRaw, connection *Connection) { } // cmdPong handles an incoming pong message -func (peer *PeerInfo) cmdPong(msg *MessageRaw, connection *Connection) { +func (peer *PeerInfo) cmdPong(msg *protocol.MessageRaw, connection *Connection) { } // cmdChat handles a chat message [debug] -func (peer *PeerInfo) cmdChat(msg *MessageRaw, connection *Connection) { +func (peer *PeerInfo) cmdChat(msg *protocol.MessageRaw, connection *Connection) { fmt.Printf("Chat from '%s': %s\n", connection.Address.String(), string(msg.PacketRaw.Payload)) } // cmdLocalDiscovery handles an incoming announcement via local discovery -func (peer *PeerInfo) cmdLocalDiscovery(msg *MessageAnnouncement, connection *Connection) { +func (peer *PeerInfo) cmdLocalDiscovery(msg *protocol.MessageAnnouncement, connection *Connection) { // 21.04.2021 update: Local peer discovery from public IPv4s is possible in datacenter situations. Keep it enabled for now. // only accept local discovery message from private IPs for IPv4 // IPv6 DHCP routers typically assign public IPv6s and they can join multicast in the local network. diff --git a/Connection.go b/Connection.go index 92d51c8..9d08dc0 100644 --- a/Connection.go +++ b/Connection.go @@ -325,7 +325,7 @@ func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.P return errors.New("invalid connection") } - packet.Protocol = ProtocolVersion + packet.Protocol = protocol.ProtocolVersion packet.SetSelfReportedPorts(c.Network.SelfReportedPorts()) Filters.PacketOut(packet, receiverPublicKey, c) diff --git a/Filter.go b/Filter.go index 90b1659..3c9bfee 100644 --- a/Filter.go +++ b/Filter.go @@ -45,14 +45,14 @@ var Filters struct { PacketOut func(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, connection *Connection) // MessageIn is a high-level filter for decoded incoming messages. message is of type nil, MessageAnnouncement, MessageResponse, or MessageTraverse - MessageIn func(peer *PeerInfo, raw *MessageRaw, message interface{}) + MessageIn func(peer *PeerInfo, raw *protocol.MessageRaw, message interface{}) // MessageOutAnnouncement is a high-level filter for outgoing announcements. Peer is nil on first contact. // Broadcast and Multicast messages are not covered. - MessageOutAnnouncement func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore) + MessageOutAnnouncement func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []protocol.KeyHash, findValue []protocol.KeyHash, files []protocol.InfoStore) // MessageOutResponse is a high-level filter for outgoing responses. - MessageOutResponse func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) + MessageOutResponse func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []protocol.Hash2Peer, filesEmbed []protocol.EmbeddedFileData, hashesNotFound [][]byte) // MessageOutTraverse is a high-level filter for outgoing traverse messages. MessageOutTraverse func(peer *PeerInfo, packet *protocol.PacketRaw, embeddedPacket *protocol.PacketRaw, receiverEnd *btcec.PublicKey) @@ -90,14 +90,14 @@ func initFilters() { Filters.PacketOut = func(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, c *Connection) {} } if Filters.MessageIn == nil { - Filters.MessageIn = func(peer *PeerInfo, raw *MessageRaw, message interface{}) {} + Filters.MessageIn = func(peer *PeerInfo, raw *protocol.MessageRaw, message interface{}) {} } if Filters.MessageOutAnnouncement == nil { - Filters.MessageOutAnnouncement = func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore) { + Filters.MessageOutAnnouncement = func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []protocol.KeyHash, findValue []protocol.KeyHash, files []protocol.InfoStore) { } } if Filters.MessageOutResponse == nil { - Filters.MessageOutResponse = func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) { + Filters.MessageOutResponse = func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []protocol.Hash2Peer, filesEmbed []protocol.EmbeddedFileData, hashesNotFound [][]byte) { } } if Filters.MessageOutTraverse == nil { diff --git a/Kademlia.go b/Kademlia.go index e50638b..f8f9d55 100644 --- a/Kademlia.go +++ b/Kademlia.go @@ -105,23 +105,23 @@ func (peer *PeerInfo) sendAnnouncementFindNode(request *dht.InformationRequest) if bytes.Equal(request.Key, nodeID) { peer.sendAnnouncement(false, true, nil, nil, nil, request) } else { - peer.sendAnnouncement(false, false, []KeyHash{{Hash: request.Key}}, nil, nil, request) + peer.sendAnnouncement(false, false, []protocol.KeyHash{{Hash: request.Key}}, nil, nil, request) } } func (peer *PeerInfo) sendAnnouncementFindValue(request *dht.InformationRequest) { findSelf := false - var findPeer []KeyHash - var findValue []KeyHash + var findPeer []protocol.KeyHash + var findValue []protocol.KeyHash - findValue = append(findValue, KeyHash{Hash: request.Key}) + findValue = append(findValue, protocol.KeyHash{Hash: request.Key}) peer.sendAnnouncement(false, findSelf, findPeer, findValue, nil, request) } func (peer *PeerInfo) sendAnnouncementStore(fileHash []byte, fileSize uint64) { - peer.sendAnnouncement(false, false, nil, nil, []InfoStore{{ID: KeyHash{Hash: fileHash}, Size: fileSize, Type: 0}}, nil) + peer.sendAnnouncement(false, false, nil, nil, []protocol.InfoStore{{ID: protocol.KeyHash{Hash: fileHash}, Size: fileSize, Type: 0}}, nil) } // ---- CORE DATA FUNCTIONS ---- diff --git a/Message Send.go b/Message Send.go index 6dae818..67db4e1 100644 --- a/Message Send.go +++ b/Message Send.go @@ -32,9 +32,9 @@ func (peer *PeerInfo) Chat(text string) { } // sendAnnouncement sends the announcement message. It acquires a new sequence for each message. -func (peer *PeerInfo) sendAnnouncement(sendUA, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore, sequenceData interface{}) { +func (peer *PeerInfo) sendAnnouncement(sendUA, findSelf bool, findPeer []protocol.KeyHash, findValue []protocol.KeyHash, files []protocol.InfoStore, sequenceData interface{}) { _, blockchainHeight, blockchainVersion := UserBlockchain.Header() - packets := EncodeAnnouncement(sendUA, findSelf, findPeer, findValue, files, FeatureSupport(), blockchainHeight, blockchainVersion) + packets := protocol.EncodeAnnouncement(sendUA, findSelf, findPeer, findValue, files, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent) for _, packet := range packets { raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packet, Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, sequenceData).SequenceNumber} @@ -44,9 +44,9 @@ func (peer *PeerInfo) sendAnnouncement(sendUA, findSelf bool, findPeer []KeyHash } // sendResponse sends the response message -func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) (err error) { +func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []protocol.Hash2Peer, filesEmbed []protocol.EmbeddedFileData, hashesNotFound [][]byte) (err error) { _, blockchainHeight, blockchainVersion := UserBlockchain.Header() - packets, err := msgEncodeResponse(sendUA, hash2Peers, filesEmbed, hashesNotFound, FeatureSupport(), blockchainHeight, blockchainVersion) + packets, err := protocol.EncodeResponse(sendUA, hash2Peers, filesEmbed, hashesNotFound, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent) for _, packet := range packets { raw := &protocol.PacketRaw{Command: protocol.CommandResponse, Payload: packet, Sequence: sequence} @@ -59,7 +59,7 @@ func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []Ha // sendTraverse sends a traverse message func (peer *PeerInfo) sendTraverse(packet *protocol.PacketRaw, receiverEnd *btcec.PublicKey) (err error) { - packet.Protocol = ProtocolVersion + packet.Protocol = protocol.ProtocolVersion // self-reported ports are not set, as this isn't sent via a specific network but a relay //packet.SetSelfReportedPorts(c.Network.SelfReportedPorts()) @@ -68,7 +68,7 @@ func (peer *PeerInfo) sendTraverse(packet *protocol.PacketRaw, receiverEnd *btce return err } - packetRaw, err := msgEncodeTraverse(peerPrivateKey, embeddedPacketRaw, receiverEnd, peer.PublicKey) + packetRaw, err := protocol.EncodeTraverse(peerPrivateKey, embeddedPacketRaw, receiverEnd, peer.PublicKey) if err != nil { return err } diff --git a/Network IPv4 Broadcast.go b/Network IPv4 Broadcast.go index 2b3366d..05f066c 100644 --- a/Network IPv4 Broadcast.go +++ b/Network IPv4 Broadcast.go @@ -92,12 +92,12 @@ func (network *Network) BroadcastIPv4Listen() { // BroadcastIPv4Send sends out a single broadcast messages to discover peers func (network *Network) BroadcastIPv4Send() (err error) { _, blockchainHeight, blockchainVersion := UserBlockchain.Header() - packets := EncodeAnnouncement(true, true, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion) + packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent) if len(packets) == 0 { return errors.New("error encoding broadcast announcement") } - raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv4BroadcastPublicKey, &protocol.PacketRaw{Protocol: ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]}) + raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv4BroadcastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]}) if err != nil { return err } diff --git a/Network IPv6 Multicast.go b/Network IPv6 Multicast.go index 6113db2..b00ff68 100644 --- a/Network IPv6 Multicast.go +++ b/Network IPv6 Multicast.go @@ -138,12 +138,12 @@ func (network *Network) MulticastIPv6Listen() { // MulticastIPv6Send sends out a single multicast messages to discover peers at the same site func (network *Network) MulticastIPv6Send() (err error) { _, blockchainHeight, blockchainVersion := UserBlockchain.Header() - packets := EncodeAnnouncement(true, true, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion) + packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent) if len(packets) == 0 { return errors.New("error encoding multicast announcement") } - raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv6MulticastPublicKey, &protocol.PacketRaw{Protocol: ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]}) + raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv6MulticastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]}) if err != nil { return err } diff --git a/Network.go b/Network.go index 72b1312..38968bc 100644 --- a/Network.go +++ b/Network.go @@ -163,11 +163,11 @@ func (nets *Networks) packetWorker() { connection.LastPacketIn = time.Now() // process the packet - raw := &MessageRaw{SenderPublicKey: senderPublicKey, PacketRaw: *decoded} + raw := &protocol.MessageRaw{SenderPublicKey: senderPublicKey, PacketRaw: *decoded} switch decoded.Command { case protocol.CommandAnnouncement: // Announce - if announce, _ := msgDecodeAnnouncement(raw); announce != nil { + if announce, _ := protocol.DecodeAnnouncement(raw); announce != nil { // Update known internal/external port and User Agent connection.PortInternal = announce.PortInternal connection.PortExternal = announce.PortExternal @@ -184,9 +184,9 @@ func (nets *Networks) packetWorker() { } case protocol.CommandResponse: // Response - if response, _ := msgDecodeResponse(raw); response != nil { + if response, _ := protocol.DecodeResponse(raw); response != nil { // Validate sequence number which prevents unsolicited responses. - sequenceInfo, valid, rtt := nets.Sequences.ValidateSequence(raw.SenderPublicKey, raw.Sequence, response.Actions&(1< 0, true) + sequenceInfo, valid, rtt := nets.Sequences.ValidateSequence(raw.SenderPublicKey, raw.Sequence, response.Actions&(1< 0, true) if !valid { //Filters.LogError("packetWorker", "message with invalid sequence %d command %d from %s\n", raw.Sequence, raw.Command, raw.connection.Address.String()) // Only log for debug purposes. continue @@ -211,7 +211,7 @@ func (nets *Networks) packetWorker() { } case protocol.CommandLocalDiscovery: // Local discovery, sent via IPv4 broadcast and IPv6 multicast - if announce, _ := msgDecodeAnnouncement(raw); announce != nil { + if announce, _ := protocol.DecodeAnnouncement(raw); announce != nil { if len(announce.UserAgent) > 0 { peer.UserAgent = announce.UserAgent } @@ -248,7 +248,7 @@ func (nets *Networks) packetWorker() { peer.cmdChat(raw, connection) case protocol.CommandTraverse: - if traverse, _ := msgDecodeTraverse(raw); traverse != nil { + if traverse, _ := protocol.DecodeTraverse(raw); traverse != nil { Filters.MessageIn(peer, raw, traverse) if traverse.TargetPeer.IsEqual(peerPublicKey) && traverse.AuthorizedRelayPeer.IsEqual(peer.PublicKey) { peer.cmdTraverseReceive(traverse) @@ -337,10 +337,10 @@ func (network *Network) SelfReportedPorts() (portI, portE uint16) { // FeatureSupport returns supported features by this peer func FeatureSupport() (feature byte) { if networks.countListen4 > 0 { - feature |= 1 << FeatureIPv4Listen + feature |= 1 << protocol.FeatureIPv4Listen } if networks.countListen6 > 0 { - feature |= 1 << FeatureIPv6Listen + feature |= 1 << protocol.FeatureIPv6Listen } return feature } diff --git a/Peer ID.go b/Peer ID.go index b582296..2a057c2 100644 --- a/Peer ID.go +++ b/Peer ID.go @@ -79,6 +79,11 @@ func SelfNodeID() []byte { return nodeID } +// SelfUserAgent returns the User Agent +func SelfUserAgent() string { + return userAgent +} + // PeerInfo stores information about a single remote peer type PeerInfo struct { PublicKey *btcec.PublicKey // Public key @@ -179,8 +184,7 @@ func PeerlistLookup(publicKey *btcec.PublicKey) (peer *PeerInfo) { peerlistMutex.RLock() defer peerlistMutex.RUnlock() - peer, _ = peerList[publicKey2Compressed(publicKey)] - return peer + return peerList[publicKey2Compressed(publicKey)] } // PeerlistCount returns the current count of peers in the peer list @@ -199,9 +203,9 @@ func publicKey2Compressed(publicKey *btcec.PublicKey) [btcec.PubKeyBytesLenCompr // records2Nodes translates infoPeer structures to nodes. If the reported nodes are not in the peer table, it will create temporary PeerInfo structures. // LastContact is passed on in the Node.LastSeen field. -func records2Nodes(records []PeerRecord, peerSource *PeerInfo) (nodes []*dht.Node) { +func records2Nodes(records []protocol.PeerRecord, peerSource *PeerInfo) (nodes []*dht.Node) { for _, record := range records { - if IsReturnedPeerBadQuality(&record) { + if isReturnedPeerBadQuality(&record) { continue } @@ -212,7 +216,7 @@ func records2Nodes(records []PeerRecord, peerSource *PeerInfo) (nodes []*dht.Nod } else if peer = PeerlistLookup(record.PublicKey); peer == nil { // Create temporary peer which is not added to the global list and not added to Kademlia. // traversePeer is set to the peer who provided the node information. - addresses := record.ToAddresses() + addresses := peerRecordToAddresses(&record) if len(addresses) == 0 { continue } @@ -227,8 +231,8 @@ func records2Nodes(records []PeerRecord, peerSource *PeerInfo) (nodes []*dht.Nod } // selfPeerRecord returns self as peer record -func selfPeerRecord() (result PeerRecord) { - return PeerRecord{ +func selfPeerRecord() (result protocol.PeerRecord) { + return protocol.PeerRecord{ PublicKey: peerPublicKey, NodeID: nodeID, //IP: network.address.IP, diff --git a/Peernet.go b/Peernet.go index 14865bd..31d38be 100644 --- a/Peernet.go +++ b/Peernet.go @@ -6,8 +6,15 @@ Author: Peter Kleissner package core +var userAgent = "Peernet Core/0.1" // must be overwritten by the caller + // Init initializes the client. The config must be loaded first! -func Init() { +// The User Agent must be provided in the form "Application Name/1.0". +func Init(UserAgent string) { + if userAgent = UserAgent; userAgent == "" { + return + } + initFilters() initPeerID() initUserBlockchain() diff --git a/Store.go b/Store.go index 01fc199..3db056c 100644 --- a/Store.go +++ b/Store.go @@ -7,6 +7,7 @@ Author: Peter Kleissner package core import ( + "github.com/PeernetOfficial/core/protocol" "github.com/PeernetOfficial/core/store" ) @@ -27,7 +28,7 @@ func announcementGetData(hash []byte) (stored bool, data []byte) { return false, nil } - if len(data) <= EmbeddedFileSizeMax { + if len(data) <= protocol.EmbeddedFileSizeMax { return true, data } @@ -35,7 +36,7 @@ func announcementGetData(hash []byte) (stored bool, data []byte) { } // announcementStore handles an incoming announcement by another peer about storing data -func (peer *PeerInfo) announcementStore(records []InfoStore) { +func (peer *PeerInfo) announcementStore(records []protocol.InfoStore) { // TODO: Only store the other peers data if certain conditions are met: // - enough storage available // - not exceeding record count per peer diff --git a/Message Encoding.go b/protocol/Message Encoding.go similarity index 89% rename from Message Encoding.go rename to protocol/Message Encoding.go index 465e467..01705cd 100644 --- a/Message Encoding.go +++ b/protocol/Message Encoding.go @@ -6,7 +6,7 @@ Author: Peter Kleissner Intermediary between low-level packets and high-level interpretation. */ -package core +package protocol import ( "bytes" @@ -16,16 +16,12 @@ import ( "time" "unicode/utf8" - "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) // ProtocolVersion is the current protocol version const ProtocolVersion = 0 -// UserAgent should be set by the caller -var UserAgent = "Peernet Core/0.1" - // Actions between peers, sent via Announcement message. They correspond to the bit array index. const ( ActionFindSelf = 0 // FIND_SELF Request closest neighbors to self @@ -47,9 +43,9 @@ const ( // MessageRaw is a high-level message between peers that has not been decoded type MessageRaw struct { - protocol.PacketRaw - SenderPublicKey *btcec.PublicKey // Sender Public Key, ECDSA (secp256k1) 257-bit - SequenceInfo *protocol.SequenceExpiry // Sequence + PacketRaw + SenderPublicKey *btcec.PublicKey // Sender Public Key, ECDSA (secp256k1) 257-bit + SequenceInfo *SequenceExpiry // Sequence } // MessageAnnouncement is the decoded announcement message. @@ -68,9 +64,6 @@ type MessageAnnouncement struct { InfoStoreFiles []InfoStore // INFO_STORE data } -// blake3 digest size in bytes -const hashSize = 32 - // KeyHash is a single blake3 key hash type KeyHash struct { Hash []byte @@ -151,8 +144,8 @@ type MessageTraverse struct { // Minimum length of Announcement payload header without User Agent const announcementPayloadHeaderSize = 20 -// msgDecodeAnnouncement decodes the incoming announcement message. Returns nil if invalid. -func msgDecodeAnnouncement(msg *MessageRaw) (result *MessageAnnouncement, err error) { +// DecodeAnnouncement decodes the incoming announcement message. Returns nil if invalid. +func DecodeAnnouncement(msg *MessageRaw) (result *MessageAnnouncement, err error) { result = &MessageAnnouncement{ MessageRaw: msg, } @@ -228,19 +221,19 @@ func msgDecodeAnnouncement(msg *MessageRaw) (result *MessageAnnouncement, err er // decodeKeys decodes keys. Header is 2 bytes (count) followed by the actual keys (each 32 bytes blake3 hash). func decodeKeys(data []byte) (keys []KeyHash, read int, valid bool) { - if len(data) < 2+hashSize { // minimum length + if len(data) < 2+HashSize { // minimum length return nil, 0, false } count := binary.LittleEndian.Uint16(data[0:2]) - if read = 2 + int(count)*hashSize; len(data) < read { + if read = 2 + int(count)*HashSize; len(data) < read { return nil, 0, false } for n := 0; n < int(count); n++ { - key := make([]byte, hashSize) - copy(key, data[2+n*hashSize:2+n*hashSize+hashSize]) + key := make([]byte, HashSize) + copy(key, data[2+n*HashSize:2+n*HashSize+HashSize]) keys = append(keys, KeyHash{Hash: key}) } @@ -260,8 +253,8 @@ func decodeInfoStore(data []byte) (files []InfoStore, read int, valid bool) { for n := 0; n < int(count); n++ { file := InfoStore{} - file.ID.Hash = make([]byte, hashSize) - copy(file.ID.Hash, data[2+n*41:2+n*41+hashSize]) + file.ID.Hash = make([]byte, HashSize) + copy(file.ID.Hash, data[2+n*41:2+n*41+HashSize]) file.Size = binary.LittleEndian.Uint64(data[2+n*41+32 : 2+n*41+32+8]) file.Type = data[2+n*41+40] @@ -271,8 +264,8 @@ func decodeInfoStore(data []byte) (files []InfoStore, read int, valid bool) { return files, read, true } -// msgDecodeResponse decodes the incoming response message. Returns nil if invalid. -func msgDecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) { +// DecodeResponse decodes the incoming response message. Returns nil if invalid. +func DecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) { result = &MessageResponse{ MessageRaw: msg, } @@ -346,7 +339,7 @@ func msgDecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) { } for n := 0; n < int(countHashesNotFound); n++ { - hash := make([]byte, hashSize) + hash := make([]byte, HashSize) copy(hash, data[n*32:n*32+32]) result.HashesNotFound = append(result.HashesNotFound, hash) @@ -368,7 +361,7 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int, return nil, 0, false } - hash := make([]byte, hashSize) + hash := make([]byte, HashSize) copy(hash, data[index:index+32]) countField := binary.LittleEndian.Uint16(data[index+32:index+32+2]) & 0x7FFF isLast := binary.LittleEndian.Uint16(data[index+32:index+32+2])&0x8000 > 0 @@ -418,7 +411,7 @@ func decodePeerRecord(data []byte, count int) (hash2Peers []Hash2Peer, read int, return nil, 0, false } - peer.NodeID = protocol.PublicKey2NodeID(peer.PublicKey) + peer.NodeID = PublicKey2NodeID(peer.PublicKey) if reason == 0 { // Peer was returned because it is close to the requested hash hash2Peer.Closest = append(hash2Peer.Closest, peer) @@ -444,7 +437,7 @@ func decodeEmbeddedFile(data []byte, count int) (filesEmbed []EmbeddedFileData, return nil, 0, false } - hash := make([]byte, hashSize) + hash := make([]byte, HashSize) copy(hash, data[index:index+32]) sizeField := int(binary.LittleEndian.Uint16(data[index+32 : index+32+2])) index += 34 @@ -459,7 +452,7 @@ func decodeEmbeddedFile(data []byte, count int) (filesEmbed []EmbeddedFileData, index += sizeField // validate the hash - if !bytes.Equal(hash, protocol.HashData(fileData)) { + if !bytes.Equal(hash, HashData(fileData)) { return nil, read, false } @@ -475,14 +468,14 @@ const udpMaxPacketSize = 65507 // isPacketSizeExceed checks if the max packet size would be exceeded with the payload func isPacketSizeExceed(currentSize int, testSize int) bool { - return currentSize+testSize > udpMaxPacketSize-protocol.PacketLengthMin + return currentSize+testSize > udpMaxPacketSize-PacketLengthMin } // EncodeAnnouncement encodes an announcement message. It may return multiple messages if the input does not fit into one. // findPeer is a list of node IDs (blake3 hash of peer ID compressed form) // findValue is a list of hashes // files is a list of files stored to inform about -func EncodeAnnouncement(sendUA, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore, features byte, blockchainHeight, blockchainVersion uint64) (packetsRaw [][]byte) { +func EncodeAnnouncement(sendUA, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore, features byte, blockchainHeight, blockchainVersion uint64, userAgent string) (packetsRaw [][]byte) { createPacketLoop: for { raw := make([]byte, 64*1024) // max UDP packet size @@ -497,7 +490,7 @@ createPacketLoop: // only on initial announcement the User Agent must be provided according to the protocol spec if sendUA { - userAgentB := []byte(UserAgent) + userAgentB := []byte(userAgent) if len(userAgentB) > 255 { userAgentB = userAgentB[:255] } @@ -609,11 +602,11 @@ createPacketLoop: } // EmbeddedFileSizeMax is the maximum size of embedded files in response messages. Any file exceeding that must be shared via regular file transfer. -const EmbeddedFileSizeMax = udpMaxPacketSize - protocol.PacketLengthMin - announcementPayloadHeaderSize - 2 - 35 +const EmbeddedFileSizeMax = udpMaxPacketSize - PacketLengthMin - announcementPayloadHeaderSize - 2 - 35 -// msgEncodeResponse encodes a response message +// EncodeResponse encodes a response message // hash2Peers will be modified. -func msgEncodeResponse(sendUA bool, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte, features byte, blockchainHeight, blockchainVersion uint64) (packetsRaw [][]byte, err error) { +func EncodeResponse(sendUA bool, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte, features byte, blockchainHeight, blockchainVersion uint64, userAgent string) (packetsRaw [][]byte, err error) { for n := range filesEmbed { if len(filesEmbed[n].Data) > EmbeddedFileSizeMax { return nil, errors.New("embedded file too big") @@ -634,7 +627,7 @@ createPacketLoop: // only on initial response the User Agent must be provided according to the protocol spec if sendUA { - userAgentB := []byte(UserAgent) + userAgentB := []byte(userAgent) if len(userAgentB) > 255 { userAgentB = userAgentB[:255] } @@ -782,10 +775,10 @@ func encodePeerRecord(raw []byte, peer *PeerRecord, reason uint8) { const traversePayloadHeaderSize = 76 + 65 + 28 -// msgDecodeTraverse decodes a traverse message. +// DecodeTraverse decodes a traverse message. // It does not verify if the receiver is authorized to read or forward this message. // It validates the signature, but does not validate the signer. -func msgDecodeTraverse(msg *MessageRaw) (result *MessageTraverse, err error) { +func DecodeTraverse(msg *MessageRaw) (result *MessageTraverse, err error) { result = &MessageTraverse{ MessageRaw: msg, } @@ -821,7 +814,7 @@ func msgDecodeTraverse(msg *MessageRaw) (result *MessageTraverse, err error) { signature := msg.Payload[76+sizePacketEmbed : 76+sizePacketEmbed+65] - result.SignerPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature, protocol.HashData(msg.Payload[:76+sizePacketEmbed])) + result.SignerPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature, HashData(msg.Payload[:76+sizePacketEmbed])) if err != nil { return nil, err } @@ -850,8 +843,8 @@ func msgDecodeTraverse(msg *MessageRaw) (result *MessageTraverse, err error) { return result, nil } -// msgEncodeTraverse encodes a traverse message -func msgEncodeTraverse(senderPrivateKey *btcec.PrivateKey, embeddedPacketRaw []byte, receiverEnd *btcec.PublicKey, relayPeer *btcec.PublicKey) (packetRaw []byte, err error) { +// EncodeTraverse encodes a traverse message +func EncodeTraverse(senderPrivateKey *btcec.PrivateKey, embeddedPacketRaw []byte, receiverEnd *btcec.PublicKey, relayPeer *btcec.PublicKey) (packetRaw []byte, err error) { sizePacketEmbed := len(embeddedPacketRaw) if isPacketSizeExceed(traversePayloadHeaderSize, sizePacketEmbed) { return nil, errors.New("traverse encode: embedded packet too big") @@ -871,7 +864,7 @@ func msgEncodeTraverse(senderPrivateKey *btcec.PrivateKey, embeddedPacketRaw []b copy(raw[76:76+sizePacketEmbed], embeddedPacketRaw) // add signature - signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, protocol.HashData(raw[:76+sizePacketEmbed]), true) + signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, HashData(raw[:76+sizePacketEmbed]), true) if err != nil { return nil, err } @@ -882,8 +875,8 @@ func msgEncodeTraverse(senderPrivateKey *btcec.PrivateKey, embeddedPacketRaw []b return raw, nil } -// msgEncodeTraverseSetAddress sets the IP and Port -func msgEncodeTraverseSetAddress(raw []byte, IPv4 net.IP, PortIPv4, PortIPv4ReportedExternal uint16, IPv6 net.IP, PortIPv6, PortIPv6ReportedExternal uint16) (err error) { +// EncodeTraverseSetAddress sets the IP and Port +func EncodeTraverseSetAddress(raw []byte, IPv4 net.IP, PortIPv4, PortIPv4ReportedExternal uint16, IPv6 net.IP, PortIPv6, PortIPv6ReportedExternal uint16) (err error) { if isPacketSizeExceed(len(raw), 0) { return errors.New("traverse encode 2: embedded packet too big") } else if len(raw) < traversePayloadHeaderSize { @@ -896,14 +889,14 @@ func msgEncodeTraverseSetAddress(raw []byte, IPv4 net.IP, PortIPv4, PortIPv4Repo } // IPv4 - if IPv4 != nil && IsIPv4(IPv4) { + if IPv4 != nil && len(IPv4) == net.IPv4len { copy(raw[76+sizePacketEmbed+65:76+sizePacketEmbed+65+4], IPv4.To4()) binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+4:76+sizePacketEmbed+65+4+2], PortIPv4) binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+6:76+sizePacketEmbed+65+6+2], PortIPv4ReportedExternal) } // IPv6 - if IPv6 != nil && IsIPv6(IPv6) { + if IPv6 != nil && len(IPv6) == net.IPv6len { copy(raw[76+sizePacketEmbed+65+8:76+sizePacketEmbed+65+8+16], IPv6.To16()) binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+24:76+sizePacketEmbed+65+24+2], PortIPv6) binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+26:76+sizePacketEmbed+65+26+2], PortIPv6ReportedExternal) diff --git a/Test_test.go b/protocol/Test_test.go similarity index 56% rename from Test_test.go rename to protocol/Test_test.go index 76a71a6..d9f73b1 100644 --- a/Test_test.go +++ b/protocol/Test_test.go @@ -1,40 +1,41 @@ // Functions to manually debug encoding/decoding. No actual automated unit tests. -package core +package protocol import ( "fmt" "testing" - "github.com/PeernetOfficial/core/protocol" + "github.com/btcsuite/btcd/btcec" ) func TestMessageEncodingAnnouncement(t *testing.T) { - _, publicKey, err := Secp256k1NewPrivateKey() + privateKey, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { fmt.Printf("Error: %s\n", err.Error()) return } + publicKey := (*btcec.PublicKey)(&privateKey.PublicKey) // encode and decode announcement - packetR := protocol.PacketRaw{Protocol: 0, Command: protocol.CommandAnnouncement, Sequence: 123} + packetR := PacketRaw{Protocol: 0, Command: CommandAnnouncement, Sequence: 123} var findPeer []KeyHash var findValue []KeyHash var files []InfoStore - hash1 := protocol.HashData([]byte("test")) - hash2 := protocol.HashData([]byte("test3")) + hash1 := HashData([]byte("test")) + hash2 := HashData([]byte("test3")) findPeer = append(findPeer, KeyHash{Hash: hash1}) findValue = append(findValue, KeyHash{Hash: hash2}) - packets := EncodeAnnouncement(true, true, findPeer, findValue, files, 1<