bip_peer 0.5.0

Communication with bittorrent peers via peer wire protocol
Documentation
use std::io;
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str;

use bip_bencode::{BencodeConvertError, BConvert, BRefAccess, BDictAccess};
use bip_util::convert;

use message::bits_ext::ExtendedType;

pub const CONVERT: IoErrorBencodeConvert = IoErrorBencodeConvert;

pub struct IoErrorBencodeConvert;

impl BConvert for IoErrorBencodeConvert {
    type Error = io::Error;

    fn handle_error(&self, error: BencodeConvertError) -> Self::Error {
        io::Error::new(io::ErrorKind::Other, error.to_string())
    }
}

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

pub const ID_MAP_KEY:              &'static [u8] = b"m";
pub const CLIENT_ID_KEY:           &'static [u8] = b"v";
pub const CLIENT_TCP_PORT_KEY:     &'static [u8] = b"p";
pub const OUR_IP_KEY:              &'static [u8] = b"yourip";
pub const CLIENT_IPV6_ADDR_KEY:    &'static [u8] = b"ipv6";
pub const CLIENT_IPV4_ADDR_KEY:    &'static [u8] = b"ipv4";
pub const CLIENT_MAX_REQUESTS_KEY: &'static [u8] = b"reqq";
pub const METADATA_SIZE_KEY:       &'static [u8] = b"metadata_size";

pub fn parse_id_map<K, V>(root: &BDictAccess<K, V>) -> HashMap<ExtendedType, u8>
    where V: BRefAccess, V::BKey: AsRef<[u8]> {
    let mut id_map = HashMap::new();
    
    if let Ok(ben_id_map) = CONVERT.lookup_and_convert_dict(root, ID_MAP_KEY) {
        for (id, ben_value) in ben_id_map.to_list() {
            match (str::from_utf8(id.as_ref()), CONVERT.convert_int(ben_value, id)) {
                (Ok(str_id), Ok(value)) => { id_map.insert(ExtendedType::from_id(str_id), value as u8); },
                _                       => ()
            }
        }
    }

    id_map
}

pub fn parse_client_id<K, V>(root: &BDictAccess<K, V>) -> Option<String>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_str(root, CLIENT_ID_KEY)
        .map(|id| id.to_string())
        .ok()
}

pub fn parse_client_tcp_port<K, V>(root: &BDictAccess<K, V>) -> Option<u16>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_int(root, CLIENT_TCP_PORT_KEY)
        .ok()
        .and_then(|port| {
            if port as u16 as i64 == port {
                Some(port as u16)
            } else {
                None
            }
        })
}

pub fn parse_our_ip<K, V>(root: &BDictAccess<K, V>) -> Option<IpAddr>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_bytes(root, OUR_IP_KEY)
        .ok()
        .and_then(|ip_bytes| {
            if ip_bytes.len() == 4 {
                Some(IpAddr::V4(parse_ipv4_addr(ip_bytes)))
            } else if ip_bytes.len() == 16 {
                Some(IpAddr::V6(parse_ipv6_addr(ip_bytes)))
            } else {
                None
            }
        })
}

pub fn parse_client_ipv6_addr<K, V>(root: &BDictAccess<K, V>) -> Option<Ipv6Addr>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_bytes(root, CLIENT_IPV6_ADDR_KEY)
        .ok()
        .and_then(|ipv6_bytes| {
            if ipv6_bytes.len() == 16  {
                Some(parse_ipv6_addr(ipv6_bytes))
            } else {
                None
            }
        })
}

pub fn parse_client_ipv4_addr<K, V>(root: &BDictAccess<K, V>) -> Option<Ipv4Addr> 
    where V: BRefAccess {
    CONVERT.lookup_and_convert_bytes(root, CLIENT_IPV4_ADDR_KEY)
        .ok()
        .and_then(|ipv4_bytes| {
            if ipv4_bytes.len() == 4 {
                Some(parse_ipv4_addr(ipv4_bytes))
            } else {
                None
            }
        })
}

pub fn parse_client_max_requests<K, V>(root: &BDictAccess<K, V>) -> Option<i64>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_int(root, CLIENT_MAX_REQUESTS_KEY)
        .ok()
}

pub fn parse_metadata_size<K, V>(root: &BDictAccess<K, V>) -> Option<i64>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_int(root, METADATA_SIZE_KEY)
        .ok()
}

fn parse_ipv4_addr(ipv4_bytes: &[u8]) -> Ipv4Addr {
    convert::bytes_be_to_ipv4([ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3]])
}

fn parse_ipv6_addr(ipv6_bytes: &[u8]) -> Ipv6Addr {
    convert::bytes_be_to_ipv6([ipv6_bytes[0], ipv6_bytes[1], ipv6_bytes[2], ipv6_bytes[3],
                               ipv6_bytes[4], ipv6_bytes[5], ipv6_bytes[6], ipv6_bytes[7],
                               ipv6_bytes[8], ipv6_bytes[9], ipv6_bytes[10], ipv6_bytes[11],
                               ipv6_bytes[12], ipv6_bytes[13], ipv6_bytes[14], ipv6_bytes[15]])
}

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

pub const MESSAGE_TYPE_KEY: &'static [u8] = b"msg_type";
pub const PIECE_INDEX_KEY:  &'static [u8] = b"piece";
pub const TOTAL_SIZE_KEY:   &'static [u8] = b"total_size";

pub fn parse_message_type<K, V>(root: &BDictAccess<K, V>) -> io::Result<u8>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_int(root, MESSAGE_TYPE_KEY).map(|msg_type| msg_type as u8).into()
}

pub fn parse_piece_index<K, V>(root: &BDictAccess<K, V>) -> io::Result<i64>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_int(root, PIECE_INDEX_KEY).into()
}

pub fn parse_total_size<K, V>(root: &BDictAccess<K, V>) -> io::Result<i64>
    where V: BRefAccess {
    CONVERT.lookup_and_convert_int(root, TOTAL_SIZE_KEY).into()
}