cloudburst/dht/krpc/
ping.rs

1// Copyright 2022 Bryant Luk
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Pings a node.
10//!
11//! The query and response are described in [BEP 5][bep_0005].
12//!
13//! [bep_0005]: http://bittorrent.org/beps/bep_0005.html
14
15use core::convert::TryFrom;
16use serde_derive::{Deserialize, Serialize};
17
18use crate::dht::node::{Id, LocalId};
19
20/// The "ping" query method name.
21pub const METHOD_PING: &[u8] = b"ping";
22
23/// The arguments for the ping query message.
24#[derive(Debug, Deserialize, Serialize)]
25pub struct QueryArgs<'a> {
26    /// The querying node's ID
27    #[serde(with = "serde_bytes")]
28    pub id: &'a [u8],
29}
30
31impl<'a> QueryArgs<'a> {
32    /// Constructs a new `QueryArgs` based on the local node ID.
33    #[must_use]
34    #[inline]
35    pub fn new(id: &'a LocalId) -> Self {
36        Self { id: &(id.0).0 }
37    }
38
39    /// Returns the querying node's ID.
40    #[must_use]
41    #[inline]
42    pub fn id(&self) -> Option<Id> {
43        Id::try_from(self.id).ok()
44    }
45}
46
47/// The arguments for the ping query message.
48#[derive(Debug, Deserialize, Serialize)]
49pub struct RespValues<'a> {
50    /// The queried node's ID
51    #[serde(with = "serde_bytes")]
52    pub id: &'a [u8],
53}
54
55impl<'a> RespValues<'a> {
56    /// Constructs a new `RespValue` based on the local node ID.
57    #[must_use]
58    #[inline]
59    pub fn new(id: &'a LocalId) -> Self {
60        Self { id: &(id.0).0 }
61    }
62
63    /// Returns the queried node's ID.
64    #[must_use]
65    #[inline]
66    pub fn id(&self) -> Option<Id> {
67        Id::try_from(self.id).ok()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    use crate::dht::krpc::{ser, Error, Msg, Ty};
76
77    #[test]
78    fn test_serde_ping_query() -> Result<(), Error> {
79        let ping_query = b"d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe";
80
81        let msg: Msg<'_> = bt_bencode::from_slice(ping_query.as_slice())?;
82        assert_eq!(msg.tx_id(), b"aa");
83        assert_eq!(msg.ty(), Ty::Query);
84        assert_eq!(msg.client_version(), None);
85        assert_eq!(msg.method_name().unwrap(), METHOD_PING);
86        assert_eq!(
87            msg.method_name_str(),
88            Some(core::str::from_utf8(METHOD_PING).unwrap())
89        );
90
91        let query_args: QueryArgs<'_> = msg.args().unwrap()?;
92        assert_eq!(query_args.id(), Some(Id::from(*b"abcdefghij0123456789")));
93
94        let ser_query_msg = ser::QueryMsg {
95            t: b"aa".as_slice(),
96            v: None,
97            q: METHOD_PING,
98            a: query_args,
99        };
100        let ser_msg = bt_bencode::to_vec(&ser_query_msg)?;
101        assert_eq!(ser_msg, ping_query);
102
103        Ok(())
104    }
105
106    #[test]
107    fn test_serde_ping_response() -> Result<(), Error> {
108        let ping_resp = b"d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re";
109
110        let msg: Msg<'_> = bt_bencode::from_slice(&ping_resp[..])?;
111        assert_eq!(msg.tx_id(), b"aa");
112        assert_eq!(msg.ty(), Ty::Response);
113        assert_eq!(msg.client_version(), None);
114
115        let resp_values: RespValues<'_> = msg.values().unwrap()?;
116        assert_eq!(resp_values.id(), Some(Id::from(*b"mnopqrstuvwxyz123456")));
117
118        let ser_resp_msg = ser::RespMsg {
119            t: b"aa".as_slice(),
120            v: None,
121            r: &resp_values,
122        };
123        let ser_msg = bt_bencode::to_vec(&ser_resp_msg)?;
124        assert_eq!(ser_msg, ping_resp);
125
126        Ok(())
127    }
128}