added simple magnet parser
This commit is contained in:
parent
0ed146f785
commit
4d134c18ff
@ -33,4 +33,4 @@ serde_urlencoded = "0.7.1" # for url enc
|
||||
sha1 = "0.10.1" # hashing
|
||||
tempfile = "3" # creating temporary directories
|
||||
thiserror = "1.0.38" # error handling
|
||||
tokio = { version = "1.23.0", features = ["full"] } # async http requests
|
||||
tokio = { version = "1.23.0", features = ["full"] } # async http requests
|
||||
@ -1,6 +1,9 @@
|
||||
use serde_urlencoded;
|
||||
use sha1::{Digest, Sha1};
|
||||
use std::{collections::HashMap, fmt::Write};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{self, Debug, Formatter, Write},
|
||||
};
|
||||
|
||||
pub fn hash(input: &[u8]) -> HashValue {
|
||||
let mut hasher = Sha1::new();
|
||||
@ -23,6 +26,17 @@ impl HashValue {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn from_hex<I: Into<String>>(value: I) -> Self {
|
||||
let mut value: &str = &value.into();
|
||||
let mut out = Vec::new();
|
||||
while !value.is_empty() {
|
||||
out.extend_from_slice(&u8::from_str_radix(&value[..2], 16).unwrap().to_be_bytes());
|
||||
value = &value[2..];
|
||||
}
|
||||
|
||||
Self { value: out }
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
self.value.iter().fold(String::new(), |mut out, b| {
|
||||
let _ = write!(out, "{b:02x}");
|
||||
@ -49,3 +63,9 @@ impl HashValue {
|
||||
encoded_value
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for HashValue {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.hex())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod encode;
|
||||
pub mod magnet;
|
||||
pub mod ouput_gen;
|
||||
pub mod rand;
|
||||
pub mod tcp;
|
||||
|
||||
59
src/magnet/data.rs
Normal file
59
src/magnet/data.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::encode::hash::HashValue;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MagnetParseError {
|
||||
#[error("Invalid magnet link")]
|
||||
InvalidMagnet,
|
||||
#[error("Unexpected end of link")]
|
||||
UnexpectedEol,
|
||||
#[error("Missing field: {0}")]
|
||||
MissingField(String),
|
||||
#[error("Invalid format of field {0}")]
|
||||
InvalidFieldFormat(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Magnet {
|
||||
urn: String,
|
||||
info_hash: HashValue,
|
||||
dn: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
impl Magnet {
|
||||
pub fn new(urn: String, info_hash: HashValue, dn: String, url: String) -> Self {
|
||||
Self {
|
||||
urn,
|
||||
info_hash,
|
||||
dn,
|
||||
url,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn urn(&self) -> &String {
|
||||
&self.urn
|
||||
}
|
||||
|
||||
pub fn dn(&self) -> &String {
|
||||
&self.dn
|
||||
}
|
||||
|
||||
pub fn url(&self) -> &String {
|
||||
&self.url
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Magnet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Tracker URL: {}\n Info Hash: {}",
|
||||
self.url,
|
||||
self.info_hash.hex(),
|
||||
)
|
||||
}
|
||||
}
|
||||
2
src/magnet/mod.rs
Normal file
2
src/magnet/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod data;
|
||||
pub mod parse;
|
||||
64
src/magnet/parse.rs
Normal file
64
src/magnet/parse.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::encode::hash::HashValue;
|
||||
|
||||
use super::data::{Magnet, MagnetParseError};
|
||||
|
||||
//fn parse_xt(link: &str)
|
||||
|
||||
pub fn parse_magnet(link: &str) -> Result<Magnet, MagnetParseError> {
|
||||
let magnet = link.split("magnet:?").collect::<Vec<&str>>();
|
||||
if magnet.len() != 2 {
|
||||
return Err(MagnetParseError::InvalidMagnet);
|
||||
}
|
||||
let magnet = magnet[1];
|
||||
let mut magnet = magnet.split("&");
|
||||
|
||||
let Some(xt) = magnet.next() else {
|
||||
return Err(MagnetParseError::MissingField("xt".to_string()));
|
||||
};
|
||||
|
||||
let parts = xt.split("=").collect::<Vec<&str>>();
|
||||
|
||||
if parts.len() != 2 {
|
||||
return Err(MagnetParseError::InvalidFieldFormat("xt".to_string()));
|
||||
}
|
||||
|
||||
let xt_parts = parts[1].split(":").collect::<Vec<_>>();
|
||||
|
||||
if xt_parts.len() != 3 {
|
||||
return Err(MagnetParseError::InvalidFieldFormat("xt".to_string()));
|
||||
}
|
||||
|
||||
if xt_parts[0] != "urn" {
|
||||
return Err(MagnetParseError::InvalidFieldFormat("xt".to_string()));
|
||||
}
|
||||
|
||||
let urn = xt_parts[1].to_string();
|
||||
let hash = HashValue::from_hex(xt_parts[2]);
|
||||
|
||||
let Some(dn_kv) = magnet.next() else {
|
||||
return Err(MagnetParseError::MissingField("dn".to_string()));
|
||||
};
|
||||
|
||||
let dn_kv = dn_kv.split("=").collect::<Vec<&str>>();
|
||||
if dn_kv.len() != 2 {
|
||||
return Err(MagnetParseError::InvalidFieldFormat("dn".to_string()));
|
||||
}
|
||||
if dn_kv[0] != "dn" {
|
||||
return Err(MagnetParseError::InvalidFieldFormat("dn".to_string()));
|
||||
}
|
||||
let dn = parts[1].to_string();
|
||||
|
||||
let Some(url) = magnet.next() else {
|
||||
return Err(MagnetParseError::MissingField("url".to_string()));
|
||||
};
|
||||
|
||||
let decoded: HashMap<String, String> =
|
||||
serde_urlencoded::from_str(&url).map_err(|_| MagnetParseError::InvalidMagnet)?;
|
||||
if let Some(url_val) = decoded.get("tr") {
|
||||
Ok(Magnet::new(urn, hash, dn, url_val.to_string()))
|
||||
} else {
|
||||
Err(MagnetParseError::MissingField("url".to_string()))
|
||||
}
|
||||
}
|
||||
15
src/main.rs
15
src/main.rs
@ -1,5 +1,6 @@
|
||||
use bittorrent_starter_rust::{
|
||||
encode::bedecode::decode_bencoded_single_value,
|
||||
encode::{bedecode::decode_bencoded_single_value, hash::HashValue},
|
||||
magnet::parse::parse_magnet,
|
||||
ouput_gen::{display, display_ips, display_peer, set_logging_enabled},
|
||||
tcp::{
|
||||
downloader::{download_and_save_file, download_and_save_sing_piece},
|
||||
@ -84,14 +85,24 @@ async fn run(command: &str, obj: &[String]) {
|
||||
//.collect();
|
||||
let _ = download_and_save_file(parsed_file, ips, obj[1].clone()).await;
|
||||
}
|
||||
"magnet_parse" => {
|
||||
let magnet = &obj[0];
|
||||
println!("{}", parse_magnet(magnet).unwrap());
|
||||
}
|
||||
_ => eprintln!("unknown command: {}", command),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
set_logging_enabled(false);
|
||||
//set_logging_enabled(false);
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let command = &args[1];
|
||||
run(command, &args[2..]).await;
|
||||
}
|
||||
|
||||
pub fn main_f() {
|
||||
let foo = "magnet:?xt=urn:btih:d69f91e6b2ae4c542468d1073a71d4ea13879a7f&dn=sample.torrent&tr=http%3A%2F%2Fbittorrent-test-tracker.codecrafters.io%2Fannounce";
|
||||
|
||||
println!("{:?}", parse_magnet(foo));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user