rsurl 0.1.7

A pure-Rust implementation of curl. Library, C FFI, and CLI for HTTP/HTTPS/FTP/FTPS.
Documentation
//! BitTorrent support: load metadata from a `.torrent` file or `magnet:` link,
//! discover peers (HTTP/UDP trackers, DHT), exchange pieces over the peer wire
//! protocol, verify and write data, and optionally seed.
//!
//! Built on the crate's existing primitives — `purecrypto` (SHA-1, RNG),
//! `crate::net` (UDP/TCP), `crate::http` (HTTP trackers) — and `std` threads +
//! channels; no async runtime and no new external dependency.
//!
//! The full client is implemented here:
//!
//! * **Metadata** — bencode codec ([`bencode`]), `.torrent` metainfo parsing +
//!   infohash ([`metainfo`]), `magnet:` link parsing ([`magnet`]), and
//!   metadata-from-peers fetch for magnets via `ut_metadata` ([`metadata`]).
//! * **Peer discovery** — HTTP and UDP trackers ([`tracker`]) and the
//!   mainline DHT ([`dht`]).
//! * **Transfer** — the peer wire protocol ([`peer`]), a rarest-first piece
//!   picker ([`picker`]), on-disk storage with SHA-1 verification
//!   ([`storage`]), and the threaded swarm engine that drives them
//!   ([`engine`], [`mod@download`]).
//! * **Seeding** — serve verified pieces back to the swarm ([`seed`]).

pub mod bencode;
pub mod dht;
pub mod download;
pub mod engine;
pub mod magnet;
pub mod metadata;
pub mod metainfo;
pub mod peer;
pub mod picker;
pub mod seed;
pub mod storage;
pub mod tracker;

pub use bencode::Value;
pub use download::{download, download_window, Progress, SeedMode, Stats, TorrentOptions};
pub use magnet::Magnet;
pub use metadata::fetch_metainfo;
pub use metainfo::{FileEntry, Metainfo};
pub use picker::{Bitfield, Picker};
pub use storage::Storage;
pub use tracker::{announce, AnnounceParams, AnnounceResponse, Event};

use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::path::{Path, PathBuf};

use purecrypto::rng::{OsRng, RngCore};

use crate::error::{Error, Result};

/// Decode a 6-byte BitTorrent compact peer address: 4-byte IPv4 followed by a
/// 2-byte big-endian port. Shared by the tracker and DHT compact-peer parsers.
/// `c` must be at least 6 bytes (callers pass exact 6-byte chunks).
pub(crate) fn compact_v4(c: &[u8]) -> SocketAddr {
    let ip = Ipv4Addr::new(c[0], c[1], c[2], c[3]);
    let port = u16::from_be_bytes([c[4], c[5]]);
    SocketAddr::V4(SocketAddrV4::new(ip, port))
}

/// Resolve each file in `meta` to an absolute path under `base` (the file
/// `path`s already carry the torrent's top directory for multi-file torrents).
pub fn file_layout(meta: &Metainfo, base: &Path) -> Vec<(PathBuf, u64)> {
    meta.files
        .iter()
        .map(|f| (base.join(&f.path), f.length))
        .collect()
}

/// Generate a 20-byte BitTorrent peer id with the Azureus-style prefix
/// `-RS` + version, the rest random. Fails closed if no OS entropy is
/// available (a predictable id is worse than an error).
pub fn generate_peer_id() -> Result<[u8; 20]> {
    let mut id = [0u8; 20];
    std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        OsRng.fill_bytes(&mut id);
    }))
    .map_err(|_| Error::BadResponse("bittorrent: no secure entropy source".into()))?;
    // Peer-id convention: "-<2-char client><4-digit version>-" then random.
    let prefix = b"-RS0001-";
    id[..prefix.len()].copy_from_slice(prefix);
    Ok(id)
}