From 185bbd9202b7c61281d876fb8b145a0b63813e56 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 10 Jun 2019 15:17:36 +0800 Subject: [PATCH] les: reject client connection if it makes too much invalid req --- les/handler.go | 39 ++++++++++++++++++++++++++++++--------- les/peer.go | 6 +++++- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/les/handler.go b/les/handler.go index 4e98e0b324..1cc7b61852 100644 --- a/les/handler.go +++ b/les/handler.go @@ -19,9 +19,11 @@ package les import ( "encoding/binary" "encoding/json" + "errors" "fmt" "math/big" "sync" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" @@ -44,6 +46,8 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +var errTooMuchInvalidRequest = errors.New("too much invalid requests made") + const ( softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header @@ -524,6 +528,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) } if origin == nil { + atomic.AddUint64(&p.invalidReq, 1) break } headers = append(headers, origin) @@ -570,7 +575,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } else { unknown = true } - case !query.Reverse: // Number based traversal towards the leaf block query.Origin.Number += query.Skip + 1 @@ -628,15 +632,18 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { sendResponse(req.ReqID, 0, nil, task.servingTime) return } + // Retrieve the requested block body, stopping if enough was found if bytes >= softResponseLimit { break } - // Retrieve the requested block body, stopping if enough was found - if number := rawdb.ReadHeaderNumber(pm.chainDb, hash); number != nil { - if data := rawdb.ReadBodyRLP(pm.chainDb, hash, *number); len(data) != 0 { - bodies = append(bodies, data) - bytes += len(data) - } + number := rawdb.ReadHeaderNumber(pm.chainDb, hash) + if number == nil { + atomic.AddUint64(&p.invalidReq, 1) + continue + } + if data := rawdb.ReadBodyRLP(pm.chainDb, hash, *number); len(data) != 0 { + bodies = append(bodies, data) + bytes += len(data) } } sendResponse(req.ReqID, uint64(reqCnt), p.ReplyBlockBodiesRLP(req.ReqID, bodies), task.done()) @@ -691,6 +698,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { number := rawdb.ReadHeaderNumber(pm.chainDb, request.BHash) if number == nil { p.Log().Warn("Failed to retrieve block num for code", "hash", request.BHash) + atomic.AddUint64(&p.invalidReq, 1) continue } header := rawdb.ReadHeader(pm.chainDb, request.BHash, *number) @@ -703,6 +711,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { local := pm.blockchain.CurrentHeader().Number.Uint64() if !pm.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) + atomic.AddUint64(&p.invalidReq, 1) continue } triedb := pm.blockchain.StateCache().TrieDB() @@ -710,6 +719,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { account, err := pm.getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) if err != nil { p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) + atomic.AddUint64(&p.invalidReq, 1) continue } code, err := triedb.Node(common.BytesToHash(account.CodeHash)) @@ -776,9 +786,12 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } // Retrieve the requested block's receipts, skipping if unknown to us var results types.Receipts - if number := rawdb.ReadHeaderNumber(pm.chainDb, hash); number != nil { - results = rawdb.ReadRawReceipts(pm.chainDb, hash, *number) + number := rawdb.ReadHeaderNumber(pm.chainDb, hash) + if number == nil { + atomic.AddUint64(&p.invalidReq, 1) + continue } + results = rawdb.ReadRawReceipts(pm.chainDb, hash, *number) if results == nil { if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { continue @@ -853,6 +866,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if number = rawdb.ReadHeaderNumber(pm.chainDb, request.BHash); number == nil { p.Log().Warn("Failed to retrieve block num for proof", "hash", request.BHash) + atomic.AddUint64(&p.invalidReq, 1) continue } if header = rawdb.ReadHeader(pm.chainDb, request.BHash, *number); header == nil { @@ -864,6 +878,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { local := pm.blockchain.CurrentHeader().Number.Uint64() if !pm.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) + atomic.AddUint64(&p.invalidReq, 1) continue } root = header.Root @@ -888,6 +903,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { account, err := pm.getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) if err != nil { p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) + atomic.AddUint64(&p.invalidReq, 1) continue } trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) @@ -1134,6 +1150,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } } } + // If the client has made too much invalid request(e.g. request a non-exist data), + // reject them to prevent SPAM attack. + if atomic.LoadUint64(&p.invalidReq) > maxRequesetErrors { + return errTooMuchInvalidRequest + } return nil } diff --git a/les/peer.go b/les/peer.go index 56d316f505..e07cb1d4ec 100644 --- a/les/peer.go +++ b/les/peer.go @@ -42,7 +42,10 @@ var ( errNotRegistered = errors.New("peer is not registered") ) -const maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam) +const ( + maxRequesetErrors = 20 // number of invalid requests tolerated (makes the protocol less brittle but still avoids spam) + maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam) +) // capacity limitation for parameter updates const ( @@ -89,6 +92,7 @@ type peer struct { // RequestProcessed is called responseLock sync.Mutex responseCount uint64 + invalidReq uint64 poolEntry *poolEntry hasBlock func(common.Hash, uint64, bool) bool