Skip to main content

cuprate_p2p_core/client/
handshaker.rs

1//! Handshake Module
2//!
3//! This module contains a [`HandShaker`] which is a [`Service`] that takes an open connection and attempts
4//! to complete a handshake with them.
5//!
6//! This module also contains a [`ping`] function that can be used to check if an address is reachable.
7use std::{
8    future::Future,
9    marker::PhantomData,
10    pin::Pin,
11    sync::{Arc, Mutex},
12    task::{Context, Poll},
13};
14
15use futures::{FutureExt, SinkExt, Stream, StreamExt};
16use tokio::{
17    sync::{mpsc, OwnedSemaphorePermit, Semaphore},
18    time::{error::Elapsed, timeout},
19};
20use tower::{Service, ServiceExt};
21use tracing::{info_span, Instrument, Span};
22
23use cuprate_pruning::{PruningError, PruningSeed};
24use cuprate_wire::{
25    admin::{
26        HandshakeRequest, HandshakeResponse, PingResponse, SupportFlagsResponse,
27        PING_OK_RESPONSE_STATUS_TEXT,
28    },
29    common::PeerSupportFlags,
30    AdminRequestMessage, AdminResponseMessage, BasicNodeData, BucketError, LevinCommand, Message,
31};
32
33use crate::{
34    client::{
35        connection::Connection, request_handler::PeerRequestHandler,
36        timeout_monitor::connection_timeout_monitor_task, Client, InternalPeerID, PeerInformation,
37        PeerSyncCallback,
38    },
39    constants::{
40        CLIENT_QUEUE_SIZE, HANDSHAKE_TIMEOUT, MAX_EAGER_PROTOCOL_MESSAGES,
41        MAX_PEERS_IN_PEER_LIST_MESSAGE, PING_TIMEOUT,
42    },
43    handles::HandleBuilder,
44    AddressBook, AddressBookRequest, AddressBookResponse, BroadcastMessage, ConnectionDirection,
45    CoreSyncDataRequest, CoreSyncDataResponse, CoreSyncSvc, NetZoneAddress, NetworkZone,
46    ProtocolRequestHandlerMaker, Transport,
47};
48
49pub mod builder;
50pub use builder::HandshakerBuilder;
51
52#[derive(Debug, thiserror::Error)]
53pub enum HandshakeError {
54    #[error("The handshake timed out")]
55    TimedOut(#[from] Elapsed),
56    #[error("Peer has the same node ID as us")]
57    PeerHasSameNodeID,
58    #[error("Peer is on a different network")]
59    IncorrectNetwork,
60    #[error("Peer sent a peer list with peers from different zones")]
61    PeerSentIncorrectPeerList(#[from] crate::services::PeerListConversionError),
62    #[error("Peer sent invalid message: {0}")]
63    PeerSentInvalidMessage(&'static str),
64    #[error("The peers pruning seed is invalid.")]
65    InvalidPruningSeed(#[from] PruningError),
66    #[error("Levin bucket error: {0}")]
67    LevinBucketError(#[from] BucketError),
68    #[error("Internal service error: {0}")]
69    InternalSvcErr(#[from] tower::BoxError),
70    #[error("I/O error: {0}")]
71    IO(#[from] std::io::Error),
72}
73
74/// A request to complete a handshake.
75pub struct DoHandshakeRequest<Z: NetworkZone, T: Transport<Z>> {
76    /// The [`InternalPeerID`] of the peer we are handshaking with.
77    pub addr: InternalPeerID<Z::Addr>,
78    /// The receiving side of the connection.
79    pub peer_stream: T::Stream,
80    /// The sending side of the connection.
81    pub peer_sink: T::Sink,
82    /// The direction of the connection.
83    pub direction: ConnectionDirection,
84    /// An [`Option`]al permit for this connection.
85    pub permit: Option<OwnedSemaphorePermit>,
86}
87
88/// The peer handshaking service.
89#[derive(Debug, Clone)]
90pub struct HandShaker<Z: NetworkZone, T: Transport<Z>, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
91{
92    /// The address book service.
93    address_book: AdrBook,
94    /// The core sync data service.
95    core_sync_svc: CSync,
96    /// The protocol request handler service.
97    protocol_request_svc_maker: ProtoHdlrMkr,
98
99    /// Our [`BasicNodeData`]
100    our_basic_node_data: BasicNodeData,
101
102    /// A function that returns a stream that will give items to be broadcast by a connection.
103    broadcast_stream_maker: BrdcstStrmMkr,
104
105    connection_parent_span: Span,
106
107    /// Called with a peer's [`CoreSyncData`].
108    on_peer_sync: Option<PeerSyncCallback>,
109
110    /// Client configuration used by the handshaker for this transport
111    transport_client_config: T::ClientConfig,
112
113    /// The network zone.
114    _zone: PhantomData<Z>,
115}
116
117impl<Z: NetworkZone, T: Transport<Z>, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
118    HandShaker<Z, T, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
119{
120    /// Creates a new handshaker.
121    #[expect(clippy::too_many_arguments)]
122    const fn new(
123        address_book: AdrBook,
124        core_sync_svc: CSync,
125        protocol_request_svc_maker: ProtoHdlrMkr,
126        broadcast_stream_maker: BrdcstStrmMkr,
127        our_basic_node_data: BasicNodeData,
128        connection_parent_span: Span,
129        on_peer_sync: Option<PeerSyncCallback>,
130        transport_client_config: T::ClientConfig,
131    ) -> Self {
132        Self {
133            address_book,
134            core_sync_svc,
135            protocol_request_svc_maker,
136            broadcast_stream_maker,
137            our_basic_node_data,
138            connection_parent_span,
139            on_peer_sync,
140            transport_client_config,
141            _zone: PhantomData,
142        }
143    }
144
145    /// Clone the Handshaker transport client config.
146    #[inline]
147    pub const fn transport_config(&self) -> &T::ClientConfig {
148        &self.transport_client_config
149    }
150}
151
152impl<Z: NetworkZone, T: Transport<Z>, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr, BrdcstStrm>
153    Service<DoHandshakeRequest<Z, T>>
154    for HandShaker<Z, T, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
155where
156    AdrBook: AddressBook<Z> + Clone,
157    CSync: CoreSyncSvc + Clone,
158    ProtoHdlrMkr: ProtocolRequestHandlerMaker<Z> + Clone,
159    BrdcstStrm: Stream<Item = BroadcastMessage> + Send + 'static,
160    BrdcstStrmMkr: Fn(InternalPeerID<Z::Addr>) -> BrdcstStrm + Clone + Send + 'static,
161{
162    type Response = Client<Z>;
163    type Error = HandshakeError;
164    type Future =
165        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
166
167    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
168        Poll::Ready(Ok(()))
169    }
170
171    fn call(&mut self, req: DoHandshakeRequest<Z, T>) -> Self::Future {
172        let broadcast_stream_maker = self.broadcast_stream_maker.clone();
173
174        let address_book = self.address_book.clone();
175        let protocol_request_svc_maker = self.protocol_request_svc_maker.clone();
176        let core_sync_svc = self.core_sync_svc.clone();
177        let our_basic_node_data = self.our_basic_node_data.clone();
178
179        let connection_parent_span = self.connection_parent_span.clone();
180        let on_peer_sync = self.on_peer_sync.clone();
181
182        let transport_client_config = self.transport_client_config.clone();
183
184        let span = info_span!(parent: &Span::current(), "handshaker", addr=%req.addr);
185
186        async move {
187            timeout(
188                HANDSHAKE_TIMEOUT,
189                handshake(
190                    req,
191                    transport_client_config,
192                    broadcast_stream_maker,
193                    address_book,
194                    core_sync_svc,
195                    protocol_request_svc_maker,
196                    our_basic_node_data,
197                    connection_parent_span,
198                    on_peer_sync,
199                ),
200            )
201            .await?
202        }
203        .instrument(span)
204        .boxed()
205    }
206}
207
208/// Send a ping to the requested peer and wait for a response, returning the `peer_id`.
209///
210/// This function does not put a timeout on the ping.
211pub async fn ping<N, T>(addr: N::Addr, config: &T::ClientConfig) -> Result<u64, HandshakeError>
212where
213    N: NetworkZone,
214    T: Transport<N>,
215{
216    tracing::debug!("Sending Ping to peer");
217
218    let (mut peer_stream, mut peer_sink) = T::connect_to_peer(addr, config).await?;
219
220    tracing::debug!("Made outbound connection to peer, sending ping.");
221
222    peer_sink
223        .send(Message::Request(AdminRequestMessage::Ping).into())
224        .await?;
225
226    if let Some(res) = peer_stream.next().await {
227        if let Message::Response(AdminResponseMessage::Ping(ping)) = res? {
228            if ping.status == PING_OK_RESPONSE_STATUS_TEXT {
229                tracing::debug!("Ping successful.");
230                return Ok(ping.peer_id);
231            }
232
233            tracing::debug!("Peer's ping response was not `OK`.");
234            return Err(HandshakeError::PeerSentInvalidMessage(
235                "Ping response was not `OK`",
236            ));
237        }
238
239        tracing::debug!("Peer sent invalid response to ping.");
240        return Err(HandshakeError::PeerSentInvalidMessage(
241            "Peer did not send correct response for ping.",
242        ));
243    }
244
245    tracing::debug!("Connection closed before ping response.");
246    Err(BucketError::IO(std::io::Error::new(
247        std::io::ErrorKind::ConnectionAborted,
248        "The peer stream returned None",
249    ))
250    .into())
251}
252
253/// This function completes a handshake with the requested peer.
254#[expect(clippy::too_many_arguments)]
255async fn handshake<
256    Z: NetworkZone,
257    T: Transport<Z>,
258    AdrBook,
259    CSync,
260    ProtoHdlrMkr,
261    BrdcstStrmMkr,
262    BrdcstStrm,
263>(
264    req: DoHandshakeRequest<Z, T>,
265    transport_client_config: T::ClientConfig,
266
267    broadcast_stream_maker: BrdcstStrmMkr,
268
269    mut address_book: AdrBook,
270    mut core_sync_svc: CSync,
271    mut protocol_request_svc_maker: ProtoHdlrMkr,
272    our_basic_node_data: BasicNodeData,
273    connection_parent_span: Span,
274    on_peer_sync: Option<PeerSyncCallback>,
275) -> Result<Client<Z>, HandshakeError>
276where
277    AdrBook: AddressBook<Z> + Clone,
278    CSync: CoreSyncSvc + Clone,
279    ProtoHdlrMkr: ProtocolRequestHandlerMaker<Z>,
280    BrdcstStrm: Stream<Item = BroadcastMessage> + Send + 'static,
281    BrdcstStrmMkr: Fn(InternalPeerID<Z::Addr>) -> BrdcstStrm + Send + 'static,
282{
283    let DoHandshakeRequest {
284        addr,
285        mut peer_stream,
286        mut peer_sink,
287        direction,
288        permit,
289    } = req;
290
291    // A list of protocol messages the peer has sent during the handshake for us to handle after the handshake.
292    // see: [`MAX_EAGER_PROTOCOL_MESSAGES`]
293    let mut eager_protocol_messages = Vec::new();
294
295    let (peer_core_sync, peer_node_data) = match direction {
296        ConnectionDirection::Inbound => {
297            // Inbound handshake the peer sends the request.
298            tracing::debug!("waiting for handshake request.");
299
300            let Message::Request(AdminRequestMessage::Handshake(handshake_req)) =
301                wait_for_message::<Z, T>(
302                    LevinCommand::Handshake,
303                    true,
304                    &mut peer_sink,
305                    &mut peer_stream,
306                    &mut eager_protocol_messages,
307                    &our_basic_node_data,
308                )
309                .await?
310            else {
311                panic!("wait_for_message returned ok with wrong message.");
312            };
313
314            tracing::debug!("Received handshake request.");
315            // We will respond to the handshake request later.
316            (handshake_req.payload_data, handshake_req.node_data)
317        }
318        ConnectionDirection::Outbound => {
319            // Outbound handshake, we send the request.
320            send_hs_request::<Z, T, _>(
321                &mut peer_sink,
322                &mut core_sync_svc,
323                our_basic_node_data.clone(),
324            )
325            .await?;
326
327            // Wait for the handshake response.
328            let Message::Response(AdminResponseMessage::Handshake(handshake_res)) =
329                wait_for_message::<Z, T>(
330                    LevinCommand::Handshake,
331                    false,
332                    &mut peer_sink,
333                    &mut peer_stream,
334                    &mut eager_protocol_messages,
335                    &our_basic_node_data,
336                )
337                .await?
338            else {
339                panic!("wait_for_message returned ok with wrong message.");
340            };
341
342            if handshake_res.local_peerlist_new.len() > MAX_PEERS_IN_PEER_LIST_MESSAGE {
343                tracing::debug!("peer sent too many peers in response, cancelling handshake");
344
345                return Err(HandshakeError::PeerSentInvalidMessage(
346                    "Too many peers in peer list message (>250)",
347                ));
348            }
349
350            tracing::debug!(
351                "Telling address book about new peers, len: {}",
352                handshake_res.local_peerlist_new.len()
353            );
354
355            // Tell our address book about the new peers.
356            address_book
357                .ready()
358                .await?
359                .call(AddressBookRequest::IncomingPeerList(
360                    addr,
361                    handshake_res
362                        .local_peerlist_new
363                        .into_iter()
364                        .map(TryInto::try_into)
365                        .collect::<Result<_, _>>()?,
366                ))
367                .await?;
368
369            (handshake_res.payload_data, handshake_res.node_data)
370        }
371    };
372
373    if peer_node_data.network_id != our_basic_node_data.network_id {
374        return Err(HandshakeError::IncorrectNetwork);
375    }
376
377    if Z::CHECK_NODE_ID && peer_node_data.peer_id == our_basic_node_data.peer_id {
378        return Err(HandshakeError::PeerHasSameNodeID);
379    }
380
381    /*
382    // monerod sends a request for support flags if the peer doesn't specify any but this seems unnecessary
383    // as the peer should specify them in the handshake.
384
385    if peer_node_data.support_flags.is_empty() {
386        tracing::debug!(
387            "Peer didn't send support flags or has no features, sending request to make sure."
388        );
389        peer_sink
390            .send(Message::Request(RequestMessage::SupportFlags).into())
391            .await?;
392
393        let Message::Response(ResponseMessage::SupportFlags(support_flags_res)) =
394            wait_for_message::<Z>(
395                LevinCommand::SupportFlags,
396                false,
397                &mut peer_sink,
398                &mut peer_stream,
399                &mut eager_protocol_messages,
400                &our_basic_node_data,
401            )
402            .await?
403        else {
404            panic!("wait_for_message returned ok with wrong message.");
405        };
406
407        tracing::debug!("Received support flag response.");
408        peer_node_data.support_flags = support_flags_res.support_flags;
409    }
410
411    */
412
413    // Make sure the pruning seed is valid.
414    let pruning_seed = PruningSeed::decompress_p2p_rules(peer_core_sync.pruning_seed)?;
415
416    // public_address, if Some, is the reachable address of the node.
417    let public_address = 'check_out_addr: {
418        match direction {
419            ConnectionDirection::Inbound => {
420                // First send the handshake response.
421                send_hs_response::<Z, T, _, _>(
422                    &mut peer_sink,
423                    &mut core_sync_svc,
424                    &mut address_book,
425                    our_basic_node_data.clone(),
426                )
427                .await?;
428
429                // Now if the peer specifies a reachable port, open a connection and ping them to check.
430                if peer_node_data.my_port != 0 {
431                    let InternalPeerID::KnownAddr(mut outbound_address) = addr else {
432                        // Anonymity network, we don't know the inbound address.
433                        break 'check_out_addr None;
434                    };
435
436                    #[expect(
437                        clippy::cast_possible_truncation,
438                        reason = "u32 does not make sense as a port so just truncate it."
439                    )]
440                    outbound_address.set_port(peer_node_data.my_port as u16);
441
442                    let Ok(Ok(ping_peer_id)) = timeout(
443                        PING_TIMEOUT,
444                        ping::<Z, T>(outbound_address, &transport_client_config)
445                            .instrument(info_span!("ping")),
446                    )
447                    .await
448                    else {
449                        // The ping was not successful.
450                        break 'check_out_addr None;
451                    };
452
453                    // Make sure we are talking to the right node.
454                    if ping_peer_id == peer_node_data.peer_id {
455                        break 'check_out_addr Some(outbound_address);
456                    }
457                }
458                // The peer did not specify a reachable port or the ping was not successful.
459                None
460            }
461            ConnectionDirection::Outbound => {
462                let InternalPeerID::KnownAddr(outbound_addr) = addr else {
463                    unreachable!("How could we make an outbound connection to an unknown address");
464                };
465
466                // This is an outbound connection, this address is obviously reachable.
467                Some(outbound_addr)
468            }
469        }
470    };
471
472    tracing::debug!("Handshake complete.");
473
474    let (connection_guard, handle) = HandleBuilder::new().with_permit(permit).build();
475
476    // Tell the address book about the new connection.
477    address_book
478        .ready()
479        .await?
480        .call(AddressBookRequest::NewConnection {
481            internal_peer_id: addr,
482            public_address,
483            handle: handle.clone(),
484            id: peer_node_data.peer_id,
485            pruning_seed,
486            rpc_port: peer_node_data.rpc_port,
487            rpc_credits_per_hash: peer_node_data.rpc_credits_per_hash,
488        })
489        .await?;
490
491    // Set up the connection data.
492    let (connection_tx, client_rx) = mpsc::channel(CLIENT_QUEUE_SIZE);
493
494    let info = PeerInformation {
495        id: addr,
496        handle,
497        direction,
498        pruning_seed,
499        basic_node_data: peer_node_data,
500        core_sync_data: Arc::new(Mutex::new(peer_core_sync)),
501    };
502
503    let protocol_request_handler = protocol_request_svc_maker
504        .ready()
505        .await?
506        .call(info.clone())
507        .await?;
508
509    let request_handler = PeerRequestHandler {
510        address_book_svc: address_book.clone(),
511        our_sync_svc: core_sync_svc.clone(),
512        protocol_request_handler,
513        our_basic_node_data,
514        peer_info: info.clone(),
515        on_peer_sync: on_peer_sync.clone(),
516    };
517
518    let connection_guard = match on_peer_sync.clone() {
519        Some(callback) => connection_guard.with_on_close(move || callback.disconnected()),
520        None => connection_guard,
521    };
522
523    let semaphore = Arc::new(Semaphore::new(1));
524
525    let timeout_handle = tokio::spawn(connection_timeout_monitor_task(
526        info.clone(),
527        connection_tx.clone(),
528        Arc::clone(&semaphore),
529        address_book,
530        core_sync_svc,
531        on_peer_sync,
532    ));
533
534    let connection = Connection::<Z, T, _, _, _, _>::new(
535        peer_sink,
536        client_rx,
537        broadcast_stream_maker(addr),
538        request_handler,
539        connection_guard,
540        timeout_handle,
541    );
542
543    let connection_span =
544        tracing::error_span!(parent: &connection_parent_span, "connection", %addr);
545
546    // TODO: we should track this task in a JoinSet.
547    tokio::spawn(
548        connection
549            .run(peer_stream.fuse(), eager_protocol_messages)
550            .instrument(connection_span)
551            .boxed(),
552    );
553
554    let client = Client::<Z>::new(info, connection_tx, semaphore);
555
556    Ok(client)
557}
558
559/// Sends a [`AdminRequestMessage::Handshake`] down the peer sink.
560async fn send_hs_request<Z, T, CSync>(
561    peer_sink: &mut T::Sink,
562    core_sync_svc: &mut CSync,
563    our_basic_node_data: BasicNodeData,
564) -> Result<(), HandshakeError>
565where
566    Z: NetworkZone,
567    T: Transport<Z>,
568    CSync: CoreSyncSvc,
569{
570    let CoreSyncDataResponse(our_core_sync_data) = core_sync_svc
571        .ready()
572        .await?
573        .call(CoreSyncDataRequest)
574        .await?;
575
576    let req = HandshakeRequest {
577        node_data: our_basic_node_data,
578        payload_data: our_core_sync_data,
579    };
580
581    tracing::debug!("Sending handshake request.");
582
583    peer_sink
584        .send(Message::Request(AdminRequestMessage::Handshake(req)).into())
585        .await?;
586
587    Ok(())
588}
589
590/// Sends a [`AdminResponseMessage::Handshake`] down the peer sink.
591async fn send_hs_response<Z, T, CSync, AdrBook>(
592    peer_sink: &mut T::Sink,
593    core_sync_svc: &mut CSync,
594    address_book: &mut AdrBook,
595    our_basic_node_data: BasicNodeData,
596) -> Result<(), HandshakeError>
597where
598    Z: NetworkZone,
599    T: Transport<Z>,
600    AdrBook: AddressBook<Z>,
601    CSync: CoreSyncSvc,
602{
603    let CoreSyncDataResponse(our_core_sync_data) = core_sync_svc
604        .ready()
605        .await?
606        .call(CoreSyncDataRequest)
607        .await?;
608
609    let AddressBookResponse::Peers(our_peer_list) = address_book
610        .ready()
611        .await?
612        .call(AddressBookRequest::GetWhitePeers(
613            MAX_PEERS_IN_PEER_LIST_MESSAGE,
614        ))
615        .await?
616    else {
617        panic!("Address book sent incorrect response");
618    };
619
620    let res = HandshakeResponse {
621        node_data: our_basic_node_data,
622        payload_data: our_core_sync_data,
623        local_peerlist_new: our_peer_list.into_iter().map(Into::into).collect(),
624    };
625
626    tracing::debug!("Sending handshake response.");
627
628    peer_sink
629        .send(Message::Response(AdminResponseMessage::Handshake(res)).into())
630        .await?;
631
632    Ok(())
633}
634
635/// Waits for a message with a specific [`LevinCommand`].
636///
637/// The message needed must not be a protocol message, only request/ response "admin" messages are allowed.
638///
639/// `levin_command` is the [`LevinCommand`] you need and `request` is for if the message is a request.
640async fn wait_for_message<Z, T>(
641    levin_command: LevinCommand,
642    request: bool,
643
644    peer_sink: &mut T::Sink,
645    peer_stream: &mut T::Stream,
646
647    eager_protocol_messages: &mut Vec<cuprate_wire::ProtocolMessage>,
648
649    our_basic_node_data: &BasicNodeData,
650) -> Result<Message, HandshakeError>
651where
652    Z: NetworkZone,
653    T: Transport<Z>,
654{
655    let mut allow_support_flag_req = true;
656    let mut allow_ping = true;
657
658    while let Some(message) = peer_stream.next().await {
659        let message = message?;
660
661        match message {
662            Message::Protocol(protocol_message) => {
663                tracing::debug!(
664                    "Received eager protocol message with ID: {}, adding to queue",
665                    protocol_message.command()
666                );
667                eager_protocol_messages.push(protocol_message);
668                if eager_protocol_messages.len() > MAX_EAGER_PROTOCOL_MESSAGES {
669                    tracing::debug!(
670                        "Peer sent too many protocol messages before a handshake response."
671                    );
672                    return Err(HandshakeError::PeerSentInvalidMessage(
673                        "Peer sent too many protocol messages",
674                    ));
675                }
676                continue;
677            }
678            Message::Request(req_message) => {
679                if req_message.command() == levin_command && request {
680                    return Ok(Message::Request(req_message));
681                }
682
683                match req_message {
684                    AdminRequestMessage::SupportFlags => {
685                        if !allow_support_flag_req {
686                            return Err(HandshakeError::PeerSentInvalidMessage(
687                                "Peer sent 2 support flag requests",
688                            ));
689                        }
690                        send_support_flags::<Z, T>(peer_sink, our_basic_node_data.support_flags)
691                            .await?;
692                        // don't let the peer send more after the first request.
693                        allow_support_flag_req = false;
694                        continue;
695                    }
696                    AdminRequestMessage::Ping => {
697                        if !allow_ping {
698                            return Err(HandshakeError::PeerSentInvalidMessage(
699                                "Peer sent 2 ping requests",
700                            ));
701                        }
702
703                        send_ping_response::<Z, T>(peer_sink, our_basic_node_data.peer_id).await?;
704
705                        // don't let the peer send more after the first request.
706                        allow_ping = false;
707                        continue;
708                    }
709                    AdminRequestMessage::Handshake(_) | AdminRequestMessage::TimedSync(_) => {
710                        return Err(HandshakeError::PeerSentInvalidMessage(
711                            "Peer sent an admin request before responding to the handshake",
712                        ));
713                    }
714                }
715            }
716            Message::Response(res_message) if !request => {
717                if res_message.command() == levin_command {
718                    return Ok(Message::Response(res_message));
719                }
720
721                tracing::debug!("Received unexpected response: {}", res_message.command());
722                return Err(HandshakeError::PeerSentInvalidMessage(
723                    "Peer sent an incorrect response",
724                ));
725            }
726
727            Message::Response(_) => Err(HandshakeError::PeerSentInvalidMessage(
728                "Peer sent an incorrect message",
729            )),
730        }?;
731    }
732
733    Err(BucketError::IO(std::io::Error::new(
734        std::io::ErrorKind::ConnectionAborted,
735        "The peer stream returned None",
736    ))
737    .into())
738}
739
740/// Sends a [`AdminResponseMessage::SupportFlags`] down the peer sink.
741async fn send_support_flags<Z, T>(
742    peer_sink: &mut T::Sink,
743    support_flags: PeerSupportFlags,
744) -> Result<(), HandshakeError>
745where
746    Z: NetworkZone,
747    T: Transport<Z>,
748{
749    tracing::debug!("Sending support flag response.");
750    Ok(peer_sink
751        .send(
752            Message::Response(AdminResponseMessage::SupportFlags(SupportFlagsResponse {
753                support_flags,
754            }))
755            .into(),
756        )
757        .await?)
758}
759
760/// Sends a [`AdminResponseMessage::Ping`] down the peer sink.
761async fn send_ping_response<Z, T>(
762    peer_sink: &mut T::Sink,
763    peer_id: u64,
764) -> Result<(), HandshakeError>
765where
766    Z: NetworkZone,
767    T: Transport<Z>,
768{
769    tracing::debug!("Sending ping response.");
770    Ok(peer_sink
771        .send(
772            Message::Response(AdminResponseMessage::Ping(PingResponse {
773                status: PING_OK_RESPONSE_STATUS_TEXT,
774                peer_id,
775            }))
776            .into(),
777        )
778        .await?)
779}