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
use bip_bencode::{Bencode, BencodeConvert, Dictionary, BencodeConvertError};
use bip_util::bt::{NodeId};

use message::compact_info::{CompactNodeInfo, CompactValueInfo};
use message::ping::{PingResponse};
use message::find_node::{FindNodeResponse};
use message::get_peers::{GetPeersResponse};
use message::announce_peer::{AnnouncePeerResponse};
use error::{DhtError, DhtErrorKind, DhtResult};

pub const RESPONSE_ARGS_KEY: &'static str = "r";

//----------------------------------------------------------------------------//

pub struct ResponseValidate<'a> {
    trans_id: &'a [u8]
}

impl<'a> ResponseValidate<'a> {
    pub fn new(trans_id: &'a [u8]) -> ResponseValidate<'a> {
        ResponseValidate{ trans_id: trans_id }
    }
    
    pub fn validate_node_id(&self, node_id: &[u8]) -> DhtResult<NodeId> {
        NodeId::from_hash(node_id).map_err(|_|
            DhtError::with_detail(DhtErrorKind::InvalidResponse, "Found Node ID With Invalid Length",
                format!("TransactionID: {:?}, NodeID Length: {:?}", self.trans_id, node_id.len()))
        )
    }
    
    /// Validate the given nodes string which should be IPv4 compact 
    pub fn validate_nodes<'b>(&self, nodes: &'b [u8]) -> DhtResult<CompactNodeInfo<'b>> {
        CompactNodeInfo::new(nodes).map_err(|_|
            DhtError::with_detail(DhtErrorKind::InvalidResponse, "Found Nodes Structure With Wrong Multiple Of Bytes",
                format!("TransactionID: {:?}", self.trans_id))
        )
    }
    
    pub fn validate_values<'b>(&self, values: &'b [Bencode<'a>]) -> DhtResult<CompactValueInfo<'b>> {
        for bencode in values.iter() {
            match bencode.bytes() {
                Some(_) => (),
                None    => return Err(DhtError::with_detail(DhtErrorKind::InvalidResponse,
                    "Found Values Structure Element With Wrong Bencode Type", format!("TransactionID: {:?}", self.trans_id)))
            }
        }
    
        CompactValueInfo::new(values).map_err(|_|
            DhtError::with_detail(DhtErrorKind::InvalidResponse, "Found values Structure Element With Wrong Number Of Bytes",
            format!("TransactionID: {:?}", self.trans_id))
        )
    }
}

impl<'a> BencodeConvert for ResponseValidate<'a> {
    type Error = DhtError;
    
    fn handle_error(&self, error: BencodeConvertError) -> DhtError {
        DhtError::with_detail(DhtErrorKind::InvalidResponse, error.desc(), error.key().to_owned())
    }
}

//----------------------------------------------------------------------------//

#[allow(unused)]
pub enum ExpectedResponse {
    Ping,
    FindNode,
    GetPeers,
    AnnouncePeer,
    GetData,
    PutData,
    None
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum ResponseType<'a> {
    Ping(PingResponse<'a>),
    FindNode(FindNodeResponse<'a>),
    GetPeers(GetPeersResponse<'a>),
    AnnouncePeer(AnnouncePeerResponse<'a>)
    /*GetData(GetDataResponse<'a>),
    PutData(PutDataResponse<'a>)*/
}

impl<'a> ResponseType<'a> {
    pub fn from_parts(root: &'a Dictionary<'a, Bencode<'a>>, trans_id: &'a [u8], rsp_type: ExpectedResponse)
        -> DhtResult<ResponseType<'a>> {
        let validate = ResponseValidate::new(trans_id);
        let rqst_root = try!(validate.lookup_and_convert_dict(root, RESPONSE_ARGS_KEY));
        
        match rsp_type {
            ExpectedResponse::Ping => {
                let ping_rsp = try!(PingResponse::from_parts(rqst_root, trans_id));
                Ok(ResponseType::Ping(ping_rsp))
            },
            ExpectedResponse::FindNode => {
                let find_node_rsp = try!(FindNodeResponse::from_parts(rqst_root, trans_id));
                Ok(ResponseType::FindNode(find_node_rsp))
            },
            ExpectedResponse::GetPeers => {
                let get_peers_rsp = try!(GetPeersResponse::from_parts(rqst_root, trans_id));
                Ok(ResponseType::GetPeers(get_peers_rsp))
            },
            ExpectedResponse::AnnouncePeer => {
                let announce_peer_rsp = try!(AnnouncePeerResponse::from_parts(rqst_root, trans_id));
                Ok(ResponseType::AnnouncePeer(announce_peer_rsp))
            },
            ExpectedResponse::GetData => {
                unimplemented!();
            },
            ExpectedResponse::PutData => {
                unimplemented!();
            },
            ExpectedResponse::None => {
                Err(DhtError::new(DhtErrorKind::UnsolicitedResponse, "Failed To Match Node Response To A Request"))
            }
        }
    }
}