1use super::ipfs;
5use super::wire::{
6 base58, multiaddr_from_text, multiaddr_text, pb_bytes, pb_fields_bounded, pb_varint, unbase58,
7 PbValue,
8};
9use anyhow::{bail, ensure, Context, Result};
10
11pub const KAD: &str = "/ipfs/kad/1.0.0";
12pub const LAN_KAD: &str = "/ipfs/lan/kad/1.0.0";
13pub const PROTOCOLS: &[&str] = &[KAD, LAN_KAD];
14pub const MAX_MESSAGE: usize = 64 * 1024;
17pub const MAX_PEERS: usize = 20;
19pub const MAX_ADDRS: usize = 16;
21pub const REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(20);
23
24pub fn is_kad(protocol: &str) -> bool {
25 PROTOCOLS.contains(&protocol)
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29pub enum Kind {
30 PutValue,
31 GetValue,
32 AddProvider,
33 GetProviders,
34 FindNode,
35 Ping,
36}
37
38impl Kind {
39 pub fn code(self) -> u64 {
40 match self {
41 Kind::PutValue => 0,
42 Kind::GetValue => 1,
43 Kind::AddProvider => 2,
44 Kind::GetProviders => 3,
45 Kind::FindNode => 4,
46 Kind::Ping => 5,
47 }
48 }
49 pub fn from_code(c: u64) -> Result<Kind> {
50 Ok(match c {
51 0 => Kind::PutValue,
52 1 => Kind::GetValue,
53 2 => Kind::AddProvider,
54 3 => Kind::GetProviders,
55 4 => Kind::FindNode,
56 5 => Kind::Ping,
57 other => bail!("unknown DHT message type {other}"),
58 })
59 }
60 pub fn name(self) -> &'static str {
61 match self {
62 Kind::PutValue => "PUT_VALUE",
63 Kind::GetValue => "GET_VALUE",
64 Kind::AddProvider => "ADD_PROVIDER",
65 Kind::GetProviders => "GET_PROVIDERS",
66 Kind::FindNode => "FIND_NODE",
67 Kind::Ping => "PING",
68 }
69 }
70}
71
72#[derive(Clone, Debug, PartialEq)]
74pub struct Peer {
75 pub id: Vec<u8>,
76 pub addrs: Vec<Vec<u8>>,
77}
78
79impl Peer {
80 pub fn to_json(&self) -> serde_json::Value {
81 serde_json::json!({
82 "peer_id": base58(&self.id),
83 "addrs": self.addrs.iter().filter_map(|a| multiaddr_text(a).ok()).collect::<Vec<_>>(),
84 })
85 }
86 pub fn from_text(id: &str, addrs: &[String]) -> Result<Peer> {
88 let id = parse_peer_id(id)?;
89 ensure!(
90 addrs.len() <= MAX_ADDRS,
91 "a peer may carry at most {MAX_ADDRS} addresses"
92 );
93 let addrs = addrs
94 .iter()
95 .map(|a| multiaddr_from_text(a))
96 .collect::<Result<Vec<_>>>()?;
97 Ok(Peer { id, addrs })
98 }
99}
100
101pub fn parse_peer_id(text: &str) -> Result<Vec<u8>> {
104 let b = unbase58(text).with_context(|| format!("{text:?} is not a base58 peer id"))?;
105 check_peer_id(&b).with_context(|| format!("{text:?} is not a peer id"))?;
106 Ok(b)
107}
108
109fn check_peer_id(b: &[u8]) -> Result<()> {
110 ensure!(b.len() >= 2, "too short");
111 match b[0] {
112 0x00 => ensure!(
113 usize::from(b[1]) == b.len() - 2 && b.len() <= 44,
114 "malformed identity multihash"
115 ),
116 0x12 => ensure!(b[1] == 32 && b.len() == 34, "malformed sha2-256 multihash"),
117 other => bail!("multihash {other:#x} is not a peer id"),
118 }
119 Ok(())
120}
121
122#[derive(Clone, Debug, PartialEq)]
123pub struct Message {
124 pub kind: Kind,
125 pub key: Vec<u8>,
126 pub closer: Vec<Peer>,
127 pub providers: Vec<Peer>,
128 pub has_record: bool,
130}
131
132impl Message {
133 pub fn request(kind: Kind, key: Vec<u8>) -> Message {
134 Message {
135 kind,
136 key,
137 closer: Vec::new(),
138 providers: Vec::new(),
139 has_record: false,
140 }
141 }
142 pub fn key_text(&self) -> String {
145 base58(&self.key)
146 }
147 pub fn key_cids(&self) -> Vec<String> {
149 match ipfs::parse_multihash(&self.key) {
150 Ok((digest, n)) if n == self.key.len() => ipfs::cids_for_digest(&digest),
151 _ => Vec::new(),
152 }
153 }
154}
155
156fn decode_peer(b: &[u8]) -> Result<Peer> {
157 let mut p = Peer {
158 id: Vec::new(),
159 addrs: Vec::new(),
160 };
161 for (f, v) in pb_fields_bounded(b, MAX_ADDRS + 4)? {
162 match (f, v) {
163 (1, PbValue::Bytes(id)) => p.id = id.to_vec(),
164 (2, PbValue::Bytes(a)) => {
165 ensure!(
166 p.addrs.len() < MAX_ADDRS,
167 "a DHT peer carries more than {MAX_ADDRS} addresses"
168 );
169 p.addrs.push(a.to_vec())
170 }
171 (3, PbValue::Varint(_)) => {}
172 _ => bail!("malformed DHT peer"),
173 }
174 }
175 check_peer_id(&p.id).context("a DHT peer's id is not a peer id")?;
176 Ok(p)
177}
178
179pub fn decode(b: &[u8]) -> Result<Message> {
180 ensure!(
181 b.len() <= MAX_MESSAGE,
182 "DHT message over {MAX_MESSAGE} bytes"
183 );
184 let mut kind = None;
185 let mut m = Message::request(Kind::Ping, Vec::new());
186 for (f, v) in pb_fields_bounded(b, 2 * MAX_PEERS + 8)? {
187 match (f, v) {
188 (1, PbValue::Varint(k)) => kind = Some(Kind::from_code(k)?),
189 (2, PbValue::Bytes(k)) => {
190 ensure!(k.len() <= 256, "DHT key longer than 256 bytes");
191 m.key = k.to_vec()
192 }
193 (3, PbValue::Bytes(_)) => m.has_record = true,
194 (8, PbValue::Bytes(p)) => {
195 ensure!(
196 m.closer.len() < MAX_PEERS,
197 "more than {MAX_PEERS} closer peers"
198 );
199 m.closer.push(decode_peer(p)?)
200 }
201 (9, PbValue::Bytes(p)) => {
202 ensure!(
203 m.providers.len() < MAX_PEERS,
204 "more than {MAX_PEERS} provider peers"
205 );
206 m.providers.push(decode_peer(p)?)
207 }
208 (10, PbValue::Varint(_)) => {}
209 _ => bail!("malformed DHT message (field {f})"),
210 }
211 }
212 m.kind = kind.unwrap_or(Kind::PutValue);
213 Ok(m)
214}
215
216fn encode_peer(p: &Peer) -> Vec<u8> {
217 let mut out = Vec::new();
218 pb_bytes(&mut out, 1, &p.id);
219 for a in &p.addrs {
220 pb_bytes(&mut out, 2, a);
221 }
222 out
223}
224
225pub fn encode(m: &Message) -> Vec<u8> {
226 let mut out = Vec::new();
227 if m.kind.code() != 0 {
229 pb_varint(&mut out, 1, m.kind.code());
230 }
231 if !m.key.is_empty() {
232 pb_bytes(&mut out, 2, &m.key);
233 }
234 for p in &m.closer {
235 pb_bytes(&mut out, 8, &encode_peer(p));
236 }
237 for p in &m.providers {
238 pb_bytes(&mut out, 9, &encode_peer(p));
239 }
240 out
241}