4
4
//! them to the handshaker service and then adds them to the client pool.
5
5
use std:: { pin:: pin, sync:: Arc } ;
6
6
7
- use futures:: StreamExt ;
7
+ use futures:: { SinkExt , StreamExt } ;
8
8
use tokio:: {
9
9
sync:: Semaphore ,
10
+ task:: JoinSet ,
10
11
time:: { sleep, timeout} ,
11
12
} ;
12
13
use tower:: { Service , ServiceExt } ;
@@ -17,14 +18,22 @@ use cuprate_p2p_core::{
17
18
services:: { AddressBookRequest , AddressBookResponse } ,
18
19
AddressBook , ConnectionDirection , NetworkZone ,
19
20
} ;
21
+ use cuprate_wire:: {
22
+ admin:: { PingResponse , PING_OK_RESPONSE_STATUS_TEXT } ,
23
+ AdminRequestMessage , AdminResponseMessage , Message ,
24
+ } ;
20
25
21
26
use crate :: {
22
27
client_pool:: ClientPool ,
23
- constants:: { HANDSHAKE_TIMEOUT , INBOUND_CONNECTION_COOL_DOWN } ,
28
+ constants:: {
29
+ HANDSHAKE_TIMEOUT , INBOUND_CONNECTION_COOL_DOWN , PING_REQUEST_CONCURRENCY ,
30
+ PING_REQUEST_TIMEOUT ,
31
+ } ,
24
32
P2PConfig ,
25
33
} ;
26
34
27
- /// Starts the inbound server.
35
+ /// Starts the inbound server. This function will listen to all incoming connections
36
+ /// and initiate handshake if needed, after verifying the address isn't banned.
28
37
#[ instrument( level = "warn" , skip_all) ]
29
38
pub async fn inbound_server < N , HS , A > (
30
39
client_pool : Arc < ClientPool < N > > ,
40
49
HS :: Future : Send + ' static ,
41
50
A : AddressBook < N > ,
42
51
{
52
+ // Copying the peer_id before borrowing for ping responses (Make us avoid a `clone()`).
53
+ let our_peer_id = config. basic_node_data ( ) . peer_id ;
54
+
55
+ // Mandatory. Extract server config from P2PConfig
43
56
let Some ( server_config) = config. server_config else {
44
57
tracing:: warn!( "No inbound server config provided, not listening for inbound connections." ) ;
45
58
return Ok ( ( ) ) ;
@@ -53,13 +66,18 @@ where
53
66
54
67
let mut listener = pin ! ( listener) ;
55
68
69
+ // Create semaphore for limiting to maximum inbound connections.
56
70
let semaphore = Arc :: new ( Semaphore :: new ( config. max_inbound_connections ) ) ;
71
+ // Create ping request handling JoinSet
72
+ let mut ping_join_set = JoinSet :: new ( ) ;
57
73
74
+ // Listen to incoming connections and extract necessary information.
58
75
while let Some ( connection) = listener. next ( ) . await {
59
- let Ok ( ( addr, peer_stream, peer_sink) ) = connection else {
76
+ let Ok ( ( addr, mut peer_stream, mut peer_sink) ) = connection else {
60
77
continue ;
61
78
} ;
62
79
80
+ // If peer is banned, drop connection
63
81
if let Some ( addr) = & addr {
64
82
let AddressBookResponse :: IsPeerBanned ( banned) = address_book
65
83
. ready ( )
@@ -75,11 +93,13 @@ where
75
93
}
76
94
}
77
95
96
+ // Create a new internal id for new peers
78
97
let addr = match addr {
79
98
Some ( addr) => InternalPeerID :: KnownAddr ( addr) ,
80
99
None => InternalPeerID :: Unknown ( rand:: random ( ) ) ,
81
100
} ;
82
101
102
+ // If we're still behind our maximum limit, Initiate handshake.
83
103
if let Ok ( permit) = semaphore. clone ( ) . try_acquire_owned ( ) {
84
104
tracing:: debug!( "Permit free for incoming connection, attempting handshake." ) ;
85
105
@@ -102,8 +122,39 @@ where
102
122
. instrument ( Span :: current ( ) ) ,
103
123
) ;
104
124
} else {
125
+ // Otherwise check if the node is simply pinging us.
105
126
tracing:: debug!( "No permit free for incoming connection." ) ;
106
- // TODO: listen for if the peer is just trying to ping us to see if we are reachable.
127
+
128
+ // We only handle 2 ping request conccurently. Otherwise we drop the connection immediately.
129
+ if ping_join_set. len ( ) < PING_REQUEST_CONCURRENCY {
130
+ ping_join_set. spawn (
131
+ async move {
132
+ // Await first message from node. If it is a ping request we respond back, otherwise we drop the connection.
133
+ let fut = timeout ( PING_REQUEST_TIMEOUT , peer_stream. next ( ) ) ;
134
+
135
+ // Ok if timeout did not elapsed -> Some if there is a message -> Ok if it has been decoded
136
+ if let Ok ( Some ( Ok ( Message :: Request ( AdminRequestMessage :: Ping ) ) ) ) = fut. await
137
+ {
138
+ let response = peer_sink
139
+ . send (
140
+ Message :: Response ( AdminResponseMessage :: Ping ( PingResponse {
141
+ status : PING_OK_RESPONSE_STATUS_TEXT ,
142
+ peer_id : our_peer_id,
143
+ } ) )
144
+ . into ( ) ,
145
+ )
146
+ . await ;
147
+
148
+ if let Err ( err) = response {
149
+ tracing:: debug!(
150
+ "Unable to respond to ping request from peer ({addr}): {err}"
151
+ )
152
+ }
153
+ }
154
+ }
155
+ . instrument ( Span :: current ( ) ) ,
156
+ ) ;
157
+ }
107
158
}
108
159
109
160
sleep ( INBOUND_CONNECTION_COOL_DOWN ) . await ;
0 commit comments