Compare commits

1 Commits

Author SHA1 Message Date
104f94a707 update 2017-12-13 00:02:24 -05:00
18 changed files with 840 additions and 976 deletions

View File

@@ -5,9 +5,15 @@ authors = ["Simon Bernier St-Pierre <sbernierstpierre@gmail.com>"]
[dependencies] [dependencies]
byteorder = "0.5" byteorder = "0.5"
libc = "0.2" failure = "0.1"
rand = "0.3"
reqwest = "0.8"
serde = "1.0"
serde_bencode = "0.2.0"
serde_derive = "1.0"
serde_bytes = "0.10"
sha1 = "0.2" sha1 = "0.2"
url = "1.2" url = "1.6"
[dependencies.hyper] [dependencies.hyper]
version = "0.9" version = "0.9"

View File

@@ -1,38 +0,0 @@
extern crate magnolia;
use std::env;
use std::fs::File;
use std::io::{self, Read};
use std::thread;
use std::time::Duration;
use std::str;
use magnolia::bencode::*;
use magnolia::metainfo::Metainfo;
use magnolia::tracker::http;
use magnolia::net::session::Session;
fn load_file(path: &str) -> io::Result<()> {
let mut buf = Vec::new();
let mut f = File::open(path)?;
f.read_to_end(&mut buf)?;
let obj = decode(&buf).unwrap();
let meta = Metainfo::from_bencode(obj).unwrap();
println!("{}", meta.pieces.len());
let s = Session::new();
s.add_torrent(meta);
loop {
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
fn main() {
let path = env::args().nth(1).expect("need path to .torrent file");
load_file(&path).unwrap();
}

View File

@@ -0,0 +1,19 @@
extern crate failure;
extern crate magnolia;
use std::env;
use failure::Fail;
use magnolia::metainfo::Metainfo;
fn main() {
let args: Vec<String> = env::args().collect();
match Metainfo::open(&args[1]) {
Ok(meta) => println!("{:?}", meta),
Err(e) => {
for cause in e.causes() {
println!("{}", cause);
}
}
}
}

6
rustfmt.toml Normal file
View File

@@ -0,0 +1,6 @@
same_line_attributes = false
reorder_imports = true
reorder_imports_in_group = true
reorder_extern_crates = true
reorder_extern_crates_in_group = true
indent_style = "Visual"

View File

@@ -1,96 +0,0 @@
use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
pub struct Buffer<'b> {
inner: &'b [u8],
pos: usize,
}
impl<'b> Buffer<'b> {
pub fn new(inner: &[u8]) -> Buffer {
Buffer {
inner: inner,
pos: 0,
}
}
pub fn advance(&mut self, amt: usize) {
self.pos += amt
}
pub fn find(&self, byte: u8) -> Option<usize> {
self[..].iter().position(|&b| b == byte)
}
pub fn get(&self, idx: usize) -> Option<u8> {
self[..].get(idx).map(|&b| b)
}
pub fn pos(&self) -> usize {
self.pos
}
}
impl<'b> Index<Range<usize>> for Buffer<'b> {
type Output = [u8];
fn index(&self, r: Range<usize>) -> &[u8] {
&self.inner[self.pos + r.start..self.pos + r.end]
}
}
impl<'b> Index<RangeFrom<usize>> for Buffer<'b> {
type Output = [u8];
fn index(&self, r: RangeFrom<usize>) -> &[u8] {
&self.inner[self.pos + r.start..]
}
}
impl<'b> Index<RangeTo<usize>> for Buffer<'b> {
type Output = [u8];
fn index(&self, r: RangeTo<usize>) -> &[u8] {
&self.inner[self.pos..self.pos + r.end]
}
}
impl<'b> Index<RangeFull> for Buffer<'b> {
type Output = [u8];
fn index(&self, _: RangeFull) -> &[u8] {
&self.inner[self.pos..]
}
}
#[test]
fn test_advance() {
let mut b = Buffer::new(b"hello");
b.advance(2);
assert_eq!(b.pos(), 2);
}
#[test]
fn test_find() {
let b = Buffer::new(b"hello");
assert_eq!(b.find(b'l'), Some(2));
assert_eq!(b.find(b'a'), None);
}
#[test]
fn test_get() {
let mut b = Buffer::new(b"hello");
b.advance(2);
assert_eq!(b.get(0), Some(b'l'));
assert_eq!(b.get(1), Some(b'l'));
assert_eq!(b.get(2), Some(b'o'));
}
#[test]
fn test_range() {
let mut b = Buffer::new(b"hello");
b.advance(2);
assert_eq!(b[1..2], b"l"[..]);
assert_eq!(b[1..], b"lo"[..]);
assert_eq!(b[..2], b"ll"[..]);
assert_eq!(b[..], b"llo"[..]);
}

View File

@@ -1,145 +0,0 @@
use std::collections::BTreeMap;
use std::num::ParseIntError;
use std::str::{self, Utf8Error};
use bencode::{Bytes, Object};
use bencode::buffer::Buffer;
#[derive(Debug)]
pub struct DecodeError;
impl From<Utf8Error> for DecodeError {
fn from(_: Utf8Error) -> Self {
DecodeError
}
}
impl From<ParseIntError> for DecodeError {
fn from(_: ParseIntError) -> Self {
DecodeError
}
}
pub type DecodeResult<T> = Result<T, DecodeError>;
pub fn decode(data: &[u8]) -> DecodeResult<Object> {
let mut buf = Buffer::new(data);
decode_object(&mut buf)
}
fn decode_object(buf: &mut Buffer) -> DecodeResult<Object> {
match buf.get(0) {
Some(b'i') => {
buf.advance(1);
decode_int(buf, b'e')
},
Some(b'0' ... b'9') => {
decode_bytes(buf)
}
Some(b'l') => {
buf.advance(1);
let mut list = Vec::new();
while let Some(term) = buf.get(0) {
if term == b'e' {
buf.advance(1);
break;
}
list.push(decode_object(buf)?);
}
Ok(list.into())
}
Some(b'd') => {
buf.advance(1);
let mut dict = BTreeMap::new();
while let Some(term) = buf.get(0) {
if term == b'e' {
buf.advance(1);
break;
}
let key = _decode_bytes(buf)?;
let val = decode_object(buf)?;
dict.insert(Bytes(key), val);
}
Ok(dict.into())
}
_ => Err(DecodeError),
}
}
fn decode_int(buf: &mut Buffer, term: u8) -> DecodeResult<Object> {
_decode_int(buf, term).map(|num| Object::Int(num))
}
fn _decode_int(buf: &mut Buffer, term: u8) -> DecodeResult<i64> {
if let Some(end) = buf.find(term) {
let obj = {
let num = str::from_utf8(&buf[..end])?;
num.parse()?
};
buf.advance(end + 1);
Ok(obj)
} else {
Err(DecodeError)
}
}
fn decode_bytes(buf: &mut Buffer) -> DecodeResult<Object> {
_decode_bytes(buf).map(Into::into)
}
fn _decode_bytes(buf: &mut Buffer) -> Result<Vec<u8>, DecodeError> {
let size = _decode_int(buf, b':')? as usize;
let bytes = buf[..size].to_vec();
buf.advance(size);
Ok(bytes)
}
#[test]
fn test_int_pos() {
let mut buf = Buffer::new(b"i1337e");
buf.advance(1);
assert_eq!(decode_int(&mut buf, b'e').unwrap(), Object::Int(1337));
assert_eq!(buf.pos(), 6);
}
#[test]
fn test_int_neg() {
let mut buf = Buffer::new(b"i-1337e");
buf.advance(1);
assert_eq!(decode_int(&mut buf, b'e').unwrap(), Object::Int(-1337));
assert_eq!(buf.pos(), 7);
}
#[test]
fn test_bytes() {
let mut buf = Buffer::new(b"5:hello");
assert_eq!(decode_bytes(&mut buf).unwrap(), "hello".into());
assert_eq!(buf.pos(), 7);
}
#[test]
fn test_list() {
use bencode::List;
let mut buf = Buffer::new(b"li1ei2ei3ee");
let obj = decode_object(&mut buf).unwrap();
let list = obj.as_list().unwrap();
assert_eq!(list, &List(vec![Object::Int(1), Object::Int(2), Object::Int(3)]));
assert_eq!(buf.pos(), 11);
}
#[test]
fn test_dict() {
let mut buf = Buffer::new(b"d5:helloi1337ee");
let obj = decode_object(&mut buf).unwrap();
let dict = obj.as_dict().unwrap();
assert_eq!(dict.get_int("hello").unwrap(), 1337);
assert_eq!(buf.pos(), 15);
}

View File

@@ -1,92 +0,0 @@
use std::borrow::Cow;
use bencode::{Dict, Object};
/// Allows the encode function to take something that can be converted into an object,
/// or a reference to an object.
pub trait Encodable<'a> {
fn get_object(self) -> Cow<'a, Object>;
}
impl<'a, T> Encodable<'a> for T where T: Into<Object> {
fn get_object(self) -> Cow<'a, Object> {
Cow::Owned(self.into())
}
}
impl<'a> Encodable<'a> for &'a Object {
fn get_object(self) -> Cow<'a, Object> {
Cow::Borrowed(self)
}
}
pub fn encode<'a, O>(obj: O) -> Vec<u8> where O: Encodable<'a> {
let mut buff = Vec::new();
encode_object(&mut buff, &obj.get_object());
buff
}
fn encode_object(buff: &mut Vec<u8>, obj: &Object) {
match *obj {
Object::Int(num) => encode_int(buff, num),
Object::Bytes(ref bytes) => encode_bytes(buff, bytes),
Object::List(ref list) => encode_list(buff, list),
Object::Dict(ref dict) => encode_dict(buff, dict),
}
}
fn encode_int(buff: &mut Vec<u8>, num: i64) {
buff.extend_from_slice(format!("i{}e", num).as_bytes());
}
fn encode_bytes(buff: &mut Vec<u8>, bytes: &[u8]) {
buff.extend_from_slice(format!("{}:", bytes.len()).as_bytes());
buff.extend_from_slice(bytes);
}
fn encode_list(buff: &mut Vec<u8>, list: &[Object]) {
buff.push(b'l');
for obj in list {
encode_object(buff, obj);
}
buff.push(b'e');
}
fn encode_dict(buff: &mut Vec<u8>, dict: &Dict) {
buff.push(b'd');
for (key, val) in dict.iter() {
encode_bytes(buff, key);
encode_object(buff, val);
}
buff.push(b'e');
}
#[test]
fn test_int_pos() {
assert_eq!(encode(1337), b"i1337e");
}
#[test]
fn test_int_neg() {
assert_eq!(encode(-1337), b"i-1337e");
}
#[test]
fn test_bytes() {
assert_eq!(encode("hello"), b"5:hello");
}
#[test]
fn test_list() {
let list = vec![Object::Int(1), Object::Int(2), Object::Int(3)];
assert_eq!(encode(list), b"li1ei2ei3ee");
}
#[test]
fn test_dict() {
use bencode::Dict;
let mut dict = Dict::new();
dict.insert("hello", 1337);
assert_eq!(encode(dict), b"d5:helloi1337ee")
}

View File

@@ -1,305 +0,0 @@
mod buffer;
mod decode;
mod encode;
use std::collections::BTreeMap;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::str;
pub use self::decode::{decode, DecodeError, DecodeResult};
pub use self::encode::{encode, Encodable};
#[derive(Clone, Eq, PartialEq)]
pub enum Object {
Int(i64),
Bytes(Bytes),
List(List),
Dict(Dict),
}
impl Object {
pub fn as_int(&self) -> Option<i64> {
match *self {
Object::Int(num) => Some(num),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&Bytes> {
match *self {
Object::Bytes(ref bytes) => Some(bytes),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
self.as_bytes().and_then(|b| str::from_utf8(b).ok())
}
pub fn as_string(&self) -> Option<String> {
self.as_str().map(|s| s.to_string())
}
pub fn as_list(&self) -> Option<&List> {
match *self {
Object::List(ref list) => Some(list),
_ => None,
}
}
pub fn as_dict(&self) -> Option<&Dict> {
match *self {
Object::Dict(ref dict) => Some(dict),
_ => None,
}
}
}
impl fmt::Debug for Object {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Object::Int(num) => write!(f, "{}", num),
Object::Bytes(ref bytes) => bytes.fmt(f),
Object::List(ref list) => list.fmt(f),
Object::Dict(ref dict) => dict.fmt(f),
}
}
}
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Bytes(Vec<u8>);
impl Bytes {
pub fn new<A>(bytes: A) -> Bytes where A: Into<Vec<u8>> {
Bytes(bytes.into())
}
pub fn str(&self) -> Option<&str> {
str::from_utf8(&self.0).ok()
}
pub fn string(&self) -> Option<String> {
self.str().map(|s| s.to_string())
}
}
impl Deref for Bytes {
type Target = Vec<u8>;
fn deref(&self) -> &Vec<u8> {
&self.0
}
}
impl DerefMut for Bytes {
fn deref_mut(&mut self) -> &mut Vec<u8> {
&mut self.0
}
}
impl fmt::Debug for Bytes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match str::from_utf8(&self.0) {
Ok(s) => write!(f, "\"{}\"", s),
Err(_) => {
write!(f, "\"")?;
for &b in self.0.iter() {
write!(f, "{:x}", b)?;
}
write!(f, "\"")?;
Ok(())
}
}
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct List(Vec<Object>);
impl List {
pub fn new() -> List {
List(Vec::new())
}
pub fn push<V>(&mut self, val: V) where V: Into<Object> {
self.0.push(val.into())
}
}
impl Deref for List {
type Target = Vec<Object>;
fn deref(&self) -> &Vec<Object> {
&self.0
}
}
impl DerefMut for List {
fn deref_mut(&mut self) -> &mut Vec<Object> {
&mut self.0
}
}
impl fmt::Debug for List {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct Dict(BTreeMap<Bytes, Object>);
impl Dict {
pub fn new() -> Dict {
Dict(BTreeMap::new())
}
pub fn insert<K, V>(&mut self, key: K, val: V) where K: Into<Bytes>, V: Into<Object> {
self.0.insert(key.into(), val.into());
}
pub fn get_object<A>(&self, key: A) -> Option<&Object> where A: Into<Bytes> {
self.0.get(&key.into())
}
pub fn get_int<A>(&self, key: A) -> Option<i64> where A: Into<Bytes> {
self.0.get(&key.into()).and_then(|o| o.as_int())
}
pub fn get_bytes<A>(&self, key: A) -> Option<&Bytes> where A: Into<Bytes> {
self.0.get(&key.into()).and_then(|o| o.as_bytes())
}
pub fn get_str<A>(&self, key: A) -> Option<&str> where A: Into<Bytes> {
self.0.get(&key.into()).and_then(|o| o.as_bytes().and_then(|b| str::from_utf8(b).ok()))
}
pub fn get_string<A>(&self, key: A) -> Option<String> where A: Into<Bytes> {
self.get_str(key).map(|s| s.to_string())
}
pub fn get_list<A>(&self, key: A) -> Option<&List> where A: Into<Bytes> {
self.0.get(&key.into()).and_then(|o| o.as_list())
}
pub fn get_dict<A>(&self, key: A) -> Option<&Dict> where A: Into<Bytes> {
self.0.get(&key.into()).and_then(|o| o.as_dict())
}
}
impl Deref for Dict {
type Target = BTreeMap<Bytes, Object>;
fn deref(&self) -> &BTreeMap<Bytes, Object> {
&self.0
}
}
impl DerefMut for Dict {
fn deref_mut(&mut self) -> &mut BTreeMap<Bytes, Object> {
&mut self.0
}
}
impl fmt::Debug for Dict {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_map()
.entries(self.iter())
.finish()
}
}
// convertion utilities
// int
impl From<i64> for Object {
fn from(from: i64) -> Self {
Object::Int(from)
}
}
// bytes
impl<'a> From<&'a [u8]> for Bytes {
fn from(from: &'a [u8]) -> Self {
Bytes(from.to_vec())
}
}
impl<'a> From<&'a str> for Bytes {
fn from(from: &'a str) -> Self {
Bytes(from.as_bytes().to_vec())
}
}
impl From<Vec<u8>> for Bytes {
fn from(from: Vec<u8>) -> Self {
Bytes(from)
}
}
impl<'a> From<&'a [u8]> for Object {
fn from(from: &'a [u8]) -> Self {
Object::Bytes(Bytes(from.to_vec()))
}
}
impl<'a> From<&'a str> for Object {
fn from(from: &'a str) -> Self {
Object::Bytes(Bytes(from.as_bytes().to_vec()))
}
}
impl From<Vec<u8>> for Object {
fn from(from: Vec<u8>) -> Self {
Object::Bytes(Bytes(from))
}
}
impl From<Bytes> for Object {
fn from(from: Bytes) -> Self {
Object::Bytes(from)
}
}
// list
impl<T> From<Vec<T>> for List where T: Into<Object> {
fn from(from: Vec<T>) -> Self {
List(from.into_iter().map(Into::into).collect())
}
}
impl<T> From<Vec<T>> for Object where T: Into<Object> {
fn from(from: Vec<T>) -> Self {
Object::List(List(from.into_iter().map(Into::into).collect()))
}
}
impl From<List> for Object {
fn from(from: List) -> Self {
Object::List(from)
}
}
// dict
impl From<BTreeMap<Bytes, Object>> for Dict {
fn from(from: BTreeMap<Bytes, Object>) -> Self {
Dict(from)
}
}
impl From<BTreeMap<Bytes, Object>> for Object {
fn from(from: BTreeMap<Bytes, Object>) -> Self {
Object::Dict(Dict(from))
}
}
impl From<Dict> for Object {
fn from(from: Dict) -> Self {
Object::Dict(from)
}
}

183
src/bitfield.rs Normal file
View File

@@ -0,0 +1,183 @@
use std::u32;
pub struct Bitfield {
inner: Vec<u32>,
len: usize,
}
impl Bitfield {
pub fn new(len: usize) -> Bitfield {
Bitfield {
inner: vec![0u32; f64::ceil(len as f64 / 32.) as usize],
len: len,
}
}
#[inline]
fn calc_size(&self, idx: usize) -> (usize, usize) {
(idx / 32, idx % 32)
}
#[inline]
fn check_index(&self, idx: usize) {
if idx >= self.len() {
panic!("index out of range");
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn get(&self, idx: usize) -> bool {
self.check_index(idx);
let (index, offset) = self.calc_size(idx);
unsafe { self.inner.get_unchecked(index) & (1 << offset) != 0 }
}
#[inline]
pub fn set(&mut self, idx: usize, val: bool) {
self.check_index(idx);
let (index, offset) = self.calc_size(idx);
let word = unsafe { self.inner.get_unchecked_mut(index) };
if val {
*word |= 1 << offset;
} else {
*word &= !(1 << offset);
}
}
#[inline]
pub fn iter(&self) -> Iter {
Iter {
bitfield: self,
index: 0,
}
}
#[inline]
pub fn num_set(&self) -> usize {
self.inner
.iter()
.map(|&x| x.count_ones() as usize)
.sum::<usize>()
}
#[inline]
pub fn ratio(&self) -> f64 {
self.num_set() as f64 / self.len() as f64
}
#[inline]
pub fn all(&self) -> bool {
self.num_set() == self.len()
}
}
pub struct Iter<'a> {
bitfield: &'a Bitfield,
index: usize,
}
impl<'a> Iterator for Iter<'a> {
type Item = bool;
fn next(&mut self) -> Option<bool> {
if self.index < self.bitfield.len() {
let bit = self.bitfield.get(self.index);
self.index += 1;
Some(bit)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.bitfield.len(), Some(self.bitfield.len()))
}
}
#[test]
fn test_new_len() {
let b = Bitfield::new(50);
assert_eq!(b.len, 50);
assert_eq!(b.inner.len(), 2);
assert_eq!(b.len(), 50);
// make sure every bit is set to 0
for i in 0..b.len() {
assert_eq!(b.get(i), false);
}
}
#[test]
fn test_get_set() {
let mut b = Bitfield::new(150);
b.set(0, true);
b.set(45, true);
b.set(140, true);
assert_eq!(b.get(0), true);
assert_eq!(b.get(45), true);
assert_eq!(b.get(140), true);
b.set(140, false);
assert_eq!(b.get(140), false);
}
#[test]
#[should_panic]
fn test_get_outside() {
let b = Bitfield::new(2);
b.get(2);
}
#[test]
#[should_panic]
fn test_set_outside() {
let mut b = Bitfield::new(2);
b.set(2, true);
}
#[test]
fn test_iter() {
let mut b = Bitfield::new(5);
b.set(0, true);
b.set(2, true);
b.set(4, true);
assert_eq!(
b.iter().collect::<Vec<_>>(),
vec![true, false, true, false, true]
);
}
#[test]
fn test_num_set() {
let mut b = Bitfield::new(70);
b.set(0, true);
b.set(45, true);
b.set(68, true);
assert_eq!(b.num_set(), 3);
b.set(45, false);
assert_eq!(b.num_set(), 2);
}
#[test]
fn test_ratio() {
let mut b = Bitfield::new(4);
b.set(0, true);
b.set(1, true);
assert_eq!(b.ratio(), 0.5);
}
#[test]
fn test_all() {
let mut b = Bitfield::new(2);
b.set(0, true);
assert_eq!(b.all(), false);
b.set(1, true);
assert_eq!(b.all(), true);
}

69
src/error.rs Normal file
View File

@@ -0,0 +1,69 @@
use std::io;
use failure;
use reqwest;
use serde_bencode;
use url;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "Bencode Error {}", _0)]
BencodeError(#[cause]
serde_bencode::Error),
#[fail(display = "Http Error {}", _0)]
HttpError(#[cause]
reqwest::Error),
#[fail(display = "IO Error {}", _0)]
IoError(#[cause]
io::Error),
#[fail(display = "Tracker Error {}", _0)]
Tracker(String),
#[fail(display = "URL Error {}", _0)]
UrlError(#[cause]
url::ParseError),
#[fail(display = "Error {}", _0)]
Custom(failure::Error),
}
impl Error {
pub fn tracker<M>(msg: M) -> Self
where M: Into<String>
{
Error::Tracker(msg.into())
}
}
impl From<serde_bencode::Error> for Error {
fn from(err: serde_bencode::Error) -> Error {
Error::BencodeError(err)
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Error {
Error::HttpError(err)
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::IoError(err)
}
}
impl From<url::ParseError> for Error {
fn from(err: url::ParseError) -> Error {
Error::UrlError(err)
}
}
impl From<failure::Error> for Error {
fn from(err: failure::Error) -> Error {
Error::Custom(err)
}
}

View File

@@ -1,10 +1,21 @@
extern crate byteorder; extern crate byteorder;
extern crate hyper; #[macro_use]
extern crate libc; extern crate failure;
extern crate rand;
extern crate reqwest;
extern crate serde;
extern crate serde_bencode;
extern crate serde_bytes;
#[macro_use]
extern crate serde_derive;
extern crate sha1; extern crate sha1;
extern crate url; extern crate url;
pub mod bencode; #[macro_use]
mod macros;
pub mod bitfield;
pub mod error;
pub mod metainfo; pub mod metainfo;
pub mod net; // pub mod net;
pub mod torrent;
pub mod tracker; pub mod tracker;

23
src/macros.rs Normal file
View File

@@ -0,0 +1,23 @@
#[macro_export]
macro_rules! bail2 {
($e:expr) => {
return Err(::failure::err_msg($e).into());
};
($fmt:expr, $($arg:tt)+) => {
return Err(::failure::err_msg(format!($fmt, $($arg)+)).into());
};
}
#[macro_export]
macro_rules! ensure2 {
($cond:expr, $e:expr) => {
if !($cond) {
bail2!($e);
}
};
($cond:expr, $fmt:expr, $($arg:tt)+) => {
if !($cond) {
bail2!($fmt, $($arg)+);
}
};
}

View File

@@ -1,154 +1,130 @@
use std::fmt; use std::fmt::{self, Write};
use std::fs;
use std::io::Read;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::path::PathBuf; use std::path::Path;
use rand::{thread_rng, Rng};
use serde_bencode;
use serde_bytes::ByteBuf;
use sha1::Sha1; use sha1::Sha1;
use bencode::{encode, Object}; use error::Error;
macro_rules! ts { #[derive(Debug, Deserialize, Serialize)]
( $e:expr ) => { pub struct Metainfo {
match $e { pub info: Info,
Some(x) => x, pub announce: String,
None => return None, #[serde(rename = "announce-list")]
} pub announce_list: Option<Vec<Vec<String>>>,
} #[serde(rename = "creation date")]
pub creation_date: Option<i64>,
pub comment: Option<String>,
#[serde(rename = "created by")]
pub created_by: Option<String>,
#[serde(default)]
pub encoding: Option<String>,
} }
#[derive(Debug)] #[derive(Debug, Deserialize, Serialize)]
pub struct Metainfo { #[serde(untagged)]
pub announce: String, pub enum Info {
pub announce_list: Vec<Vec<String>>, Single {
pub files: Vec<MetainfoFile>, #[serde(rename = "piece length")]
pub info_hash: Hash, piece_length: u64,
pub piece_length: u32, pieces: ByteBuf,
pub num_pieces: u32, private: Option<u8>,
pub pieces: Vec<Hash>, name: String,
length: u64,
md5sum: Option<String>,
},
Multiple {
#[serde(rename = "piece length")]
piece_length: u64,
pieces: ByteBuf,
private: Option<bool>,
name: String,
files: Vec<File>,
},
}
#[derive(Debug, Deserialize, Serialize)]
pub struct File {
pub length: u64, pub length: u64,
pub md5sum: Option<String>,
pub path: Vec<String>,
} }
impl Metainfo { impl Metainfo {
pub fn from_bencode<O>(obj: O) -> Option<Metainfo> where O: Into<Object> { /// Create a Metainfo structure from reading a file.
let obj = obj.into(); pub fn open<P: AsRef<Path>>(path: P) -> Result<Metainfo, Error> {
let metainfo = ts!(obj.as_dict()); let mut f = fs::File::open(path.as_ref())?;
let info = ts!(metainfo.get_dict("info")); let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
let announce = ts!(metainfo.get_string("announce")); let meta = serde_bencode::de::from_bytes(&buf)?;
let info_hash = sha1(&encode(ts!(metainfo.get_object("info")))); Ok(meta)
let name = ts!(info.get_str("name"));
let piece_length = ts!(info.get_int("piece length"));
let pieces = ts!(info.get_bytes("pieces"));
let mut total_length = 0;
if pieces.len() % 20 != 0 {
return None;
}
let mut announce_list = vec![];
if let Some(list) = metainfo.get_list("announce-list") {
for tier in list.iter() {
let mut urls = vec![];
for url in ts!(tier.as_list()).iter() {
urls.push(ts!(url.as_string()));
}
announce_list.push(urls);
}
}
let mut files = vec![];
if let Some(list) = info.get_list("files") {
for obj in list.iter() {
let file = ts!(obj.as_dict());
let mut path = PathBuf::new();
path.push(name);
for component in ts!(file.get_list("path")).iter() {
path.push(ts!(component.as_str()));
}
let length = ts!(file.get_int("length")) as u64;
total_length += length;
files.push(MetainfoFile {
length: length,
path: path,
});
}
} else {
let mut path = PathBuf::new();
path.push(name);
total_length = ts!(info.get_int("length")) as u64;
files.push(MetainfoFile{
length: total_length,
path: path,
})
}
let pieces = pieces.chunks(20).map(Hash::from_slice).collect::<Vec<_>>();
Some(Metainfo {
announce: announce,
announce_list: announce_list,
files: files,
info_hash: info_hash,
piece_length: piece_length as u32,
num_pieces: pieces.len() as u32,
pieces: pieces,
length: total_length,
})
} }
} }
#[derive(Debug)]
pub struct MetainfoFile {
pub length: u64,
pub path: PathBuf,
}
impl MetainfoFile {
pub fn new(length: u64, path: PathBuf) -> Self {
MetainfoFile {
length: length,
path: path,
}
}
}
fn sha1(bytes: &[u8]) -> Hash {
let mut hasher = Sha1::new();
hasher.update(bytes);
Hash::new(hasher.digest().bytes())
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct Hash([u8; 20]); pub struct Hash([u8; 20]);
impl Hash { impl Hash {
/// Create a Hash filled with zeroes.
pub fn alloc() -> Hash { pub fn alloc() -> Hash {
Hash([0u8; 20]) Hash([0u8; 20])
} }
/// Create a Hash from a 20 byte array.
pub fn new(inner: [u8; 20]) -> Hash { pub fn new(inner: [u8; 20]) -> Hash {
Hash(inner) Hash(inner)
} }
/// Create a Hash from a 20 byte slice.
///
/// # Panics
/// If the slice is not of length 20, this function panics.
pub fn from_slice(bytes: &[u8]) -> Hash { pub fn from_slice(bytes: &[u8]) -> Hash {
assert_eq!(bytes.len(), 20); assert_eq!(bytes.len(), 20);
let mut hash = Hash::alloc(); let mut hash = Hash::alloc();
hash.copy_from_slice(bytes); hash.copy_from_slice(bytes);
hash hash
} }
/// Create a Hash filled with random bytes.
pub fn random() -> Hash {
let mut hash = Hash::alloc();
thread_rng().fill_bytes(&mut hash);
hash
}
/// Compute the SHA-1 hash of the slice.
pub fn sha1(bytes: &[u8]) -> Hash {
let mut hasher = Sha1::new();
hasher.update(bytes);
Hash::new(hasher.digest().bytes())
}
/// Compute the info hash of a Metainfo structure.
pub fn info_hash(metainfo: &Metainfo) -> Hash {
let buf = serde_bencode::to_bytes(&metainfo.info).expect("unable to compute info hash");
Hash::sha1(&buf)
}
/// Create a hexadecimal representation of this Hash.
pub fn hex(&self) -> String {
let mut buf = String::with_capacity(40);
for byte in self.0.iter() {
let _ = write!(buf, "{:02x}", byte);
}
buf
}
} }
impl fmt::Debug for Hash { impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &b in self.0.iter() { write!(f, "Hash({})", self.hex())
write!(f, "{:x}", b)?
}
Ok(())
} }
} }
@@ -166,68 +142,9 @@ impl DerefMut for Hash {
} }
} }
#[cfg(test)] #[test]
mod tests { fn test_hash() {
use std::path::Path; let metainfo = Metainfo::open("samples/debian-9.1.0-amd64-netinst.iso.torrent").unwrap();
let info_hash = Hash::info_hash(&metainfo);
use bencode::{Dict, List, Object}; assert_eq!(info_hash.hex(), "fd5fdf21aef4505451861da97aa39000ed852988");
use metainfo::Metainfo;
#[test]
fn test_single_file() {
let mut meta = Dict::new();
meta.insert("announce", "http://ubuntu.com/tracker:6969");
let mut info = Dict::new();
info.insert("name", "ubuntu-16.04-desktop.iso");
info.insert("length", 1024 * 1024 * 1024);
info.insert("piece length", 1024 * 512);
info.insert("pieces", "");
meta.insert("info", info);
let metainfo = Metainfo::from_bencode(meta).unwrap();
assert_eq!(metainfo.announce, "http://ubuntu.com/tracker:6969");
assert_eq!(metainfo.piece_length, 1024 * 512);
let file = &metainfo.files[0];
assert_eq!(file.path.to_str().unwrap(), "ubuntu-16.04-desktop.iso");
assert_eq!(file.length, 1024 * 1024 * 1024);
}
#[test]
fn test_multiple_files() {
let mut meta = Dict::new();
meta.insert("announce", "http://ubuntu.com/tracker:6969");
let mut info = Dict::new();
info.insert("name", "base_folder");
info.insert("piece length", 1024 * 512);
info.insert("pieces", "");
let mut files = List::new();
let mut file1 = Dict::new();
file1.insert("path", vec!["folder2", "image.txt"]);
file1.insert("length", 512);
files.push(file1);
let mut file2 = Dict::new();
file2.insert("path", vec!["txt.jpg"]);
file2.insert("length", 64);
files.push(file2);
info.insert("files", Object::List(files));
meta.insert("info", info);
let metainfo = Metainfo::from_bencode(meta).unwrap();
assert_eq!(metainfo.announce, "http://ubuntu.com/tracker:6969");
assert_eq!(metainfo.piece_length, 1024 * 512);
let file1 = &metainfo.files[0];
assert_eq!(file1.path, Path::new("base_folder/folder2/image.txt"));
assert_eq!(file1.length, 512);
let file2 = &metainfo.files[1];
assert_eq!(file2.path, Path::new("base_folder/txt.jpg"));
assert_eq!(file2.length, 64);
}
} }

32
src/torrent.rs Normal file
View File

@@ -0,0 +1,32 @@
use metainfo::{Hash, Info, Metainfo};
pub struct Torrent {
pub info_hash: Hash,
pub metainfo: Metainfo,
pub uploaded: u64,
pub downloaded: u64,
pub left: u64,
pub size: u64,
}
impl Torrent {
pub fn new(metainfo: Metainfo) -> Self {
let size = match metainfo.info {
Info::Single { length, .. } => length,
Info::Multiple { ref files, .. } => {
let mut size = 0;
for file in files.iter() {
size += file.length;
}
size
}
};
Torrent { info_hash: Hash::info_hash(&metainfo),
metainfo: metainfo,
uploaded: 0,
downloaded: 0,
left: size,
size: size, }
}
}

184
src/tracker/bin.rs Normal file
View File

@@ -0,0 +1,184 @@
use std::error;
use std::fmt::{self, Display};
use std::io::{self, Write};
use std::marker::PhantomData;
use byteorder::{ByteOrder, WriteBytesExt};
use serde::ser::{self, Impossible};
#[derive(Debug)]
pub enum Error {
Custom(String),
Io(io::Error),
NotImplemented,
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Custom(_) => "custom",
Error::Io(ref err) => err.description(),
Error::NotImplemented => "not implemented",
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl Display for Error {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
impl ser::Error for Error {
fn custom<T>(msg: T) -> Self
where T: Display
{
Error::Custom(format!("{}", msg))
}
}
pub struct Serializer<'a, W: Write + 'a, B: ByteOrder> {
buf: &'a mut W,
_order: PhantomData<B>,
}
impl<'a, W: Write + 'a, B: ByteOrder> ser::Serializer for Serializer<'a, W, B> {
type Ok = ();
type Error = Error;
type SerializeSeq = Impossible<(), Error>;
type SerializeTuple = Impossible<(), Error>;
type SerializeTupleStruct = Impossible<(), Error>;
type SerializeTupleVariant = Impossible<(), Error>;
type SerializeMap = Impossible<(), Error>;
type SerializeStruct = Impossible<(), Error>;
type SerializeStructVariant = Impossible<(), Error>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
self.buf.write_u8(v as u8)?;
Ok(())
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
self.buf.write_i8(v)?;
Ok(())
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where T: ser::Serialize
{
Err(Error::NotImplemented)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_unit_variant(self,
name: &'static str,
variant_index: u32,
variant: &'static str)
-> Result<Self::Ok, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_newtype_struct<T: ?Sized>(self,
name: &'static str,
value: &T)
-> Result<Self::Ok, Self::Error>
where T: ser::Serialize
{
Err(Error::NotImplemented)
}
fn serialize_newtype_variant<T: ?Sized>(self,
name: &'static str,
variant_index: u32,
variant: &'static str,
value: &T)
-> Result<Self::Ok, Self::Error>
where T: ser::Serialize
{
Err(Error::NotImplemented)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_tuple_struct(self,
name: &'static str,
len: usize)
-> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_tuple_variant(self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize)
-> Result<Self::SerializeTupleVariant, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_struct(self,
name: &'static str,
len: usize)
-> Result<Self::SerializeStruct, Self::Error> {
Err(Error::NotImplemented)
}
fn serialize_struct_variant(self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize)
-> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::NotImplemented)
}
}

View File

@@ -1,96 +1,100 @@
use std::fmt::Display;
use std::io::Read; use std::io::Read;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use hyper::Client; use reqwest;
use url::form_urlencoded::byte_serialize; use serde_bencode;
use serde_bytes::ByteBuf;
use url::Url;
use bencode::{decode, Dict}; use super::{Event, Peer};
use metainfo::{Hash, Metainfo}; use error::Error;
use tracker::{Peer, TrackerError, TrackerResponse, TrackerResult}; use metainfo::Hash;
use torrent::Torrent;
macro_rules! ts { pub struct QueryBuilder {
( $e:expr ) => { params: Vec<(&'static str, String)>,
match $e { }
Some(x) => x,
None => return Err(TrackerError::InvalidResponse), impl QueryBuilder {
pub fn new() -> Self {
QueryBuilder { params: Vec::new() }
}
pub fn add(&mut self, name: &'static str, val: &Display) {
self.params.push((name, format!("{}", val)));
}
pub fn apply_to(&self, url: &mut Url) {
let mut query_mut = url.query_pairs_mut();
for &(key, ref val) in self.params.iter() {
query_mut.append_pair(key, &val);
} }
} }
} }
pub fn get_peers(peer_id: Hash, port: u16, metainfo: &Metainfo, uploaded: u64, downloaded: u64, left: u64) -> TrackerResult { fn peer_from_bytes(chunk: &[u8]) -> Peer {
let url = format!("{}?info_hash={}&peer_id={}&port={}&uploaded={}\ assert!(chunk.len() == 6);
&downloaded={}&left={}&compact=1", Peer { addr: Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]),
metainfo.announce, port: (chunk[4] as u16) << 8 | (chunk[5] as u16) << 0, }
urlencode(&metainfo.info_hash),
urlencode(&peer_id),
port,
uploaded,
downloaded,
left);
let client = Client::new();
let mut resp = client.get(&url).send()?;
let mut buff = Vec::new();
resp.read_to_end(&mut buff)?;
let obj = decode(&buff)?;
let info = ts!(obj.as_dict());
parse_object(info)
} }
fn parse_object(info: &Dict) -> TrackerResult {
if let Some(reason) = info.get_bytes("failure reason") {
Err(TrackerError::TrackerFailure(ts!(reason.string())))
} else {
let interval = ts!(info.get_int("interval"));
let peerstring = ts!(info.get_bytes("peers"));
if peerstring.len() % 6 != 0 { pub fn fetch_peers(mut url: Url,
return Err(TrackerError::InvalidResponse) event: Option<Event>,
torrent: &Torrent,
peer_id: &Hash,
port: u16)
-> Result<Vec<Peer>, Error> {
let mut query = QueryBuilder::new();
query.add("info_hash", &torrent.info_hash.hex());
query.add("peer_id", &peer_id.hex());
query.add("port", &port);
query.add("uploaded", &torrent.uploaded);
query.add("downloaded", &torrent.downloaded);
query.add("left", &torrent.left);
query.add("compact", &1);
if let Some(event) = event {
query.add("event", &event.as_str());
} }
let peers = peerstring.chunks(6).map(|chunk| { query.apply_to(&mut url);
Peer {
addr: Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]),
port: (chunk[4] as u16) << 8 | (chunk[5] as u16) << 0,
}
}).collect();
Ok(TrackerResponse { let mut resp = reqwest::get(url)?;
interval: interval as u64, ensure2!(resp.status().is_success(),
peers: peers, "Tracker failed, http code {}",
}) resp.status().as_u16());
let mut buf = Vec::new();
resp.read_to_end(&mut buf)?;
match serde_bencode::from_bytes(&buf)? {
Response::Failure { reason } => bail2!("Tracker failure, {}", reason),
Response::Ok { peers, .. } => {
ensure2!(peers.len() % 6 == 0, "Tracker blob is invalid");
let peers = peers.chunks(6).map(peer_from_bytes).collect();
Ok(peers)
}
} }
} }
fn urlencode(bytes: &[u8]) -> String { #[derive(Deserialize, Serialize)]
byte_serialize(bytes).collect::<Vec<_>>().join("") pub enum Response {
} Failure {
#[serde(rename = "failure reason")]
reason: String,
},
#[test] Ok {
fn test_failure() { #[serde(rename = "warning message")]
let mut info = Dict::new(); warning: Option<String>,
info.insert("failure reason", "unknown hash"); interval: i64,
assert!(parse_object(&info).is_err()); #[serde(rename = "min interval")]
} min_interval: Option<i64>,
tracker_id: String,
#[test] complete: u64,
fn test_invalid_peer_list() { incomplete: u64,
let mut info = Dict::new(); peers: ByteBuf, // only supports the compact format for now
info.insert("interval", 900); },
info.insert("peers", vec![1u8, 1, 1, 1]);
assert!(parse_object(&info).is_err());
}
#[test]
fn test_success() {
let mut info = Dict::new();
info.insert("interval", 900);
info.insert("peers", vec![1u8, 1, 1, 1, 1, 1]);
let resp = parse_object(&info).unwrap();
assert_eq!(resp.interval, 900);
assert_eq!(resp.peers.len(), 1);
assert_eq!(resp.peers[0].addr.octets(), [1, 1, 1, 1]);
assert_eq!(resp.peers[0].port, 257);
} }

View File

@@ -1,48 +1,58 @@
pub mod http;
use std::io;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use hyper; use url::Url;
use bencode::DecodeError; mod bin;
mod http;
mod udp;
pub type TrackerResult = Result<TrackerResponse, TrackerError>; use failure;
#[derive(Debug)] use error::Error;
pub struct TrackerResponse { use metainfo::Hash;
pub interval: u64, use torrent::Torrent;
pub peers: Vec<Peer>,
}
#[derive(Debug)]
pub enum TrackerError {
IoError(io::Error),
HttpError(hyper::Error),
InvalidResponse,
TrackerFailure(String),
}
impl From<io::Error> for TrackerError {
fn from(from: io::Error) -> Self {
TrackerError::IoError(from)
}
}
impl From<hyper::Error> for TrackerError {
fn from(from: hyper::Error) -> Self {
TrackerError::HttpError(from)
}
}
impl From<DecodeError> for TrackerError {
fn from(_: DecodeError) -> Self {
TrackerError::InvalidResponse
}
}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Peer { pub struct Peer {
pub addr: Ipv4Addr, pub addr: Ipv4Addr,
pub port: u16, pub port: u16,
} }
pub fn fetch_peers(url: &str,
event: Option<Event>,
torrent: &Torrent,
peer_id: &Hash,
port: u16)
-> Result<Vec<Peer>, Error> {
let url = Url::parse(url)?;
match url.scheme() {
"http" | "https" => http::fetch_peers(url, event, torrent, peer_id, port),
"udp" => udp::fetch_peers(url, event, torrent, peer_id, port),
_ => bail2!("Unknown tracker scheme: %s {}", url.scheme()),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Event {
Completed,
Started,
Stopped,
}
impl Event {
pub fn as_str(&self) -> &'static str {
match *self {
Event::Completed => "completed",
Event::Started => "started",
Event::Stopped => "stopped",
}
}
pub fn as_int(&self) -> u32 {
match *self {
Event::Completed => 1,
Event::Started => 2,
Event::Stopped => 3,
}
}
}

76
src/tracker/udp.rs Normal file
View File

@@ -0,0 +1,76 @@
use std::io::{self, Cursor, Read, Write};
use std::net::UdpSocket;
use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
use rand::{thread_rng, Rng};
use serde::ser;
use url::Url;
use super::{Event, Peer};
use error::Error;
use metainfo::Hash;
use torrent::Torrent;
const CONNECT: u32 = 0;
const ANNOUNCE: u32 = 1;
pub fn fetch_peers(mut url: Url,
event: Option<Event>,
torrent: &Torrent,
peer_id: &Hash,
port: u16)
-> Result<Vec<Peer>, Error> {
let host = url.host_str().ok_or_else(|| Error::tracker("invalid host"))?;
let port = url.port().ok_or_else(|| Error::tracker("invalid port"))?;
let mut sock = UdpSocket::bind("0.0.0.0:0")?;
let trans = send_connect(&mut sock)?;
let conn = recv_connect(&mut sock, trans);
Ok(Vec::new())
}
fn send_connect(sock: &mut UdpSocket) -> io::Result<u32> {
let trans = thread_rng().next_u32();
let mut buf = [0u8; 128];
let mut cur = Cursor::new(&mut buf[..]);
cur.write_u64::<BigEndian>(0x41727101980);
cur.write_u32::<BigEndian>(CONNECT);
cur.write_u32::<BigEndian>(trans);
Ok(trans)
}
fn recv_connect(sock: &mut UdpSocket, own_trans: u32) -> io::Result<u64> {
let mut buf = [0u8; 128];
let _ = sock.recv_from(&mut buf);
let mut cur = Cursor::new(&mut buf[..]);
let action = cur.read_u32::<BigEndian>()?;
let trans = cur.read_u32::<BigEndian>()?;
let conn = cur.read_u64::<BigEndian>()?;
assert!(action == CONNECT); // TODO error
assert!(trans == own_trans); // TODO error
Ok(conn)
}
fn send_announce(sock: &mut UdpSocket,
conn: u64,
event: Option<Event>,
torrent: &Torrent,
peer_id: &Hash,
port: u16)
-> io::Result<(u32)> {
let trans = thread_rng().next_u32();
let mut buf = [0u8; 256];
let mut cur = Cursor::new(&mut buf[..]);
cur.write_u64::<BigEndian>(conn);
cur.write_u32::<BigEndian>(ANNOUNCE);
Ok(trans)
}