1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::io::{self, Write};
use byteorder::{BigEndian, WriteBytesExt};
use nom::{be_u64, be_u32, IResult};
use announce::{AnnounceRequest};
use scrape::{ScrapeRequest};
pub const CONNECT_ID_PROTOCOL_ID: u64 = 0x41727101980;
pub enum RequestType<'a> {
Connect,
Announce(AnnounceRequest<'a>),
Scrape(ScrapeRequest<'a>)
}
impl<'a> RequestType<'a> {
pub fn to_owned(&self) -> RequestType<'static> {
match self {
&RequestType::Connect => RequestType::Connect,
&RequestType::Announce(ref req) => RequestType::Announce(req.to_owned()),
&RequestType::Scrape(ref req) => RequestType::Scrape(req.to_owned())
}
}
}
pub struct TrackerRequest<'a> {
connection_id: u64,
transaction_id: u32,
request_type: RequestType<'a>
}
impl<'a> TrackerRequest<'a> {
pub fn new(conn_id: u64, trans_id: u32, req_type: RequestType<'a>) -> TrackerRequest<'a> {
TrackerRequest{ connection_id: conn_id, transaction_id: trans_id, request_type: req_type }
}
pub fn from_bytes(bytes: &'a [u8]) -> IResult<&'a [u8], TrackerRequest<'a>> {
parse_request(bytes)
}
pub fn write_bytes<W>(&self, mut writer: W) -> io::Result<()>
where W: Write {
try!(writer.write_u64::<BigEndian>(self.connection_id()));
match self.request_type() {
&RequestType::Connect => {
try!(writer.write_u32::<BigEndian>(::CONNECT_ACTION_ID));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
},
&RequestType::Announce(ref req) => {
let action_id = if req.source_ip().is_ipv4() {
::ANNOUNCE_IPV4_ACTION_ID
} else { ::ANNOUNCE_IPV6_ACTION_ID };
try!(writer.write_u32::<BigEndian>(action_id));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
try!(req.write_bytes(writer));
},
&RequestType::Scrape(ref req) => {
try!(writer.write_u32::<BigEndian>(::SCRAPE_ACTION_ID));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
try!(req.write_bytes(writer));
}
};
Ok(())
}
pub fn connection_id(&self) -> u64 {
self.connection_id
}
pub fn transaction_id(&self) -> u32 {
self.transaction_id
}
pub fn request_type(&self) -> &RequestType {
&self.request_type
}
pub fn to_owned(&self) -> TrackerRequest<'static> {
TrackerRequest{ connection_id: self.connection_id, transaction_id: self.transaction_id,
request_type: self.request_type.to_owned() }
}
}
fn parse_request<'a>(bytes: &'a [u8]) -> IResult<&'a [u8], TrackerRequest<'a>> {
switch!(bytes, tuple!(be_u64, be_u32, be_u32),
(CONNECT_ID_PROTOCOL_ID, ::CONNECT_ACTION_ID, tid) => value!(
TrackerRequest::new(CONNECT_ID_PROTOCOL_ID, tid, RequestType::Connect)
) |
(cid, ::ANNOUNCE_IPV4_ACTION_ID, tid) => map!(call!(AnnounceRequest::from_bytes_v4), |ann_req| {
TrackerRequest::new(cid, tid, RequestType::Announce(ann_req))
}) |
(cid, ::SCRAPE_ACTION_ID, tid) => map!(call!(ScrapeRequest::from_bytes), |scr_req| {
TrackerRequest::new(cid, tid, RequestType::Scrape(scr_req))
}) |
(cid, ::ANNOUNCE_IPV6_ACTION_ID, tid) => map!(call!(AnnounceRequest::from_bytes_v6), |ann_req| {
TrackerRequest::new(cid, tid, RequestType::Announce(ann_req))
})
)
}