use bendy::decoding::{Error as DecodingError, Object}; use bendy::encoding::{Error as EncodingError, SingleItemEncoder}; use std::convert::TryInto; use std::fmt::{Display, Formatter}; use std::option::Option::Some; use std::str::FromStr; use torment_core::infohash::v1::U160; use torment_core::infohash::InfoHashCapable; use torment_core::{CompactContact, ContactInfo, ParsingError}; pub use bendy::decoding::FromBencode; pub use bendy::encoding::ToBencode; use std::net::SocketAddr; const VERSION: &'static [u8] = &[b'e', b't', 0, 0]; fn ver() -> Option { // None Some(String::from_utf8_lossy(VERSION).to_string()) } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Message { pub transaction_id: [u8; 2], pub body: MessageBody, pub version: Option, } impl Message { pub fn expect_response(self) -> ([u8; 2], ResponseBody, Option) { ( self.transaction_id, match self.body { MessageBody::Response(resp) => resp, b => panic!("Expected response got {}", b.name()), }, self.version, ) } pub fn expect_request(self) -> ([u8; 2], RequestBody, Option) { ( self.transaction_id, match self.body { MessageBody::Request(req) => req, b => panic!("Expected request got {}", b.name()), }, self.version, ) } pub fn expect_error(self) -> ([u8; 2], ErrorBody, Option) { ( self.transaction_id, match self.body { MessageBody::Error(err) => err, b => panic!("Expected error got {}", b.name()), }, self.version, ) } pub fn request_ping(id: U160, t: [u8; 2]) -> Message { Message { transaction_id: t, body: MessageBody::Request(RequestBody::Ping(id)), version: ver(), } } pub fn request_get_peers(id: U160, t: [u8; 2], info_hash: U160, want: Option) -> Message { Message { transaction_id: t, body: MessageBody::Request(RequestBody::GetPeers(GetPeersRequest { id, want, info_hash, })), version: ver(), } } pub fn request_find_node(id: U160, t: [u8; 2], target: U160, want: Option) -> Message { Message { transaction_id: t, body: MessageBody::Request(RequestBody::FindNode(FindNodeRequest { id, want, target })), version: ver(), } } pub fn request_announce_peer( id: U160, t: [u8; 2], info_hash: U160, token: Vec, port: u16, implied_port: bool, ) -> Message { Message { transaction_id: t, body: MessageBody::Request(RequestBody::AnnouncePeer(AnnouncePeerRequest { id, implied_port, info_hash, port, token, })), version: ver(), } } pub fn response_get_peers_found( id: U160, t: [u8; 2], token: Vec, peers: Vec, ) -> Message { Message { transaction_id: t, body: MessageBody::Response(ResponseBody::GetPeers(GetPeersResponse { id, token, values: peers, nodes: ContactNodes::default(), })), version: ver(), } } pub fn response_get_peers_not_found( id: U160, t: [u8; 2], token: Vec, nodes: Vec, nodes6: Vec, ) -> Message { Message { transaction_id: t, body: MessageBody::Response(ResponseBody::GetPeers(GetPeersResponse { id, token, values: vec![], nodes: ContactNodes { nodes, nodes6 }, })), version: ver(), } } pub fn response_find_node( id: U160, t: [u8; 2], nodes: Vec, nodes6: Vec, ) -> Message { Message { transaction_id: t, body: MessageBody::Response(ResponseBody::FindNode(FindNodeResponse { id, nodes, nodes6, })), version: ver(), } } pub fn response_empty(id: U160, t: [u8; 2]) -> Message { Message { transaction_id: t, body: MessageBody::Response(ResponseBody::Empty(id)), version: ver(), } } pub fn error(t: [u8; 2], code: u16, text: String) -> Message { Message { transaction_id: t, body: MessageBody::Error(ErrorBody { code, text }), version: ver(), } } } impl ToBencode for Message { const MAX_DEPTH: usize = 3; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> { encoder.emit_unsorted_dict(|d| { d.emit_pair_with(b"t", |e| e.emit_bytes(&self.transaction_id[..]))?; match self.body { MessageBody::Error(ref err) => { d.emit_pair(b"y", "e")?; d.emit_pair_with(b"e", |d| { d.emit_list(|l| { l.emit_int(err.code)?; l.emit_str(&err.text) }) })?; } MessageBody::Request(ref req) => { d.emit_pair(b"y", "q")?; match req { RequestBody::Ping(ref p) => { d.emit_pair(b"q", "ping")?; d.emit_pair_with(b"a", |d| d.emit_dict(|mut d| d.emit_pair(b"id", p)))?; } RequestBody::AnnouncePeer(ref ann) => { d.emit_pair(b"q", "announce_peer")?; d.emit_pair_with(b"a", |d| { d.emit_unsorted_dict(|d| { d.emit_pair(b"id", ann.id)?; d.emit_pair( b"implied_port", if ann.implied_port { 1 } else { 0 }, )?; d.emit_pair(b"info_hash", ann.info_hash)?; d.emit_pair(b"port", ann.port)?; d.emit_pair_with(b"token", |v| v.emit_bytes(&ann.token)) }) })?; } RequestBody::FindNode(ref fin) => { d.emit_pair(b"q", "find_node")?; d.emit_pair_with(b"a", |d| { d.emit_unsorted_dict(|d| { d.emit_pair(b"id", fin.id)?; if let Some(ref want) = fin.want { d.emit_pair( b"want", match want { Want::N4 => "n4", Want::N6 => "n6", }, )?; } d.emit_pair(b"target", fin.target) }) })?; } RequestBody::GetPeers(ref get) => { d.emit_pair(b"q", "get_peers")?; d.emit_pair_with(b"a", |d| { d.emit_unsorted_dict(|d| { d.emit_pair(b"id", get.id)?; if let Some(ref want) = get.want { d.emit_pair( b"want", match want { Want::N4 => "n4", Want::N6 => "n6", }, )?; } d.emit_pair(b"info_hash", get.info_hash) }) })?; } }; } MessageBody::Response(ref resp) => { d.emit_pair(b"y", "r")?; match resp { ResponseBody::GetPeers(ref get) => { d.emit_pair_with(b"r", |d| { d.emit_unsorted_dict(|d| { d.emit_pair(b"id", get.id)?; d.emit_pair_with(b"token", |v| v.emit_bytes(&get.token))?; if get.values.len() > 0 { d.emit_pair_with(b"values", |e| { e.emit_list(|l| { for item in &get.values { l.emit_bytes(&item.to_compact_contact())?; } Ok(()) }) })?; } if get.nodes.nodes.len() > 0 { d.emit_pair_with(b"nodes", |v| { v.emit_bytes(&compact_nodes(&get.nodes.nodes)) })?; } if get.nodes.nodes6.len() > 0 { d.emit_pair_with(b"nodes6", |v| { v.emit_bytes(&compact_nodes(&get.nodes.nodes6)) })?; } Ok(()) }) })?; } ResponseBody::FindNode(ref find) => { d.emit_pair_with(b"r", |d| { d.emit_unsorted_dict(|d| { d.emit_pair(b"id", find.id)?; if find.nodes.len() > 0 { d.emit_pair_with(b"nodes", |v| { v.emit_bytes(&compact_nodes(&find.nodes)) })?; } if find.nodes6.len() > 0 { d.emit_pair_with(b"nodes6", |v| { v.emit_bytes(&compact_nodes(&find.nodes6)) })?; } Ok(()) }) })?; } ResponseBody::Empty(ref id) => { d.emit_pair_with(b"r", |d| { d.emit_dict(|mut d| d.emit_pair(b"id", id)) })?; } } } } if let Some(ref version) = self.version { d.emit_pair(b"v", version)?; } Ok(()) }) } } #[derive(Debug)] enum KRPCError { Generic(String), } impl std::error::Error for KRPCError {} impl Display for KRPCError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { KRPCError::Generic(ref error) => write!(f, "Generic error: {}", error), } } } impl FromBencode for Message { fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut dict = object.try_into_dictionary()?; let mut t = None; let mut y = None; let mut v = None; let mut q = None; let mut info_hash = None; let mut target = None; let mut id = None; let mut token = None; let mut implied_port = false; let mut want = None; let mut port = 0; let mut nodes = vec![]; let mut nodes6 = vec![]; let mut values = vec![]; let mut code = 0; let mut text = String::new(); while let Some((name, obj)) = dict.next_pair()? { match name { b"y" => y = obj.try_into_bytes()?.first(), b"t" => t = Some(obj.try_into_bytes()?.to_vec()), b"v" => v = Some(obj.try_into_bytes()?.to_vec()), b"q" => q = Some(obj.try_into_bytes()?.to_vec()), b"a" => { let mut args = obj.try_into_dictionary()?; while let Some((arg_name, arg_obj)) = args.next_pair()? { match arg_name { b"info_hash" => { info_hash = Some(U160::from_bytes(arg_obj.try_into_bytes()?)?) } b"id" => id = Some(U160::from_bytes(arg_obj.try_into_bytes()?)?), b"target" => { target = Some(U160::from_bytes(arg_obj.try_into_bytes()?)?) } b"token" => token = Some(arg_obj.try_into_bytes()?.to_vec()), b"implied_port" => { implied_port = usize::from_str(arg_obj.try_into_integer()?)? > 0 } b"port" => port = u16::from_str(arg_obj.try_into_integer()?)?, b"want" => { want = match arg_obj.try_into_bytes() { Ok(b"n4") => Some(Want::N4), Ok(b"n6") => Some(Want::N6), _ => None, } } _ => {} } } } b"r" => { let mut ret = obj.try_into_dictionary()?; while let Some((ret_name, ret_obj)) = ret.next_pair()? { match ret_name { b"id" => id = Some(U160::from_bytes(ret_obj.try_into_bytes()?)?), b"token" => token = Some(ret_obj.try_into_bytes()?.to_vec()), b"values" => { let mut list = ret_obj.try_into_list()?; while let Some(value) = list.next_object()? { values.push(SocketAddr::from_compact_contact( value.try_into_bytes()?, )?); } } b"nodes" => { nodes = ret_obj .try_into_bytes()? .chunks(26) .filter_map(|x| ContactInfo::from_bytes(x).ok()) .collect() } b"nodes6" => { nodes6 = ret_obj .try_into_bytes()? .chunks(38) .filter_map(|x| ContactInfo::from_bytes(x).ok()) .collect() } _ => {} } } } b"e" => { let mut err = obj.try_into_list()?; code = u16::from_str(err.next_object()?.ok_or(ParsingError)?.try_into_integer()?)?; text = String::from_utf8_lossy( err.next_object()?.ok_or(ParsingError)?.try_into_bytes()?, ) .to_string(); } _ => {} } } let t = t.ok_or(ParsingError)?; let t = if t.len() == 0 { [0, 0] } else if t.len() == 1 { [0, t[0]] } else { t[..2].try_into()? }; Ok(Message { transaction_id: t, body: match y { Some(b'q') => { let q = if let Some(q) = q { q } else { return Err(KRPCError::Generic("DHT Query without type".to_string()).into()); }; let id = id.ok_or(ParsingError)?; MessageBody::Request(match &q[..] { b"ping" => RequestBody::Ping(id), b"find_node" => RequestBody::FindNode(FindNodeRequest { id, want, target: target.ok_or(ParsingError)?, }), b"get_peers" => RequestBody::GetPeers(GetPeersRequest { id, want, info_hash: info_hash.ok_or(ParsingError)?, }), b"announce_peer" => RequestBody::AnnouncePeer(AnnouncePeerRequest { id, implied_port, info_hash: info_hash.ok_or(ParsingError)?, port, token: token.ok_or(ParsingError)?, }), _ => Err(ParsingError)?, }) } Some(b'r') => { let id = id.ok_or(ParsingError)?; MessageBody::Response(if let Some(token) = token { ResponseBody::GetPeers(GetPeersResponse { id, token, values, nodes: ContactNodes { nodes, nodes6 }, }) } else if nodes6.len() > 0 || nodes.len() > 0 { ResponseBody::FindNode(FindNodeResponse { id, nodes, nodes6 }) } else { ResponseBody::Empty(id) }) } Some(b'e') => MessageBody::Error(ErrorBody { code, text }), Some(x) => return Err(KRPCError::Generic(format!("Type {} is unknown", x)).into()), None => { return Err(KRPCError::Generic("No type given for message".to_string()).into()); } }, version: v.map(|x| String::from_utf8_lossy(&x).to_string()), }) } } fn compact_nodes(input: &Vec) -> Vec { let mut output = vec![]; for item in input { output.extend(item.to_bytes()) } output } #[derive(Debug, Clone, Eq, PartialEq)] pub enum MessageBody { Request(RequestBody), Response(ResponseBody), Error(ErrorBody), } impl MessageBody { fn name(&self) -> &str { match self { MessageBody::Request(_) => "request", MessageBody::Response(_) => "response", MessageBody::Error(_) => "error", } } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum RequestBody { Ping(U160), FindNode(FindNodeRequest), GetPeers(GetPeersRequest), AnnouncePeer(AnnouncePeerRequest), } impl RequestBody { pub fn node_id(&self) -> U160 { match self { RequestBody::Ping(id) => *id, RequestBody::FindNode(find_node) => find_node.id, RequestBody::GetPeers(get_peers) => get_peers.id, RequestBody::AnnouncePeer(announce_peer) => announce_peer.id, } } pub fn expect_ping(self) -> U160 { match self { RequestBody::Ping(id) => id, _ => panic!("Expected ping request"), } } pub fn expect_find_node(self) -> FindNodeRequest { match self { RequestBody::FindNode(find_node) => find_node, _ => panic!("Expected find node request"), } } pub fn expect_get_peers(self) -> GetPeersRequest { match self { RequestBody::GetPeers(get_peers) => get_peers, _ => panic!("Expected get peers request"), } } pub fn expect_announce_peer(self) -> AnnouncePeerRequest { match self { RequestBody::AnnouncePeer(announce_peer) => announce_peer, _ => panic!("Expected announce peer request"), } } } #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Copy, Hash)] pub enum Want { N6, N4, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct FindNodeRequest { pub id: U160, pub want: Option, pub target: U160, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct GetPeersRequest { pub id: U160, pub want: Option, pub info_hash: U160, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct AnnouncePeerRequest { pub id: U160, pub implied_port: bool, pub info_hash: U160, pub port: u16, pub token: Vec, } #[derive(Debug, Clone, Eq, PartialEq)] pub enum ResponseBody { Empty(U160), FindNode(FindNodeResponse), GetPeers(GetPeersResponse), } impl ResponseBody { pub fn node_id(&self) -> U160 { match self { ResponseBody::Empty(id) => *id, ResponseBody::FindNode(find_node) => find_node.id, ResponseBody::GetPeers(get_peers) => get_peers.id, } } pub fn expect_empty(self) -> U160 { match self { ResponseBody::Empty(id) => id, _ => panic!("Expected empty response"), } } pub fn expect_find_node(self) -> FindNodeResponse { match self { ResponseBody::FindNode(find_node) => find_node, _ => panic!("Expected find node response"), } } pub fn expect_get_peers(self) -> GetPeersResponse { match self { ResponseBody::GetPeers(get_peers) => get_peers, _ => panic!("Expected get peers response"), } } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct FindNodeResponse { pub id: U160, pub nodes: Vec, pub nodes6: Vec, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct GetPeersResponse { pub id: U160, pub token: Vec, pub values: Vec, pub nodes: ContactNodes, } #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct ContactNodes { pub nodes: Vec, pub nodes6: Vec, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct ErrorBody { pub code: u16, pub text: String, } #[cfg(test)] mod test { use crate::krpc::Message; use bendy::decoding::FromBencode; use bendy::encoding::ToBencode; use std::borrow::Borrow; #[test] fn error_response() { let be: &[u8] = b"d1:eli201e23:A Generic Error Ocurrede1:t2:aa1:y1:ee"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn ping_request() { let be: &[u8] = b"d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn ping_response() { let be: &[u8] = b"d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn find_node_request() { let be: &[u8] = b"d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn find_node_response() { let be: &[u8] = b"d1:rd2:id20:0123456789abcdefghij5:nodes26:abcdefghij0123456789axje.ue1:t2:aa1:y1:re"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn get_peers_request() { let be: &[u8] = b"d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn get_peers_response_a() { let be: &[u8] = b"d1:rd2:id20:abcdefghij01234567895:token8:aoeusnth6:valuesl6:axje.u6:idhtnmee1:t2:aa1:y1:re"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn get_peers_response_b() { let be: &[u8] = b"d1:rd2:id20:abcdefghij01234567895:nodes26:abcdefghij0123456789axje.u5:token8:aoeusnthe1:t2:aa1:y1:re"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn announce_peer_request() { let be: &[u8] = b"d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:mnopqrstuvwxyz1234564:porti6881e5:token8:aoeusnthe1:q13:announce_peer1:t2:aa1:y1:qe"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } #[test] fn announce_peer_response() { let be: &[u8] = b"d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re"; let msg = Message::from_bencode(be).expect("de-bencoding"); let bencoded = msg.to_bencode().expect("Failed bencoding"); println!("{:?}", msg); println!("{:?}", bencoded); assert_eq!( be, bencoded.borrow() as &[u8], "left [{}] != right [{}]", String::from_utf8_lossy(be), String::from_utf8_lossy(&bencoded) ) } }