1#![allow(
2 clippy::cast_possible_truncation,
3 clippy::cast_possible_wrap,
4 clippy::cast_sign_loss,
5 reason = "M175: BEP 5 KRPC — port/transaction-id field widths fixed by spec"
6)]
7
8use std::collections::BTreeMap;
19
20use irontide_bencode::{self as bencode, BencodeValue};
21use irontide_core::Id20;
22
23use crate::compact::{
24 CompactNodeInfo, CompactNodeInfo6, encode_compact_nodes, encode_compact_nodes6,
25 parse_compact_nodes, parse_compact_nodes6,
26};
27use crate::error::{Error, Result};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub struct TransactionId(pub [u8; 2]);
32
33impl TransactionId {
34 #[must_use]
36 pub fn from_u16(val: u16) -> Self {
37 Self(val.to_be_bytes())
38 }
39
40 #[must_use]
42 pub fn as_u16(&self) -> u16 {
43 u16::from_be_bytes(self.0)
44 }
45
46 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
52 if bytes.len() < 2 {
53 let mut buf = [0u8; 2];
55 buf[..bytes.len()].copy_from_slice(bytes);
56 return Ok(Self(buf));
57 }
58 Ok(Self([bytes[0], bytes[1]]))
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct KrpcMessage {
65 pub transaction_id: TransactionId,
67 pub body: KrpcBody,
69 pub sender_ip: Option<std::net::SocketAddr>,
71 pub read_only: bool,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum KrpcBody {
80 Query(KrpcQuery),
82 Response(KrpcResponse),
84 Error {
86 code: i64,
88 message: String,
90 },
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
95pub enum WantFamily {
96 N4,
98 N6,
100}
101
102impl WantFamily {
103 #[must_use]
105 pub fn as_bytes(&self) -> &'static [u8] {
106 match self {
107 Self::N4 => b"n4",
108 Self::N6 => b"n6",
109 }
110 }
111
112 #[must_use]
114 pub fn from_bytes(b: &[u8]) -> Option<Self> {
115 match b {
116 b"n4" => Some(Self::N4),
117 b"n6" => Some(Self::N6),
118 _ => None,
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
125pub enum KrpcQuery {
126 Ping {
128 id: Id20,
130 },
131 FindNode {
133 id: Id20,
135 target: Id20,
137 want: Option<Vec<WantFamily>>,
139 },
140 GetPeers {
142 id: Id20,
144 info_hash: Id20,
146 noseed: Option<i64>,
148 scrape: Option<i64>,
150 want: Option<Vec<WantFamily>>,
152 },
153 AnnouncePeer {
155 id: Id20,
157 info_hash: Id20,
159 port: u16,
161 implied_port: bool,
163 token: Vec<u8>,
165 },
166 Get {
168 id: Id20,
170 target: Id20,
172 seq: Option<i64>,
174 },
175 Put {
177 id: Id20,
179 token: Vec<u8>,
181 value: Vec<u8>,
183 key: Option<[u8; 32]>,
185 signature: Option<[u8; 64]>,
187 seq: Option<i64>,
189 salt: Option<Vec<u8>>,
191 cas: Option<i64>,
193 },
194 SampleInfohashes {
196 id: Id20,
198 target: Id20,
200 },
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
205pub enum KrpcResponse {
206 NodeId {
208 id: Id20,
210 },
211 FindNode {
213 id: Id20,
215 nodes: Vec<CompactNodeInfo>,
217 nodes6: Vec<CompactNodeInfo6>,
219 },
220 GetPeers(GetPeersResponse),
222 GetItem {
224 id: Id20,
226 token: Option<Vec<u8>>,
228 nodes: Vec<CompactNodeInfo>,
230 nodes6: Vec<CompactNodeInfo6>,
232 value: Option<Vec<u8>>,
234 key: Option<[u8; 32]>,
236 signature: Option<[u8; 64]>,
238 seq: Option<i64>,
240 },
241 SampleInfohashes(SampleInfohashesResponse),
243}
244
245#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct GetPeersResponse {
248 pub id: Id20,
250 pub token: Option<Vec<u8>>,
252 pub peers: Vec<std::net::SocketAddr>,
254 pub nodes: Vec<CompactNodeInfo>,
256 pub nodes6: Vec<CompactNodeInfo6>,
258 pub bfpe: Option<Vec<u8>>,
260 pub bfsd: Option<Vec<u8>>,
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
266pub struct SampleInfohashesResponse {
267 pub id: Id20,
269 pub interval: i64,
271 pub num: i64,
273 pub samples: Vec<Id20>,
275 pub nodes: Vec<CompactNodeInfo>,
277}
278
279impl KrpcQuery {
280 #[must_use]
282 pub fn method_name(&self) -> &'static str {
283 match self {
284 Self::Ping { .. } => "ping",
285 Self::FindNode { .. } => "find_node",
286 Self::GetPeers { .. } => "get_peers",
287 Self::AnnouncePeer { .. } => "announce_peer",
288 Self::Get { .. } => "get",
289 Self::Put { .. } => "put",
290 Self::SampleInfohashes { .. } => "sample_infohashes",
291 }
292 }
293
294 #[must_use]
296 pub fn sender_id(&self) -> &Id20 {
297 match self {
298 Self::Ping { id }
299 | Self::FindNode { id, .. }
300 | Self::GetPeers { id, .. }
301 | Self::AnnouncePeer { id, .. }
302 | Self::Get { id, .. }
303 | Self::Put { id, .. }
304 | Self::SampleInfohashes { id, .. } => id,
305 }
306 }
307}
308
309impl KrpcResponse {
310 #[must_use]
312 pub fn sender_id(&self) -> &Id20 {
313 match self {
314 Self::NodeId { id } | Self::FindNode { id, .. } | Self::GetItem { id, .. } => id,
315 Self::GetPeers(gp) => &gp.id,
316 Self::SampleInfohashes(si) => &si.id,
317 }
318 }
319}
320
321impl KrpcMessage {
324 pub fn to_bytes(&self) -> Result<Vec<u8>> {
330 let mut dict = BTreeMap::<Vec<u8>, BencodeValue>::new();
331 dict.insert(
332 b"t".to_vec(),
333 BencodeValue::Bytes(self.transaction_id.0.to_vec()),
334 );
335
336 if let Some(addr) = &self.sender_ip {
337 let ip_bytes = encode_compact_addr(addr);
338 dict.insert(b"ip".to_vec(), BencodeValue::Bytes(ip_bytes));
339 }
340
341 if self.read_only {
343 dict.insert(b"ro".to_vec(), BencodeValue::Integer(1));
344 }
345
346 match &self.body {
347 KrpcBody::Query(query) => {
348 dict.insert(b"y".to_vec(), BencodeValue::Bytes(b"q".to_vec()));
349 dict.insert(
350 b"q".to_vec(),
351 BencodeValue::Bytes(query.method_name().as_bytes().to_vec()),
352 );
353 dict.insert(b"a".to_vec(), encode_query_args(query));
354 }
355 KrpcBody::Response(resp) => {
356 dict.insert(b"y".to_vec(), BencodeValue::Bytes(b"r".to_vec()));
357 dict.insert(b"r".to_vec(), encode_response_values(resp));
358 }
359 KrpcBody::Error { code, message } => {
360 dict.insert(b"y".to_vec(), BencodeValue::Bytes(b"e".to_vec()));
361 dict.insert(
362 b"e".to_vec(),
363 BencodeValue::List(vec![
364 BencodeValue::Integer(*code),
365 BencodeValue::Bytes(message.as_bytes().to_vec()),
366 ]),
367 );
368 }
369 }
370
371 bencode::to_bytes(&BencodeValue::Dict(dict)).map_err(Error::from)
372 }
373
374 pub fn from_bytes(data: &[u8]) -> Result<Self> {
381 let value: BencodeValue = bencode::from_bytes(data)?;
382 let dict = value
383 .as_dict()
384 .ok_or_else(|| Error::InvalidMessage("top-level value is not a dict".into()))?;
385
386 let txn_bytes = dict_bytes(dict, b"t")?;
387 let transaction_id = TransactionId::from_bytes(txn_bytes)?;
388
389 let msg_type = dict_str(dict, b"y")?;
390 let body = match msg_type {
391 b"q" => {
392 let method = dict_str(dict, b"q")?;
393 let args = dict_dict(dict, b"a")?;
394 KrpcBody::Query(decode_query(method, args)?)
395 }
396 b"r" => {
397 let values = dict_dict(dict, b"r")?;
398 KrpcBody::Response(decode_response(values, None)?)
399 }
400 b"e" => {
401 let err_list = dict
402 .get(&b"e"[..])
403 .and_then(|v| v.as_list())
404 .ok_or_else(|| Error::InvalidMessage("missing 'e' list".into()))?;
405 if err_list.len() < 2 {
406 return Err(Error::InvalidMessage("error list too short".into()));
407 }
408 let code = err_list[0]
409 .as_int()
410 .ok_or_else(|| Error::InvalidMessage("error code not integer".into()))?;
411 let message = err_list[1]
412 .as_bytes_raw()
413 .map(|b| String::from_utf8_lossy(b).into_owned())
414 .ok_or_else(|| Error::InvalidMessage("error message not string".into()))?;
415 KrpcBody::Error { code, message }
416 }
417 other => {
418 return Err(Error::InvalidMessage(format!(
419 "unknown message type: {}",
420 String::from_utf8_lossy(other)
421 )));
422 }
423 };
424
425 let sender_ip = dict
426 .get(&b"ip"[..])
427 .and_then(|v| v.as_bytes_raw())
428 .and_then(decode_compact_addr);
429
430 let read_only = dict
432 .get(&b"ro"[..])
433 .and_then(irontide_bencode::BencodeValue::as_int)
434 .is_some_and(|i| i != 0);
435
436 Ok(Self {
437 transaction_id,
438 body,
439 sender_ip,
440 read_only,
441 })
442 }
443
444 pub fn from_bytes_with_query_hint(data: &[u8], query_method: &str) -> Result<Self> {
456 let value: BencodeValue = bencode::from_bytes(data)?;
457 let dict = value
458 .as_dict()
459 .ok_or_else(|| Error::InvalidMessage("top-level value is not a dict".into()))?;
460
461 let txn_bytes = dict_bytes(dict, b"t")?;
462 let transaction_id = TransactionId::from_bytes(txn_bytes)?;
463
464 let msg_type = dict_str(dict, b"y")?;
465 let body = match msg_type {
466 b"q" => {
467 let method = dict_str(dict, b"q")?;
468 let args = dict_dict(dict, b"a")?;
469 KrpcBody::Query(decode_query(method, args)?)
470 }
471 b"r" => {
472 let values = dict_dict(dict, b"r")?;
473 KrpcBody::Response(decode_response(values, Some(query_method))?)
474 }
475 b"e" => {
476 let err_list = dict
477 .get(&b"e"[..])
478 .and_then(|v| v.as_list())
479 .ok_or_else(|| Error::InvalidMessage("missing 'e' list".into()))?;
480 if err_list.len() < 2 {
481 return Err(Error::InvalidMessage("error list too short".into()));
482 }
483 let code = err_list[0]
484 .as_int()
485 .ok_or_else(|| Error::InvalidMessage("error code not integer".into()))?;
486 let message = err_list[1]
487 .as_bytes_raw()
488 .map(|b| String::from_utf8_lossy(b).into_owned())
489 .ok_or_else(|| Error::InvalidMessage("error message not string".into()))?;
490 KrpcBody::Error { code, message }
491 }
492 other => {
493 return Err(Error::InvalidMessage(format!(
494 "unknown message type: {}",
495 String::from_utf8_lossy(other)
496 )));
497 }
498 };
499
500 let sender_ip = dict
501 .get(&b"ip"[..])
502 .and_then(|v| v.as_bytes_raw())
503 .and_then(decode_compact_addr);
504
505 let read_only = dict
507 .get(&b"ro"[..])
508 .and_then(irontide_bencode::BencodeValue::as_int)
509 .is_some_and(|i| i != 0);
510
511 Ok(Self {
512 transaction_id,
513 body,
514 sender_ip,
515 read_only,
516 })
517 }
518}
519
520fn encode_query_args(query: &KrpcQuery) -> BencodeValue {
523 let mut args = BTreeMap::<Vec<u8>, BencodeValue>::new();
524 match query {
525 KrpcQuery::Ping { id } => {
526 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
527 }
528 KrpcQuery::FindNode { id, target, want } => {
529 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
530 args.insert(b"target".to_vec(), BencodeValue::Bytes(target.0.to_vec()));
531 if let Some(w) = want {
532 encode_want(&mut args, w);
533 }
534 }
535 KrpcQuery::SampleInfohashes { id, target } => {
536 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
537 args.insert(b"target".to_vec(), BencodeValue::Bytes(target.0.to_vec()));
538 }
539 KrpcQuery::GetPeers {
540 id,
541 info_hash,
542 noseed,
543 scrape,
544 want,
545 } => {
546 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
547 args.insert(
548 b"info_hash".to_vec(),
549 BencodeValue::Bytes(info_hash.0.to_vec()),
550 );
551 if let Some(ns) = noseed {
552 args.insert(b"noseed".to_vec(), BencodeValue::Integer(*ns));
553 }
554 if let Some(sc) = scrape {
555 args.insert(b"scrape".to_vec(), BencodeValue::Integer(*sc));
556 }
557 if let Some(w) = want {
558 encode_want(&mut args, w);
559 }
560 }
561 KrpcQuery::AnnouncePeer {
562 id,
563 info_hash,
564 port,
565 implied_port,
566 token,
567 } => {
568 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
569 if *implied_port {
570 args.insert(b"implied_port".to_vec(), BencodeValue::Integer(1));
571 }
572 args.insert(
573 b"info_hash".to_vec(),
574 BencodeValue::Bytes(info_hash.0.to_vec()),
575 );
576 args.insert(b"port".to_vec(), BencodeValue::Integer(i64::from(*port)));
577 args.insert(b"token".to_vec(), BencodeValue::Bytes(token.clone()));
578 }
579 KrpcQuery::Get { id, target, seq } => {
580 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
581 if let Some(seq) = seq {
582 args.insert(b"seq".to_vec(), BencodeValue::Integer(*seq));
583 }
584 args.insert(b"target".to_vec(), BencodeValue::Bytes(target.0.to_vec()));
585 }
586 KrpcQuery::Put {
587 id,
588 token,
589 value,
590 key,
591 signature,
592 seq,
593 salt,
594 cas,
595 } => {
596 args.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
597 if let Some(cas) = cas {
598 args.insert(b"cas".to_vec(), BencodeValue::Integer(*cas));
599 }
600 if let Some(key) = key {
601 args.insert(b"k".to_vec(), BencodeValue::Bytes(key.to_vec()));
602 }
603 if let Some(salt) = salt
604 && !salt.is_empty()
605 {
606 args.insert(b"salt".to_vec(), BencodeValue::Bytes(salt.clone()));
607 }
608 if let Some(seq) = seq {
609 args.insert(b"seq".to_vec(), BencodeValue::Integer(*seq));
610 }
611 if let Some(sig) = signature {
612 args.insert(b"sig".to_vec(), BencodeValue::Bytes(sig.to_vec()));
613 }
614 args.insert(b"token".to_vec(), BencodeValue::Bytes(token.clone()));
615 args.insert(b"v".to_vec(), BencodeValue::Bytes(value.clone()));
616 }
617 }
618 BencodeValue::Dict(args)
619}
620
621fn encode_response_values(resp: &KrpcResponse) -> BencodeValue {
622 let mut values = BTreeMap::<Vec<u8>, BencodeValue>::new();
623 match resp {
624 KrpcResponse::NodeId { id } => {
625 values.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
626 }
627 KrpcResponse::FindNode { id, nodes, nodes6 } => {
628 values.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
629 values.insert(
630 b"nodes".to_vec(),
631 BencodeValue::Bytes(encode_compact_nodes(nodes)),
632 );
633 if !nodes6.is_empty() {
634 values.insert(
635 b"nodes6".to_vec(),
636 BencodeValue::Bytes(encode_compact_nodes6(nodes6)),
637 );
638 }
639 }
640 KrpcResponse::GetPeers(gp) => {
641 values.insert(b"id".to_vec(), BencodeValue::Bytes(gp.id.0.to_vec()));
642 if let Some(token) = &gp.token {
643 values.insert(b"token".to_vec(), BencodeValue::Bytes(token.clone()));
644 }
645 if !gp.peers.is_empty() {
646 let peer_list: Vec<BencodeValue> = gp
647 .peers
648 .iter()
649 .map(|addr| match addr {
650 SocketAddr::V4(v4) => {
651 let mut buf = [0u8; 6];
652 buf[..4].copy_from_slice(&v4.ip().octets());
653 buf[4..6].copy_from_slice(&v4.port().to_be_bytes());
654 BencodeValue::Bytes(buf.to_vec())
655 }
656 SocketAddr::V6(v6) => {
657 let mut buf = [0u8; 18];
658 buf[..16].copy_from_slice(&v6.ip().octets());
659 buf[16..18].copy_from_slice(&v6.port().to_be_bytes());
660 BencodeValue::Bytes(buf.to_vec())
661 }
662 })
663 .collect();
664 values.insert(b"values".to_vec(), BencodeValue::List(peer_list));
665 }
666 if !gp.nodes.is_empty() {
667 values.insert(
668 b"nodes".to_vec(),
669 BencodeValue::Bytes(encode_compact_nodes(&gp.nodes)),
670 );
671 }
672 if !gp.nodes6.is_empty() {
673 values.insert(
674 b"nodes6".to_vec(),
675 BencodeValue::Bytes(encode_compact_nodes6(&gp.nodes6)),
676 );
677 }
678 if let Some(ref bfpe) = gp.bfpe {
679 values.insert(b"BFpe".to_vec(), BencodeValue::Bytes(bfpe.clone()));
680 }
681 if let Some(ref bfsd) = gp.bfsd {
682 values.insert(b"BFsd".to_vec(), BencodeValue::Bytes(bfsd.clone()));
683 }
684 }
685 KrpcResponse::GetItem {
686 id,
687 token,
688 nodes,
689 nodes6,
690 value,
691 key,
692 signature,
693 seq,
694 } => {
695 values.insert(b"id".to_vec(), BencodeValue::Bytes(id.0.to_vec()));
696 if let Some(key) = key {
697 values.insert(b"k".to_vec(), BencodeValue::Bytes(key.to_vec()));
698 }
699 if !nodes.is_empty() {
700 values.insert(
701 b"nodes".to_vec(),
702 BencodeValue::Bytes(encode_compact_nodes(nodes)),
703 );
704 }
705 if !nodes6.is_empty() {
706 values.insert(
707 b"nodes6".to_vec(),
708 BencodeValue::Bytes(encode_compact_nodes6(nodes6)),
709 );
710 }
711 if let Some(seq) = seq {
712 values.insert(b"seq".to_vec(), BencodeValue::Integer(*seq));
713 }
714 if let Some(sig) = signature {
715 values.insert(b"sig".to_vec(), BencodeValue::Bytes(sig.to_vec()));
716 }
717 if let Some(token) = token {
718 values.insert(b"token".to_vec(), BencodeValue::Bytes(token.clone()));
719 }
720 if let Some(v) = value {
721 values.insert(b"v".to_vec(), BencodeValue::Bytes(v.clone()));
722 }
723 }
724 KrpcResponse::SampleInfohashes(si) => {
725 values.insert(b"id".to_vec(), BencodeValue::Bytes(si.id.0.to_vec()));
726 values.insert(b"interval".to_vec(), BencodeValue::Integer(si.interval));
727 if !si.nodes.is_empty() {
728 values.insert(
729 b"nodes".to_vec(),
730 BencodeValue::Bytes(encode_compact_nodes(&si.nodes)),
731 );
732 }
733 values.insert(b"num".to_vec(), BencodeValue::Integer(si.num));
734 let mut samples_buf = Vec::with_capacity(si.samples.len() * 20);
736 for hash in &si.samples {
737 samples_buf.extend_from_slice(hash.as_bytes());
738 }
739 values.insert(b"samples".to_vec(), BencodeValue::Bytes(samples_buf));
740 }
741 }
742 BencodeValue::Dict(values)
743}
744
745fn encode_want(args: &mut BTreeMap<Vec<u8>, BencodeValue>, want: &[WantFamily]) {
748 let list: Vec<BencodeValue> = want
749 .iter()
750 .map(|w| BencodeValue::Bytes(w.as_bytes().to_vec()))
751 .collect();
752 args.insert(b"want".to_vec(), BencodeValue::List(list));
753}
754
755fn decode_want(args: &BTreeMap<Vec<u8>, BencodeValue>) -> Option<Vec<WantFamily>> {
756 let list = args.get(&b"want"[..])?.as_list()?;
757 let families: Vec<WantFamily> = list
758 .iter()
759 .filter_map(|v| v.as_bytes_raw().and_then(WantFamily::from_bytes))
760 .collect();
761 if families.is_empty() {
762 None
763 } else {
764 Some(families)
765 }
766}
767
768use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
771
772fn encode_compact_addr(addr: &SocketAddr) -> Vec<u8> {
774 match addr {
775 SocketAddr::V4(v4) => {
776 let mut buf = Vec::with_capacity(6);
777 buf.extend_from_slice(&v4.ip().octets());
778 buf.extend_from_slice(&v4.port().to_be_bytes());
779 buf
780 }
781 SocketAddr::V6(v6) => {
782 let mut buf = Vec::with_capacity(18);
783 buf.extend_from_slice(&v6.ip().octets());
784 buf.extend_from_slice(&v6.port().to_be_bytes());
785 buf
786 }
787 }
788}
789
790fn decode_compact_addr(data: &[u8]) -> Option<SocketAddr> {
792 match data.len() {
793 6 => {
794 let ip = Ipv4Addr::new(data[0], data[1], data[2], data[3]);
795 let port = u16::from_be_bytes([data[4], data[5]]);
796 Some(SocketAddr::V4(SocketAddrV4::new(ip, port)))
797 }
798 18 => {
799 let ip = Ipv6Addr::from(<[u8; 16]>::try_from(&data[..16]).unwrap());
800 let port = u16::from_be_bytes([data[16], data[17]]);
801 Some(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)))
802 }
803 _ => None,
804 }
805}
806
807fn decode_query(method: &[u8], args: &BTreeMap<Vec<u8>, BencodeValue>) -> Result<KrpcQuery> {
810 let id = args_id20(args, b"id")?;
811 match method {
812 b"ping" => Ok(KrpcQuery::Ping { id }),
813 b"find_node" => {
814 let target = args_id20(args, b"target")?;
815 let want = decode_want(args);
816 Ok(KrpcQuery::FindNode { id, target, want })
817 }
818 b"get_peers" => {
819 let info_hash = args_id20(args, b"info_hash")?;
820 let noseed = args
821 .get(&b"noseed"[..])
822 .and_then(irontide_bencode::BencodeValue::as_int);
823 let scrape = args
824 .get(&b"scrape"[..])
825 .and_then(irontide_bencode::BencodeValue::as_int);
826 let want = decode_want(args);
827 Ok(KrpcQuery::GetPeers {
828 id,
829 info_hash,
830 noseed,
831 scrape,
832 want,
833 })
834 }
835 b"announce_peer" => {
836 let info_hash = args_id20(args, b"info_hash")?;
837 let implied_port = args
838 .get(&b"implied_port"[..])
839 .and_then(irontide_bencode::BencodeValue::as_int)
840 .unwrap_or(0)
841 != 0;
842 let port = if implied_port {
854 args.get(&b"port"[..])
855 .and_then(irontide_bencode::BencodeValue::as_int)
856 .and_then(|p| u16::try_from(p).ok())
857 .unwrap_or(0)
858 } else {
859 let port_raw = args_int(args, b"port")?;
860 let port = u16::try_from(port_raw).map_err(|_| {
861 Error::InvalidMessage(format!(
862 "announce_peer 'port' {port_raw} is outside the valid range 0..=65535"
863 ))
864 })?;
865 if port == 0 {
866 return Err(Error::InvalidMessage(
867 "announce_peer 'port' must not be 0 when implied_port is unset".into(),
868 ));
869 }
870 port
871 };
872 let token = args
873 .get(&b"token"[..])
874 .and_then(|v| v.as_bytes_raw())
875 .map(<[u8]>::to_vec)
876 .ok_or_else(|| Error::InvalidMessage("missing 'token' in announce_peer".into()))?;
877 Ok(KrpcQuery::AnnouncePeer {
878 id,
879 info_hash,
880 port,
881 implied_port,
882 token,
883 })
884 }
885 b"get" => {
886 let target = args_id20(args, b"target")?;
887 let seq = args
888 .get(&b"seq"[..])
889 .and_then(irontide_bencode::BencodeValue::as_int);
890 Ok(KrpcQuery::Get { id, target, seq })
891 }
892 b"put" => {
893 let token = args
894 .get(&b"token"[..])
895 .and_then(|v| v.as_bytes_raw())
896 .map(<[u8]>::to_vec)
897 .ok_or_else(|| Error::InvalidMessage("missing 'token' in put".into()))?;
898 let value = args
899 .get(&b"v"[..])
900 .and_then(|v| v.as_bytes_raw())
901 .map(<[u8]>::to_vec)
902 .ok_or_else(|| Error::InvalidMessage("missing 'v' in put".into()))?;
903 let key = args
904 .get(&b"k"[..])
905 .and_then(|v| v.as_bytes_raw())
906 .and_then(|b| <[u8; 32]>::try_from(b).ok());
907 let signature = args
908 .get(&b"sig"[..])
909 .and_then(|v| v.as_bytes_raw())
910 .and_then(|b| <[u8; 64]>::try_from(b).ok());
911 let seq = args
912 .get(&b"seq"[..])
913 .and_then(irontide_bencode::BencodeValue::as_int);
914 let salt = args
915 .get(&b"salt"[..])
916 .and_then(|v| v.as_bytes_raw())
917 .map(<[u8]>::to_vec);
918 let cas = args
919 .get(&b"cas"[..])
920 .and_then(irontide_bencode::BencodeValue::as_int);
921 Ok(KrpcQuery::Put {
922 id,
923 token,
924 value,
925 key,
926 signature,
927 seq,
928 salt,
929 cas,
930 })
931 }
932 b"sample_infohashes" => {
933 let target = args_id20(args, b"target")?;
934 Ok(KrpcQuery::SampleInfohashes { id, target })
935 }
936 _ => Err(Error::InvalidMessage(format!(
937 "unknown query method: {}",
938 String::from_utf8_lossy(method)
939 ))),
940 }
941}
942
943fn decode_response(
951 values: &BTreeMap<Vec<u8>, BencodeValue>,
952 query_method: Option<&str>,
953) -> Result<KrpcResponse> {
954 let id = args_id20(values, b"id")?;
955
956 if query_method == Some("get") {
959 return decode_get_item_response(id, values);
960 }
961
962 let has_samples = values.contains_key(&b"samples"[..]);
964 let has_interval = values.contains_key(&b"interval"[..]);
965
966 if has_samples && has_interval {
967 let interval = values
968 .get(&b"interval"[..])
969 .and_then(irontide_bencode::BencodeValue::as_int)
970 .unwrap_or(0);
971 let num = values
972 .get(&b"num"[..])
973 .and_then(irontide_bencode::BencodeValue::as_int)
974 .unwrap_or(0);
975
976 let samples_bytes = values
977 .get(&b"samples"[..])
978 .and_then(|v| v.as_bytes_raw())
979 .unwrap_or(&[]);
980 let mut samples = Vec::new();
981 if samples_bytes.len().is_multiple_of(20) {
982 for chunk in samples_bytes.chunks_exact(20) {
983 if let Ok(hash) = Id20::from_bytes(chunk) {
984 samples.push(hash);
985 }
986 }
987 }
988
989 let nodes =
990 if let Some(nodes_bytes) = values.get(&b"nodes"[..]).and_then(|v| v.as_bytes_raw()) {
991 parse_compact_nodes(nodes_bytes)?
992 } else {
993 Vec::new()
994 };
995
996 return Ok(KrpcResponse::SampleInfohashes(SampleInfohashesResponse {
997 id,
998 interval,
999 num,
1000 samples,
1001 nodes,
1002 }));
1003 }
1004
1005 let has_values = values.contains_key(&b"values"[..]);
1008 let has_v = values.contains_key(&b"v"[..]);
1009 let has_k = values.contains_key(&b"k"[..]);
1010 let has_sig = values.contains_key(&b"sig"[..]);
1011 let has_seq = values.contains_key(&b"seq"[..]);
1012
1013 if has_k || has_sig || (has_v && !has_values) || (has_seq && !has_values) {
1014 return decode_get_item_response(id, values);
1015 }
1016
1017 let has_token = values.contains_key(&b"token"[..]);
1019
1020 if has_values || has_token {
1021 let token = values
1022 .get(&b"token"[..])
1023 .and_then(|v| v.as_bytes_raw())
1024 .map(<[u8]>::to_vec);
1025
1026 let mut peers = Vec::new();
1027 if let Some(BencodeValue::List(peer_list)) = values.get(&b"values"[..]) {
1028 for item in peer_list {
1029 if let Some(data) = item.as_bytes_raw() {
1030 match data.len() {
1031 6 => {
1032 let ip = Ipv4Addr::new(data[0], data[1], data[2], data[3]);
1033 let port = u16::from_be_bytes([data[4], data[5]]);
1034 peers.push(SocketAddr::V4(SocketAddrV4::new(ip, port)));
1035 }
1036 18 => {
1037 let ip = Ipv6Addr::from(<[u8; 16]>::try_from(&data[..16]).unwrap());
1038 let port = u16::from_be_bytes([data[16], data[17]]);
1039 peers.push(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)));
1040 }
1041 _ => {} }
1043 }
1044 }
1045 }
1046
1047 let nodes =
1048 if let Some(nodes_bytes) = values.get(&b"nodes"[..]).and_then(|v| v.as_bytes_raw()) {
1049 parse_compact_nodes(nodes_bytes)?
1050 } else {
1051 Vec::new()
1052 };
1053
1054 let nodes6 =
1055 if let Some(nodes6_bytes) = values.get(&b"nodes6"[..]).and_then(|v| v.as_bytes_raw()) {
1056 parse_compact_nodes6(nodes6_bytes)?
1057 } else {
1058 Vec::new()
1059 };
1060
1061 let bfpe = values
1062 .get(&b"BFpe"[..])
1063 .and_then(|v| v.as_bytes_raw())
1064 .map(<[u8]>::to_vec);
1065 let bfsd = values
1066 .get(&b"BFsd"[..])
1067 .and_then(|v| v.as_bytes_raw())
1068 .map(<[u8]>::to_vec);
1069
1070 return Ok(KrpcResponse::GetPeers(GetPeersResponse {
1071 id,
1072 token,
1073 peers,
1074 nodes,
1075 nodes6,
1076 bfpe,
1077 bfsd,
1078 }));
1079 }
1080
1081 let has_nodes = values.contains_key(&b"nodes"[..]);
1083 let has_nodes6 = values.contains_key(&b"nodes6"[..]);
1084
1085 if has_nodes || has_nodes6 {
1086 let nodes =
1087 if let Some(nodes_bytes) = values.get(&b"nodes"[..]).and_then(|v| v.as_bytes_raw()) {
1088 parse_compact_nodes(nodes_bytes)?
1089 } else {
1090 Vec::new()
1091 };
1092
1093 let nodes6 =
1094 if let Some(nodes6_bytes) = values.get(&b"nodes6"[..]).and_then(|v| v.as_bytes_raw()) {
1095 parse_compact_nodes6(nodes6_bytes)?
1096 } else {
1097 Vec::new()
1098 };
1099
1100 return Ok(KrpcResponse::FindNode { id, nodes, nodes6 });
1101 }
1102
1103 Ok(KrpcResponse::NodeId { id })
1105}
1106
1107fn decode_get_item_response(
1109 id: Id20,
1110 values: &BTreeMap<Vec<u8>, BencodeValue>,
1111) -> Result<KrpcResponse> {
1112 let token = values
1113 .get(&b"token"[..])
1114 .and_then(|v| v.as_bytes_raw())
1115 .map(<[u8]>::to_vec);
1116
1117 let nodes = if let Some(nodes_bytes) = values.get(&b"nodes"[..]).and_then(|v| v.as_bytes_raw())
1118 {
1119 parse_compact_nodes(nodes_bytes)?
1120 } else {
1121 Vec::new()
1122 };
1123
1124 let nodes6 =
1125 if let Some(nodes6_bytes) = values.get(&b"nodes6"[..]).and_then(|v| v.as_bytes_raw()) {
1126 parse_compact_nodes6(nodes6_bytes)?
1127 } else {
1128 Vec::new()
1129 };
1130
1131 let value = values
1132 .get(&b"v"[..])
1133 .and_then(|v| v.as_bytes_raw())
1134 .map(<[u8]>::to_vec);
1135
1136 let key = values
1137 .get(&b"k"[..])
1138 .and_then(|v| v.as_bytes_raw())
1139 .and_then(|b| <[u8; 32]>::try_from(b).ok());
1140
1141 let signature = values
1142 .get(&b"sig"[..])
1143 .and_then(|v| v.as_bytes_raw())
1144 .and_then(|b| <[u8; 64]>::try_from(b).ok());
1145
1146 let seq = values
1147 .get(&b"seq"[..])
1148 .and_then(irontide_bencode::BencodeValue::as_int);
1149
1150 Ok(KrpcResponse::GetItem {
1151 id,
1152 token,
1153 nodes,
1154 nodes6,
1155 value,
1156 key,
1157 signature,
1158 seq,
1159 })
1160}
1161
1162fn dict_bytes<'a>(dict: &'a BTreeMap<Vec<u8>, BencodeValue>, key: &[u8]) -> Result<&'a [u8]> {
1165 dict.get(key).and_then(|v| v.as_bytes_raw()).ok_or_else(|| {
1166 Error::InvalidMessage(format!(
1167 "missing or invalid key '{}'",
1168 String::from_utf8_lossy(key)
1169 ))
1170 })
1171}
1172
1173fn dict_str<'a>(dict: &'a BTreeMap<Vec<u8>, BencodeValue>, key: &[u8]) -> Result<&'a [u8]> {
1174 dict_bytes(dict, key)
1175}
1176
1177fn dict_dict<'a>(
1178 dict: &'a BTreeMap<Vec<u8>, BencodeValue>,
1179 key: &[u8],
1180) -> Result<&'a BTreeMap<Vec<u8>, BencodeValue>> {
1181 dict.get(key).and_then(|v| v.as_dict()).ok_or_else(|| {
1182 Error::InvalidMessage(format!(
1183 "missing or invalid dict key '{}'",
1184 String::from_utf8_lossy(key)
1185 ))
1186 })
1187}
1188
1189fn args_id20(args: &BTreeMap<Vec<u8>, BencodeValue>, key: &[u8]) -> Result<Id20> {
1190 let bytes = args
1191 .get(key)
1192 .and_then(|v| v.as_bytes_raw())
1193 .ok_or_else(|| {
1194 Error::InvalidMessage(format!(
1195 "missing '{}' in args",
1196 String::from_utf8_lossy(key)
1197 ))
1198 })?;
1199 Id20::from_bytes(bytes).map_err(|e| Error::InvalidMessage(e.to_string()))
1200}
1201
1202fn args_int(args: &BTreeMap<Vec<u8>, BencodeValue>, key: &[u8]) -> Result<i64> {
1203 args.get(key)
1204 .and_then(irontide_bencode::BencodeValue::as_int)
1205 .ok_or_else(|| {
1206 Error::InvalidMessage(format!(
1207 "missing '{}' integer in args",
1208 String::from_utf8_lossy(key)
1209 ))
1210 })
1211}
1212
1213#[cfg(test)]
1214mod tests {
1215 use super::*;
1216 use pretty_assertions::assert_eq;
1217
1218 fn test_id() -> Id20 {
1219 Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap()
1220 }
1221
1222 fn target_id() -> Id20 {
1223 Id20::from_hex("0000000000000000000000000000000000000001").unwrap()
1224 }
1225
1226 #[test]
1227 fn ping_query_round_trip() {
1228 let msg = KrpcMessage {
1229 transaction_id: TransactionId::from_u16(42),
1230 body: KrpcBody::Query(KrpcQuery::Ping { id: test_id() }),
1231 sender_ip: None,
1232 read_only: false,
1233 };
1234 let bytes = msg.to_bytes().unwrap();
1235 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1236 assert_eq!(msg, decoded);
1237 }
1238
1239 #[test]
1240 fn ping_response_round_trip() {
1241 let msg = KrpcMessage {
1242 transaction_id: TransactionId::from_u16(42),
1243 body: KrpcBody::Response(KrpcResponse::NodeId { id: test_id() }),
1244 sender_ip: None,
1245 read_only: false,
1246 };
1247 let bytes = msg.to_bytes().unwrap();
1248 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1249 assert_eq!(msg, decoded);
1250 }
1251
1252 #[test]
1253 fn find_node_query_round_trip() {
1254 let msg = KrpcMessage {
1255 transaction_id: TransactionId::from_u16(100),
1256 body: KrpcBody::Query(KrpcQuery::FindNode {
1257 id: test_id(),
1258 target: target_id(),
1259 want: None,
1260 }),
1261 sender_ip: None,
1262 read_only: false,
1263 };
1264 let bytes = msg.to_bytes().unwrap();
1265 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1266 assert_eq!(msg, decoded);
1267 }
1268
1269 #[test]
1270 fn find_node_response_round_trip() {
1271 let nodes = vec![CompactNodeInfo {
1272 id: target_id(),
1273 addr: "10.0.0.1:6881".parse().unwrap(),
1274 }];
1275 let msg = KrpcMessage {
1276 transaction_id: TransactionId::from_u16(100),
1277 body: KrpcBody::Response(KrpcResponse::FindNode {
1278 id: test_id(),
1279 nodes,
1280 nodes6: Vec::new(),
1281 }),
1282 sender_ip: None,
1283 read_only: false,
1284 };
1285 let bytes = msg.to_bytes().unwrap();
1286 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1287 assert_eq!(msg, decoded);
1288 }
1289
1290 #[test]
1291 fn get_peers_query_round_trip() {
1292 let msg = KrpcMessage {
1293 transaction_id: TransactionId::from_u16(200),
1294 body: KrpcBody::Query(KrpcQuery::GetPeers {
1295 id: test_id(),
1296 info_hash: target_id(),
1297 noseed: None,
1298 scrape: None,
1299 want: None,
1300 }),
1301 sender_ip: None,
1302 read_only: false,
1303 };
1304 let bytes = msg.to_bytes().unwrap();
1305 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1306 assert_eq!(msg, decoded);
1307 }
1308
1309 #[test]
1310 fn get_peers_response_with_peers_round_trip() {
1311 let msg = KrpcMessage {
1312 transaction_id: TransactionId::from_u16(200),
1313 body: KrpcBody::Response(KrpcResponse::GetPeers(GetPeersResponse {
1314 id: test_id(),
1315 token: Some(b"aoeusnth".to_vec()),
1316 peers: vec!["192.168.1.1:6881".parse().unwrap()],
1317 nodes: Vec::new(),
1318 nodes6: Vec::new(),
1319 bfpe: None,
1320 bfsd: None,
1321 })),
1322 sender_ip: None,
1323 read_only: false,
1324 };
1325 let bytes = msg.to_bytes().unwrap();
1326 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1327 assert_eq!(msg, decoded);
1328 }
1329
1330 #[test]
1331 fn get_peers_response_with_nodes_round_trip() {
1332 let msg = KrpcMessage {
1333 transaction_id: TransactionId::from_u16(200),
1334 body: KrpcBody::Response(KrpcResponse::GetPeers(GetPeersResponse {
1335 id: test_id(),
1336 token: Some(b"token123".to_vec()),
1337 peers: Vec::new(),
1338 nodes: vec![CompactNodeInfo {
1339 id: target_id(),
1340 addr: "10.0.0.1:6881".parse().unwrap(),
1341 }],
1342 nodes6: Vec::new(),
1343 bfpe: None,
1344 bfsd: None,
1345 })),
1346 sender_ip: None,
1347 read_only: false,
1348 };
1349 let bytes = msg.to_bytes().unwrap();
1350 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1351 assert_eq!(msg, decoded);
1352 }
1353
1354 #[test]
1355 fn announce_peer_query_round_trip() {
1356 let msg = KrpcMessage {
1357 transaction_id: TransactionId::from_u16(300),
1358 body: KrpcBody::Query(KrpcQuery::AnnouncePeer {
1359 id: test_id(),
1360 info_hash: target_id(),
1361 port: 6881,
1362 implied_port: true,
1363 token: b"aoeusnth".to_vec(),
1364 }),
1365 sender_ip: None,
1366 read_only: false,
1367 };
1368 let bytes = msg.to_bytes().unwrap();
1369 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1370 assert_eq!(msg, decoded);
1371 }
1372
1373 #[test]
1374 fn error_message_round_trip() {
1375 let msg = KrpcMessage {
1376 transaction_id: TransactionId::from_u16(500),
1377 body: KrpcBody::Error {
1378 code: 201,
1379 message: "A Generic Error Occurred".into(),
1380 },
1381 sender_ip: None,
1382 read_only: false,
1383 };
1384 let bytes = msg.to_bytes().unwrap();
1385 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1386 assert_eq!(msg, decoded);
1387 }
1388
1389 #[test]
1390 fn decode_bep5_ping_example() {
1391 let data = b"d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe";
1394 let msg = KrpcMessage::from_bytes(data).unwrap();
1395 assert_eq!(msg.transaction_id.0, *b"aa");
1396 match &msg.body {
1397 KrpcBody::Query(KrpcQuery::Ping { id }) => {
1398 assert_eq!(id.as_bytes(), b"abcdefghij0123456789");
1399 }
1400 other => panic!("expected Ping query, got {other:?}"),
1401 }
1402 }
1403
1404 #[test]
1405 fn decode_bep5_error_example() {
1406 let data = b"d1:eli201e24:A Generic Error Occurrede1:t2:aa1:y1:ee";
1408 let msg = KrpcMessage::from_bytes(data).unwrap();
1409 assert_eq!(msg.transaction_id.0, *b"aa");
1410 match &msg.body {
1411 KrpcBody::Error { code, message } => {
1412 assert_eq!(*code, 201);
1413 assert_eq!(message, "A Generic Error Occurred");
1414 }
1415 other => panic!("expected Error, got {other:?}"),
1416 }
1417 }
1418
1419 #[test]
1420 fn transaction_id_from_single_byte() {
1421 let tid = TransactionId::from_bytes(&[0x42]).unwrap();
1422 assert_eq!(tid.0, [0x42, 0x00]);
1423 }
1424
1425 #[test]
1426 fn query_method_names() {
1427 assert_eq!(KrpcQuery::Ping { id: Id20::ZERO }.method_name(), "ping");
1428 assert_eq!(
1429 KrpcQuery::FindNode {
1430 id: Id20::ZERO,
1431 target: Id20::ZERO,
1432 want: None,
1433 }
1434 .method_name(),
1435 "find_node"
1436 );
1437 }
1438
1439 #[test]
1442 fn find_node_response_with_nodes6_round_trip() {
1443 let nodes = vec![CompactNodeInfo {
1444 id: target_id(),
1445 addr: "10.0.0.1:6881".parse().unwrap(),
1446 }];
1447 let nodes6 = vec![CompactNodeInfo6 {
1448 id: target_id(),
1449 addr: "[2001:db8::1]:6881".parse().unwrap(),
1450 }];
1451 let msg = KrpcMessage {
1452 transaction_id: TransactionId::from_u16(100),
1453 body: KrpcBody::Response(KrpcResponse::FindNode {
1454 id: test_id(),
1455 nodes,
1456 nodes6,
1457 }),
1458 sender_ip: None,
1459 read_only: false,
1460 };
1461 let bytes = msg.to_bytes().unwrap();
1462 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1463 assert_eq!(msg, decoded);
1464 }
1465
1466 #[test]
1467 fn get_peers_response_with_nodes6_round_trip() {
1468 let nodes6 = vec![CompactNodeInfo6 {
1469 id: target_id(),
1470 addr: "[::1]:8080".parse().unwrap(),
1471 }];
1472 let msg = KrpcMessage {
1473 transaction_id: TransactionId::from_u16(200),
1474 body: KrpcBody::Response(KrpcResponse::GetPeers(GetPeersResponse {
1475 id: test_id(),
1476 token: Some(b"tok".to_vec()),
1477 peers: Vec::new(),
1478 nodes: Vec::new(),
1479 nodes6,
1480 bfpe: None,
1481 bfsd: None,
1482 })),
1483 sender_ip: None,
1484 read_only: false,
1485 };
1486 let bytes = msg.to_bytes().unwrap();
1487 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1488 assert_eq!(msg, decoded);
1489 }
1490
1491 #[test]
1492 fn get_peers_response_with_ipv6_peer_values() {
1493 let msg = KrpcMessage {
1494 transaction_id: TransactionId::from_u16(200),
1495 body: KrpcBody::Response(KrpcResponse::GetPeers(GetPeersResponse {
1496 id: test_id(),
1497 token: Some(b"tok".to_vec()),
1498 peers: vec![
1499 "192.168.1.1:6881".parse().unwrap(),
1500 "[2001:db8::1]:8080".parse().unwrap(),
1501 ],
1502 nodes: Vec::new(),
1503 nodes6: Vec::new(),
1504 bfpe: None,
1505 bfsd: None,
1506 })),
1507 sender_ip: None,
1508 read_only: false,
1509 };
1510 let bytes = msg.to_bytes().unwrap();
1511 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1512 assert_eq!(msg, decoded);
1513 }
1514
1515 #[test]
1518 fn response_with_ip_field_round_trip() {
1519 let msg = KrpcMessage {
1520 transaction_id: TransactionId::from_u16(42),
1521 body: KrpcBody::Response(KrpcResponse::NodeId { id: test_id() }),
1522 sender_ip: Some("203.0.113.5:6881".parse().unwrap()),
1523 read_only: false,
1524 };
1525 let bytes = msg.to_bytes().unwrap();
1526 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1527 assert_eq!(decoded.sender_ip, Some("203.0.113.5:6881".parse().unwrap()));
1528 }
1529
1530 #[test]
1531 fn response_with_ipv6_ip_field_round_trip() {
1532 let msg = KrpcMessage {
1533 transaction_id: TransactionId::from_u16(42),
1534 body: KrpcBody::Response(KrpcResponse::NodeId { id: test_id() }),
1535 sender_ip: Some("[2001:db8::1]:6881".parse().unwrap()),
1536 read_only: false,
1537 };
1538 let bytes = msg.to_bytes().unwrap();
1539 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1540 assert_eq!(
1541 decoded.sender_ip,
1542 Some("[2001:db8::1]:6881".parse().unwrap())
1543 );
1544 }
1545
1546 #[test]
1547 fn message_without_ip_field_parses_as_none() {
1548 let msg = KrpcMessage {
1549 transaction_id: TransactionId::from_u16(42),
1550 body: KrpcBody::Response(KrpcResponse::NodeId { id: test_id() }),
1551 sender_ip: None,
1552 read_only: false,
1553 };
1554 let bytes = msg.to_bytes().unwrap();
1555 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1556 assert!(decoded.sender_ip.is_none());
1557 }
1558
1559 #[test]
1562 fn get_immutable_query_round_trip() {
1563 let msg = KrpcMessage {
1564 transaction_id: TransactionId::from_u16(400),
1565 body: KrpcBody::Query(KrpcQuery::Get {
1566 id: test_id(),
1567 target: target_id(),
1568 seq: None,
1569 }),
1570 sender_ip: None,
1571 read_only: false,
1572 };
1573 let bytes = msg.to_bytes().unwrap();
1574 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1575 assert_eq!(msg, decoded);
1576 }
1577
1578 #[test]
1579 fn get_mutable_query_with_seq_round_trip() {
1580 let msg = KrpcMessage {
1581 transaction_id: TransactionId::from_u16(401),
1582 body: KrpcBody::Query(KrpcQuery::Get {
1583 id: test_id(),
1584 target: target_id(),
1585 seq: Some(42),
1586 }),
1587 sender_ip: None,
1588 read_only: false,
1589 };
1590 let bytes = msg.to_bytes().unwrap();
1591 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1592 assert_eq!(msg, decoded);
1593 }
1594
1595 #[test]
1596 fn put_immutable_query_round_trip() {
1597 let msg = KrpcMessage {
1598 transaction_id: TransactionId::from_u16(402),
1599 body: KrpcBody::Query(KrpcQuery::Put {
1600 id: test_id(),
1601 token: b"tok12345".to_vec(),
1602 value: b"12:Hello World!".to_vec(),
1603 key: None,
1604 signature: None,
1605 seq: None,
1606 salt: None,
1607 cas: None,
1608 }),
1609 sender_ip: None,
1610 read_only: false,
1611 };
1612 let bytes = msg.to_bytes().unwrap();
1613 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1614 assert_eq!(msg, decoded);
1615 }
1616
1617 #[test]
1618 fn put_mutable_query_round_trip() {
1619 let key = [0xABu8; 32];
1620 let sig = [0xCDu8; 64];
1621 let msg = KrpcMessage {
1622 transaction_id: TransactionId::from_u16(403),
1623 body: KrpcBody::Query(KrpcQuery::Put {
1624 id: test_id(),
1625 token: b"tok12345".to_vec(),
1626 value: b"12:Hello World!".to_vec(),
1627 key: Some(key),
1628 signature: Some(sig),
1629 seq: Some(4),
1630 salt: Some(b"foobar".to_vec()),
1631 cas: Some(3),
1632 }),
1633 sender_ip: None,
1634 read_only: false,
1635 };
1636 let bytes = msg.to_bytes().unwrap();
1637 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1638 assert_eq!(msg, decoded);
1639 }
1640
1641 #[test]
1642 fn get_response_immutable_round_trip() {
1643 let msg = KrpcMessage {
1644 transaction_id: TransactionId::from_u16(404),
1645 body: KrpcBody::Response(KrpcResponse::GetItem {
1646 id: test_id(),
1647 token: Some(b"tok".to_vec()),
1648 nodes: Vec::new(),
1649 nodes6: Vec::new(),
1650 value: Some(b"12:Hello World!".to_vec()),
1651 key: None,
1652 signature: None,
1653 seq: None,
1654 }),
1655 sender_ip: None,
1656 read_only: false,
1657 };
1658 let bytes = msg.to_bytes().unwrap();
1659 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1660 assert_eq!(msg, decoded);
1661 }
1662
1663 #[test]
1664 fn get_response_mutable_round_trip() {
1665 let key = [0xABu8; 32];
1666 let sig = [0xCDu8; 64];
1667 let msg = KrpcMessage {
1668 transaction_id: TransactionId::from_u16(405),
1669 body: KrpcBody::Response(KrpcResponse::GetItem {
1670 id: test_id(),
1671 token: Some(b"tok".to_vec()),
1672 nodes: vec![CompactNodeInfo {
1673 id: target_id(),
1674 addr: "10.0.0.1:6881".parse().unwrap(),
1675 }],
1676 nodes6: Vec::new(),
1677 value: Some(b"4:test".to_vec()),
1678 key: Some(key),
1679 signature: Some(sig),
1680 seq: Some(7),
1681 }),
1682 sender_ip: None,
1683 read_only: false,
1684 };
1685 let bytes = msg.to_bytes().unwrap();
1686 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1687 assert_eq!(msg, decoded);
1688 }
1689
1690 #[test]
1691 fn get_response_not_found_with_hint_round_trip() {
1692 let msg = KrpcMessage {
1695 transaction_id: TransactionId::from_u16(406),
1696 body: KrpcBody::Response(KrpcResponse::GetItem {
1697 id: test_id(),
1698 token: Some(b"tok".to_vec()),
1699 nodes: vec![CompactNodeInfo {
1700 id: target_id(),
1701 addr: "10.0.0.1:6881".parse().unwrap(),
1702 }],
1703 nodes6: Vec::new(),
1704 value: None,
1705 key: None,
1706 signature: None,
1707 seq: None,
1708 }),
1709 sender_ip: None,
1710 read_only: false,
1711 };
1712 let bytes = msg.to_bytes().unwrap();
1713 let decoded = KrpcMessage::from_bytes_with_query_hint(&bytes, "get").unwrap();
1715 assert_eq!(msg, decoded);
1716 }
1717
1718 #[test]
1719 fn bep44_query_method_names() {
1720 assert_eq!(
1721 KrpcQuery::Get {
1722 id: Id20::ZERO,
1723 target: Id20::ZERO,
1724 seq: None,
1725 }
1726 .method_name(),
1727 "get"
1728 );
1729 assert_eq!(
1730 KrpcQuery::Put {
1731 id: Id20::ZERO,
1732 token: Vec::new(),
1733 value: Vec::new(),
1734 key: None,
1735 signature: None,
1736 seq: None,
1737 salt: None,
1738 cas: None,
1739 }
1740 .method_name(),
1741 "put"
1742 );
1743 }
1744
1745 #[test]
1748 fn sample_infohashes_query_round_trip() {
1749 let msg = KrpcMessage {
1750 transaction_id: TransactionId::from_u16(400),
1751 body: KrpcBody::Query(KrpcQuery::SampleInfohashes {
1752 id: test_id(),
1753 target: target_id(),
1754 }),
1755 sender_ip: None,
1756 read_only: false,
1757 };
1758 let bytes = msg.to_bytes().unwrap();
1759 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1760 assert_eq!(msg, decoded);
1761 }
1762
1763 #[test]
1764 fn sample_infohashes_response_round_trip() {
1765 let sample1 = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
1766 let sample2 = Id20::from_hex("0000000000000000000000000000000000000001").unwrap();
1767 let nodes = vec![CompactNodeInfo {
1768 id: target_id(),
1769 addr: "10.0.0.1:6881".parse().unwrap(),
1770 }];
1771 let msg = KrpcMessage {
1772 transaction_id: TransactionId::from_u16(400),
1773 body: KrpcBody::Response(KrpcResponse::SampleInfohashes(SampleInfohashesResponse {
1774 id: test_id(),
1775 interval: 300,
1776 num: 42,
1777 samples: vec![sample1, sample2],
1778 nodes,
1779 })),
1780 sender_ip: None,
1781 read_only: false,
1782 };
1783 let bytes = msg.to_bytes().unwrap();
1784 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1785 assert_eq!(msg, decoded);
1786 }
1787
1788 #[test]
1789 fn sample_infohashes_response_empty_samples() {
1790 let msg = KrpcMessage {
1791 transaction_id: TransactionId::from_u16(401),
1792 body: KrpcBody::Response(KrpcResponse::SampleInfohashes(SampleInfohashesResponse {
1793 id: test_id(),
1794 interval: 60,
1795 num: 0,
1796 samples: Vec::new(),
1797 nodes: Vec::new(),
1798 })),
1799 sender_ip: None,
1800 read_only: false,
1801 };
1802 let bytes = msg.to_bytes().unwrap();
1803 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1804 assert_eq!(msg, decoded);
1805 }
1806
1807 #[test]
1808 fn sample_infohashes_query_method_name() {
1809 assert_eq!(
1810 KrpcQuery::SampleInfohashes {
1811 id: Id20::ZERO,
1812 target: Id20::ZERO,
1813 }
1814 .method_name(),
1815 "sample_infohashes"
1816 );
1817 }
1818
1819 #[test]
1822 fn krpc_ro_flag_roundtrip() {
1823 let msg = KrpcMessage {
1825 transaction_id: TransactionId::from_u16(43),
1826 body: KrpcBody::Query(KrpcQuery::Ping { id: test_id() }),
1827 sender_ip: None,
1828 read_only: true,
1829 };
1830 let bytes = msg.to_bytes().unwrap();
1831 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1832 assert!(decoded.read_only, "ro flag should survive round-trip");
1833 assert_eq!(decoded.body, msg.body);
1834 }
1835
1836 #[test]
1837 fn krpc_ro_absent_defaults_false() {
1838 let msg = KrpcMessage {
1840 transaction_id: TransactionId::from_u16(43),
1841 body: KrpcBody::Query(KrpcQuery::Ping { id: test_id() }),
1842 sender_ip: None,
1843 read_only: false,
1844 };
1845 let bytes = msg.to_bytes().unwrap();
1846 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1847 assert!(!decoded.read_only, "absent ro should default to false");
1848 }
1849
1850 #[test]
1853 fn krpc_get_peers_with_scrape_flag() {
1854 let msg = KrpcMessage {
1855 transaction_id: TransactionId::from_u16(330),
1856 body: KrpcBody::Query(KrpcQuery::GetPeers {
1857 id: test_id(),
1858 info_hash: target_id(),
1859 noseed: None,
1860 scrape: Some(1),
1861 want: None,
1862 }),
1863 sender_ip: None,
1864 read_only: false,
1865 };
1866 let bytes = msg.to_bytes().unwrap();
1867 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1868 assert_eq!(msg, decoded);
1869 match &decoded.body {
1870 KrpcBody::Query(KrpcQuery::GetPeers { scrape, noseed, .. }) => {
1871 assert_eq!(*scrape, Some(1));
1872 assert_eq!(*noseed, None);
1873 }
1874 other => panic!("expected GetPeers query, got {other:?}"),
1875 }
1876 }
1877
1878 #[test]
1879 fn krpc_get_peers_response_with_bloom() {
1880 let bfpe_data = vec![0xAA; 256];
1881 let bfsd_data = vec![0x55; 256];
1882 let msg = KrpcMessage {
1883 transaction_id: TransactionId::from_u16(331),
1884 body: KrpcBody::Response(KrpcResponse::GetPeers(GetPeersResponse {
1885 id: test_id(),
1886 token: Some(b"tok".to_vec()),
1887 peers: vec!["192.168.1.1:6881".parse().unwrap()],
1888 nodes: Vec::new(),
1889 nodes6: Vec::new(),
1890 bfpe: Some(bfpe_data.clone()),
1891 bfsd: Some(bfsd_data.clone()),
1892 })),
1893 sender_ip: None,
1894 read_only: false,
1895 };
1896 let bytes = msg.to_bytes().unwrap();
1897 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
1898 assert_eq!(msg, decoded);
1899 match &decoded.body {
1900 KrpcBody::Response(KrpcResponse::GetPeers(gp)) => {
1901 assert_eq!(gp.bfpe.as_deref(), Some(bfpe_data.as_slice()));
1902 assert_eq!(gp.bfsd.as_deref(), Some(bfsd_data.as_slice()));
1903 }
1904 other => panic!("expected GetPeers response, got {other:?}"),
1905 }
1906 }
1907
1908 fn bep5_id(ascii: &[u8; 20]) -> Id20 {
1912 Id20(*ascii)
1913 }
1914
1915 #[test]
1916 fn krpc_decode_find_node_query_spec() {
1917 let data = b"d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe";
1919 let msg = KrpcMessage::from_bytes(data).unwrap();
1920 assert_eq!(msg.transaction_id.0, *b"aa");
1921 assert!(!msg.read_only);
1922 match &msg.body {
1923 KrpcBody::Query(KrpcQuery::FindNode { id, target, .. }) => {
1924 assert_eq!(id.as_bytes(), b"abcdefghij0123456789");
1925 assert_eq!(target.as_bytes(), b"mnopqrstuvwxyz123456");
1926 }
1927 other => panic!("expected FindNode query, got {other:?}"),
1928 }
1929 }
1930
1931 #[test]
1932 fn krpc_decode_get_peers_query_spec() {
1933 let data = b"d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe";
1935 let msg = KrpcMessage::from_bytes(data).unwrap();
1936 assert_eq!(msg.transaction_id.0, *b"aa");
1937 assert!(!msg.read_only);
1938 match &msg.body {
1939 KrpcBody::Query(KrpcQuery::GetPeers {
1940 id,
1941 info_hash,
1942 noseed,
1943 scrape,
1944 ..
1945 }) => {
1946 assert_eq!(id.as_bytes(), b"abcdefghij0123456789");
1947 assert_eq!(info_hash.as_bytes(), b"mnopqrstuvwxyz123456");
1948 assert_eq!(*noseed, None);
1949 assert_eq!(*scrape, None);
1950 }
1951 other => panic!("expected GetPeers query, got {other:?}"),
1952 }
1953 }
1954
1955 #[test]
1956 fn krpc_decode_announce_peer_query_spec() {
1957 let data = b"d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:mnopqrstuvwxyz1234564:porti6881e5:token8:aoaborahe1:q13:announce_peer1:t2:aa1:y1:qe";
1959 let msg = KrpcMessage::from_bytes(data).unwrap();
1960 assert_eq!(msg.transaction_id.0, *b"aa");
1961 assert!(!msg.read_only);
1962 match &msg.body {
1963 KrpcBody::Query(KrpcQuery::AnnouncePeer {
1964 id,
1965 info_hash,
1966 port,
1967 implied_port,
1968 token,
1969 }) => {
1970 assert_eq!(id.as_bytes(), b"abcdefghij0123456789");
1971 assert_eq!(info_hash.as_bytes(), b"mnopqrstuvwxyz123456");
1972 assert_eq!(*port, 6881);
1973 assert!(*implied_port);
1974 assert_eq!(token, b"aoaborah");
1975 }
1976 other => panic!("expected AnnouncePeer query, got {other:?}"),
1977 }
1978 }
1979
1980 fn m241_announce_args(
1983 port: Option<i64>,
1984 implied: Option<i64>,
1985 ) -> BTreeMap<Vec<u8>, BencodeValue> {
1986 let mut args = BTreeMap::new();
1987 args.insert(b"id".to_vec(), BencodeValue::Bytes(vec![0u8; 20]));
1988 args.insert(b"info_hash".to_vec(), BencodeValue::Bytes(vec![1u8; 20]));
1989 if let Some(p) = port {
1990 args.insert(b"port".to_vec(), BencodeValue::Integer(p));
1991 }
1992 if let Some(i) = implied {
1993 args.insert(b"implied_port".to_vec(), BencodeValue::Integer(i));
1994 }
1995 args.insert(b"token".to_vec(), BencodeValue::Bytes(b"tok".to_vec()));
1996 args
1997 }
1998
1999 #[test]
2000 fn m241_announce_peer_rejects_port_above_u16() {
2001 let args = m241_announce_args(Some(70000), None);
2003 assert!(matches!(
2004 decode_query(b"announce_peer", &args),
2005 Err(Error::InvalidMessage(_))
2006 ));
2007 }
2008
2009 #[test]
2010 fn m241_announce_peer_rejects_negative_port() {
2011 let args = m241_announce_args(Some(-1), None);
2013 assert!(matches!(
2014 decode_query(b"announce_peer", &args),
2015 Err(Error::InvalidMessage(_))
2016 ));
2017 }
2018
2019 #[test]
2020 fn m241_announce_peer_rejects_zero_port_without_implied() {
2021 let args = m241_announce_args(Some(0), None);
2022 assert!(matches!(
2023 decode_query(b"announce_peer", &args),
2024 Err(Error::InvalidMessage(_))
2025 ));
2026 }
2027
2028 #[test]
2029 fn m241_announce_peer_accepts_zero_port_with_implied() {
2030 let args = m241_announce_args(Some(0), Some(1));
2033 assert!(matches!(
2034 decode_query(b"announce_peer", &args),
2035 Ok(KrpcQuery::AnnouncePeer {
2036 port: 0,
2037 implied_port: true,
2038 ..
2039 })
2040 ));
2041 }
2042
2043 #[test]
2044 fn m241_announce_peer_accepts_out_of_range_port_with_implied() {
2045 let args = m241_announce_args(Some(70000), Some(1));
2048 assert!(matches!(
2049 decode_query(b"announce_peer", &args),
2050 Ok(KrpcQuery::AnnouncePeer {
2051 port: 0,
2052 implied_port: true,
2053 ..
2054 })
2055 ));
2056 }
2057
2058 #[test]
2059 fn m241_announce_peer_accepts_valid_port() {
2060 let args = m241_announce_args(Some(6881), None);
2061 assert!(matches!(
2062 decode_query(b"announce_peer", &args),
2063 Ok(KrpcQuery::AnnouncePeer { port: 6881, .. })
2064 ));
2065 }
2066
2067 #[test]
2068 fn krpc_encode_ping_matches_spec() {
2069 let msg = KrpcMessage {
2076 transaction_id: TransactionId([b'a', b'a']),
2077 body: KrpcBody::Query(KrpcQuery::Ping {
2078 id: bep5_id(b"abcdefghij0123456789"),
2079 }),
2080 sender_ip: None,
2081 read_only: false,
2082 };
2083 let encoded = msg.to_bytes().unwrap();
2084 let expected = b"d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe";
2085 assert_eq!(
2086 encoded, expected,
2087 "encoded ping query should match BEP 5 spec bytes exactly"
2088 );
2089
2090 let decoded = KrpcMessage::from_bytes(&encoded).unwrap();
2092 assert_eq!(decoded.transaction_id.0, *b"aa");
2093 match &decoded.body {
2094 KrpcBody::Query(KrpcQuery::Ping { id }) => {
2095 assert_eq!(id.as_bytes(), b"abcdefghij0123456789");
2096 }
2097 other => panic!("expected Ping query, got {other:?}"),
2098 }
2099 }
2100
2101 #[test]
2102 fn krpc_encode_roundtrip_all_types() {
2103 let node_id = bep5_id(b"abcdefghij0123456789");
2105 let target = bep5_id(b"mnopqrstuvwxyz123456");
2106
2107 let ping = KrpcMessage {
2109 transaction_id: TransactionId::from_u16(1),
2110 body: KrpcBody::Query(KrpcQuery::Ping { id: node_id }),
2111 sender_ip: None,
2112 read_only: false,
2113 };
2114 let ping_decoded = KrpcMessage::from_bytes(&ping.to_bytes().unwrap()).unwrap();
2115 assert_eq!(ping, ping_decoded, "ping round-trip mismatch");
2116
2117 let find_node = KrpcMessage {
2119 transaction_id: TransactionId::from_u16(2),
2120 body: KrpcBody::Query(KrpcQuery::FindNode {
2121 id: node_id,
2122 target,
2123 want: None,
2124 }),
2125 sender_ip: None,
2126 read_only: false,
2127 };
2128 let find_node_decoded = KrpcMessage::from_bytes(&find_node.to_bytes().unwrap()).unwrap();
2129 assert_eq!(
2130 find_node, find_node_decoded,
2131 "find_node round-trip mismatch"
2132 );
2133
2134 let get_peers = KrpcMessage {
2136 transaction_id: TransactionId::from_u16(3),
2137 body: KrpcBody::Query(KrpcQuery::GetPeers {
2138 id: node_id,
2139 info_hash: target,
2140 noseed: None,
2141 scrape: None,
2142 want: None,
2143 }),
2144 sender_ip: None,
2145 read_only: false,
2146 };
2147 let get_peers_decoded = KrpcMessage::from_bytes(&get_peers.to_bytes().unwrap()).unwrap();
2148 assert_eq!(
2149 get_peers, get_peers_decoded,
2150 "get_peers round-trip mismatch"
2151 );
2152
2153 let announce = KrpcMessage {
2155 transaction_id: TransactionId::from_u16(4),
2156 body: KrpcBody::Query(KrpcQuery::AnnouncePeer {
2157 id: node_id,
2158 info_hash: target,
2159 port: 6881,
2160 implied_port: true,
2161 token: b"tok123".to_vec(),
2162 }),
2163 sender_ip: None,
2164 read_only: false,
2165 };
2166 let announce_decoded = KrpcMessage::from_bytes(&announce.to_bytes().unwrap()).unwrap();
2167 assert_eq!(
2168 announce, announce_decoded,
2169 "announce_peer round-trip mismatch"
2170 );
2171 match &announce_decoded.body {
2172 KrpcBody::Query(KrpcQuery::AnnouncePeer {
2173 id,
2174 info_hash,
2175 port,
2176 implied_port,
2177 token,
2178 }) => {
2179 assert_eq!(*id, node_id);
2180 assert_eq!(*info_hash, target);
2181 assert_eq!(*port, 6881);
2182 assert!(*implied_port);
2183 assert_eq!(token, b"tok123");
2184 }
2185 other => panic!("expected AnnouncePeer query, got {other:?}"),
2186 }
2187
2188 let error = KrpcMessage {
2190 transaction_id: TransactionId::from_u16(5),
2191 body: KrpcBody::Error {
2192 code: 201,
2193 message: "A Generic Error Occurred".into(),
2194 },
2195 sender_ip: None,
2196 read_only: false,
2197 };
2198 let error_decoded = KrpcMessage::from_bytes(&error.to_bytes().unwrap()).unwrap();
2199 assert_eq!(error, error_decoded, "error round-trip mismatch");
2200
2201 let response = KrpcMessage {
2203 transaction_id: TransactionId::from_u16(6),
2204 body: KrpcBody::Response(KrpcResponse::NodeId { id: node_id }),
2205 sender_ip: None,
2206 read_only: false,
2207 };
2208 let response_decoded = KrpcMessage::from_bytes(&response.to_bytes().unwrap()).unwrap();
2209 assert_eq!(response, response_decoded, "response round-trip mismatch");
2210 match &response_decoded.body {
2211 KrpcBody::Response(KrpcResponse::NodeId { id }) => {
2212 assert_eq!(*id, node_id);
2213 }
2214 other => panic!("expected NodeId response, got {other:?}"),
2215 }
2216 }
2217
2218 #[test]
2219 fn scrape_response_noseed_parsed() {
2220 let msg = KrpcMessage {
2221 transaction_id: TransactionId::from_u16(332),
2222 body: KrpcBody::Query(KrpcQuery::GetPeers {
2223 id: test_id(),
2224 info_hash: target_id(),
2225 noseed: Some(1),
2226 scrape: None,
2227 want: None,
2228 }),
2229 sender_ip: None,
2230 read_only: false,
2231 };
2232 let bytes = msg.to_bytes().unwrap();
2233 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
2234 assert_eq!(msg, decoded);
2235 match &decoded.body {
2236 KrpcBody::Query(KrpcQuery::GetPeers { noseed, scrape, .. }) => {
2237 assert_eq!(*noseed, Some(1));
2238 assert_eq!(*scrape, None);
2239 }
2240 other => panic!("expected GetPeers query, got {other:?}"),
2241 }
2242 }
2243
2244 #[test]
2247 fn want_family_round_trip_bytes() {
2248 assert_eq!(WantFamily::from_bytes(b"n4"), Some(WantFamily::N4));
2249 assert_eq!(WantFamily::from_bytes(b"n6"), Some(WantFamily::N6));
2250 assert_eq!(WantFamily::from_bytes(b"n8"), None);
2251 assert_eq!(WantFamily::N4.as_bytes(), b"n4");
2252 assert_eq!(WantFamily::N6.as_bytes(), b"n6");
2253 }
2254
2255 #[test]
2256 fn find_node_want_round_trip() {
2257 let msg = KrpcMessage {
2258 transaction_id: TransactionId::from_u16(500),
2259 body: KrpcBody::Query(KrpcQuery::FindNode {
2260 id: test_id(),
2261 target: target_id(),
2262 want: Some(vec![WantFamily::N4, WantFamily::N6]),
2263 }),
2264 sender_ip: None,
2265 read_only: false,
2266 };
2267 let bytes = msg.to_bytes().unwrap();
2268 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
2269 assert_eq!(msg, decoded);
2270 match &decoded.body {
2271 KrpcBody::Query(KrpcQuery::FindNode { want, .. }) => {
2272 let w = want.as_ref().expect("want should be present");
2273 assert_eq!(w.len(), 2);
2274 assert_eq!(w[0], WantFamily::N4);
2275 assert_eq!(w[1], WantFamily::N6);
2276 }
2277 other => panic!("expected FindNode query, got {other:?}"),
2278 }
2279 }
2280
2281 #[test]
2282 fn get_peers_want_round_trip() {
2283 let msg = KrpcMessage {
2284 transaction_id: TransactionId::from_u16(501),
2285 body: KrpcBody::Query(KrpcQuery::GetPeers {
2286 id: test_id(),
2287 info_hash: target_id(),
2288 noseed: None,
2289 scrape: None,
2290 want: Some(vec![WantFamily::N6]),
2291 }),
2292 sender_ip: None,
2293 read_only: false,
2294 };
2295 let bytes = msg.to_bytes().unwrap();
2296 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
2297 assert_eq!(msg, decoded);
2298 match &decoded.body {
2299 KrpcBody::Query(KrpcQuery::GetPeers { want, .. }) => {
2300 let w = want.as_ref().expect("want should be present");
2301 assert_eq!(w, &[WantFamily::N6]);
2302 }
2303 other => panic!("expected GetPeers query, got {other:?}"),
2304 }
2305 }
2306
2307 #[test]
2308 fn find_node_want_none_omitted() {
2309 let msg = KrpcMessage {
2310 transaction_id: TransactionId::from_u16(502),
2311 body: KrpcBody::Query(KrpcQuery::FindNode {
2312 id: test_id(),
2313 target: target_id(),
2314 want: None,
2315 }),
2316 sender_ip: None,
2317 read_only: false,
2318 };
2319 let bytes = msg.to_bytes().unwrap();
2320 let decoded = KrpcMessage::from_bytes(&bytes).unwrap();
2321 match &decoded.body {
2322 KrpcBody::Query(KrpcQuery::FindNode { want, .. }) => {
2323 assert!(want.is_none(), "want should be omitted when None");
2324 }
2325 other => panic!("expected FindNode query, got {other:?}"),
2326 }
2327 }
2328
2329 #[test]
2330 fn want_unknown_family_filtered() {
2331 let mut args = BTreeMap::new();
2332 args.insert(
2333 b"want".to_vec(),
2334 BencodeValue::List(vec![
2335 BencodeValue::Bytes(b"n4".to_vec()),
2336 BencodeValue::Bytes(b"n9".to_vec()),
2337 BencodeValue::Bytes(b"n6".to_vec()),
2338 ]),
2339 );
2340 let want = decode_want(&args).expect("should parse known families");
2341 assert_eq!(want, vec![WantFamily::N4, WantFamily::N6]);
2342 }
2343}