@@ -73,6 +73,12 @@ use session::Session;
7373// seconds).
7474const BANNED_NODES_CHECK : u64 = 300 ; // Check every 5 minutes.
7575
76+ // The one-time session timeout.
77+ const ONE_TIME_SESSION_TIMEOUT : u64 = 30 ;
78+
79+ // The maximum number of established one-time sessions to maintain.
80+ const ONE_TIME_SESSION_CACHE_CAPACITY : usize = 100 ;
81+
7682/// Messages sent from the application layer to `Handler`.
7783#[ derive( Debug , Clone , PartialEq ) ]
7884#[ allow( clippy:: large_enum_variant) ]
@@ -191,6 +197,8 @@ pub struct Handler {
191197 active_challenges : HashMapDelay < NodeAddress , Challenge > ,
192198 /// Established sessions with peers.
193199 sessions : LruTimeCache < NodeAddress , Session > ,
200+ /// Established sessions with peers for a specific request, stored just one per node.
201+ one_time_sessions : LruTimeCache < NodeAddress , ( RequestId , Session ) > ,
194202 /// The channel to receive messages from the application layer.
195203 service_recv : mpsc:: UnboundedReceiver < HandlerIn > ,
196204 /// The channel to send messages to the application layer.
@@ -281,6 +289,10 @@ impl Handler {
281289 config. session_timeout ,
282290 Some ( config. session_cache_capacity ) ,
283291 ) ,
292+ one_time_sessions : LruTimeCache :: new (
293+ Duration :: from_secs ( ONE_TIME_SESSION_TIMEOUT ) ,
294+ Some ( ONE_TIME_SESSION_CACHE_CAPACITY ) ,
295+ ) ,
284296 active_challenges : HashMapDelay :: new ( config. request_timeout ) ,
285297 service_recv,
286298 service_send,
@@ -516,23 +528,23 @@ impl Handler {
516528 response : Response ,
517529 ) {
518530 // Check for an established session
519- if let Some ( session) = self . sessions . get_mut ( & node_address) {
520- // Encrypt the message and send
521- let packet = match session. encrypt_message :: < P > ( self . node_id , & response. encode ( ) ) {
522- Ok ( packet) => packet,
523- Err ( e) => {
524- warn ! ( "Could not encrypt response: {:?}" , e) ;
525- return ;
526- }
527- } ;
528- self . send ( node_address, packet) . await ;
531+ let packet = if let Some ( session) = self . sessions . get_mut ( & node_address) {
532+ session. encrypt_message :: < P > ( self . node_id , & response. encode ( ) )
533+ } else if let Some ( mut session) = self . remove_one_time_session ( & node_address, & response. id )
534+ {
535+ session. encrypt_message :: < P > ( self . node_id , & response. encode ( ) )
529536 } else {
530537 // Either the session is being established or has expired. We simply drop the
531538 // response in this case.
532- warn ! (
539+ return warn ! (
533540 "Session is not established. Dropping response {} for node: {}" ,
534541 response, node_address. node_id
535542 ) ;
543+ } ;
544+
545+ match packet {
546+ Ok ( packet) => self . send ( node_address, packet) . await ,
547+ Err ( e) => warn ! ( "Could not encrypt response: {:?}" , e) ,
536548 }
537549 }
538550
@@ -780,7 +792,7 @@ impl Handler {
780792 ephem_pubkey,
781793 enr_record,
782794 ) {
783- Ok ( ( session, enr) ) => {
795+ Ok ( ( mut session, enr) ) => {
784796 // Receiving an AuthResponse must give us an up-to-date view of the node ENR.
785797 // Verify the ENR is valid
786798 if self . verify_enr ( & enr, & node_address) {
@@ -820,6 +832,38 @@ impl Handler {
820832 ) ;
821833 self . fail_session ( & node_address, RequestError :: InvalidRemoteEnr , true )
822834 . await ;
835+
836+ // Respond to PING request even if the ENR or NodeAddress don't match
837+ // so that the source node can notice its external IP address has been changed.
838+ let maybe_ping_request = match session. decrypt_message (
839+ message_nonce,
840+ message,
841+ authenticated_data,
842+ ) {
843+ Ok ( m) => match Message :: decode ( & m) {
844+ Ok ( Message :: Request ( request) ) if request. msg_type ( ) == 1 => {
845+ Some ( request)
846+ }
847+ _ => None ,
848+ } ,
849+ _ => None ,
850+ } ;
851+ if let Some ( request) = maybe_ping_request {
852+ debug ! (
853+ "Responding to a PING request using a one-time session. node_address: {}" ,
854+ node_address
855+ ) ;
856+ self . one_time_sessions
857+ . insert ( node_address. clone ( ) , ( request. id . clone ( ) , session) ) ;
858+ if let Err ( e) = self
859+ . service_send
860+ . send ( HandlerOut :: Request ( node_address. clone ( ) , Box :: new ( request) ) )
861+ . await
862+ {
863+ warn ! ( "Failed to report request to application {}" , e) ;
864+ self . one_time_sessions . remove ( & node_address) ;
865+ }
866+ }
823867 }
824868 }
825869 Err ( Discv5Error :: InvalidChallengeSignature ( challenge) ) => {
@@ -1119,6 +1163,24 @@ impl Handler {
11191163 }
11201164 }
11211165
1166+ /// Remove one-time session by the given NodeAddress and RequestId if exists.
1167+ fn remove_one_time_session (
1168+ & mut self ,
1169+ node_address : & NodeAddress ,
1170+ request_id : & RequestId ,
1171+ ) -> Option < Session > {
1172+ match self . one_time_sessions . peek ( node_address) {
1173+ Some ( ( id, _) ) if id == request_id => {
1174+ let ( _, session) = self
1175+ . one_time_sessions
1176+ . remove ( node_address)
1177+ . expect ( "one-time session must exist" ) ;
1178+ Some ( session)
1179+ }
1180+ _ => None ,
1181+ }
1182+ }
1183+
11221184 /// A request has failed.
11231185 async fn fail_request (
11241186 & mut self ,
0 commit comments