|
@ -20,7 +20,10 @@ const ( |
|
|
// PexChannel is a channel for PEX messages
|
|
|
// PexChannel is a channel for PEX messages
|
|
|
PexChannel = byte(0x00) |
|
|
PexChannel = byte(0x00) |
|
|
|
|
|
|
|
|
maxMsgSize = 1048576 // 1MB
|
|
|
|
|
|
|
|
|
// TODO: make smaller. Should match the maxGetSelection
|
|
|
|
|
|
// this is basically the amplification factor since a request
|
|
|
|
|
|
// msg is like 1 byte ... it can cause us to send msgs of this size!
|
|
|
|
|
|
maxPexMessageSize = 1048576 // 1MB
|
|
|
|
|
|
|
|
|
// ensure we have enough peers
|
|
|
// ensure we have enough peers
|
|
|
defaultEnsurePeersPeriod = 30 * time.Second |
|
|
defaultEnsurePeersPeriod = 30 * time.Second |
|
@ -61,7 +64,7 @@ type PEXReactor struct { |
|
|
|
|
|
|
|
|
book AddrBook |
|
|
book AddrBook |
|
|
config *PEXReactorConfig |
|
|
config *PEXReactorConfig |
|
|
ensurePeersPeriod time.Duration |
|
|
|
|
|
|
|
|
ensurePeersPeriod time.Duration // TODO: should go in the config
|
|
|
|
|
|
|
|
|
// maps to prevent abuse
|
|
|
// maps to prevent abuse
|
|
|
requestsSent *cmn.CMap // ID->struct{}: unanswered send requests
|
|
|
requestsSent *cmn.CMap // ID->struct{}: unanswered send requests
|
|
@ -70,6 +73,12 @@ type PEXReactor struct { |
|
|
attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)}
|
|
|
attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)}
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pexR *PEXReactor) minReceiveRequestInterval() time.Duration { |
|
|
|
|
|
// NOTE: must be less than ensurePeersPeriod, otherwise we'll request
|
|
|
|
|
|
// peers too quickly from others and they'll think we're bad!
|
|
|
|
|
|
return pexR.ensurePeersPeriod / 3 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// PEXReactorConfig holds reactor specific configuration data.
|
|
|
// PEXReactorConfig holds reactor specific configuration data.
|
|
|
type PEXReactorConfig struct { |
|
|
type PEXReactorConfig struct { |
|
|
// Seed/Crawler mode
|
|
|
// Seed/Crawler mode
|
|
@ -113,6 +122,9 @@ func (r *PEXReactor) OnStart() error { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// return err if user provided a bad seed address
|
|
|
// return err if user provided a bad seed address
|
|
|
|
|
|
// NOTE: only if its an invalid address.
|
|
|
|
|
|
// If we simply fail to resovle a DNS name,
|
|
|
|
|
|
// we shouldn't exit here ...
|
|
|
if err := r.checkSeeds(); err != nil { |
|
|
if err := r.checkSeeds(); err != nil { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
@ -195,6 +207,10 @@ func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Seeds disconnect after sending a batch of addrs
|
|
|
// Seeds disconnect after sending a batch of addrs
|
|
|
|
|
|
// NOTE: this is a prime candidate for amplification attacks
|
|
|
|
|
|
// so it's important we
|
|
|
|
|
|
// 1) restrict how frequently peers can request
|
|
|
|
|
|
// 2) limit the output size
|
|
|
if r.config.SeedMode { |
|
|
if r.config.SeedMode { |
|
|
r.SendAddrs(src, r.book.GetSelectionWithBias(biasToSelectNewPeers)) |
|
|
r.SendAddrs(src, r.book.GetSelectionWithBias(biasToSelectNewPeers)) |
|
|
r.Switch.StopPeerGracefully(src) |
|
|
r.Switch.StopPeerGracefully(src) |
|
@ -213,6 +229,7 @@ func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// enforces a minimum amount of time between requests
|
|
|
func (r *PEXReactor) receiveRequest(src Peer) error { |
|
|
func (r *PEXReactor) receiveRequest(src Peer) error { |
|
|
id := string(src.ID()) |
|
|
id := string(src.ID()) |
|
|
v := r.lastReceivedRequests.Get(id) |
|
|
v := r.lastReceivedRequests.Get(id) |
|
@ -232,8 +249,14 @@ func (r *PEXReactor) receiveRequest(src Peer) error { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
now := time.Now() |
|
|
now := time.Now() |
|
|
if now.Sub(lastReceived) < r.ensurePeersPeriod/3 { |
|
|
|
|
|
return fmt.Errorf("Peer (%v) is sending too many PEX requests. Disconnecting", src.ID()) |
|
|
|
|
|
|
|
|
minInterval := r.minReceiveRequestInterval() |
|
|
|
|
|
if now.Sub(lastReceived) < minInterval { |
|
|
|
|
|
return fmt.Errorf("Peer (%v) send next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", |
|
|
|
|
|
src.ID(), |
|
|
|
|
|
lastReceived, |
|
|
|
|
|
now, |
|
|
|
|
|
minInterval, |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
r.lastReceivedRequests.Set(id, now) |
|
|
r.lastReceivedRequests.Set(id, now) |
|
|
return nil |
|
|
return nil |
|
@ -264,7 +287,11 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { |
|
|
|
|
|
|
|
|
srcAddr := src.NodeInfo().NetAddress() |
|
|
srcAddr := src.NodeInfo().NetAddress() |
|
|
for _, netAddr := range addrs { |
|
|
for _, netAddr := range addrs { |
|
|
|
|
|
// TODO: make sure correct nodes never send nil and return error
|
|
|
|
|
|
// if a netAddr == nil
|
|
|
if netAddr != nil && !isAddrPrivate(netAddr, r.config.PrivatePeerIDs) { |
|
|
if netAddr != nil && !isAddrPrivate(netAddr, r.config.PrivatePeerIDs) { |
|
|
|
|
|
// TODO: Should we moe the list of private peers into the AddrBook so AddAddress
|
|
|
|
|
|
// can do the check for us, and we don't have to worry about checking before calling ?
|
|
|
err := r.book.AddAddress(netAddr, srcAddr) |
|
|
err := r.book.AddAddress(netAddr, srcAddr) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
r.Logger.Error("Failed to add new address", "err", err) |
|
|
r.Logger.Error("Failed to add new address", "err", err) |
|
@ -360,6 +387,9 @@ func (r *PEXReactor) ensurePeers() { |
|
|
if connected := r.Switch.Peers().Has(try.ID); connected { |
|
|
if connected := r.Switch.Peers().Has(try.ID); connected { |
|
|
continue |
|
|
continue |
|
|
} |
|
|
} |
|
|
|
|
|
// TODO: consider moving some checks from toDial into here
|
|
|
|
|
|
// so we don't even consider dialing peers that we want to wait
|
|
|
|
|
|
// before dialling again, or have dialled too many times already
|
|
|
r.Logger.Info("Will dial address", "addr", try) |
|
|
r.Logger.Info("Will dial address", "addr", try) |
|
|
toDial[try.ID] = try |
|
|
toDial[try.ID] = try |
|
|
} |
|
|
} |
|
@ -387,13 +417,17 @@ func (r *PEXReactor) ensurePeers() { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { |
|
|
|
|
|
var attempts int |
|
|
|
|
|
var lastDialed time.Time |
|
|
|
|
|
if lAttempts, attempted := r.attemptsToDial.Load(addr.DialString()); attempted { |
|
|
|
|
|
attempts = lAttempts.(_attemptsToDial).number |
|
|
|
|
|
lastDialed = lAttempts.(_attemptsToDial).lastDialed |
|
|
|
|
|
|
|
|
func (r *PEXReactor) dialAttemptsInfo(addr *p2p.NetAddress) (attempts int, lastDialed time.Time) { |
|
|
|
|
|
_attempts, ok := r.attemptsToDial.Load(addr.DialString()) |
|
|
|
|
|
if !ok { |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
atd := _attempts.(_attemptsToDial) |
|
|
|
|
|
return atd.number, atd.lastDialed |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { |
|
|
|
|
|
attempts, lastDialed := r.dialAttemptsInfo(addr) |
|
|
|
|
|
|
|
|
if attempts > maxAttemptsToDial { |
|
|
if attempts > maxAttemptsToDial { |
|
|
r.Logger.Error("Reached max attempts to dial", "addr", addr, "attempts", attempts) |
|
|
r.Logger.Error("Reached max attempts to dial", "addr", addr, "attempts", attempts) |
|
@ -439,6 +473,9 @@ func (r *PEXReactor) checkSeeds() error { |
|
|
if lSeeds == 0 { |
|
|
if lSeeds == 0 { |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
// TODO: don't exit the program if we simply cant resolve a DNS name.
|
|
|
|
|
|
// But if names or addresses are incorrectly speficied (ie. invalid),
|
|
|
|
|
|
// then we should return an err that causes an exit
|
|
|
_, errs := p2p.NewNetAddressStrings(r.config.Seeds) |
|
|
_, errs := p2p.NewNetAddressStrings(r.config.Seeds) |
|
|
for _, err := range errs { |
|
|
for _, err := range errs { |
|
|
if err != nil { |
|
|
if err != nil { |
|
|