github.com/zhiqiangxu/go-ethereum@v1.9.16-0.20210824055606-be91cfdebc48/p2p/discover/v5_session.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package discover
    18  
    19  import (
    20  	crand "crypto/rand"
    21  
    22  	"github.com/hashicorp/golang-lru/simplelru"
    23  	"github.com/zhiqiangxu/go-ethereum/common/mclock"
    24  	"github.com/zhiqiangxu/go-ethereum/p2p/enode"
    25  )
    26  
    27  // The sessionCache keeps negotiated encryption keys and
    28  // state for in-progress handshakes in the Discovery v5 wire protocol.
    29  type sessionCache struct {
    30  	sessions   *simplelru.LRU
    31  	handshakes map[sessionID]*whoareyouV5
    32  	clock      mclock.Clock
    33  }
    34  
    35  // sessionID identifies a session or handshake.
    36  type sessionID struct {
    37  	id   enode.ID
    38  	addr string
    39  }
    40  
    41  // session contains session information
    42  type session struct {
    43  	writeKey     []byte
    44  	readKey      []byte
    45  	nonceCounter uint32
    46  }
    47  
    48  func newSessionCache(maxItems int, clock mclock.Clock) *sessionCache {
    49  	cache, err := simplelru.NewLRU(maxItems, nil)
    50  	if err != nil {
    51  		panic("can't create session cache")
    52  	}
    53  	return &sessionCache{
    54  		sessions:   cache,
    55  		handshakes: make(map[sessionID]*whoareyouV5),
    56  		clock:      clock,
    57  	}
    58  }
    59  
    60  // nextNonce creates a nonce for encrypting a message to the given session.
    61  func (sc *sessionCache) nextNonce(id enode.ID, addr string) []byte {
    62  	n := make([]byte, gcmNonceSize)
    63  	crand.Read(n)
    64  	return n
    65  }
    66  
    67  // session returns the current session for the given node, if any.
    68  func (sc *sessionCache) session(id enode.ID, addr string) *session {
    69  	item, ok := sc.sessions.Get(sessionID{id, addr})
    70  	if !ok {
    71  		return nil
    72  	}
    73  	return item.(*session)
    74  }
    75  
    76  // readKey returns the current read key for the given node.
    77  func (sc *sessionCache) readKey(id enode.ID, addr string) []byte {
    78  	if s := sc.session(id, addr); s != nil {
    79  		return s.readKey
    80  	}
    81  	return nil
    82  }
    83  
    84  // writeKey returns the current read key for the given node.
    85  func (sc *sessionCache) writeKey(id enode.ID, addr string) []byte {
    86  	if s := sc.session(id, addr); s != nil {
    87  		return s.writeKey
    88  	}
    89  	return nil
    90  }
    91  
    92  // storeNewSession stores new encryption keys in the cache.
    93  func (sc *sessionCache) storeNewSession(id enode.ID, addr string, r, w []byte) {
    94  	sc.sessions.Add(sessionID{id, addr}, &session{
    95  		readKey: r, writeKey: w,
    96  	})
    97  }
    98  
    99  // getHandshake gets the handshake challenge we previously sent to the given remote node.
   100  func (sc *sessionCache) getHandshake(id enode.ID, addr string) *whoareyouV5 {
   101  	return sc.handshakes[sessionID{id, addr}]
   102  }
   103  
   104  // storeSentHandshake stores the handshake challenge sent to the given remote node.
   105  func (sc *sessionCache) storeSentHandshake(id enode.ID, addr string, challenge *whoareyouV5) {
   106  	challenge.sent = sc.clock.Now()
   107  	sc.handshakes[sessionID{id, addr}] = challenge
   108  }
   109  
   110  // deleteHandshake deletes handshake data for the given node.
   111  func (sc *sessionCache) deleteHandshake(id enode.ID, addr string) {
   112  	delete(sc.handshakes, sessionID{id, addr})
   113  }
   114  
   115  // handshakeGC deletes timed-out handshakes.
   116  func (sc *sessionCache) handshakeGC() {
   117  	deadline := sc.clock.Now().Add(-handshakeTimeout)
   118  	for key, challenge := range sc.handshakes {
   119  		if challenge.sent < deadline {
   120  			delete(sc.handshakes, key)
   121  		}
   122  	}
   123  }