1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use ckb_logger::{debug, error, trace, warn};
6use ckb_systemtime::{Duration, Instant};
7use p2p::{
8 SessionId, async_trait,
9 bytes::Bytes,
10 context::{ProtocolContext, ProtocolContextMutRef, SessionContext},
11 multiaddr::{Multiaddr, Protocol},
12 service::TargetProtocol,
13 traits::ServiceProtocol,
14 utils::{extract_peer_id, is_reachable, multiaddr_to_socketaddr},
15};
16
17mod protocol;
18
19use crate::{NetworkState, PeerIdentifyInfo, SupportProtocols, peer_store::required_flags_filter};
20use ckb_types::{packed, prelude::*};
21
22use protocol::IdentifyMessage;
23
24const MAX_RETURN_LISTEN_ADDRS: usize = 10;
25const BAN_ON_NOT_SAME_NET: Duration = Duration::from_secs(5 * 60);
26const CHECK_TIMEOUT_TOKEN: u64 = 100;
27const CHECK_TIMEOUT_INTERVAL: u64 = 1;
29const DEFAULT_TIMEOUT: u64 = 8;
30const MAX_ADDRS: usize = 10;
31
32pub(super) fn is_remote_listen_addr_allowed(addr: &Multiaddr, global_ip_only: bool) -> bool {
33 if let Some(socket_addr) = multiaddr_to_socketaddr(addr) {
34 !global_ip_only || is_reachable(socket_addr.ip())
35 } else {
36 addr.iter()
37 .any(|protocol| matches!(protocol, Protocol::Onion3(_)))
38 }
39}
40
41#[allow(dead_code)]
43#[derive(Clone, Debug)]
44pub enum Misbehavior {
45 DuplicateReceived,
47 Timeout,
49 InvalidData,
51 TooManyAddresses(usize),
53}
54
55pub enum MisbehaveResult {
57 Continue,
59 Disconnect,
61}
62
63impl MisbehaveResult {
64 pub fn is_disconnect(&self) -> bool {
65 matches!(self, MisbehaveResult::Disconnect)
66 }
67}
68
69#[async_trait]
71pub trait Callback: Clone + Send {
72 fn register(&self, context: &ProtocolContextMutRef, version: &str) -> bool;
74 fn unregister(&self, context: &ProtocolContextMutRef);
76 async fn received_identify(
78 &mut self,
79 context: &mut ProtocolContextMutRef<'_>,
80 identify: &[u8],
81 ) -> MisbehaveResult;
82 fn identify(&mut self) -> &[u8];
84 fn local_listen_addrs(&mut self) -> Vec<Multiaddr>;
86 fn add_remote_listen_addrs(&mut self, session: &SessionContext, addrs: Vec<Multiaddr>);
88 fn add_observed_addr(&mut self, addr: Multiaddr, session_id: SessionId) -> MisbehaveResult;
90 fn misbehave(&mut self, session: &SessionContext, kind: Misbehavior) -> MisbehaveResult;
92}
93
94pub struct IdentifyProtocol<T> {
96 callback: T,
97 remote_infos: HashMap<SessionId, RemoteInfo>,
98 global_ip_only: bool,
99}
100
101impl<T: Callback> IdentifyProtocol<T> {
102 pub fn new(callback: T) -> IdentifyProtocol<T> {
103 IdentifyProtocol {
104 callback,
105 remote_infos: HashMap::default(),
106 global_ip_only: true,
107 }
108 }
109
110 #[cfg(test)]
111 pub fn global_ip_only(mut self, only: bool) -> Self {
112 self.global_ip_only = only;
113 self
114 }
115
116 fn check_duplicate(&mut self, context: &mut ProtocolContextMutRef) -> MisbehaveResult {
117 let session = context.session;
118 let info = self
119 .remote_infos
120 .get_mut(&session.id)
121 .expect("RemoteInfo must exists");
122
123 if info.has_received {
124 self.callback
125 .misbehave(&info.session, Misbehavior::DuplicateReceived)
126 } else {
127 info.has_received = true;
128 MisbehaveResult::Continue
129 }
130 }
131
132 fn process_listens(
133 &mut self,
134 context: &mut ProtocolContextMutRef,
135 listens: Vec<Multiaddr>,
136 ) -> MisbehaveResult {
137 let session = context.session;
138 let info = self
139 .remote_infos
140 .get_mut(&session.id)
141 .expect("RemoteInfo must exists");
142
143 if listens.len() > MAX_ADDRS {
144 self.callback
145 .misbehave(&info.session, Misbehavior::TooManyAddresses(listens.len()))
146 } else {
147 let global_ip_only = self.global_ip_only;
148 let reachable_addrs = listens
149 .into_iter()
150 .filter(|addr| is_remote_listen_addr_allowed(addr, global_ip_only))
151 .collect::<Vec<_>>();
152 self.callback
153 .add_remote_listen_addrs(session, reachable_addrs);
154 MisbehaveResult::Continue
155 }
156 }
157
158 fn process_observed(
159 &mut self,
160 context: &mut ProtocolContextMutRef,
161 observed: Multiaddr,
162 ) -> MisbehaveResult {
163 debug!(
164 "IdentifyProtocol process observed address, session: {:?}, observed: {}",
165 context.session, observed,
166 );
167
168 let session = context.session;
169 let info = self
170 .remote_infos
171 .get_mut(&session.id)
172 .expect("RemoteInfo must exists");
173 self.callback.add_observed_addr(observed, info.session.id);
174 MisbehaveResult::Continue
175 }
176}
177
178pub(crate) struct RemoteInfo {
179 session: SessionContext,
180 connected_at: Instant,
181 timeout: Duration,
182 has_received: bool,
183}
184
185impl RemoteInfo {
186 fn new(session: SessionContext, timeout: Duration) -> RemoteInfo {
187 RemoteInfo {
188 session,
189 connected_at: Instant::now(),
190 timeout,
191 has_received: false,
192 }
193 }
194}
195
196#[async_trait]
197impl<T: Callback> ServiceProtocol for IdentifyProtocol<T> {
198 async fn init(&mut self, context: &mut ProtocolContext) {
199 let proto_id = context.proto_id;
200 if let Err(err) = context
201 .set_service_notify(
202 proto_id,
203 Duration::from_secs(CHECK_TIMEOUT_INTERVAL),
204 CHECK_TIMEOUT_TOKEN,
205 )
206 .await
207 {
208 error!("IdentifyProtocol init error: {:?}", err)
209 }
210 }
211
212 async fn connected(&mut self, context: ProtocolContextMutRef<'_>, version: &str) {
213 let session = context.session;
214 debug!("IdentifyProtocol connected, session: {:?}", session);
215 let remote_info = RemoteInfo::new(session.clone(), Duration::from_secs(DEFAULT_TIMEOUT));
216 self.remote_infos.insert(session.id, remote_info);
217 let listen_addrs = if self.callback.register(&context, version) {
218 Vec::new()
219 } else {
220 self.callback
221 .local_listen_addrs()
222 .iter()
223 .filter(|addr| {
224 if let Some(socket_addr) = multiaddr_to_socketaddr(addr) {
225 !self.global_ip_only || is_reachable(socket_addr.ip())
226 } else {
227 addr.iter()
229 .any(|protocol| matches!(protocol, Protocol::Onion3(_)))
230 }
231 })
232 .take(MAX_ADDRS)
233 .cloned()
234 .collect()
235 };
236
237 let identify = self.callback.identify();
238 let data = IdentifyMessage::new(listen_addrs, session.address.clone(), identify).encode();
239 let _ = context
240 .quick_send_message(data)
241 .await
242 .map_err(|err| error!("IdentifyProtocol quick_send_message, error: {:?}", err));
243 }
244
245 async fn disconnected(&mut self, context: ProtocolContextMutRef<'_>) {
246 self.remote_infos
247 .remove(&context.session.id)
248 .expect("RemoteInfo must exists");
249 debug!(
250 "IdentifyProtocol disconnected, session: {:?}",
251 context.session
252 );
253 self.callback.unregister(&context);
254 }
255
256 async fn received(&mut self, mut context: ProtocolContextMutRef<'_>, data: Bytes) {
257 let session = context.session;
258 match IdentifyMessage::decode(&data) {
259 Some(message) => {
260 trace!(
261 "IdentifyProtocol received, session: {:?}, listen_addrs: {:?}, observed_addr: {}",
262 context.session, message.listen_addrs, message.observed_addr
263 );
264
265 if let MisbehaveResult::Disconnect = self.check_duplicate(&mut context) {
267 error!(
268 "Disconnect IdentifyProtocol session {:?} due to duplication.",
269 session
270 );
271 let _ = context.disconnect(session.id).await;
272 return;
273 }
274 if let MisbehaveResult::Disconnect = self
275 .callback
276 .received_identify(&mut context, message.identify)
277 .await
278 {
279 error!(
280 "Disconnect IdentifyProtocol session {:?} due to invalid identify message.",
281 session,
282 );
283 let _ = context.disconnect(session.id).await;
284 return;
285 }
286 if let MisbehaveResult::Disconnect =
287 self.process_listens(&mut context, message.listen_addrs.clone())
288 {
289 error!(
290 "Disconnect IdentifyProtocol session {:?} due to invalid listen addrs: {:?}.",
291 session, message.listen_addrs,
292 );
293 let _ = context.disconnect(session.id).await;
294 return;
295 }
296 if let MisbehaveResult::Disconnect =
297 self.process_observed(&mut context, message.observed_addr.clone())
298 {
299 error!(
300 "Disconnect IdentifyProtocol session {:?} due to invalid observed addr: {}.",
301 session, message.observed_addr,
302 );
303 let _ = context.disconnect(session.id).await;
304 }
305 }
306 None => {
307 let info = self
308 .remote_infos
309 .get(&session.id)
310 .expect("RemoteInfo must exists");
311 if self
312 .callback
313 .misbehave(&info.session, Misbehavior::InvalidData)
314 .is_disconnect()
315 {
316 let _ = context.disconnect(session.id).await;
317 }
318 }
319 }
320 }
321
322 async fn notify(&mut self, context: &mut ProtocolContext, _token: u64) {
323 for (session_id, info) in &self.remote_infos {
324 if !info.has_received && (info.connected_at + info.timeout) <= Instant::now() {
325 let misbehave_result = self.callback.misbehave(&info.session, Misbehavior::Timeout);
326 if misbehave_result.is_disconnect() {
327 let _ = context.disconnect(*session_id).await;
328 }
329 }
330 }
331 }
332}
333
334#[derive(Clone)]
335pub struct IdentifyCallback {
336 network_state: Arc<NetworkState>,
337 identify: Identify,
338}
339
340impl IdentifyCallback {
341 pub(crate) fn new(
342 network_state: Arc<NetworkState>,
343 name: String,
344 client_version: String,
345 flags: Flags,
346 ) -> IdentifyCallback {
347 IdentifyCallback {
348 network_state,
349 identify: Identify::new(name, flags, client_version),
350 }
351 }
352
353 fn listen_addrs(&self) -> Vec<Multiaddr> {
354 let addrs = self.network_state.public_addrs(MAX_RETURN_LISTEN_ADDRS * 2);
355 addrs
356 .into_iter()
357 .take(MAX_RETURN_LISTEN_ADDRS)
358 .collect::<Vec<_>>()
359 }
360}
361
362#[async_trait]
363impl Callback for IdentifyCallback {
364 fn register(&self, context: &ProtocolContextMutRef, version: &str) -> bool {
365 let session_id = context.session.id;
366 self.network_state.with_peer_registry_mut(|reg| {
367 if let Some(peer) = reg.get_peer_mut(session_id) {
368 peer.protocols.insert(context.proto_id, version.to_owned());
369 }
370 reg.is_anchor(session_id)
371 })
372 }
373
374 fn unregister(&self, context: &ProtocolContextMutRef) {
375 if context.session.ty.is_outbound() {
376 self.network_state.with_peer_store_mut(|peer_store| {
381 peer_store.update_outbound_addr_last_connected_ms(context.session.address.clone());
382 });
383 }
384 }
385
386 fn identify(&mut self) -> &[u8] {
387 self.identify.encode()
388 }
389
390 async fn received_identify(
391 &mut self,
392 context: &mut ProtocolContextMutRef<'_>,
393 identify: &[u8],
394 ) -> MisbehaveResult {
395 match self.identify.verify(identify) {
396 None => {
397 self.network_state.ban_session(
398 &context.control().clone().into(),
399 context.session.id,
400 BAN_ON_NOT_SAME_NET,
401 "The nodes are not on the same network".to_string(),
402 );
403 MisbehaveResult::Disconnect
404 }
405 Some((flags, client_version)) => {
406 let registry_client_version = |version: String| {
407 self.network_state.with_peer_registry_mut(|registry| {
408 if let Some(peer) = registry.get_peer_mut(context.session.id) {
409 peer.identify_info = Some(PeerIdentifyInfo {
410 client_version: version,
411 flags,
412 })
413 }
414 });
415 };
416
417 registry_client_version(client_version);
418
419 let required_flags = self.network_state.required_flags;
420
421 if context.session.ty.is_outbound() {
422 self.network_state.with_peer_store_mut(|peer_store| {
428 peer_store.add_outbound_addr(context.session.address.clone(), flags);
429 });
430
431 if self.network_state.with_peer_registry_mut(|reg| {
432 reg.change_feeler_flags(&context.session.address, flags)
433 }) {
434 let _ = context
435 .open_protocols(
436 context.session.id,
437 TargetProtocol::Single(SupportProtocols::Feeler.protocol_id()),
438 )
439 .await;
440 } else if required_flags_filter(required_flags, flags) {
441 let _ = context
443 .open_protocols(
444 context.session.id,
445 TargetProtocol::Filter(Box::new(move |id| {
446 id != &SupportProtocols::Feeler.protocol_id()
447 })),
448 )
449 .await;
450 } else {
451 warn!(
453 "Session closed from IdentifyProtocol due to peer's flag not meeting the requirements"
454 );
455 return MisbehaveResult::Disconnect;
456 }
457 }
458 MisbehaveResult::Continue
459 }
460 }
461 }
462
463 fn local_listen_addrs(&mut self) -> Vec<Multiaddr> {
465 let mut listens = self.listen_addrs();
466
467 if listens.len() < MAX_RETURN_LISTEN_ADDRS {
468 let observe_addrs = self
469 .network_state
470 .observed_addrs(MAX_RETURN_LISTEN_ADDRS - listens.len());
471 listens.extend(observe_addrs);
472 listens
473 } else {
474 listens
475 }
476 }
477
478 fn add_remote_listen_addrs(&mut self, session: &SessionContext, addrs: Vec<Multiaddr>) {
479 trace!(
480 "IdentifyProtocol add remote listening addresses, session: {:?}, addresses : {:?}",
481 session, addrs,
482 );
483 let flags = self.network_state.with_peer_registry_mut(|reg| {
484 if let Some(peer) = reg.get_peer_mut(session.id) {
485 peer.listened_addrs = addrs.clone();
486 peer.identify_info
487 .as_ref()
488 .map(|a| a.flags)
489 .unwrap_or(Flags::COMPATIBILITY)
490 } else {
491 Flags::COMPATIBILITY
492 }
493 });
494 self.network_state.with_peer_store_mut(|peer_store| {
495 for addr in addrs {
496 if let Err(err) = peer_store.add_addr(addr.clone(), flags) {
497 error!("IdentifyProtocol failed to add address to peer store, address: {}, error: {:?}", addr, err);
498 }
499 }
500 })
501 }
502
503 fn add_observed_addr(&mut self, mut addr: Multiaddr, session_id: SessionId) -> MisbehaveResult {
504 if extract_peer_id(&addr).is_none() {
505 addr.push(Protocol::P2P(Cow::Borrowed(
506 self.network_state.local_peer_id().as_bytes(),
507 )))
508 }
509
510 self.network_state.add_observed_addr(session_id, addr);
511 MisbehaveResult::Continue
513 }
514
515 fn misbehave(&mut self, session: &SessionContext, reason: Misbehavior) -> MisbehaveResult {
516 error!(
517 "IdentifyProtocol detects abnormal behavior, session: {:?}, reason: {:?}",
518 session, reason
519 );
520 MisbehaveResult::Disconnect
521 }
522}
523
524#[derive(Clone)]
525struct Identify {
526 name: String,
527 encode_data: ckb_types::bytes::Bytes,
528}
529
530impl Identify {
531 fn new(name: String, flags: Flags, client_version: String) -> Self {
532 Identify {
533 encode_data: packed::Identify::new_builder()
534 .name(name.as_str())
535 .flag(flags.bits())
536 .client_version(client_version.as_str())
537 .build()
538 .as_bytes(),
539 name,
540 }
541 }
542
543 fn encode(&mut self) -> &[u8] {
544 &self.encode_data
545 }
546
547 fn verify(&self, data: &[u8]) -> Option<(Flags, String)> {
548 let reader = packed::IdentifyReader::from_slice(data).ok()?;
549
550 let name = reader.name().as_utf8().ok()?.to_owned();
551 if self.name != name {
552 warn!(
553 "IdentifyProtocol detects peer has different network identifiers, local network id: {}, remote network id: {}",
554 self.name, name,
555 );
556 return None;
557 }
558
559 let flag: u64 = reader.flag().into();
560 if flag == 0 {
561 return None;
562 }
563
564 let raw_client_version = reader.client_version().as_utf8().ok()?.to_owned();
565
566 Some((Flags::from_bits_truncate(flag), raw_client_version))
567 }
568}
569
570bitflags::bitflags! {
571 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
573 pub struct Flags: u64 {
574 const COMPATIBILITY = 0b1;
576 const DISCOVERY = 0b10;
578 const SYNC = 0b100;
580 const RELAY = 0b1000;
582 const LIGHT_CLIENT = 0b10000;
584 const BLOCK_FILTER = 0b100000;
586 }
587}
588
589#[cfg(test)]
590mod tests {
591 use super::is_remote_listen_addr_allowed;
592 use p2p::multiaddr::Multiaddr;
593
594 #[test]
595 fn test_identify_rejects_dns_loopback_listen_addr() {
596 let addr: Multiaddr = format!(
597 "/dns4/localhost/tcp/{}/p2p/{}",
598 rand::random::<u16>(),
599 crate::PeerId::random().to_base58()
600 )
601 .parse()
602 .unwrap();
603
604 assert!(!is_remote_listen_addr_allowed(&addr, true));
605 assert!(!is_remote_listen_addr_allowed(&addr, false));
606 }
607
608 #[test]
609 fn test_identify_remote_listen_addr_allows_socket_addrs_by_policy() {
610 let global_addr: Multiaddr = format!(
611 "/ip4/8.8.8.8/tcp/{}/p2p/{}",
612 rand::random::<u16>(),
613 crate::PeerId::random().to_base58()
614 )
615 .parse()
616 .unwrap();
617 let loopback_addr: Multiaddr = format!(
618 "/ip4/127.0.0.1/tcp/{}/p2p/{}",
619 rand::random::<u16>(),
620 crate::PeerId::random().to_base58()
621 )
622 .parse()
623 .unwrap();
624
625 assert!(is_remote_listen_addr_allowed(&global_addr, true));
626 assert!(!is_remote_listen_addr_allowed(&loopback_addr, true));
627 assert!(is_remote_listen_addr_allowed(&loopback_addr, false));
628 }
629
630 #[test]
631 fn test_identify_remote_listen_addr_allows_onion3() {
632 let onion_addr: Multiaddr =
633 "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234"
634 .parse()
635 .unwrap();
636
637 assert!(is_remote_listen_addr_allowed(&onion_addr, true));
638 assert!(is_remote_listen_addr_allowed(&onion_addr, false));
639 }
640}