Skip to content

Commit

Permalink
Introducing Zung Torrent. (#31)
Browse files Browse the repository at this point in the history
(WIP) A custom implementation of torrent in rust as a library and a client.
  • Loading branch information
ishaan26 authored Dec 20, 2024
2 parents 3fb4a4e + 3bcedee commit 7dff116
Show file tree
Hide file tree
Showing 28 changed files with 3,922 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ Cargo.lock

/target

.DS_Store

lol*
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ readme = "README.md"
keywords = ["projects", "learning", "monolith"]

[workspace]
members = ["zung_mini", "zung_parsers"]
members = ["zung_mini", "zung_parsers", "zung_torrent"]
resolver = "2"

[dependencies]
zung_mini = { version = "0.4.0", path = "./zung_mini" }
zung_parsers = { version = "0.1.1", path = "./zung_parsers" }
zung_torrent = { version = "0.1.0", path = "./zung_torrent" }

anyhow = "1.0.89"
anstyle = "1.0.8"
clap = { version = "4.5.20", features = ["derive"] }
tokio = "1.41.1"


[profile.release]
Expand Down
13 changes: 13 additions & 0 deletions TODOs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ZUNG TODOs

## ZUNG_PARSERS

- Value enum should have better methonds.
- from_value deserializer to the implemented.

## ZUNG_TORRENT

- FileTree should be indexable.
- Infohash should be calculated by the MetaInfo type and not the client. I think.
- Implement Trackers.
- Write tests.
8 changes: 7 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand};

use zung_mini::MiniArgs;
use zung_parsers::ParserArgs;
use zung_torrent::TorrentArgs;

#[derive(Parser)]
#[command(author, version, about, long_about = None, styles=get_styles())] // Read from `Cargo.toml`
Expand All @@ -17,14 +18,19 @@ enum Commands {

/// Parsers for different data formats
Parsers(ParserArgs),

/// Torrent Client
Torrent(TorrentArgs),
}

fn main() -> anyhow::Result<()> {
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();

match cli.commands {
Commands::Mini(mini_args) => mini_args.run(),
Commands::Parsers(bencode_args) => bencode_args.run()?,
Commands::Torrent(torrent_args) => torrent_args.run().await?,
}

Ok(())
Expand Down
9 changes: 9 additions & 0 deletions utilities/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "utilities"
version = "0.1.0"
edition = "2021"
authors = ["Ishaan Goel <[email protected]>"]

[dependencies]

zung_torrent = { version = "0.1.0", path = "../zung_torrent" }

Large diffs are not rendered by default.

277 changes: 277 additions & 0 deletions utilities/sample_torrents/MIT6.00SCS11_archive.torrent

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions utilities/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod torrent;
46 changes: 46 additions & 0 deletions utilities/src/torrent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::{path::PathBuf, sync::LazyLock};
use zung_torrent::*;

pub struct TestClient {
pub arch: Client,
pub mit: Client,
pub kali: Client,
pub mc: Client,
}

impl TestClient {
pub fn new() -> Self {
// Contains only url-list and no announce field
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("sample_torrents/archlinux-2024.04.01-x86_64.iso.torrent");
let arch = Client::new(path).expect("Unable to open the arch torrrent");

// Contains both url-list and announce field
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("sample_torrents/MIT6.00SCS11_archive.torrent");
let mit = Client::new(path).expect("Unable to read mit torrent");

let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("sample_torrents/kali-linux-2024.1-installer-amd64.iso.torrent");
let kali = Client::new(path).expect("Unable to read kali torrent");

let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("sample_torrents/MC_GRID-7f06f8280a3b496f2af0f78131ced619df14a0c3.torrent");
let mc = Client::new(path).expect("Unable to read kali torrent");

TestClient {
arch,
mit,
kali,
mc,
}
}
}

impl Default for TestClient {
fn default() -> Self {
Self::new()
}
}

pub static CLIENT: LazyLock<TestClient> = LazyLock::new(TestClient::new);
4 changes: 2 additions & 2 deletions zung_parsers/src/bencode/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
V: Visitor<'de>,
{
if self.peek_byte()? != b'i' {
return Err(Error::InvalidType("Expcted String length".to_string()));
return Err(Error::InvalidType("Expected String length".to_string()));
}

visitor.visit_i64(self.bencode.parse_integer()?)
Expand Down Expand Up @@ -261,7 +261,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
visitor.visit_string(parsed)
}
e => Err(Error::InvalidType(format!(
"Expcted String length, found '{}'",
"Expected String length, found '{}'",
std::str::from_utf8(&[e]).expect("Invalid utf8 character in string len")
))),
}
Expand Down
6 changes: 4 additions & 2 deletions zung_parsers/src/bencode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ pub use value::Value;
use std::collections::HashMap;
use value::ValueInput;

// Allow only str or bytes to be parsed. Public function is to_value
pub(crate) fn parse<'a, T>(input: T) -> Result<Value>
/// Parses the given value into bencode [Value]
///
/// Input can be either in the form of bytes or string
pub fn parse<'a, T>(input: T) -> Result<Value>
where
T: Into<ValueInput<'a>>,
{
Expand Down
22 changes: 13 additions & 9 deletions zung_parsers/src/bencode/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{
ser::{SerializeMap, SerializeSeq},
Deserialize, Serialize, Serializer,
Serialize, Serializer,
};

use std::{
Expand All @@ -9,8 +9,7 @@ use std::{
};

/// Representation of Bencode values in Rust.
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(untagged)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
/// Represents an integer value in Bencode. Bencoded integers are prefixed with `i` and
/// suffixed with `e` (e.g., `i42e` for the integer 42).
Expand All @@ -35,18 +34,24 @@ pub enum Value {
Dictionary(HashMap<String, Value>),
}

impl Value {
pub fn get_from_dictionary(&self, index: &str) -> Option<&Value> {
if let Value::Dictionary(map) = self {
map.get(index)
} else {
None
}
}
}

impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Integer(i) => serializer.serialize_i64(*i),
Value::Bytes(b) => {
// Convert bytes to hexadecimal string
let hex_string = hex::encode(b);
serializer.serialize_str(&hex_string)
}
Value::Bytes(b) => serializer.serialize_bytes(b),
Value::String(s) => serializer.serialize_str(s),
Value::List(l) => {
let mut seq = serializer.serialize_seq(Some(l.len()))?;
Expand Down Expand Up @@ -196,7 +201,6 @@ mod tests {
let value = Value::Dictionary(dict);

let result = value.to_string();
dbg!(&result);
assert!(result.contains("key1: 10"));
assert!(result.contains("key2: value"));
}
Expand Down
40 changes: 40 additions & 0 deletions zung_torrent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name = "zung_torrent"
version = "0.1.0"
edition = "2021"
authors = ["Ishaan Goel <[email protected]>"]
description = "A BitTorrent Client Library"
license = "MIT"
repository = "https://github.com/ishaan26/zung"
readme = "README.md"
keywords = ["projects", "learning", "torrent", "protocol"]

[features]
default = ["client"]
client = ["dep:colored", "dep:human_bytes"]

[dependencies]
anyhow = "1.0.89"
bytes = { version = "1.7.2", features = ["serde"] }
clap = { version = "4.5.18", features = ["derive"] }
hex = "0.4.3"
chrono = { version = "0.4.38", features = ["serde"] }
sha1_smol = "1.0.1"
rayon = "1.10.0"
indexmap = "2.6.0"
rand = "0.8.5"
tokio = { version = "1.41.1", features = ["full"] }

colored = { version = "2.1.0", optional = true }
human_bytes = { version = "0.4.3", optional = true }


serde = { version = "1.0.210", features = ["derive"] }
serde_bytes = "0.11.15"
serde_urlencoded = "0.7.1"

zung_parsers = { version = "0.1.1", path = "../zung_parsers" }
futures = "0.3.31"

[dev-dependencies]
utilities = { path = "../utilities" }
29 changes: 29 additions & 0 deletions zung_torrent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Zung Torrent - A BitTorrent Client Library

This Library is an implementation of the [The BitTorrent Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html)

## Zung Family

This library is part of the [zung](https://github.com/ishaan26/zung) family.
Install the zung binary with `cargo install zung` to try out some of the features of this
library.

## Disclaimer

_This library is intended for **learning purposes only**. While I will do my best to write the most professional code I can (with my limited coding knowledge), it is not my intention for this library to be used in any production environment._

Ateast not yet...

## Table of Contents

- [Introduction](#introduction)

# Introduction

BitTorrent is a peer-to-peer file sharing protocol designed by Bram Cohen. BitTorrent is designed to facilitate file transfers among multiple peers across unreliable networks.

BitTorrent is a protocol for distributing files. It identifies content by URL and is designed to integrate seamlessly with the web. Its advantage over plain HTTP is that when multiple downloads of the same file happen concurrently, the downloaders upload to each other, making it possible for the file source to support very large numbers of downloaders with only a modest increase in its load.

# Usage

TODO: Update
Loading

0 comments on commit 7dff116

Please sign in to comment.