diff --git a/Message Encoding.go b/Message Encoding.go index 1686186..465e467 100644 --- a/Message Encoding.go +++ b/Message Encoding.go @@ -48,8 +48,8 @@ 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 *SequenceExpiry // Sequence + SenderPublicKey *btcec.PublicKey // Sender Public Key, ECDSA (secp256k1) 257-bit + SequenceInfo *protocol.SequenceExpiry // Sequence } // MessageAnnouncement is the decoded announcement message. diff --git a/Network.go b/Network.go index ea69895..72b1312 100644 --- a/Network.go +++ b/Network.go @@ -186,12 +186,14 @@ func (nets *Networks) packetWorker() { case protocol.CommandResponse: // Response if response, _ := msgDecodeResponse(raw); response != nil { // Validate sequence number which prevents unsolicited responses. - if valid, rtt := nets.Sequences.ValidateSequence(raw, response.Actions&(1< 0); !valid { + 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 } else if rtt > 0 { connection.RoundTripTime = rtt } + raw.SequenceInfo = sequenceInfo // Update known internal/external port and User Agent connection.PortInternal = response.PortInternal @@ -228,12 +230,14 @@ func (nets *Networks) packetWorker() { case protocol.CommandPong: // Ping // Validate sequence number which prevents unsolicited responses. - if valid, rtt := nets.Sequences.ValidateSequence(raw, true); !valid { + sequenceInfo, valid, rtt := nets.Sequences.ValidateSequence(raw.SenderPublicKey, raw.Sequence, true, false) + 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 } else if rtt > 0 { connection.RoundTripTime = rtt } + raw.SequenceInfo = sequenceInfo Filters.MessageIn(peer, raw, nil) diff --git a/Networks.go b/Networks.go index 09d83a4..aa15733 100644 --- a/Networks.go +++ b/Networks.go @@ -6,7 +6,11 @@ Author: Peter Kleissner package core -import "sync" +import ( + "sync" + + "github.com/PeernetOfficial/core/protocol" +) // Networks is the collection of all connected networks type Networks struct { @@ -24,7 +28,7 @@ type Networks struct { rawPacketsIncoming chan networkWire // Sequences keeps track of all message sequence number, regardless of the network connection. - Sequences *SequenceManager + Sequences *protocol.SequenceManager // ipListen keeps a simple list of IPs listened to. This allows quickly identifying if an IP matches with a listened one. ipListen *ipList @@ -40,7 +44,7 @@ func initMessageSequence() { networks.rawPacketsIncoming = make(chan networkWire, 1000) // buffer up to 1000 UDP packets before they get buffered by the OS network stack and eventually dropped - networks.Sequences = NewSequenceManager(ReplyTimeout) + networks.Sequences = protocol.NewSequenceManager(ReplyTimeout) networks.ipListen = NewIPList() } diff --git a/Message Sequence.go b/protocol/Sequence.go similarity index 79% rename from Message Sequence.go rename to protocol/Sequence.go index b4fa1ac..d1d3d59 100644 --- a/Message Sequence.go +++ b/protocol/Sequence.go @@ -1,9 +1,10 @@ /* -File Name: Message Sequence.go +File Name: Sequence.go Copyright: 2021 Peernet s.r.o. Author: Peter Kleissner -Records and verifies message sequences. Sequence numbers are valid on a peer level, independent of which network connection was used. +This code caches and verifies message sequences. Sequence numbers are valid on a peer level, independent of which network connection was used. +They can be used to map incoming response messages to previous outgoing requests. The remote peer ID is used together with a consecutive sequence number as unique key. Advantages: * This secures against replay and poisoning attacks. @@ -12,7 +13,7 @@ Advantages: * (future) It can be used to detect missed and lost replies. */ -package core +package protocol import ( "math/rand" @@ -21,7 +22,6 @@ import ( "sync/atomic" "time" - "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -111,13 +111,8 @@ func (manager *SequenceManager) ArbitrarySequence(publicKey *btcec.PublicKey, da } // ValidateSequence validates the sequence number of an incoming message. It will set raw.sequence if valid. -func (manager *SequenceManager) ValidateSequence(raw *MessageRaw, invalidate bool) (valid bool, rtt time.Duration) { - // Only Response and Pong - if raw.Command != protocol.CommandResponse && raw.Command != protocol.CommandPong { - return true, rtt - } - - key := string(raw.SenderPublicKey.SerializeCompressed()) + strconv.FormatUint(uint64(raw.Sequence), 10) +func (manager *SequenceManager) ValidateSequence(publicKey *btcec.PublicKey, sequenceNumber uint32, invalidate, extendValidity bool) (sequenceInfo *SequenceExpiry, valid bool, rtt time.Duration) { + key := string(publicKey.SerializeCompressed()) + strconv.FormatUint(uint64(sequenceNumber), 10) manager.Lock() defer manager.Unlock() @@ -125,7 +120,7 @@ func (manager *SequenceManager) ValidateSequence(raw *MessageRaw, invalidate boo // lookup the sequence sequence, ok := manager.sequences[key] if !ok { - return false, rtt + return nil, false, rtt } // Initial reply: Store latest roundtrip time. That value might be distorted on Response vs Pong since Response messages might send data @@ -139,24 +134,17 @@ func (manager *SequenceManager) ValidateSequence(raw *MessageRaw, invalidate boo // invalidate the sequence immediately? if invalidate { delete(manager.sequences, key) - } else if raw.Command == protocol.CommandResponse { - // Special case CommandResponse: Extend validity in case there are follow-up responses by half of the round-trip time since they will be sent one-way. + } else if extendValidity { + // Special case CommandResponse: Extend validity in case there are follow-up responses, by half of the round-trip time since they will be sent one-way. sequence.expires = time.Now().Add(time.Duration(manager.ReplyTimeout) * time.Second / 2) } - raw.SequenceInfo = sequence - - return sequence.expires.After(time.Now()), rtt + return sequence, sequence.expires.After(time.Now()), rtt } // InvalidateSequence invalidates the sequence number. -func (manager *SequenceManager) InvalidateSequence(raw *MessageRaw) { - // Only Response - if raw.Command != protocol.CommandResponse { - return - } - - key := string(raw.SenderPublicKey.SerializeCompressed()) + strconv.FormatUint(uint64(raw.Sequence), 10) +func (manager *SequenceManager) InvalidateSequence(publicKey *btcec.PublicKey, sequenceNumber uint32) { + key := string(publicKey.SerializeCompressed()) + strconv.FormatUint(uint64(sequenceNumber), 10) manager.Lock() delete(manager.sequences, key)