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
127
128
129
130
131
132
133
134
135
136
//! Messaging primitives for requests.

use std::io::{self, Write};

use byteorder::{BigEndian, WriteBytesExt};
use nom::{be_u64, be_u32, IResult};

use announce::AnnounceRequest;
use scrape::ScrapeRequest;

// For all practical applications, this value should be hardcoded as a valid
// connection id for connection requests when operating in server mode and processing
// incoming requests.
/// Global connection id for connect requests.
pub const CONNECT_ID_PROTOCOL_ID: u64 = 0x41727101980;

/// Enumerates all types of requests that can be made to a tracker.
pub enum RequestType<'a> {
    Connect,
    Announce(AnnounceRequest<'a>),
    Scrape(ScrapeRequest<'a>),
}

impl<'a> RequestType<'a> {
    /// Create an owned version of the RequestType.
    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()),
        }
    }
}

/// TrackerRequest which encapsulates any request sent to a tracker.
pub struct TrackerRequest<'a> {
    // Both the connection id and transaction id are techinically not unsigned according
    // to the spec, but since they are just bits we will keep them as unsigned since it
    // doesnt really make sense to not have them as unsigned (easier to generate transactions).
    connection_id: u64,
    transaction_id: u32,
    request_type: RequestType<'a>,
}

impl<'a> TrackerRequest<'a> {
    /// Create a new TrackerRequest.
    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,
        }
    }

    /// Create a new TrackerRequest from the given bytes.
    pub fn from_bytes(bytes: &'a [u8]) -> IResult<&'a [u8], TrackerRequest<'a>> {
        parse_request(bytes)
    }

    /// Write the TrackerRequest to the given writer.
    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(())
    }

    /// Connection ID supplied with a request to validate the senders address.
    ///
    /// For Connect requests, this will always be equal to 0x41727101980. Therefore,
    /// you should not hand out that specific ID to peers that make a connect request.
    pub fn connection_id(&self) -> u64 {
        self.connection_id
    }

    /// Transaction ID supplied with a request to uniquely identify a response.
    pub fn transaction_id(&self) -> u32 {
        self.transaction_id
    }

    /// Actual type of request that this TrackerRequest represents.
    pub fn request_type(&self) -> &RequestType {
        &self.request_type
    }

    /// Create an owned version of the TrackerRequest.
    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))
        })
    )
}