From 4fa5ac51ea8700b5b48a84b5d36a7e26ee67e7c8 Mon Sep 17 00:00:00 2001 From: ivanjermakov Date: Mon, 23 Oct 2023 00:26:09 +0200 Subject: [PATCH] Handshake fixes --- data/knoppix.torrent | Bin 0 -> 27614 bytes src/main.rs | 13 +++--- src/peer.rs | 101 +++++++++++++++++++++++++++++-------------- src/tracker.rs | 10 ++--- 4 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 data/knoppix.torrent diff --git a/data/knoppix.torrent b/data/knoppix.torrent new file mode 100644 index 0000000000000000000000000000000000000000..3bb254d56d4face3f4bfba7db73caf565d1273b5 GIT binary patch literal 27614 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 literal 0 HcmV?d00001 diff --git a/src/main.rs b/src/main.rs index 1cc27be..308d09b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,18 +19,18 @@ mod tracker; mod types; fn main() { - let path = PathBuf::from("data/academic_test.torrent"); + let path = PathBuf::from("data/knoppix.torrent"); let bencoded = fs::read(path).unwrap(); let metainfo_dict = match parse_bencoded(bencoded) { (Some(metadata), left) if left.is_empty() => metadata, _ => panic!("metadata file parsing error"), }; - println!("{metainfo_dict:?}"); + println!("metainfo dict: {metainfo_dict:?}"); let metainfo = match Metainfo::try_from(metainfo_dict.clone()) { Ok(info) => info, Err(e) => panic!("metadata file structure error: {e}"), }; - println!("{metainfo:?}"); + println!("metainfo: {metainfo:?}"); let info_dict_str = match metainfo_dict { bencode::BencodeValue::Dict(d) => d.get("info").unwrap().encode(), _ => unreachable!(), @@ -52,11 +52,8 @@ fn main() { if let TrackerResponse::Success(resp) = tracker_response { for p in resp.peers { match handshake(&p, &info_hash, &peer_id) { - Ok(_) => { - println!("successfull handshake with peer {:?}", p); - break; - } - Err(e) => eprintln!("{}", e), + Ok(_) => println!("successfull handshake with peer {:?}", p), + Err(e) => eprintln!("handshake error: {}", e), } } } diff --git a/src/peer.rs b/src/peer.rs index a01d674..0a6f08b 100644 --- a/src/peer.rs +++ b/src/peer.rs @@ -1,27 +1,74 @@ -use std::io::{self, Read, Write}; +use std::io::{self, BufReader, Read, Write}; use std::net::{IpAddr, SocketAddr, TcpStream}; use std::str::FromStr; -use std::thread::sleep; use std::time::Duration; use crate::hex::hex; use crate::tracker::TrackerPeer; use crate::types::ByteString; +pub struct HandshakePacket { + info_hash: Vec, + peer_id: Vec, +} + +impl From for Vec { + fn from(value: HandshakePacket) -> Self { + let pstr = "BitTorrent protocol"; + let pstrlen = &[pstr.len() as u8]; + let reserved = &[0u8; 8]; + [ + pstrlen, + pstr.as_bytes(), + reserved, + &value.info_hash, + &value.peer_id, + ] + .concat() + } +} + +impl TryFrom> for HandshakePacket { + type Error = String; + + fn try_from(value: Vec) -> Result { + if value.len() != 68 { + return Err(format!("invalid handshake len: {}", value.len())); + } + let pstrlen = &value.as_slice()[0..1]; + if pstrlen != [19u8] { + return Err(format!("invalid pstrlen: {}", hex(pstrlen))); + } + let pstr = &value.as_slice()[1..20]; + if pstr != "BitTorrent protocol".as_bytes() { + return Err(format!("invalid pstr: {}", hex(pstr))); + } + Ok(HandshakePacket { + info_hash: value.as_slice()[29..48].to_vec(), + peer_id: value.as_slice()[49..68].to_vec(), + }) + } +} + pub fn handshake( peer: &TrackerPeer, info_hash: &ByteString, peer_id: &ByteString, -) -> io::Result<()> { +) -> io::Result { + let timeout = Duration::new(4, 0); println!("connecting to peer {peer:?}"); - let timeout = Duration::new(2, 0); let mut stream = TcpStream::connect_timeout( &SocketAddr::new(IpAddr::from_str(&peer.ip).unwrap(), peer.port as u16), timeout, )?; stream.set_read_timeout(Some(timeout))?; stream.set_write_timeout(Some(timeout))?; - let handshake = handshake_packet(info_hash, peer_id); + let handshake: Vec = HandshakePacket { + info_hash: info_hash.clone(), + peer_id: peer_id.clone(), + } + .into(); + println!("writing handshake {}", hex(&handshake.to_vec())); match stream.write_all(&handshake) { Err(e) => { @@ -31,34 +78,22 @@ pub fn handshake( _ => println!("write ok"), }; stream.flush()?; - let mut reader = stream; - let mut read_packet = vec![]; - println!("reading response"); - let mut retry = 0; - loop { - if retry > 3 { - return Err(io::Error::new(io::ErrorKind::Other, "read timeout")); - }; - match reader.read_to_end(&mut read_packet) { - Err(e) => { - eprintln!("read error: {}", e); - return Err(e); - } - Ok(n) if n > 0 => { - println!("peer response: {}", hex(&read_packet.to_vec())); - } - _ => { - println!("no data"); - retry += 1; - sleep(Duration::new(1, 0)); + + let mut reader = BufReader::new(stream); + let mut read_packet = [0u8; 68]; + println!("reading handshake"); + match reader.read_exact(&mut read_packet) { + Ok(_) => { + let msg: Vec = read_packet.to_vec(); + println!("peer response: {}", hex(&msg)); + match HandshakePacket::try_from(msg) { + Ok(p) => Ok(p), + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } - }; + } + Err(e) => { + eprintln!("read error: {}", e); + Err(e) + } } } - -pub fn handshake_packet(info_hash: &ByteString, peer_id: &ByteString) -> Vec { - let pstr = "BitTorrent protocol"; - let pstrlen = &[pstr.len() as u8]; - let reserved = &[0u8; 8]; - [pstrlen, pstr.as_bytes(), reserved, &info_hash, &peer_id].concat() -} diff --git a/src/tracker.rs b/src/tracker.rs index 585d23b..91528e7 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -203,8 +203,7 @@ pub fn tracker_request( announce: String, request: TrackerRequest, ) -> Result { - let params = request.to_params(); - let query = format!( + let params = format!( "?{}", request .to_params() @@ -213,14 +212,15 @@ pub fn tracker_request( .collect::>() .join("&") ); - println!("params: {params:?}"); + let url = format!("{announce}{params}"); + println!("url: {url}"); let resp = Client::new() - .get(format!("{announce}{query}")) + .get(url) .send() .map_err(|e| format!("request error: {}", e))? .bytes() .map_err(|e| format!("request body error: {}", e))?; - println!("{}", String::from_utf8_lossy(&resp)); + println!("raw response: {}", String::from_utf8_lossy(&resp)); let resp_dict = parse_bencoded(resp.to_vec()) .0 .ok_or("Malformed response")?;