@@ -73,6 +73,12 @@ use session::Session;
73
73
// seconds).
74
74
const BANNED_NODES_CHECK : u64 = 300 ; // Check every 5 minutes.
75
75
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
+
76
82
/// Messages sent from the application layer to `Handler`.
77
83
#[ derive( Debug , Clone , PartialEq ) ]
78
84
#[ allow( clippy:: large_enum_variant) ]
@@ -191,6 +197,8 @@ pub struct Handler {
191
197
active_challenges : HashMapDelay < NodeAddress , Challenge > ,
192
198
/// Established sessions with peers.
193
199
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 ) > ,
194
202
/// The channel to receive messages from the application layer.
195
203
service_recv : mpsc:: UnboundedReceiver < HandlerIn > ,
196
204
/// The channel to send messages to the application layer.
@@ -281,6 +289,10 @@ impl Handler {
281
289
config. session_timeout ,
282
290
Some ( config. session_cache_capacity ) ,
283
291
) ,
292
+ one_time_sessions : LruTimeCache :: new (
293
+ Duration :: from_secs ( ONE_TIME_SESSION_TIMEOUT ) ,
294
+ Some ( ONE_TIME_SESSION_CACHE_CAPACITY ) ,
295
+ ) ,
284
296
active_challenges : HashMapDelay :: new ( config. request_timeout ) ,
285
297
service_recv,
286
298
service_send,
@@ -516,23 +528,23 @@ impl Handler {
516
528
response : Response ,
517
529
) {
518
530
// 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 ( ) )
529
536
} else {
530
537
// Either the session is being established or has expired. We simply drop the
531
538
// response in this case.
532
- warn ! (
539
+ return warn ! (
533
540
"Session is not established. Dropping response {} for node: {}" ,
534
541
response, node_address. node_id
535
542
) ;
543
+ } ;
544
+
545
+ match packet {
546
+ Ok ( packet) => self . send ( node_address, packet) . await ,
547
+ Err ( e) => warn ! ( "Could not encrypt response: {:?}" , e) ,
536
548
}
537
549
}
538
550
@@ -780,7 +792,7 @@ impl Handler {
780
792
ephem_pubkey,
781
793
enr_record,
782
794
) {
783
- Ok ( ( session, enr) ) => {
795
+ Ok ( ( mut session, enr) ) => {
784
796
// Receiving an AuthResponse must give us an up-to-date view of the node ENR.
785
797
// Verify the ENR is valid
786
798
if self . verify_enr ( & enr, & node_address) {
@@ -820,6 +832,38 @@ impl Handler {
820
832
) ;
821
833
self . fail_session ( & node_address, RequestError :: InvalidRemoteEnr , true )
822
834
. 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
+ }
823
867
}
824
868
}
825
869
Err ( Discv5Error :: InvalidChallengeSignature ( challenge) ) => {
@@ -1119,6 +1163,24 @@ impl Handler {
1119
1163
}
1120
1164
}
1121
1165
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
+
1122
1184
/// A request has failed.
1123
1185
async fn fail_request (
1124
1186
& mut self ,
0 commit comments