From 1c6058368fef0a0ea9a509d38c87f6135759eaae Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Mon, 26 Mar 2018 15:02:28 +0300 Subject: [PATCH] fix(old_dht): remove a lot of old code --- src/toxcore/dht_old/binary_io_tests.rs | 99 -- src/toxcore/dht_old/dht.rs | 1903 ---------------------- src/toxcore/dht_old/dht_node.rs | 1222 -------------- src/toxcore/dht_old/dht_tests.rs | 1665 ------------------- src/toxcore/dht_old/example_dht_node.rs | 137 -- src/toxcore/dht_old/network.rs | 275 ---- src/toxcore/dht_old/network_tests.rs | 88 - src/toxcore/dht_old/packet_kind.rs | 106 -- src/toxcore/dht_old/packet_kind_tests.rs | 109 -- 9 files changed, 5604 deletions(-) delete mode 100644 src/toxcore/dht_old/binary_io_tests.rs delete mode 100644 src/toxcore/dht_old/dht.rs delete mode 100644 src/toxcore/dht_old/dht_node.rs delete mode 100644 src/toxcore/dht_old/dht_tests.rs delete mode 100644 src/toxcore/dht_old/example_dht_node.rs delete mode 100644 src/toxcore/dht_old/network.rs delete mode 100644 src/toxcore/dht_old/network_tests.rs delete mode 100644 src/toxcore/dht_old/packet_kind.rs delete mode 100644 src/toxcore/dht_old/packet_kind_tests.rs diff --git a/src/toxcore/dht_old/binary_io_tests.rs b/src/toxcore/dht_old/binary_io_tests.rs deleted file mode 100644 index e4508fa83..000000000 --- a/src/toxcore/dht_old/binary_io_tests.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - Copyright © 2016 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - -//! Tests for `binary_io` module. - -use byteorder::{NativeEndian, WriteBytesExt}; -use nom::IResult; -use quickcheck::{quickcheck, TestResult}; - -use toxcore::binary_io::*; - - -// append_zeros() - -macro_rules! test_append_zeros_for { - ($($num:ty, $tname_f:ident, $tname_p:ident),+) => ($( - #[test] - #[should_panic] - fn $tname_f() { - fn with_bytes(bytes: Vec<$num>, len: usize) -> TestResult { - if bytes.len() > len { - // this should nicely panic - append_zeros(&mut bytes.clone(), len); - } - TestResult::discard() - } - quickcheck(with_bytes as fn(Vec<$num>, usize) -> TestResult); - } - - #[test] - fn $tname_p() { - fn with_bytes(bytes: Vec<$num>, len: usize) -> TestResult { - if bytes.len() > len { - return TestResult::discard() - } - - let mut bc = bytes.clone(); - append_zeros(&mut bc, len); - assert_eq!(bytes[..], bc[..bytes.len()]); - assert_eq!(&vec![0u8; len - bytes.len()] as &[$num], - &bc[bytes.len()..]); - TestResult::passed() - } - quickcheck(with_bytes as fn(Vec<$num>, usize) -> TestResult); - } - )+) -} -test_append_zeros_for!( - u8, append_zeros_test_u8_fail, append_zeros_test_u8_pass -); - -// xor_checksum() - -#[test] -fn xor_checksum_test() { - assert_eq!([0; 2], xor_checksum(&[0; 2], &[0; 2])); - assert_eq!([1; 2], xor_checksum(&[1; 2], &[0; 2])); - assert_eq!([0; 2], xor_checksum(&[1; 2], &[1; 2])); - assert_eq!([255; 2], xor_checksum(&[255; 2], &[0; 2])); - assert_eq!([0; 2], xor_checksum(&[255; 2], &[255; 2])); - - fn with_numbers(a: u8, b: u8, c: u8, d: u8) { - let checksum = xor_checksum(&[a, b], &[c, d]); - assert_eq!(checksum[0], a ^ c); - assert_eq!(checksum[1], b ^ d); - } - quickcheck(with_numbers as fn(u8, u8, u8, u8)); -} - -// ne_u64() - -#[test] -fn ne_u64_test() { - fn with_numbers(n: u64) { - let mut v = Vec::new(); - v.write_u64::(n).unwrap(); - // TODO: remove `as &[u8]` when Rust RFC 803 gets fully - // implemented on ~stable - 1 - // https://github.com/rust-lang/rust/issues/23416 - assert_eq!(ne_u64(&v), IResult::Done(&[] as &[u8], n)); - } - quickcheck(with_numbers as fn(u64)); -} diff --git a/src/toxcore/dht_old/dht.rs b/src/toxcore/dht_old/dht.rs deleted file mode 100644 index 0be08b91d..000000000 --- a/src/toxcore/dht_old/dht.rs +++ /dev/null @@ -1,1903 +0,0 @@ -/* - Copyright (C) 2013 Tox project All Rights Reserved. - Copyright © 2016-2017 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - - -// ↓ FIXME expand doc -/*! DHT part of the toxcore. - - * takes care of the serializing and de-serializing DHT packets - * .. -*/ - - -use byteorder::{ - BigEndian, - LittleEndian, - NativeEndian, - WriteBytesExt -}; -use nom::{be_u16, le_u8, le_u16, rest}; - -use std::cmp::{Ord, Ordering}; -use std::convert::From; -use std::fmt::Debug; -use std::net::{ - IpAddr, - Ipv4Addr, - Ipv6Addr, - SocketAddr, - SocketAddrV4, - SocketAddrV6 -}; -use std::ops::Deref; - -use toxcore::binary_io::*; -use toxcore::crypto_core::*; -use toxcore::packet_kind::PacketKind; - - - -/// Length in bytes of [`PingReq`](./struct.PingReq.html) and -/// [`PingResp`](./struct.PingResp.html) when serialized into bytes. -pub const PING_SIZE: usize = 9; - - -macro_rules! impls_for_pings { - ($($n:ident),+) => ($( - /** - Used to request/respond to ping. Use in an encrypted form. - - Used in: - - - [`DhtPacket`](./struct.DhtPacket.html) - - [`DhtRequest`](./struct.DhtRequest.html) - - Serialized form: - - Ping Packet (request and response) - - Packet type `0x00` for request and `0x01` for response. - - Response ID must match ID of the request, otherwise ping is invalid. - - Length | Contents - ----------- | -------- - `1` | `u8` packet type - `8` | Ping ID - - Serialized form should be put in the encrypted part of DHT packet. - - # Creating new - - [`PingResp`](./struct.PingResp.html) can only be created as a response - to [`PingReq`](./struct.PingReq.html). - */ - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub struct $n { - id: u64, - } - - impl $n { - /// An ID of the request / response. - pub fn id(&self) -> u64 { - self.id - } - } - - impl DhtPacketT for $n { - fn kind(&self) -> PacketKind { - PacketKind::$n - } - } - - /** De-seralize from bytes. Tries to parse first - [`PING_SIZE`](./constant.PING_SIZE.html) bytes from supplied slice - as `Ping`. - */ - from_bytes!($n, do_parse!( - packet_t: call!(PacketKind::parse_bytes) >> - id: cond_reduce!( - packet_t == PacketKind::$n, - ne_u64 - ) >> - ($n { - id: id - }) - )); - - /// Serialize to bytes. - impl ToBytes for $n { - fn to_bytes(&self) -> Vec { - let pname = stringify!($n); - debug!(target: pname, "Serializing {} into bytes.", pname); - trace!(target: pname, "With {}: {:?}", pname, self); - let mut res = Vec::with_capacity(PING_SIZE); - // `PingType` - res.push(self.kind() as u8); - // And random ping_id as bytes - res.write_u64::(self.id) - .expect("Failed to write Ping id!"); - trace!("Serialized Ping: {:?}", &res); - res - } - } - )+) - // TODO: add To/From impls for both(↔)? -} -impls_for_pings!(PingReq, PingResp); - -impl PingReq { - /// Create new ping request with a randomly generated `request id`. - pub fn new() -> Self { - trace!("Creating new Ping."); - PingReq { id: random_u64() } - } -} - -impl From for PingResp { - fn from(p: PingReq) -> Self { - PingResp { id: p.id } - } -} - - - - - -/** Used by [`PackedNode`](./struct.PackedNode.html). - -* 1st bit – protocol -* 3 bits – `0` -* 4th bit – address family - -Value | Type ------ | ---- -`2` | UDP IPv4 -`10` | UDP IPv6 -`130` | TCP IPv4 -`138` | TCP IPv6 - -DHT module *should* use only UDP variants of `IpType`, given that DHT runs -solely over the UDP. - -TCP variants are to be used for sending/receiving info about TCP relays. -*/ -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum IpType { - /// UDP over IPv4. - U4 = 2, - /// UDP over IPv6. - U6 = 10, - /// TCP over IPv4. - T4 = 130, - /// TCP over IPv6. - T6 = 138, -} - -/// Match first byte from the provided slice as `IpType`. If no match found, -/// return `None`. -from_bytes!(IpType, switch!(le_u8, - 2 => value!(IpType::U4) | - 10 => value!(IpType::U6) | - 130 => value!(IpType::T4) | - 138 => value!(IpType::T6) -)); - - -// TODO: move it somewhere else -impl ToBytes for IpAddr { - fn to_bytes(&self) -> Vec { - debug!(target: "IpAddr", "Serializing IpAddr to bytes."); - trace!(target: "IpAddr", "With IpAddr: {:?}", self); - match *self { - IpAddr::V4(a) => a.octets().to_vec(), - IpAddr::V6(a) => { - let mut result: Vec = vec![]; - for n in &a.segments() { - result.write_u16::(*n) // TODO: check if LittleEndian is correct here - .expect("Failed to write Ipv6Addr segments!"); - } - result - } - } - } -} - -// TODO: move it somewhere else -/// Fail if there are less than 4 bytes supplied, otherwise parses first -/// 4 bytes as an `Ipv4Addr`. -from_bytes!(Ipv4Addr, map!(take!(4), |bytes| Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]))); - -// TODO: move it somewhere else -/// Fail if there are less than 16 bytes supplied, otherwise parses first -/// 16 bytes as an `Ipv6Addr`. -from_bytes!(Ipv6Addr, map!(count!(le_u16, 8), |v| Ipv6Addr::new(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]))); - - -/** `PackedNode` format is a way to store the node info in a small yet easy to -parse format. - -It is used in many places in Tox, e.g. in `DHT Send nodes`. - -To store more than one node, simply append another on to the previous one: - -`[packed node 1][packed node 2][...]` - -Serialized Packed node: - -Length | Content ------- | ------- -`1` | [`IpType`](./.enum.IpType.html) -`4` or `16` | IPv4 or IPv6 address -`2` | port -`32` | node ID - -Size of serialized `PackedNode` is 39 bytes with IPv4 node info, or 51 with -IPv6 node info. - -DHT module *should* use only UDP variants of `IpType`, given that DHT runs -solely on the UDP. -*/ -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct PackedNode { - /// IP type, includes also info about protocol used. - ip_type: IpType, - /// Socket addr of node. - saddr: SocketAddr, - /// Public Key of the node. - pk: PublicKey, -} - -/// Size in bytes of serialized [`PackedNode`](./struct.PackedNode.html) with -/// IPv4. -pub const PACKED_NODE_IPV4_SIZE: usize = PUBLICKEYBYTES + 7; -/// Size in bytes of serialized [`PackedNode`](./struct.PackedNode.html) with -/// IPv6. -pub const PACKED_NODE_IPV6_SIZE: usize = PUBLICKEYBYTES + 19; - -impl PackedNode { - /** New `PackedNode`. - - `udp` - whether UDP or TCP should be used. UDP is used for DHT nodes, - whereas TCP is used for TCP relays. When `true`, UDP is used, otherwise - TCP is used. - */ - pub fn new(udp: bool, saddr: SocketAddr, pk: &PublicKey) -> Self { - debug!(target: "PackedNode", "Creating new PackedNode."); - trace!(target: "PackedNode", "With args: udp: {}, saddr: {:?}, PK: {:?}", - udp, &saddr, pk); - - let v4: bool = match saddr { - SocketAddr::V4(_) => true, - SocketAddr::V6(_) => false, - }; - - let ip_type = match (udp, v4) { - (true, true) => IpType::U4, - (true, false) => IpType::U6, - (false, true) => IpType::T4, - (false, false) => IpType::T6, - }; - - PackedNode { - ip_type: ip_type, - saddr: saddr, - pk: *pk, - } - } - - /// Get an IP type from the `PackedNode`. - pub fn ip_type(&self) -> IpType { - trace!(target: "PackedNode", "Getting IP type from PackedNode."); - trace!("With address: {:?}", self); - self.ip_type - } - - /// Get an IP address from the `PackedNode`. - pub fn ip(&self) -> IpAddr { - trace!(target: "PackedNode", "Getting IP address from PackedNode."); - trace!("With address: {:?}", self); - match self.saddr { - SocketAddr::V4(addr) => IpAddr::V4(*addr.ip()), - SocketAddr::V6(addr) => IpAddr::V6(*addr.ip()), - } - } - - /// Get a Socket address from the `PackedNode`. - pub fn socket_addr(&self) -> SocketAddr { - trace!(target: "PackedNode", "Getting Socket address from PackedNode."); - trace!("With address: {:?}", self); - self.saddr - } - - /// Get an IP address from the `PackedNode`. - pub fn pk(&self) -> &PublicKey { - trace!(target: "PackedNode", "Getting PK from PackedNode."); - trace!("With address: {:?}", self); - &self.pk - } - -} - -/** Serialize `PackedNode` into bytes. - -Can be either [`PACKED_NODE_IPV4_SIZE`] -(./constant.PACKED_NODE_IPV4_SIZE.html) or [`PACKED_NODE_IPV6_SIZE`] -(./constant.PACKED_NODE_IPV6_SIZE.html) bytes long, depending on whether -IPv4 or IPv6 is being used. -*/ -impl ToBytes for PackedNode { - fn to_bytes(&self) -> Vec { - debug!(target: "PackedNode", "Serializing PackedNode into bytes."); - trace!(target: "PackedNode", "With PackedNode: {:?}", self); - let mut result: Vec = Vec::with_capacity(PACKED_NODE_IPV6_SIZE); - - result.push(self.ip_type as u8); - - let addr: Vec = self.ip().to_bytes(); - result.extend_from_slice(&addr); - // port - result.write_u16::(self.saddr.port()) - .expect("Failed to write PackedNode port!"); - - let PublicKey(ref pk) = self.pk; - result.extend_from_slice(pk); - - trace!("Result: {:?}", &result); - result - } -} - -// Parse bytes as an IPv4 PackedNode. -named_args!(as_ipv4_packed_node(iptype: IpType) , do_parse!( - addr: call!(Ipv4Addr::parse_bytes) >> - port: be_u16 >> - saddr: value!(SocketAddrV4::new(addr, port)) >> - pk: call!(PublicKey::parse_bytes) >> - (PackedNode { - ip_type: iptype, - saddr: SocketAddr::V4(saddr), - pk: pk - }) -)); - -// Parse bytes as an IPv6 PackedNode. -named_args!(as_ipv6_packed_node(iptype: IpType) , do_parse!( - addr: call!(Ipv6Addr::parse_bytes) >> - port: be_u16 >> - saddr: value!(SocketAddrV6::new(addr, port, 0, 0)) >> - pk: call!(PublicKey::parse_bytes) >> - (PackedNode { - ip_type: iptype, - saddr: SocketAddr::V6(saddr), - pk: pk - }) -)); - -/** Deserialize bytes into `PackedNode`. Returns `None` if deseralizing -failed. - -Can fail if: - - - length is too short for given [`IpType`](./enum.IpType.html) - - PK can't be parsed - -Blindly trusts that provided `IpType` matches - i.e. if there are provided -51 bytes (which is length of `PackedNode` that contains IPv6), and `IpType` -says that it's actually IPv4, bytes will be parsed as if that was an IPv4 -address. -*/ -from_bytes!(PackedNode, switch!(call!(IpType::parse_bytes), - IpType::U4 => call!(as_ipv4_packed_node, IpType::U4) | - IpType::T4 => call!(as_ipv4_packed_node, IpType::T4) | - IpType::U6 => call!(as_ipv6_packed_node, IpType::U6) | - IpType::T6 => call!(as_ipv6_packed_node, IpType::T6) -)); - - -/** Request to get address of given DHT PK, or nodes that are closest in DHT -to the given PK. - -Packet type [`PacketKind::GetN`](../packet_kind/enum.PacketKind.html). - -Serialized form: - -Length | Content ------- | ------ -`32` | DHT Public Key -`8` | ping id - -Serialized form should be put in the encrypted part of DHT packet. -*/ -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct GetNodes { - /// Public Key of the DHT node `GetNodes` is supposed to get address of. - pub pk: PublicKey, - /// An ID of the request. - pub id: u64, -} - -/// Size of serialized [`GetNodes`](./struct.GetNodes.html) in bytes. -pub const GET_NODES_SIZE: usize = PUBLICKEYBYTES + 8; - -impl GetNodes { - /// Create new `GetNodes` with given PK. - pub fn new(their_public_key: &PublicKey) -> Self { - trace!(target: "GetNodes", "Creating new GetNodes request."); - GetNodes { pk: *their_public_key, id: random_u64() } - } - - - /** - Create response to `self` request with nodes provided from the `Kbucket`. - - Fails (returns `None`) if `Kbucket` is empty. - */ - pub fn response(&self, kbucket: &Kbucket) -> Option { - let nodes = kbucket.get_closest(&self.pk); - SendNodes::with_nodes(self, nodes) - } -} - -impl DhtPacketT for GetNodes { - fn kind(&self) -> PacketKind { - PacketKind::GetN - } -} - -/// Serialization of `GetNodes`. Resulting length should be -/// [`GET_NODES_SIZE`](./constant.GET_NODES_SIZE.html). -impl ToBytes for GetNodes { - fn to_bytes(&self) -> Vec { - debug!(target: "GetNodes", "Serializing GetNodes as bytes."); - trace!(target: "GetNodes", "With GetNodes: {:?}", self); - let mut result = Vec::with_capacity(GET_NODES_SIZE); - let PublicKey(pk_bytes) = self.pk; - result.extend_from_slice(&pk_bytes); - result.write_u64::(self.id) - .expect("Failed to write GetNodes id!"); - trace!("Resulting bytes: {:?}", &result); - result - } -} - -/** De-serialization of bytes into `GetNodes`. If less than -[`GET_NODES_SIZE`](./constant.GET_NODES_SIZE.html) bytes are provided, -de-serialization will fail, returning `None`. -*/ -from_bytes!(GetNodes, do_parse!( - pk: call!(PublicKey::parse_bytes) >> - id: ne_u64 >> - (GetNodes { pk: pk, id: id }) -)); - - -/** Response to [`GetNodes`](./struct.GetNodes.html) request, containing up to -`4` nodes closest to the requested node. - -Packet type `0x04`. - -Serialized form: - -Length | Contents ------------ | -------- -`1` | Number of packed nodes (maximum 4) -`[39, 204]` | Nodes in packed format -`8` | Ping ID - -An IPv4 node is 39 bytes, an IPv6 node is 51 bytes, so the maximum size is -`51 * 4 = 204` bytes. - -Serialized form should be put in the encrypted part of DHT packet. -*/ -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SendNodes { - /** Nodes sent in response to [`GetNodes`](./struct.GetNodes.html) request. - - There can be only 1 to 4 nodes in `SendNodes`. - */ - pub nodes: Vec, - /// Ping id that was received in [`GetNodes`](./struct.GetNodes.html) - /// request. - pub id: u64, -} - -impl SendNodes { - /** - Create new `SendNodes`. Returns `None` if 0 or more than 4 nodes are - supplied. - - Created as a response to `GetNodes` request. - */ - pub fn with_nodes(request: &GetNodes, nodes: Vec) -> Option { - debug!(target: "SendNodes", "Creating SendNodes from GetNodes."); - trace!(target: "SendNodes", "With GetNodes: {:?}", request); - trace!("With nodes: {:?}", &nodes); - - if nodes.is_empty() || nodes.len() > 4 { - warn!(target: "SendNodes", "Wrong number of nodes supplied!"); - return None - } - - Some(SendNodes { nodes: nodes, id: request.id }) - } -} - -impl DhtPacketT for SendNodes { - fn kind(&self) -> PacketKind { - PacketKind::SendN - } -} - -/// Method assumes that supplied `SendNodes` has correct number of nodes -/// included – `[1, 4]`. -impl ToBytes for SendNodes { - fn to_bytes(&self) -> Vec { - debug!(target: "SendNodes", "Serializing SendNodes into bytes."); - trace!(target: "SendNodes", "With SendNodes: {:?}", self); - // first byte is number of nodes - let mut result: Vec = vec![self.nodes.len() as u8]; - for node in &*self.nodes { - result.extend_from_slice(&node.to_bytes()); - } - result.write_u64::(self.id) - .expect("Failed to write SendNodes id!"); - trace!("Resulting bytes: {:?}", &result); - result - } -} - -/** Method to parse received bytes as `SendNodes`. - - Returns `None` if bytes can't be parsed into `SendNodes`. -*/ -from_bytes!(SendNodes, do_parse!( - nodes_number: le_u8 >> - nodes: cond_reduce!( - nodes_number > 0 && nodes_number <= 4, - count!(PackedNode::parse_bytes, nodes_number as usize) - ) >> - id: ne_u64 >> - (SendNodes { - nodes: nodes, - id: id - }) -)); - -/// Trait for types of DHT packets that can be put in [`DhtPacket`] -/// (./struct.DhtPacket.html). -pub trait DhtPacketT: ToBytes + FromBytes + Eq + PartialEq + Debug { - /// Provide packet type number. - /// - /// To use for serialization: `.kind() as u8`. - fn kind(&self) -> PacketKind; - - /// Create a payload for [`DhtPacket`](./struct.DhtPacket.html) from - /// `self`. - // TODO: better name? - fn into_dht_packet_payload( - &self, - symmetric_key: &PrecomputedKey, - nonce: &Nonce) -> Vec - { - seal_precomputed(&self.to_bytes(), nonce, symmetric_key) - } -} - - -/** Standard DHT packet that encapsulates in the encrypted payload -[`DhtPacketT`](./trait.DhtPacketT.html). - -Length | Contents ------------ | -------- -`1` | `uint8_t` [`PacketKind`](../packet_kind/enum.PacketKind.html) -`32` | Sender DHT Public Key -`24` | Random nonce -variable | Encrypted payload - -`PacketKind` values for `DhtPacket` can be only `<= 4`. - -https://zetok.github.io/tox-spec/#dht-packet -*/ -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DhtPacket { - packet_type: PacketKind, - /// Public key of sender. - pub sender_pk: PublicKey, - nonce: Nonce, - payload: Vec, -} - -// TODO: max dht_packet size? -/// Minimal size of [`DhtPacket`](./struct.DhtPacket.html) in bytes. -pub const DHT_PACKET_MIN_SIZE: usize = 1 // packet type, plain - + PUBLICKEYBYTES - + NONCEBYTES - + MACBYTES - + PING_SIZE; // smallest payload - -impl DhtPacket { - /// Create new `DhtPacket` with `bytes`. - pub fn new

(symmetric_key: &PrecomputedKey, - own_public_key: &PublicKey, - nonce: &Nonce, - dp: &P) -> Self - where P: DhtPacketT - { - debug!(target: "DhtPacket", "Creating new DhtPacket."); - trace!(target: "DhtPacket", "With args: symmetric_key: , - own_public_key: {:?}, nonce: {:?}, packet: {:?}", - own_public_key, nonce, &dp); - - - let payload = dp.into_dht_packet_payload(symmetric_key, nonce); - - DhtPacket { - packet_type: dp.kind(), - sender_pk: *own_public_key, - nonce: *nonce, - payload: payload, - } - } - - /** Get [`PacketKind`](../packet_kind/enum.PacketKind.html) that - `DhtPacket`'s payload is supposed to contain. - */ - // TODO: write test(?) - pub fn kind(&self) -> PacketKind { - self.packet_type - } - - /** - Decrypt payload and try to parse it as packet type. - - To get info about it's packet type use - [`.kind()`](./struct.DhtPacket.html#method.kind) method. - - Returns `None` in case of faliure: - - - fails to decrypt - - fails to parse as given packet type - */ - /* TODO: perhaps switch to using precomputed symmetric key? - - given that computing shared key is apparently the most - costly operation when it comes to crypto, using precomputed - key might (would significantly?) lower resource usage - - Alternatively, another method `get_payloadnm()` which would use - symmetric key. - */ - pub fn get_payload

(&self, own_secret_key: &SecretKey) -> Option

- where P: DhtPacketT - { - debug!(target: "DhtPacket", "Getting packet data from DhtPacket."); - trace!(target: "DhtPacket", "With DhtPacket: {:?}", self); - let decrypted = match open(&self.payload, &self.nonce, &self.sender_pk, - own_secret_key) { - Ok(d) => d, - Err(_) => { - debug!("Decrypting DhtPacket failed!"); - return None - }, - }; - - trace!("Decrypted bytes: {:?}", &decrypted); - - P::from_bytes(&decrypted) - } - - /** - Create DHT Packet with [`Ping`](./struct.Ping.html) response to `Ping` - request that packet contained. - - Nonce for the response is automatically generated. - */ - pub fn ping_resp(&self, - secret_key: &SecretKey, - symmetric_key: &PrecomputedKey, - own_public_key: &PublicKey) -> Option { - - debug!(target: "DhtPacket", "Creating Ping response from Ping request - that DHT packet contained."); - trace!(target: "DhtPacket", "With args: DhtPacket: {:?}, own_pk: {:?}", - self, own_public_key); - - if self.kind() != PacketKind::PingReq { - return None - } - - let payload: PingReq = match self.get_payload(secret_key) { - Some(dpt) => dpt, - None => return None, - }; - - let resp = PingResp::from(payload); - let nonce = gen_nonce(); - - Some(DhtPacket::new(symmetric_key, own_public_key, &nonce, &resp)) - } -} - -/// Serialize `DhtPacket` into bytes. -impl ToBytes for DhtPacket { - fn to_bytes(&self) -> Vec { - debug!(target: "DhtPacket", "Serializing DhtPacket into bytes."); - trace!(target: "DhtPacket", "With DhtPacket: {:?}", self); - let mut result = Vec::with_capacity(DHT_PACKET_MIN_SIZE); - result.push(self.packet_type as u8); - - let PublicKey(pk) = self.sender_pk; - result.extend_from_slice(&pk); - - let Nonce(nonce) = self.nonce; - result.extend_from_slice(&nonce); - - result.extend_from_slice(&self.payload); - trace!("Resulting bytes: {:?}", &result); - result - } -} - -/// De-serialize bytes into `DhtPacket`. -from_bytes!(DhtPacket, do_parse!( - packet_type: verify!(call!(PacketKind::parse_bytes), |packet_type| match packet_type { - PacketKind::PingReq | PacketKind::PingResp | - PacketKind::GetN | PacketKind::SendN => true, - _ => false - }) >> - sender_pk: call!(PublicKey::parse_bytes) >> - nonce: call!(Nonce::parse_bytes) >> - payload: map!(rest, |bytes| bytes.to_vec() ) >> - (DhtPacket { - packet_type: packet_type, - sender_pk: sender_pk, - nonce: nonce, - payload: payload - }) -)); - - -/// Trait for functionality related to distance between `PublicKey`s. -pub trait Distance { - /// Check whether distance between PK1 and own PK is smaller than distance - /// between PK2 and own PK. - fn distance(&self, &PublicKey, &PublicKey) -> Ordering; -} - -impl Distance for PublicKey { - fn distance(&self, - &PublicKey(ref pk1): &PublicKey, - &PublicKey(ref pk2): &PublicKey) -> Ordering { - - trace!(target: "Distance", "Comparing distance between PKs."); - let &PublicKey(own) = self; - for i in 0..PUBLICKEYBYTES { - if pk1[i] != pk2[i] { - return Ord::cmp(&(own[i] ^ pk1[i]), &(own[i] ^ pk2[i])) - } - } - Ordering::Equal - } -} - - -/** Calculate the [`k-bucket`](./struct.Kbucket.html) index of a PK compared -to "own" PK. - -According to the [spec](https://zetok.github.io/tox-spec#bucket-index). - -Fails (returns `None`) if supplied keys are the same. -*/ -pub fn kbucket_index(&PublicKey(ref own_pk): &PublicKey, - &PublicKey(ref other_pk): &PublicKey) -> Option { - - debug!(target: "KBucketIndex", "Calculating KBucketIndex for PKs."); - trace!(target: "KBucketIndex", "With PK1: {:?}; PK2: {:?}", own_pk, other_pk); - - for byte in 0..PUBLICKEYBYTES { - for bit in 0..8 { - let shift = 7 - bit; - if (own_pk[byte] >> shift) & 0b1 != (other_pk[byte] >> shift) & 0b1 { - return Some((byte * 8 + bit) as u8) - } - } - } - None // PKs are equal -} - -/** -Structure for holding nodes. - -Number of nodes it can contain is set during creation. If not set (aka `None` -is supplied), number of nodes defaults to [`BUCKET_DEFAULT_SIZE`] -(./constant.BUCKET_DEFAULT_SIZE.html). - -Nodes stored in `Bucket` are in [`PackedNode`](./struct.PackedNode.html) -format. - -Used in [`Kbucket`](./struct.Kbucket.html) for storing nodes close to given -PK; and additionally used to store nodes closest to friends. - -[Spec definition](https://zetok.github.io/tox-spec#updating-k-buckets). -*/ -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Bucket { - /// Amount of nodes it can hold. - capacity: u8, - /// Nodes that bucket has, sorted by distance to PK. - nodes: Vec -} - -/// Default number of nodes that bucket can hold. -pub const BUCKET_DEFAULT_SIZE: usize = 8; - -impl Bucket { - /** Create a new `Bucket` to store nodes close to the `PublicKey`. - - Can hold up to `num` nodes if number is supplied. If `None` is - supplied, holds up to [`BUCKET_DEFAULT_SIZE`] - (./constant.BUCKET_DEFAULT_SIZE.html) nodes. If `Some(0)` is - supplied, it is treated as `None`. - */ - pub fn new(num: Option) -> Self { - trace!(target: "Bucket", "Creating a new Bucket."); - match num { - None => { - trace!("Creating a new Bucket with default capacity."); - Bucket { - capacity: BUCKET_DEFAULT_SIZE as u8, - nodes: Vec::with_capacity(BUCKET_DEFAULT_SIZE) - } - }, - Some(0) => { - error!("Treating Some(0) as None"); - Bucket::new(None) - }, - Some(n) => { - trace!("Creating a new Bucket with capacity: {}", n); - Bucket { capacity: n, nodes: Vec::with_capacity(n as usize) } - } - } - } - - /** - Try to get position of [`PackedNode`] in the bucket by PK. Used in - tests to check whether a `PackedNode` was added or removed. - - This method uses linear search as the simplest one. - - Returns Some(index) if it was found. - Returns None if there is no a `PackedNode` with the given PK. - - [`PackedNode`]: ./struct.PackedNode.html - */ - #[cfg(test)] - fn find(&self, pk: &PublicKey) -> Option { - for (n, node) in self.iter().enumerate() { - if node.pk() == pk { - return Some(n) - } - } - None - } - - /** - Try to add [`PackedNode`] to the bucket. - - - If the [`PackedNode`] with given `PublicKey` is already in the `Bucket`, - the [`PackedNode`] is updated (since its `SocketAddr` can differ). - - If bucket is not full, node is appended. - - If bucket is full, node's closeness is compared to nodes already - in bucket, and if it's closer than some node, it prepends that - node, and last node is removed from the list. - - If the node being added is farther away than the nodes in the bucket, - it isn't added and `false` is returned. - - Note that you must pass the same `base_pk` each call or the internal - state will be undefined. - - Returns `true` if node was added, `false` otherwise. - - [`PackedNode`]: ./struct.PackedNode.html - */ - pub fn try_add(&mut self, base_pk: &PublicKey, new_node: &PackedNode) - -> bool - { - debug!(target: "Bucket", "Trying to add PackedNode."); - trace!(target: "Bucket", "With bucket: {:?}; PK: {:?} and new node: {:?}", - self, base_pk, new_node); - - match self.nodes.binary_search_by(|n| base_pk.distance(n.pk(), new_node.pk())) { - Ok(index) => { - debug!(target: "Bucket", - "Updated: the node was already in the bucket."); - self.nodes.remove(index); - self.nodes.insert(index, *new_node); - true - }, - Err(index) if index == self.nodes.len() => { - // index is pointing past the end - if self.is_full() { - debug!(target: "Bucket", - "Node is too distant to add to the bucket."); - false - } else { - // distance to the PK was bigger than the other keys, but - // there's still free space in the bucket for a node - debug!(target: "Bucket", - "Node inserted at the end of the bucket."); - self.nodes.push(*new_node); - true - } - }, - Err(index) => { - // index is pointing inside the list - if self.is_full() { - debug!(target: "Bucket", - "No free space left in the bucket, the last node removed."); - self.nodes.pop(); - } - debug!(target: "Bucket", "Node inserted inside the bucket."); - self.nodes.insert(index, *new_node); - true - }, - } - } - - /** Remove [`PackedNode`](./struct.PackedNode.html) with given PK from the - `Bucket`. - - Note that you must pass the same `base_pk` each call or the internal - state will be undefined. Also `base_pk` must be equal to `base_pk` you added - a node with. Normally you don't call this function on your own but Kbucket does. - - If there's no `PackedNode` with given PK, nothing is being done. - */ - pub fn remove(&mut self, base_pk: &PublicKey, node_pk: &PublicKey) { - trace!(target: "Bucket", "Removing PackedNode with PK: {:?}", node_pk); - match self.nodes.binary_search_by(|n| base_pk.distance(n.pk(), node_pk) ) { - Ok(index) => { - self.nodes.remove(index); - }, - Err(_) => { - trace!("No PackedNode to remove with PK: {:?}", node_pk); - } - } - } - - /// Check if node with given PK is in the `Bucket`. - pub fn contains(&self, pk: &PublicKey) -> bool { - self.iter().any(|n| n.pk() == pk) - } - - /// Get the capacity of the Bucket. - pub fn capacity(&self) -> usize { - self.capacity as usize - } - - /** Check if `Bucket` is empty. - - Returns `true` if there are no nodes in the `Bucket`, `false` - otherwise. - */ - pub fn is_empty(&self) -> bool { - self.nodes.is_empty() - } - - /** Check if `Bucket` is full. - - Returns `true` if there is no free space in the `Bucket`, `false` - otherwise. - */ - pub fn is_full(&self) -> bool { - self.nodes.len() == self.capacity() - } - - /// Returns an iterator over contained [`PackedNode`] - /// (./struct.PackedNode.html)s. - pub fn iter(&self) -> BucketIter { - BucketIter { iter: self.nodes.iter() } - } -} - -/// Iterator over `Bucket`. -pub struct BucketIter<'a> { - iter: ::std::slice::Iter<'a, PackedNode>, -} - -impl<'a> Iterator for BucketIter<'a> { - type Item = &'a PackedNode; - - fn next(&mut self) -> Option { - self.iter.next() - } -} - -/** -Equivalent to calling [`Bucket::new()`] with `None`: - -``` -# use tox::toxcore::dht::Bucket; -assert_eq!(Bucket::new(None), Bucket::default()); -``` - -[`Bucket::new()`]: ./struct.Bucket.html#method.new -*/ -impl Default for Bucket { - fn default() -> Self { - Bucket::new(Some(BUCKET_DEFAULT_SIZE as u8)) - } -} - - -/** K-buckets structure to hold up to -[`KBUCKET_MAX_ENTRIES`](./constant.KBUCKET_MAX_ENTRIES.html) * -[`BUCKET_DEFAULT_SIZE`](./constant.BUCKET_DEFAULT_SIZE.html) nodes close to -own PK. - -Nodes in bucket are sorted by closeness to the PK; closest node is the first, -while furthest is last. - -Further reading: [Tox spec](https://zetok.github.io/tox-spec#k-buckets). -*/ -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Kbucket { - /// `PublicKey` for which `Kbucket` holds close nodes. - pk: PublicKey, - - /// List of [`Bucket`](./struct.Bucket.html)s. - buckets: Vec>, -} - -/** Maximum number of [`Bucket`](./struct.Bucket.html)s that [`Kbucket`] -(./struct.Kbucket.html) can hold. - -Realistically, not even half of that will be ever used, given how -[index calculation](./fn.kbucket_index.html) works. -*/ -pub const KBUCKET_MAX_ENTRIES: u8 = ::std::u8::MAX; - -/** Default number of [`Bucket`](./struct.Bucket.html)s that [`Kbucket`] -(./struct.Kbucket.html) holds. -*/ -pub const KBUCKET_BUCKETS: u8 = 128; - -impl Kbucket { - /// Create a new `Kbucket`. - /// - /// `n` – number of [`Bucket`](./struct.Bucket.html)s held. - pub fn new(n: u8, pk: &PublicKey) -> Self { - trace!(target: "Kbucket", "Creating new Kbucket with k: {:?} and PK: - {:?}", n, pk); - Kbucket { - pk: *pk, - buckets: vec![Box::new(Bucket::new(None)); n as usize] - } - } - - /// Number of [`Bucket`](./struct.Bucket.html)s held. - pub fn size(&self) -> u8 { - self.buckets.len() as u8 - } - - /// Get the PK of the Kbucket. Used in tests only - #[cfg(test)] - pub fn pk(&self) -> PublicKey { - self.pk - } - - /** - Try to get position of [`PackedNode`] in the kbucket by PK. Used in - tests to check whether a `PackedNode` was added or removed. - - This method uses quadratic search as the simplest one. - - Returns `Some(bucket_index, node_index)` if it was found. - Returns `None` if there is no a [`PackedNode`] with the given PK. - - [`PackedNode`]: ./struct.PackedNode.html - */ - #[cfg(test)] - fn find(&self, pk: &PublicKey) -> Option<(usize, usize)> { - for (bucket_index, bucket) in self.buckets.iter().enumerate() { - match bucket.find(pk) { - None => {}, - Some(node_index) => return Some((bucket_index, node_index)) - } - } - None - } - - - /** Return the possible internal index of [`Bucket`](./struct.Bucket.html) - where the key could be inserted/removed. - - Returns `Some(index)` if [`kbucket index`](./fn.kbucket_index.html) is - defined and it is lower than the number of buckets. - - Returns `None` otherwise. - */ - fn bucket_index(&self, pubkey: &PublicKey) -> Option { - match kbucket_index(&self.pk, pubkey) { - Some(index) if index < self.size() => Some(index as usize), - _ => None - } - } - - /** Add [`PackedNode`](./struct.PackedNode.html) to `Kbucket`. - - Node can be added only if: - - * its [`kbucket index`](./fn.kbucket_index.html) is lower than the - number of buckets. - * [`Bucket`](./struct.Bucket.html) to which it is added has free space - or added node is closer to the PK than other node in the bucket. - - Returns `true` if node was added successfully, `false` otherwise. - */ - pub fn try_add(&mut self, node: &PackedNode) -> bool { - debug!(target: "Kbucket", "Trying to add PackedNode."); - trace!(target: "Kbucket", "With PN: {:?}; and self: {:?}", node, self); - - match self.bucket_index(node.pk()) { - Some(index) => self.buckets[index].try_add(&self.pk, node), - None => { - trace!("Failed to add node: {:?}", node); - false - } - } - } - - /// Remove [`PackedNode`](./struct.PackedNode.html) with given PK from the - /// `Kbucket`. - pub fn remove(&mut self, node_pk: &PublicKey) { - trace!(target: "Kbucket", "Removing PK: {:?} from Kbucket: {:?}", node_pk, - self); - - match self.bucket_index(node_pk) { - Some(index) => self.buckets[index].remove(&self.pk, node_pk), - None => trace!("Failed to remove PK: {:?}", node_pk) - } - } - - /** Get (up to) 4 closest nodes to given PK. - - Functionality for [`SendNodes`](./struct.SendNodes.html). - - Returns less than 4 nodes only if `Kbucket` contains less than 4 - nodes. - */ - pub fn get_closest(&self, pk: &PublicKey) -> Vec { - debug!(target: "Kbucket", "Getting closest nodes."); - trace!(target: "Kbucket", "With PK: {:?} and self: {:?}", pk, self); - // create a new Bucket with associated pk, and add nodes that are close - // to the PK - let mut bucket = Bucket::new(Some(4)); - for buc in &*self.buckets { - for node in &*buc.nodes { - bucket.try_add(pk, node); - } - } - trace!("Returning nodes: {:?}", &bucket.nodes); - bucket.nodes - } - - /** - Check if `Kbucket` contains [`PackedNode`] with given PK. - - [`PackedNode`]: ./struct.PackedNode.html - */ - pub fn contains(&self, pk: &PublicKey) -> bool { - match self.bucket_index(pk) { - Some(i) => self.buckets[i].contains(pk), - None => false, - } - } - - /** - Naive check whether a [`PackedNode`] can be added to the `Kbucket`. - - Returns `true` if [`Bucket`] where node could be placed is not full - and node is not already in the [`Bucket`]. - - Otherwise `false` is returned. - - [`Bucket`]: ./struct.Bucket.html - [`PackedNode`]: ./struct.PackedNode.html - */ - pub fn can_add(&self, pk: &PublicKey) -> bool { - match self.bucket_index(pk) { - None => false, - Some(i) => - !self.buckets[i].is_full() && !self.buckets[i].contains(pk), - } - } - - /** Check if `Kbucket` is empty. - - Returns `true` if all `buckets` are empty, `false` - otherwise. - */ - pub fn is_empty(&self) -> bool { - self.buckets.iter().all(|bucket| bucket.is_empty()) - } - - /// Create iterator over [`PackedNode`](./struct.PackedNode.html)s in - /// `Kbucket`. - pub fn iter(&self) -> KbucketIter { - KbucketIter { - pos_b: 0, - pos_pn: 0, - buckets: self.buckets.as_slice(), - } - } -} - -/// Iterator over `PackedNode`s in `Kbucket`. -pub struct KbucketIter<'a> { - pos_b: usize, - pos_pn: usize, - buckets: &'a [Box], -} - -impl<'a> Iterator for KbucketIter<'a> { - type Item = &'a PackedNode; - - fn next(&mut self) -> Option { - if self.pos_b < self.buckets.len() { - match self.buckets[self.pos_b].iter().nth(self.pos_pn) { - Some(s) => { - self.pos_pn += 1; - Some(s) - }, - None => { - self.pos_b += 1; - self.pos_pn = 0; - self.next() - }, - } - } else { - None - } - } -} - - -/** `NatPing` type byte for [`NatPingReq`] and [`NatPingResp`]. - -https://zetok.github.io/tox-spec/#nat-ping-request - -[`NatPingReq`]: ./struct.PingReq.html -[`NatPingResp`]: ./struct.PingResp.html -*/ -pub const NAT_PING_TYPE: u8 = 0xfe; - -/** Length in bytes of NatPings when serialized into bytes. - -NatPings: - - - [`NatPingReq`](./struct.PingReq.html) - - [`NatPingResp`](./struct.PingResp.html) -*/ -pub const NAT_PING_SIZE: usize = PING_SIZE + 1; - -macro_rules! impls_for_nat_pings { - ($($np:ident($p:ident)),+) => ($( - /** NAT Ping; used to see if a friend we are not connected to directly - is online and ready to do the hole punching. - - Basically a wrapper + customization of DHT [`PingReq`]/[`PingResp`]. - - Added: - `0xfe` prepended in serialized form. - - Used by [`DhtRequest`](./struct.DhtRequest.html). - - Serialized form: - - Length | Contents - -------|--------- - 1 | type (`0xfe`) - 9 | [`PingReq`]/[`PingResp`] - - Spec: https://zetok.github.io/tox-spec/#nat-ping-packets - - [`PingReq`]: ./struct.PingReq.html - [`PingResp`]: ./struct.PingResp.html - */ - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub struct $np(pub $p); - - impl $np { - /// Return ID of the ping. - pub fn id(&self) -> u64 { - $p::id(self) - } - } - - impl DhtRequestT for $np {} - - from_bytes!($np, do_parse!( - tag!(&[NAT_PING_TYPE][..]) >> - p: call!($p::parse_bytes) >> - ($np(p)) - )); - - /// Serializes into bytes. - impl ToBytes for $np { - fn to_bytes(&self) -> Vec { - debug!(target: "NatPing", "Serializing NatPing into bytes."); - let mut result = Vec::with_capacity(NAT_PING_SIZE); - - // special, "magic" type of NatPing, according to spec: - // https://zetok.github.io/tox-spec/#nat-ping-request - result.push(NAT_PING_TYPE); - // and the rest of stuff inherited from `Ping` - result.extend_from_slice($p::to_bytes(self).as_slice()); - trace!("Serialized {}: {:?}", stringify!($np), &result); - result - } - } - - impl Deref for $np { - type Target = $p; - - fn deref(&self) -> &$p { - let $np(ref ping) = *self; - ping - } - } - )+) -} -impls_for_nat_pings!(NatPingReq(PingReq), NatPingResp(PingResp)); - -impl From for NatPingResp { - fn from(p: NatPingReq) -> Self { - NatPingResp(PingResp::from(p.0)) - } -} - -impl NatPingReq { - /// Create new `NatPingReq` request with a randomly generated `request id`. - pub fn new() -> Self { - trace!(target: "NatPingReq", "Creating new Ping."); - NatPingReq(PingReq::new()) - } -} - - -/** Trait for types of DHT requests that can be put in [`DhtRequest`] -(./struct.DhtRequest.html). - -*Currently only NatPings, in the future also onion-related stuff.* See -[Implementors](./trait.DhtRequestT.html#implementors). -*/ -pub trait DhtRequestT: FromBytes + ToBytes + Eq + PartialEq + Debug { - /// Create DHT request payload to use by `DhtRequest`. - fn to_dht_request_payload(&self, - sender_secret_key: &SecretKey, - receiver_public_key: &PublicKey, - nonce: &Nonce) - -> Vec - { - seal(&self.to_bytes(), nonce, receiver_public_key, sender_secret_key) - } -} - - - - -/** DHT Request packet structure. - -Used to send data via one node1 to other node2 via intermediary node when -there is no direct connection between nodes 1 and 2. - -`` - -When receiving `DhtRequest` own instance should check whether receiver PK -matches own PK, or PK of a known node. - -- if it matches own PK, handle it. -- if it matches PK of a known node, send packet to that node - -Serialized structure: - -Length | Contents --------|--------- -1 | `32` -32 | receiver's DHT public key -32 | sender's DHT public key -24 | Nonce -? | encrypted data - -https://zetok.github.io/tox-spec/#dht-request-packets -*/ -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DhtRequest { - /// `PublicKey` of receiver. - pub receiver: PublicKey, - /// `PUblicKey` of sender. - pub sender: PublicKey, - nonce: Nonce, - payload: Vec, -} - -impl DhtRequest { - /// Create a new `DhtRequest`. - pub fn new

(secret_key: &SecretKey, - own_public_key: &PublicKey, - receiver_public_key: &PublicKey, - nonce: &Nonce, - drt: &P) -> Self - where P: DhtRequestT - { - - debug!(target: "DhtRequest", "Creating new DhtRequest."); - trace!(target: "DhtRequest", "With args: summetric_key: , - own_public_key: {:?}, receiver_public_key: {:?} nonce: {:?}, - packet: {:?}", - own_public_key, receiver_public_key, nonce, drt); - - let payload = drt.to_dht_request_payload(secret_key, - receiver_public_key, - nonce); - - DhtRequest { - receiver: *receiver_public_key, - sender: *own_public_key, - nonce: *nonce, - payload: payload, - } - } - - /** Get request data. This function decrypts payload and tries to parse it - as request type. - - Returns `None` in case of failure. - */ - pub fn get_request

(&self, secret_key: &SecretKey) -> Option

- where P: DhtRequestT - { - debug!(target: "DhtRequest", "Getting request data from DhtRequest."); - trace!(target: "DhtRequest", "With DhtRequest: {:?}", self); - let decrypted = match open(&self.payload, &self.nonce, &self.sender, - secret_key) { - - Ok(d) => d, - Err(_) => { - debug!("Decrypting DhtRequest failed!"); - return None - }, - }; - - trace!("Decrypted bytes: {:?}", &decrypted); - - P::from_bytes(&decrypted) - } -} - - -#[cfg(test)] -mod test { - use quickcheck::{quickcheck, TestResult}; - - use toxcore::dht::*; - - - // DhtPacket:: - - // DhtPacket::new() - - #[test] - fn dht_packet_new_test() { - fn with_dht_packet

(dpt: P) - where P: DhtPacketT - { - let (pk, sk) = gen_keypair(); - let precomputed = precompute(&pk, &sk); - let nonce = gen_nonce(); - let dhtp = DhtPacket::new(&precomputed, &pk, &nonce, &dpt); - assert_eq!(dhtp.sender_pk, pk); - assert_eq!(dpt.kind(), dhtp.packet_type); - assert_eq!(nonce, dhtp.nonce); - } - quickcheck(with_dht_packet as fn(PingReq)); - quickcheck(with_dht_packet as fn(PingResp)); - quickcheck(with_dht_packet as fn(GetNodes)); - quickcheck(with_dht_packet as fn(SendNodes)); - } - - - // Bucket:: - - // Bucket::position() - - #[test] - fn bucket_position_test() { - fn with_data(test_fn: F) - where F: Fn(&mut Bucket, // bucket - &PublicKey, // base_pk - &PackedNode, // n1 - &PackedNode, // n2 - &PackedNode) // n3 - { - let mut bucket = Bucket::new(None); - - let base_pk = PublicKey([3; PUBLICKEYBYTES]); - - let addr = Ipv4Addr::new(0, 0, 0, 0); - let saddr = SocketAddrV4::new(addr, 0); - - let pk1 = PublicKey([1; PUBLICKEYBYTES]); - let n1 = PackedNode::new(false, SocketAddr::V4(saddr), &pk1); - - let pk2 = PublicKey([2; PUBLICKEYBYTES]); - let n2 = PackedNode::new(false, SocketAddr::V4(saddr), &pk2); - - let pk3 = PublicKey([4; PUBLICKEYBYTES]); - let n3 = PackedNode::new(false, SocketAddr::V4(saddr), &pk3); - - assert!(base_pk > pk1); - assert!(base_pk > pk2); - assert!(base_pk < pk3); - - assert!(pk1 < pk2); - assert!(pk2 < pk3); - assert!(pk1 < pk3); - - test_fn(&mut bucket, &base_pk, &n1, &n2, &n3); - } - // Check that insertion order does not affect - // the result order in the bucket if the number nodes = - // bucket size and nodes' pk are unique - with_data(|bucket, base_pk, n1, n2, n3| { - // insert order: n1 n2 n3 maps to position - // n1 => 1, n2 => 0, n3 => 2 - bucket.try_add(base_pk, n1); - bucket.try_add(base_pk, n2); - bucket.try_add(base_pk, n3); - assert_eq!(Some(1), bucket.find(n1.pk())); - assert_eq!(Some(0), bucket.find(n2.pk())); - assert_eq!(Some(2), bucket.find(n3.pk())); - }); - with_data(|bucket, base_pk, n1, n2, n3| { - // insert order: n3 n2 n1 maps to position - // n1 => 1, n2 => 0, n3 => 2 - bucket.try_add(base_pk, n3); - bucket.try_add(base_pk, n2); - bucket.try_add(base_pk, n1); - assert_eq!(Some(1), bucket.find(n1.pk())); - assert_eq!(Some(0), bucket.find(n2.pk())); - assert_eq!(Some(2), bucket.find(n3.pk())); - }); - with_data(|bucket, base_pk, n1, n2, n3| { - // insert order: n1 n2 n1 n2 n3 n2 maps to position - // n1 => 1, n2 => 0, n3 => 2 - bucket.try_add(base_pk, n1); - bucket.try_add(base_pk, n2); - bucket.try_add(base_pk, n1); - bucket.try_add(base_pk, n2); - bucket.try_add(base_pk, n3); - bucket.try_add(base_pk, n2); - assert_eq!(Some(1), bucket.find(n1.pk())); - assert_eq!(Some(0), bucket.find(n2.pk())); - assert_eq!(Some(2), bucket.find(n3.pk())); - }); - // Check that removing order does not affect - // the order of nodes inside - with_data(|bucket, base_pk, n1, n2, n3| { - // prepare bucket - bucket.try_add(base_pk, n1); // => 1 - bucket.try_add(base_pk, n2); // => 0 - bucket.try_add(base_pk, n3); // => 2 - // test removing from the beginning (n2 => 0) - bucket.remove(base_pk, n2.pk()); - assert_eq!(Some(0), bucket.find(n1.pk())); - assert_eq!(None, bucket.find(n2.pk())); - assert_eq!(Some(1), bucket.find(n3.pk())); - }); - with_data(|bucket, base_pk, n1, n2, n3| { - // prepare bucket - bucket.try_add(base_pk, n1); // => 1 - bucket.try_add(base_pk, n2); // => 0 - bucket.try_add(base_pk, n3); // => 2 - // test removing from the middle (n1 => 1) - bucket.remove(base_pk, n1.pk()); - assert_eq!(None, bucket.find(n1.pk())); - assert_eq!(Some(0), bucket.find(n2.pk())); - assert_eq!(Some(1), bucket.find(n3.pk())); - }); - with_data(|bucket, base_pk, n1, n2, n3| { - // prepare bucket - bucket.try_add(base_pk, n1); // => 1 - bucket.try_add(base_pk, n2); // => 0 - bucket.try_add(base_pk, n3); // => 2 - // test removing from the end (n3 => 2) - bucket.remove(base_pk, n3.pk()); - assert_eq!(Some(1), bucket.find(n1.pk())); - assert_eq!(Some(0), bucket.find(n2.pk())); - assert_eq!(None, bucket.find(n3.pk())); - }); - } - - // Bucket::contains() - - quickcheck! { - fn bucket_contains_test(n: u8, pns: Vec) -> () { - let mut bucket = Bucket::new(Some(n)); - // empty never contains any node - assert!(!bucket.contains(&PublicKey([0; PUBLICKEYBYTES]))); - for pn in &pns { - assert!(!bucket.contains(pn.pk())); - } - - let (pk, _) = gen_keypair(); - for node in &pns { - bucket.try_add(&pk, node); - } - - for node in &bucket.nodes { - assert!(bucket.contains(node.pk())); - } - } - } - - // Bucket::default() - - #[test] - fn bucket_default_test() { - assert_eq!(Bucket::new(None), Bucket::default()); - } - - // BucketIter::next() - - quickcheck! { - fn bucket_iter_next_test(n: u8, pns: Vec) -> () { - // can contain all nodes - let mut bucket = Bucket::new(Some(n)); - // empty always returns None - assert!(bucket.iter().next().is_none()); - - let (pk, _) = gen_keypair(); - - for node in &pns { - bucket.try_add(&pk, node); - } - - let mut expect = Vec::new(); - for node in &bucket.nodes { - expect.push(*node); - } - - let mut e_iter = expect.iter(); - let mut b_iter = bucket.iter(); - loop { - let enext = e_iter.next(); - let bnext = b_iter.next(); - assert_eq!(enext, bnext); - if enext.is_none() { - break; - } - } - } - } - - - // Kbucket:: - - // Kbucket::position() - - #[test] - fn kbucket_position_test() { - fn with_data(test_fn: F) - where F: Fn(&mut Kbucket, // kbucket - &PackedNode, // n1 - &PackedNode, // n2 - &PackedNode) // n3 - { - let mut pk_bytes = [3; PUBLICKEYBYTES]; - - pk_bytes[0] = 1; - let base_pk = PublicKey(pk_bytes); - - let mut kbucket = Kbucket::new(KBUCKET_MAX_ENTRIES, &base_pk); - - let addr = Ipv4Addr::new(0, 0, 0, 0); - let saddr = SocketAddrV4::new(addr, 0); - - let n0_base_pk = PackedNode::new(false, SocketAddr::V4(saddr), &base_pk); - assert!(!kbucket.try_add(&n0_base_pk)); - kbucket.remove(&base_pk); - - pk_bytes[5] = 1; - let pk1 = PublicKey(pk_bytes); - let n1 = PackedNode::new(false, SocketAddr::V4(saddr), &pk1); - - pk_bytes[10] = 2; - let pk2 = PublicKey(pk_bytes); - let n2 = PackedNode::new(false, SocketAddr::V4(saddr), &pk2); - - pk_bytes[14] = 4; - let pk3 = PublicKey(pk_bytes); - let n3 = PackedNode::new(false, SocketAddr::V4(saddr), &pk3); - - assert!(pk1 > pk2); - assert!(pk2 < pk3); - assert!(pk1 > pk3); - - assert_eq!(Some(46), kbucket_index(&base_pk, &pk1)); - assert_eq!(Some(46), kbucket_index(&base_pk, &pk2)); - assert_eq!(Some(46), kbucket_index(&base_pk, &pk3)); - - test_fn(&mut kbucket, &n1, &n2, &n3); - } - // Check that insertion order does not affect - // the result order in the kbucket - with_data(|kbucket, n1, n2, n3| { - // insert order: n1 n2 n3 maps to position - // n1 => 0, n2 => 1, n3 => 2 - kbucket.try_add(n1); - kbucket.try_add(n2); - kbucket.try_add(n3); - assert_eq!(Some((46, 0)), kbucket.find(n1.pk())); - assert_eq!(Some((46, 1)), kbucket.find(n2.pk())); - assert_eq!(Some((46, 2)), kbucket.find(n3.pk())); - }); - with_data(|kbucket, n1, n2, n3| { - // insert order: n3 n2 n1 maps to position - // n1 => 0, n2 => 1, n3 => 2 - kbucket.try_add(n3); - kbucket.try_add(n2); - kbucket.try_add(n1); - assert_eq!(Some((46, 0)), kbucket.find(n1.pk())); - assert_eq!(Some((46, 1)), kbucket.find(n2.pk())); - assert_eq!(Some((46, 2)), kbucket.find(n3.pk())); - }); - // Check that removing order does not affect - // the order of nodes inside - with_data(|kbucket, n1, n2, n3| { - // prepare kbucket - kbucket.try_add(n1); // => 0 - kbucket.try_add(n2); // => 1 - kbucket.try_add(n3); // => 2 - // test removing from the beginning (n1 => 0) - kbucket.remove(n1.pk()); - assert_eq!(None, kbucket.find(n1.pk())); - assert_eq!(Some((46, 0)), kbucket.find(n2.pk())); - assert_eq!(Some((46, 1)), kbucket.find(n3.pk())); - }); - with_data(|kbucket, n1, n2, n3| { - // prepare kbucket - kbucket.try_add(n1); // => 0 - kbucket.try_add(n2); // => 1 - kbucket.try_add(n3); // => 2 - // test removing from the middle (n2 => 1) - kbucket.remove(n2.pk()); - assert_eq!(Some((46, 0)), kbucket.find(n1.pk())); - assert_eq!(None, kbucket.find(n2.pk())); - assert_eq!(Some((46, 1)), kbucket.find(n3.pk())); - }); - with_data(|kbucket, n1, n2, n3| { - // prepare kbucket - kbucket.try_add(n1); // => 0 - kbucket.try_add(n2); // => 1 - kbucket.try_add(n3); // => 2 - // test removing from the end (n3 => 2) - kbucket.remove(n3.pk()); - assert_eq!(Some((46, 0)), kbucket.find(n1.pk())); - assert_eq!(Some((46, 1)), kbucket.find(n2.pk())); - assert_eq!(None, kbucket.find(n3.pk())); - }); - } - - // Kbucket::contains() - - quickcheck! { - fn kbucket_contains_test(n: u8, pns: Vec) -> TestResult { - if pns.is_empty() { return TestResult::discard() } - - let (pk, _) = gen_keypair(); - let mut kbucket = Kbucket::new(n, &pk); - assert!(!kbucket.contains(&pk)); - assert!(pns.iter().all(|pn| !kbucket.contains(pn.pk()))); - - for pn in &pns { - kbucket.try_add(pn); - } - - assert!(kbucket.iter().all(|pn| kbucket.contains(pn.pk()))); - - TestResult::passed() - } - } - - // Kbucket::can_add() - - quickcheck! { - fn kbucket_can_add_test(n: u8, pns: Vec) -> TestResult { - if pns.len() < 2 { return TestResult::discard() } - - let (pk, _) = gen_keypair(); - // there should be at least a pair of nodes with same index - { - let fitting_nodes = pns.iter().any(|p1| pns.iter() - .filter(|p2| p1 != *p2) - .any(|p2| kbucket_index(&pk, p1.pk()) == kbucket_index(&pk, p2.pk()))); - if !fitting_nodes { - return TestResult::discard() - } - } - - let mut kbucket = Kbucket { - pk: pk, - buckets: vec![Box::new(Bucket::new(Some(1))); n as usize], - }; - - for node in &pns { - if kbucket.try_add(node) { - let index = kbucket_index(&pk, node.pk()); - // none of nodes with the same index can be added - // to the kbucket - assert!(pns.iter() - .filter(|pn| kbucket_index(&pk, pn.pk()) == index) - .all(|pn| !kbucket.can_add(pn.pk()))); - } - } - - TestResult::passed() - } - } - - - - - // KbucketIter::next() - - quickcheck! { - fn kbucket_iter_next_test(n: u8, pns: Vec) -> () { - let (pk, _) = gen_keypair(); - let mut kbucket = Kbucket::new(n, &pk); - // empty always returns None - assert!(kbucket.iter().next().is_none()); - - for node in &pns { - kbucket.try_add(node); - } - - let mut expect = Vec::new(); - for bucket in &kbucket.buckets { - for node in bucket.iter() { - expect.push(*node); - } - } - - let mut e_iter = expect.iter(); - let mut k_iter = kbucket.iter(); - loop { - let enext = e_iter.next(); - let knext = k_iter.next(); - assert_eq!(enext, knext); - if enext.is_none() { - break; - } - } - } - } -} diff --git a/src/toxcore/dht_old/dht_node.rs b/src/toxcore/dht_old/dht_node.rs deleted file mode 100644 index b7963506d..000000000 --- a/src/toxcore/dht_old/dht_node.rs +++ /dev/null @@ -1,1222 +0,0 @@ -/* - Copyright © 2017 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - - -/*! -Functionality needed to work as a DHT node. - -Made on top of `dht` and `network` modules. -*/ -// TODO: expand doc - - -use futures::*; -use futures::sink; -use futures::stream::*; -use futures::sync::mpsc; -use tokio_core::net::{UdpCodec, UdpFramed}; -use tokio_core::reactor::Core; -use tokio_proto::multiplex::RequestId; - -use std::collections::VecDeque; -use std::io::{self, ErrorKind}; -use std::net::SocketAddr; -use std::thread; - -use toxcore::binary_io::{FromBytes, ToBytes}; -use toxcore::crypto_core::*; -use toxcore::dht::*; -use toxcore::packet_kind::PacketKind; -//use toxcore::timeout::*; - - -/// Type for sending `SplitSink` with `ToxCodec`. -// FIXME: docs -// TODO: rename -pub type ToxSplitSink = SplitSink>; - -/// Type for receiving `SplitStream` with `ToxCodec`. -// FIXME: docs -// TODO: rename -pub type ToxSplitStream = SplitStream>; - -/// Type representing future `Send` via `SplitSink`. -// FIXME: docs -// TODO: rename -pub type SendSink = sink::Send>>; - -/// Type representing Tox UDP packets. -// TODO: change DhtPacket to and enum with all possible packets -pub type ToxUdpPacket = (SocketAddr, DhtPacket); - -/// Type representing received Tox UDP packets. -// TODO: change DhtPacket to and enum with all possible packets -pub type ToxRecvUdpPacket = (SocketAddr, Option); - -/** -Own DHT node data. - -Contains: - -- DHT public key -- DHT secret key -- Close List ([`Kbucket`] with nodes close to own DHT public key) -- ping timeout lists ([`TimeoutQueue`]) - -# Adding node to Close List - -Before a [`PackedNode`] is added to the Close List, it needs to be -checked whether: - -- it can be added to [`Kbucket`] \(using [`Kbucket::can_add()`]) -- [`PackedNode`] is actually online - -Once the first check passes node is added to the temporary list, and -a [`GetNodes`] request is sent to it in order to check whether it's -online. If the node responds correctly within [`PING_TIMEOUT`], it's -removed from temporary list and added to the Close List. - -[`GetNodes`]: ../dht/struct.GetNodes.html -[`Kbucket`]: ../dht/struct.Kbucket.html -[`Kbucket::can_add()`]: ../dht/struct.Kbucket.html#method.can_add -[`PackedNode`]: ../dht/struct.PackedNode.html -[`PING_TIMEOUT`]: ../timeout/constant.PING_TIMEOUT.html -[`TimeoutQueue`]: ../timeout/struct.TimeoutQueue.html -*/ -#[derive(Clone, Eq, PartialEq)] -pub struct DhtNode { - dht_secret_key: Box, - dht_public_key: Box, - /// Close List (contains nodes close to own DHT PK) - kbucket: Box, - //getn_timeout: TimeoutQueue, - // timeouts for requests that check whether a node is online before - // adding it to the Close List - // TODO: rename - //to_close_tout: TimeoutQueue, - /// list of nodes that are checked for being online before adding - /// to the Close List - // TODO: rename - to_close_nodes: VecDeque, - // TODO: add a "verify" TimeoutQueue to check if nodes are online - // before adding them to the kbucket - - // TODO: track sent ping request IDs - // TODO: have a table with precomputed keys for all known nodes? - // (use lru-cache for storing last used 1024?) -} - - -impl DhtNode { - /** - Create new `DhtNode` instance. - - Note: a new instance generates new DHT public and secret keys. - - DHT `PublicKey` and `SecretKey` are supposed to be ephemeral. - */ - pub fn new() -> io::Result { - if !crypto_init() { - return Err(io::Error::new(ErrorKind::Other, - "Crypto initialization failed.")); - } - - let (pk, sk) = gen_keypair(); - let kbucket = Kbucket::new(KBUCKET_BUCKETS, &pk); - - debug!("Created new DhtNode instance"); - Ok(DhtNode { - dht_secret_key: Box::new(sk), - dht_public_key: Box::new(pk), - kbucket: Box::new(kbucket), - //getn_timeout: Default::default(), - //to_close_tout: Default::default(), - to_close_nodes: Default::default(), - }) - } - - - /** Try to add nodes to [Kbucket](../dht/struct.Kbucket.html). - - Wrapper around Kbucket's method. - */ - pub fn try_add(&mut self, node: &PackedNode) -> bool { - self.kbucket.try_add(node) - } - - /** - Reference to own DHT `PublicKey`. - */ - fn pk(&self) -> &PublicKey { - &self.dht_public_key - } - - /** - Reference to own DHT `SecretKey`. - */ - fn sk(&self) -> &SecretKey { - &self.dht_secret_key - } - - /** - Remove nodes that have crossed `secs` timeout threshold. - */ - // TODO: test - // TODO: add fn for ping/getn req timeouts with hardcoded consts? - pub fn remove_timed_out(&mut self, secs: u64) { - /*for pk in self.getn_timeout.get_timed_out(secs) { - debug!("Removing timed out node"); - self.kbucket.remove(&pk); - }*/ - } - - /** - Create a [`DhtPacket`] to peer with `peer_pk` `PublicKey` containing - a [`PingReq`] request. - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [`PingReq`]: ../dht/struct.PingReq.html - */ - fn create_ping_req(&self, peer_pk: &PublicKey) -> DhtPacket { - let ping = PingReq::new(); - // TODO: precompute shared key to calculate it 1 time - let shared_secret = &encrypt_precompute(peer_pk, self.sk()); - let nonce = &gen_nonce(); - DhtPacket::new(shared_secret, self.pk(), nonce, &ping) - } - - /** - Create a [`ToxUdpPacket`] with request for ping response from a peer. - - [`ToxUdpPacket`] is to be passed to `Sender` created by - [`send_packets()`]. - - [`send_packets()`]: ./fn.send_packets.html - [`ToxUdpPacket`]: ./type.ToxUdpPacket.html - */ - // TODO: track requests - pub fn request_ping(&self, peer: &PackedNode) -> ToxUdpPacket { - let request = self.create_ping_req(peer.pk()); - (peer.socket_addr(), request) - } - - /** - Create a [`DhtPacket`] in response to [`DhtPacket`] containing - [`PingReq`] packet. - - Returns `None` if [`DhtPacket`] is not a [`PingReq`]. - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [`PingReq`]: ../dht/struct.PingReq.html - */ - fn create_ping_resp(&self, request: &DhtPacket) - -> Option - { - // TODO: precompute shared key to calculate it 1 time - let precomp = encrypt_precompute(&request.sender_pk, self.sk()); - request.ping_resp(self.sk(), &precomp, self.pk()) - } - - /** - Create a future sending [`DhtPacket`] that encapsulates - [ping response] to supplied ping request. - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [ping response]: ../dht/struct.PingResp.html - */ - // TODO: change to return Option - pub fn respond_ping(&self, - sink: ToxSplitSink, - peer_addr: SocketAddr, - request: &DhtPacket) - -> Option - { - self.create_ping_resp(request) - .map(|p| sink.send((peer_addr, p))) - } - - /** - Create a [`DhtPacket`] to peer's `PublicKey` containing - a [`GetNodes`] request for nodes close to own DHT `PublicKey`. - - `RequestId` is to be used for tracking node timeouts. - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [`GetNodes`]: ../dht/struct.GetNodes.html - */ - pub fn create_getn(&self, peer_pk: &PublicKey) - -> (RequestId, DhtPacket) { - // request for nodes that are close to our own DHT PK - let req = &GetNodes::new(self.pk()); - let shared_secret = &encrypt_precompute(peer_pk, self.sk()); - let nonce = &gen_nonce(); - (req.id, DhtPacket::new(shared_secret, self.pk(), nonce, req)) - } - - /** - Create a [`ToxUdpPacket`] with request for nodes from a peer. - - [`ToxUdpPacket`] is to be passed to `Sender` created by - [`send_packets()`]. - - `RequestId` is to be used for tracking node timeouts. - - [`send_packets()`]: ./fn.send_packets.html - [`ToxUdpPacket`]: ./type.ToxUdpPacket.html - */ - pub fn request_nodes(&mut self, peer: &PackedNode) - -> (RequestId, ToxUdpPacket) - { - let (id, request) = self.create_getn(peer.pk()); - (id, (peer.socket_addr(), request)) - } - - /** - Create [`ToxUdpPacket`]s with request for nodes from every peer in - the Close List. - - [`ToxUdpPacket`]s are to be passed to `Sender` created by - [`send_packets()`]. - - **Adds request to response timeout queue.** - - **Note**: returned `Vec` can be empty if there are no known nodes. - - [`send_packets()`]: ./fn.send_packets.html - [`ToxUdpPacket`]: ./type.ToxUdpPacket.html - */ - pub fn request_nodes_close(&mut self) -> Vec { - self.kbucket.iter() - // copy, collect & iter again to work around borrow checker - .cloned() - .collect::>() - .iter() - .map(|pn| { - let (id, packet) = self.request_nodes(pn); - // add to timeout queue - // self.getn_timeout.add(pn.pk(), id); - packet - }) - .collect() - } - - - /** - Create a [`DhtPacket`] to peer with `peer_pk` `PublicKey` - containing [`SendNodes`] response. - - Returns `None` if own `Kbucket` is empty or supplied `DhtPacket` - doesn't contain [`GetNodes`] request. - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [`GetNodes`]: ../dht/struct.GetNodes.html - [`SendNodes`]: ../dht/struct.SendNodes.html - */ - fn create_sendn(&self, request: &DhtPacket) - -> Option - { - // TODO: precompute shared key to calculate it 1 time - let getn = match request.get_payload::(self.sk()) { - Some(g) => g, - None => return None, - }; - let sendn = match getn.response(&*self.kbucket) { - Some(s) => s, - None => return None, - }; - let shared_secret = &encrypt_precompute(&request.sender_pk, self.sk()); - let nonce = &gen_nonce(); - Some(DhtPacket::new(shared_secret, self.pk(), nonce, &sendn)) - } - - /** - Send nodes in response to [`GetNodes`] request contained in - [`DhtPacket`]. - - Can fail (return `None`) if Kbucket is empty or `DhtPacket` doesn't - contain `GetNodes` request. - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [`GetNodes`]: ../dht/struct.GetNodes.html - */ - pub fn send_nodes(&self, - sink: ToxSplitSink, - peer_addr: SocketAddr, - request: &DhtPacket) - -> Option - { - self.create_sendn(request) - .map(|sn| sink.send((peer_addr, sn))) - } - - /** - Handle [`DhtPacket`] that claims to contain [`SendNodes`] packet. - - Packet is dropped if: - - - it doesn't contain [`SendNodes`] - - it's not a response to a [`GetNodes`] request (invalid ID) - - [`DhtPacket`]: ../dht/struct.DhtPacket.html - [`GetNodes`]: ../dht/struct.GetNodes.html - [`SendNodes`]: ../dht/struct.SendNodes.html - */ - fn handle_packet_sendn(&mut self, packet: &DhtPacket) { - match packet.get_payload::(self.sk()) { - Some(sn) => { - /*if self.getn_timeout.remove(sn.id) { - debug!("Received SendN is a valid response"); - // received SendNodes packet is a response to our request - trace!("Adding nodes from SendNodes to DhtNode's Kbucket"); - for node in &sn.nodes { - self.try_add(node); - } - }*/ - }, - None => - debug!("Wrong DhtPacket; should have contained SendNodes"), - } - } - - /** - Function to handle incoming packets. If there is a response packet, - `Some(DhtPacket)` is returned. - */ - pub fn handle_packet(&mut self, packet: &DhtPacket) - -> Option - { - match packet.kind() { - PacketKind::PingReq => { - debug!("Received ping request"); - self.create_ping_resp(packet) - }, - PacketKind::GetN => { - debug!("Received GetN request"); - self.create_sendn(packet) - }, - PacketKind::SendN => { - debug!("Received SendN packet"); - self.handle_packet_sendn(packet); - None - }, - // TODO: handle other kinds of packets - p => { - debug!("Received unhandled packet kind: {:?}", p); - None - }, - } - } -} - - -/// Struct to use for {de-,}serializing Tox UDP packets. -// TODO: move elsewhere(?) -// TODO: rename? or implement UdpCodec for something else (enum) -pub struct ToxCodec; - -impl UdpCodec for ToxCodec { - // TODO: make `In`/`Out` support more than just DhtPacket - // (by using enum or Trait: FromBytes + ToBytes ?) - type In = ToxRecvUdpPacket; - type Out = ToxUdpPacket; - - fn decode(&mut self, src: &SocketAddr, buf: &[u8]) -> io::Result - { - match DhtPacket::from_bytes(buf) { - Some(dp) => Ok((*src, Some(dp))), - None => { - match PacketKind::from_bytes(buf) { - Some(p) => { - debug!("Received currently not supported packet kind: \ - {:?}", p); - }, - None => { - warn!("Received not supported UDP packet."); - trace!("Not supported UDP packet from {:?}: {:?}", - src, buf); - }, - } - Ok((*src, None)) - } - } - } - - fn encode(&mut self, (addr, dp): Self::Out, into: &mut Vec) -> SocketAddr { - into.extend(dp.to_bytes()); - addr - } -} - - -/** -Spawn a thread that will start receiving packets from [`ToxSplitStream`]. - -[`ToxSplitStream`]: ./type.ToxSplitStream.html -*/ -// TODO: move to network.rs ? -pub fn receive_packets(stream: ToxSplitStream) - -> mpsc::Receiver -{ - let (tx, rx) = mpsc::channel(2048); - thread::spawn(move || { - // can this fail to unwrap? - let mut core = Core::new().unwrap(); - let handle = core.handle(); - - let f = stream.for_each(|(src, p)| { - if let Some(packet) = p { - let tx = tx.clone(); - let send_one = tx.send((src, packet)).then(|_| Ok(())); - handle.spawn(send_one); - } - Ok(()) - }); - - core.run(f).unwrap(); - }); - - rx -} - -/** -Spawn a thread that will start sending packets via [`ToxSplitSink`]. - -Send all packets that need to be sent via returned `Sender`. - -[`ToxSplitSink`]: ./type.ToxSplitSink.html -*/ -// TODO: move to network.rs ? -pub fn send_packets(sink: ToxSplitSink) - -> mpsc::Sender -{ - let (tx, rx) = mpsc::channel(2048); - thread::spawn(move || { - // can this fail to unwrap? - let mut core = Core::new().unwrap(); - - let f = sink.send_all(rx.map_err(|_| { - // needed only to satisfy Sink::send_all() error constraints - io::Error::new(ErrorKind::Other, "") - })); - drop(core.run(f)); - }); - - tx -} - - -#[cfg(test)] -mod test { - use futures::*; - use futures::future::*; - use tokio_core::reactor::{Core, Timeout}; - use tokio_core::net::UdpCodec; - - use std::net::SocketAddr; - use std::time::Duration; - - use toxcore::binary_io::*; - use toxcore::crypto_core::*; - use toxcore::dht::*; - use toxcore::network::*; - use toxcore::dht_node::*; - use toxcore::packet_kind::PacketKind; - - use quickcheck::{quickcheck, TestResult}; - - /// Bind to this IpAddr. - // NOTE: apparently using `0.0.0.0`/`::` is not allowed on CIs like - // appveyor / travis - const SOCKET_ADDR: &str = "127.0.0.1"; - - /// Provide: - /// - mut core ($c) - /// - handle ($h) - macro_rules! create_core { - ($c:ident, $h:ident) => ( - let $c = Core::new().unwrap(); - let $h = $c.handle(); - ); - - (mut $c:ident, $h:ident) => ( - let mut $c = Core::new().unwrap(); - let $h = $c.handle(); - ); - } - - /// Accept: - /// - handle ($h) - /// Provide: - /// - [mut] DhtNode $name - /// - socket $name_socket - macro_rules! node_socket { - ($h:ident, mut $name:ident, $name_socket:ident) => ( - let mut $name = DhtNode::new().unwrap(); - let $name_socket = bind_udp(SOCKET_ADDR.parse().unwrap(), - // make port range sufficiently big - 2048..65_000, - &$h) - .expect("failed to bind to socket"); - ); - ($($h:ident, $name:ident, $name_socket:ident),+) => ($( - let $name = DhtNode::new().unwrap(); - let $name_socket = bind_udp(SOCKET_ADDR.parse().unwrap(), - // make port range sufficiently big - 2048..65_000, - &$h) - .expect("failed to bind to socket"); - )+); - } - - /// Add timeout to the future, and panic upon timing out. - /// - /// If not specified, default timeout = 5s. - macro_rules! add_timeout { - ($f:expr, $handle:expr) => ( - add_timeout!($f, $handle, 5) - ); - - ($f:expr, $handle:expr, $seconds:expr) => ( - $f.map(Ok) - .select( - Timeout::new(Duration::from_secs($seconds), $handle) - .unwrap() - .map(Err)) - .then(|res| { - match res { - Ok((Err(()), _received)) => - panic!("timed out"), - Err((e, _other)) => panic!("{}", e), - Ok((f, _timeout)) => f, - } - }) - ); - } - - /** - Verify that given `$packet` can't be parsed as packet type `$kind` - using secret key `$sk`. - */ - macro_rules! cant_parse_as_packet { - ($packet:expr, $sk:expr, $($kind:ty)+) => ($( - assert!($packet.get_payload::<$kind>(&$sk).is_none()); - )+) - } - - // DhtNode:: - - // DhtNode::new() - - #[test] - fn dht_node_new() { - let _ = DhtNode::new().unwrap(); - } - - // DhtNode::try_add() - - #[test] - fn dht_node_try_add_to_empty() { - fn with_nodes(pns: Vec) { - let mut dhtn = DhtNode::new().unwrap(); - let mut kbuc = Kbucket::new(KBUCKET_BUCKETS, dhtn.pk()); - - for pn in &pns { - assert_eq!(dhtn.try_add(pn), kbuc.try_add(pn)); - assert_eq!(kbuc, *dhtn.kbucket); - } - } - quickcheck(with_nodes as fn(Vec)); - } - - - // DhtNode::pk() - - #[test] - fn dht_node_pk_test() { - let dn = DhtNode::new().unwrap(); - assert_eq!(&*dn.dht_public_key, dn.pk()); - } - - // DhtNode::sk() - - #[test] - fn dht_node_sk_test() { - let dn = DhtNode::new().unwrap(); - assert_eq!(&*dn.dht_secret_key, dn.sk()); - } - - // DhtNode::create_ping_req() - - #[test] - fn dht_node_create_ping_req_test() { - let alice = DhtNode::new().unwrap(); - let (bob_pk, bob_sk) = gen_keypair(); - let (_, eve_sk) = gen_keypair(); - let packet1 = alice.create_ping_req(&bob_pk); - assert_eq!(alice.pk(), &packet1.sender_pk); - assert_eq!(PacketKind::PingReq, packet1.kind()); - - let packet2 = alice.create_ping_req(&bob_pk); - assert_ne!(packet1, packet2); - - // eve can't decrypt it - assert_eq!(None, packet1.get_payload::(&eve_sk)); - - let payload1: PingReq = packet1.get_payload(&bob_sk) - .expect("failed to get payload1"); - let payload2: PingReq = packet2.get_payload(&bob_sk) - .expect("failed to get payload2"); - assert_ne!(payload1.id(), payload2.id()); - - // wrong packet kind - cant_parse_as_packet!(packet1, bob_sk, - PingResp GetNodes SendNodes); - } - - // DhtNode::request_ping() - - #[test] - fn dht_node_request_ping_test() { - // bob creates & sends PingReq to alice - // received PingReq has to be succesfully decrypted - create_core!(core, handle); - node_socket!(handle, alice, alice_socket); - let bob = DhtNode::new().unwrap(); - let alice_addr = alice_socket.local_addr().unwrap(); - let alice_pn = PackedNode::new(true, alice_addr, alice.pk()); - - let (dest_addr, bob_request) = bob.request_ping(&alice_pn); - assert_eq!(alice_addr, dest_addr); - - let payload: PingReq = bob_request - .get_payload(alice.sk()) - .expect("Failed to decrypt payload"); - - assert_eq!(PacketKind::PingReq, payload.kind()); - } - - // DhtNode::create_ping_resp() - - quickcheck! { - fn dht_node_create_ping_resp_test(req: PingReq) -> () { - // alice creates DhtPacket containing PingReq request - // bob has to respond to it with PingResp - // alice has to be able to decrypt response - // eve can't decrypt response - - let alice = DhtNode::new().unwrap(); - let bob = DhtNode::new().unwrap(); - let (_, eve_sk) = gen_keypair(); - let precomp = encrypt_precompute(bob.pk(), alice.sk()); - let nonce = gen_nonce(); - let a_ping = DhtPacket::new(&precomp, alice.pk(), &nonce, &req); - - let resp1 = bob.create_ping_resp(&a_ping) - .expect("failed to create ping resp1"); - let resp2 = bob.create_ping_resp(&a_ping) - .expect("failed to create ping resp2"); - - assert_eq!(resp1.sender_pk, *bob.pk()); - assert_eq!(PacketKind::PingResp, resp1.kind()); - // encrypted payload differs due to different nonce - assert_ne!(resp1, resp2); - - // eve can't decrypt - assert_eq!(None, resp1.get_payload::(&eve_sk)); - - let resp1_payload: PingResp = resp1 - .get_payload(alice.sk()).unwrap(); - let resp2_payload: PingResp = resp2 - .get_payload(alice.sk()).unwrap(); - assert_eq!(resp1_payload, resp2_payload); - assert_eq!(req.id(), resp1_payload.id()); - assert_eq!(PacketKind::PingResp, resp1_payload.kind()); - - // can't create response from DhtPacket containing PingResp - assert!(alice.create_ping_resp(&resp1).is_none()); - - // wrong packet kind - cant_parse_as_packet!(resp1, alice.sk(), PingReq); - } - } - - // DhtNode::respond_ping() - - quickcheck! { - fn dht_node_respond_ping_test(req: PingReq) -> () { - // bob creates a DhtPacket with PingReq, and alice - // sends a response to it - // response has to be successfully decrypted by alice - // response can't be decrypted by eve - create_core!(mut core, handle); - node_socket!(handle, alice, alice_socket, - handle, bob, bob_socket); - let (_, eve_sk) = gen_keypair(); - - let precomp = encrypt_precompute(alice.pk(), bob.sk()); - let nonce = gen_nonce(); - let bob_ping = DhtPacket::new(&precomp, bob.pk(), &nonce, &req); - - let (alice_sink, _) = alice_socket.framed(ToxCodec).split(); - let alice_send = alice.respond_ping( - alice_sink, - bob_socket.local_addr().unwrap(), - &bob_ping); - - let mut recv_buf = [0; MAX_UDP_PACKET_SIZE]; - let future_recv = bob_socket.recv_dgram(&mut recv_buf[..]); - let future_recv = add_timeout!(future_recv, &handle); - handle.spawn(alice_send.then(|_| ok(()))); - - let received = core.run(future_recv).unwrap(); - let (_bob_socket, recv_buf, size, _saddr) = received; - assert!(size != 0); - assert_eq!(size, bob_ping.to_bytes().len()); - - let recv_packet = DhtPacket::from_bytes(&recv_buf[..size]) - .expect("failed to parse as DhtPacket"); - assert_eq!(PacketKind::PingResp, recv_packet.kind()); - - // eve can't decrypt it - assert_eq!(None, recv_packet.get_payload::(&eve_sk)); - - let _payload: PingResp = recv_packet - .get_payload(bob.sk()).unwrap(); - } - } - - // DhtNode::create_getn() - - #[test] - fn dht_node_create_getn_test() { - // alice sends GetNodes request to bob - // bob has to successfully decrypt the request - // eve can't decrypt the request - let alice = DhtNode::new().unwrap(); - let (bob_pk, bob_sk) = gen_keypair(); - let (_, eve_sk) = gen_keypair(); - let (req_id1, packet1) = alice.create_getn(&bob_pk); - assert_eq!(alice.pk(), &packet1.sender_pk); - assert_eq!(PacketKind::GetN, packet1.kind()); - - // eve can't decrypt - assert_eq!(None, packet1.get_payload::(&eve_sk)); - - let payload1: GetNodes = packet1.get_payload(&bob_sk) - .expect("failed to get payload1"); - assert_eq!(alice.pk(), &payload1.pk); - assert_eq!(req_id1, payload1.id); - - let (_req_id2, packet2) = alice.create_getn(&bob_pk); - assert_ne!(packet1, packet2); - - let payload2: GetNodes = packet2.get_payload(&bob_sk) - .expect("failed to get payload2"); - assert_ne!(payload1.id, payload2.id); - - // wrong packet kind - cant_parse_as_packet!(packet1, bob_sk, SendNodes); - } - - // DhtNode::request_nodes() - - #[test] - fn dht_node_request_nodes_test() { - // bob creates a ToxUdpPacket with GetNodes request to alice - // alice has to successfully decrypt & parse it - create_core!(core, handle); - node_socket!(handle, alice, alice_socket); - let mut bob = DhtNode::new().unwrap(); - let alice_addr = alice_socket.local_addr().unwrap(); - let alice_pn = PackedNode::new(true, alice_addr, alice.pk()); - - let (id, (dest_addr, bob_request)) = bob.request_nodes(&alice_pn); - assert_eq!(alice_addr, dest_addr); - - let payload: GetNodes = bob_request - .get_payload(alice.sk()) - .expect("Failed to decrypt payload"); - - assert_eq!(&payload.pk, bob.pk()); - assert_eq!(payload.id, id); - } - - // DhtNode::request_nodes_close() - - quickcheck! { - fn dht_node_request_nodes_close_test(pns: Vec) - -> TestResult - { - if pns.is_empty() { return TestResult::discard() } - - let mut dnode = DhtNode::new().unwrap(); - for pn in &pns { - dnode.try_add(pn); - } - - let requests = dnode.request_nodes_close(); - - for (n, node) in dnode.kbucket.iter().enumerate() { - // each request creates a response timeout - //assert_eq!(dnode.getn_timeout.get(n).unwrap().pk(), - // node.pk()); - let (req_addr, ref _req_packet) = requests[n]; - assert_eq!(node.socket_addr(), req_addr); - } - - TestResult::passed() - } - } - - - - // DhtNode::create_sendn() - - quickcheck! { - fn dht_node_create_sendn_test(pns: Vec) -> TestResult { - if pns.is_empty() { return TestResult::discard() } - - // alice creates DhtPacket containing GetNodes request - // bob has to respond to it with SendNodes - // alice has to be able to decrypt response - // alice has to be able to successfully add received nodes - // eve can't decrypt response - - let mut alice = DhtNode::new().unwrap(); - let mut bob = DhtNode::new().unwrap(); - let (_, eve_sk) = gen_keypair(); - let (_id, req) = alice.create_getn(bob.pk()); - - // errors with an empty kbucket - let error = bob.create_sendn(&req); - assert_eq!(None, error); - - for pn in &pns { - bob.try_add(pn); - } - - let resp1 = bob.create_sendn(&req) - .expect("failed to create response1"); - let resp2 = bob.create_sendn(&req) - .expect("failed to create response2"); - - assert_eq!(resp1.sender_pk, *bob.pk()); - assert_eq!(PacketKind::SendN, resp1.kind()); - // encrypted payload differs due to different nonce - assert_ne!(resp1, resp2); - - // eve can't decrypt - assert_eq!(None, resp1.get_payload::(&eve_sk)); - - let resp1_payload: SendNodes = resp1 - .get_payload(alice.sk()) - .expect("failed to get payload1"); - let resp2_payload: SendNodes = resp2 - .get_payload(alice.sk()) - .expect("failed to get payload2"); - assert_eq!(resp1_payload, resp2_payload); - assert!(!resp1_payload.nodes.is_empty()); - - for node in &resp1_payload.nodes { - // has to succeed, since nodes in response have to differ - assert!(alice.try_add(node)); - } - - TestResult::passed() - } - } - - // DhtNode::send_nodes() - - #[test] - quickcheck! { - fn dht_node_send_nodes(pns: Vec) -> TestResult { - if pns.is_empty() { return TestResult::discard() } - - // alice sends SendNodes response to random GetNodes request - // to bob - - create_core!(mut core, handle); - node_socket!(handle, mut alice, alice_socket); - node_socket!(handle, bob, bob_socket); - - for pn in &pns { - alice.try_add(pn); - } - - let (_id, getn) = bob.create_getn(alice.pk()); - - let (alice_sink, _) = alice_socket.framed(ToxCodec).split(); - let alice_response = alice.send_nodes( - alice_sink, - bob_socket.local_addr().unwrap(), - &getn); - - let mut recv_buf = [0; MAX_UDP_PACKET_SIZE]; - let future_recv = bob_socket.recv_dgram(&mut recv_buf[..]); - let future_recv = add_timeout!(future_recv, &handle); - handle.spawn(alice_response.then(|_| ok(()))); - - let received = core.run(future_recv).unwrap(); - let (_bob_socket, recv_buf, size, _saddr) = received; - assert!(size != 0); - - let _recv_packet = DhtPacket::from_bytes(&recv_buf[..size]) - .expect("failed to parse as DhtPacket"); - - TestResult::passed() - } - } - - // DhtNode::handle_packet_sendn() - - /* - quickcheck! { - fn dht_node_handle_packet_sendn_test(sn: SendNodes, - gn: GetNodes, - pq: PingReq, - pr: PingResp) - -> () - { - // bob creates a DhtPacket to alice that contains SendNodes - // alice adds the nodes - - let mut alice = DhtNode::new().unwrap(); - let (bob_pk, bob_sk) = gen_keypair(); - let precomp = precompute(alice.pk(), &bob_sk); - let nonce = gen_nonce(); - macro_rules! try_add_with { - ($($kind:expr)+) => ($( - alice.handle_packet_sendn(&DhtPacket::new(&precomp, - &bob_pk, - &nonce, - &$kind)); - )+) - } - // also try to add nodes from a DhtPacket that don't contain - // SendNodes - try_add_with!(sn /* and invalid ones */ gn pq pr); - - // since alice doesn't have stored ID for SendNodes response, - // packet is supposed to be ignored - assert!(alice.kbucket.is_empty()); - - // add needed packet ID to alice's timeout table - alice.getn_timeout.add(&bob_pk, sn.id); - // now nodes from SendNodes can be processed - try_add_with!(sn); - - // verify that alice's kbucket's contents are the same as - // stand-alone kbucket - let mut kbuc = Kbucket::new(KBUCKET_BUCKETS, alice.pk()); - for pn in &sn.nodes { - kbuc.try_add(pn); - } - assert_eq!(kbuc, *alice.kbucket); - } - } - */ - - // DhtNode::handle_packet() - /* - quickcheck! { - fn dht_node_handle_packet(pq: PingReq, - pr: PingResp, - gn: GetNodes, - sn: SendNodes) - -> () - { - let alice = DhtNode::new().unwrap(); - let mut bob = DhtNode::new().unwrap(); - let precom = precompute(bob.pk(), alice.sk()); - let nonce = gen_nonce(); - - // test with - - { - // PingReq - let dp = DhtPacket::new(&precom, alice.pk(), &nonce, &pq); - assert_eq!(bob.create_ping_resp(&dp).unwrap().kind(), - bob.handle_packet(&dp).unwrap().kind()); - } - - { - // PingResp - let dp = DhtPacket::new(&precom, alice.pk(), &nonce, &pr); - assert_eq!(None, bob.handle_packet(&dp)); - } - - { - // GetNodes with an empty kbucket - let dp = DhtPacket::new(&precom, alice.pk(), &nonce, &gn); - assert_eq!(None, bob.handle_packet(&dp)); - } - - { - // SendNodes - let dp = DhtPacket::new(&precom, alice.pk(), &nonce, &sn); - assert_eq!(None, bob.handle_packet(&dp)); - // bob doesn't have request ID, thus packet is dropped - assert!(bob.kbucket.is_empty()); - // add request ID, so that nods could be processed - bob.getn_timeout.add(alice.pk(), sn.id); - assert_eq!(None, bob.handle_packet(&dp)); - assert!(!bob.kbucket.is_empty()); - } - - { - // GetNodes with something in kbucket - let dp = DhtPacket::new(&precom, alice.pk(), &nonce, &gn); - assert_eq!(bob.create_sendn(&dp).unwrap().kind(), - bob.handle_packet(&dp).unwrap().kind()); - } - } - } - */ - - - // ToxCodec:: - - // ToxCodec::decode() - - #[test] - fn tox_codec_decode_test() { - fn with_dp(dp: DhtPacket, kind: u8) -> TestResult { - // need an invalid PacketKind for DhtPacket - if kind <= PacketKind::SendN as u8 { - return TestResult::discard() - } - - // TODO: random SocketAddr - let addr = SocketAddr::V4("0.1.2.3:4".parse().unwrap()); - let mut tc = ToxCodec; - - let mut bytes = dp.to_bytes(); - - let (decoded_a, decoded_dp) = tc.decode(&addr, &bytes) - .unwrap(); - // it did have correct packet - let decoded_dp = decoded_dp.unwrap(); - - assert_eq!(addr, decoded_a); - assert_eq!(dp, decoded_dp); - - // make it error - bytes[0] = kind; - let (r_addr, none) = tc.decode(&addr, &bytes).unwrap(); - assert_eq!(addr, r_addr); - assert!(none.is_none()); - - TestResult::passed() - } - quickcheck(with_dp as fn(DhtPacket, u8) -> TestResult); - } - - // ToxCodec::encode() - - #[test] - fn tox_codec_encode_test() { - fn with_dp(dp: DhtPacket) { - // TODO: random SocketAddr - let addr = SocketAddr::V4("5.6.7.8:9".parse().unwrap()); - let mut buf = Vec::new(); - let mut tc = ToxCodec; - - let socket = tc.encode((addr, dp.clone()), &mut buf); - assert_eq!(addr, socket); - assert_eq!(buf, dp.to_bytes()); - } - quickcheck(with_dp as fn(DhtPacket)); - } - - - // receive_packets() - - quickcheck! { - fn receive_packets_test(dps: Vec) -> TestResult { - if dps.is_empty() { return TestResult::discard() } - // alice sends packets to bob - create_core!(mut core, handle); - node_socket!(handle, _alice, a_socket); - node_socket!(handle, _bob, b_socket); - - let a_addr = a_socket.local_addr().unwrap(); - let b_addr = b_socket.local_addr().unwrap(); - let (_sink, stream) = b_socket.framed(ToxCodec).split(); - - // start receiving packets - let to_receive = receive_packets(stream); - - let mut a_socket = a_socket; - for dp in &dps { - let send = a_socket.send_dgram(dp.to_bytes(), b_addr); - let (s, _) = core.run(send).unwrap(); - a_socket = s; - } - - let f_recv = to_receive.take(dps.len() as u64).collect(); - let received = core.run(f_recv).unwrap(); - - for (n, &(ref addr, ref packet)) in received.iter().enumerate() { - assert_eq!(a_addr, *addr); - assert_eq!(dps[n], *packet); - } - - TestResult::passed() - } - } - - // send_packets() - - quickcheck! { - fn send_packets_test(dps: Vec) -> TestResult { - if dps.is_empty() { return TestResult::discard() } - // alice sends packets to bob - create_core!(mut core, handle); - node_socket!(handle, _alice, a_socket); - node_socket!(handle, _bob, b_socket); - - let a_addr = a_socket.local_addr().unwrap(); - let b_addr = b_socket.local_addr().unwrap(); - let (sink, _stream) = a_socket.framed(ToxCodec).split(); - let (_sink, stream) = b_socket.framed(ToxCodec).split(); - - // start receiving/sending packets - let receiver = receive_packets(stream); - let sender = send_packets(sink); - - let dps_send = dps.clone(); - for dp in dps_send { - let tx = sender.clone(); - let send = tx.send((b_addr, dp)).then(|_| Ok(())); - handle.spawn(send); - } - - let f_recv = receiver.take(dps.len() as u64).collect(); - let received = core.run(f_recv).unwrap(); - - for (n, &(ref addr, ref packet)) in received.iter().enumerate() { - assert_eq!(a_addr, *addr); - assert_eq!(dps[n], *packet); - } - - TestResult::passed() - } - } -} diff --git a/src/toxcore/dht_old/dht_tests.rs b/src/toxcore/dht_old/dht_tests.rs deleted file mode 100644 index 0a0cd92e3..000000000 --- a/src/toxcore/dht_old/dht_tests.rs +++ /dev/null @@ -1,1665 +0,0 @@ -/* - Copyright © 2016 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - -//! Tests for the DHT module. - -#![cfg_attr(feature = "clippy", allow(many_single_char_names, too_many_arguments))] - -use toxcore::binary_io::*; -use toxcore::crypto_core::*; -use toxcore::dht::*; -use toxcore::packet_kind::PacketKind; - -use std::cmp::Ordering; -use std::net::{ - IpAddr, - Ipv4Addr, - Ipv6Addr, - SocketAddr, - SocketAddrV4, - SocketAddrV6 -}; -use std::str::FromStr; -use byteorder::{ByteOrder, BigEndian, LittleEndian, NativeEndian, WriteBytesExt}; - -use quickcheck::{Arbitrary, Gen, quickcheck, StdGen, TestResult}; -use super::rand::chacha::ChaChaRng; - - -/// Safely casts `u64` to 4 `u16`. -fn u64_as_u16s(num: u64) -> (u16, u16, u16, u16) { - let mut array: [u16; 4] = [0; 4]; - for (pos, item) in array.iter_mut().enumerate() { - *item = (num >> (16 * pos)) as u16; - } - (array[0], array[1], array[2], array[3]) -} - - -/// Get a PK from 4 `u64`s. -fn nums_to_pk(a: u64, b: u64, c: u64, d: u64) -> PublicKey { - let mut pk_bytes: Vec = Vec::with_capacity(PUBLICKEYBYTES); - pk_bytes.write_u64::(a).unwrap(); - pk_bytes.write_u64::(b).unwrap(); - pk_bytes.write_u64::(c).unwrap(); - pk_bytes.write_u64::(d).unwrap(); - let pk_bytes = &pk_bytes[..]; - PublicKey::from_slice(pk_bytes).expect("Making PK out of bytes failed!") -} - - - -// PingReq:: - -impl Arbitrary for PingReq { - fn arbitrary(_g: &mut G) -> Self { - PingReq::new() - } -} - -// PingReq::new() - -#[test] -fn ping_req_new_test() { - let p1 = PingReq::new(); - let p2 = PingReq::new(); - assert!(p1 != p2); - assert!(p1.id() != p2.id()); -} - -// PingReq::id() - -#[test] -fn ping_req_id_test() { - let ping = PingReq::new(); - assert_eq!(ping.id(), ping.id()); -} - - - -// PingResp:: - -impl Arbitrary for PingResp { - fn arbitrary(_g: &mut G) -> Self { - PingReq::new().into() - } -} - -// PingResp::id() - -#[test] -fn ping_resp_id_test() { - fn with_ping_req(p: PingReq) { - assert_eq!(p.id(), PingResp::from(p).id()); - } - quickcheck(with_ping_req as fn(PingReq)); -} - -// PingResp::to_bytes() - -#[test] -fn ping_resp_to_bytes_test_cmp_ping_req() { - fn with_ping_req(p: PingReq) { - let pqb = p.to_bytes(); - let prb = PingResp::from(p).to_bytes(); - assert!(pqb[0] != prb[0]); - assert_eq!(pqb[1..], prb[1..]); - } - quickcheck(with_ping_req as fn(PingReq)); -} - -// PingResp::from() - -#[test] -fn ping_resp_from_test() { - fn with_ping_req(p: PingReq) { - assert_eq!(p.id(), PingResp::from(p).id()); - } - quickcheck(with_ping_req as fn(PingReq)); -} - - -macro_rules! tests_for_pings { - ($($p:ident $k_t:ident $b_t:ident $f_t:ident $p_t:ident)+) => ($( - // ::kind() - - #[test] - fn $k_t() { - fn with_ping(p: $p) { - assert_eq!(PacketKind::$p, p.kind()) - } - quickcheck(with_ping as fn($p)); - } - - // ::to_bytes() - - #[test] - fn $b_t() { - fn with_ping(p: $p) { - let pb = p.to_bytes(); - assert_eq!(PING_SIZE, pb.len()); - assert_eq!(PacketKind::$p as u8, pb[0]); - } - quickcheck(with_ping as fn($p)); - } - - // ::from_bytes() - - #[test] - fn $f_t() { - fn with_bytes(bytes: Vec) { - if bytes.len() < PING_SIZE || - bytes[0] != PacketKind::$p as u8 { - assert_eq!(None, $p::from_bytes(&bytes)); - } else { - let p = $p::from_bytes(&bytes).unwrap(); - // `id` should not differ - assert_eq!(p.id(), NativeEndian::read_u64(&bytes[1..PING_SIZE])); - } - } - quickcheck(with_bytes as fn(Vec)); - - // just in case - let mut ping = vec![PacketKind::$p as u8]; - ping.write_u64::(random_u64()).unwrap(); - with_bytes(ping); - } - - // ::parse_bytes() - - #[test] - fn $p_t() { - fn with_bytes(p: $p, r_rest: Vec) { - let mut bytes = Vec::with_capacity(PING_SIZE + r_rest.len()); - bytes.extend_from_slice(&p.to_bytes()); - bytes.extend_from_slice(&r_rest); - - let (rest, _) = $p::parse_bytes(&bytes).unwrap(); - assert_eq!(&r_rest[..], rest); - } - quickcheck(with_bytes as fn($p, Vec)); - } - )+) -} -tests_for_pings!(PingReq - ping_req_kind_test - ping_req_to_bytes_test - ping_req_from_bytes_test - ping_req_parse_test - PingResp - ping_resp_kind_test - ping_resp_to_bytes_test - ping_resp_from_bytes_test - ping_resp_parse_test -); - - -// IpType - -impl Arbitrary for IpType { - fn arbitrary(g: &mut G) -> Self { - *g.choose(&[IpType::U4, IpType::U6, IpType::T4, IpType::T6]) - .unwrap() - } -} - - -// IpType::from_bytes() - -#[test] -fn ip_type_from_bytes_test() { - fn with_bytes(bytes: Vec) { - if bytes.is_empty() { - assert_eq!(None, IpType::from_bytes(&bytes)); - return - } - match bytes[0] { - 2 => assert_eq!(IpType::U4, IpType::from_bytes(&bytes).unwrap()), - 10 => assert_eq!(IpType::U6, IpType::from_bytes(&bytes).unwrap()), - 130 => assert_eq!(IpType::T4, IpType::from_bytes(&bytes).unwrap()), - 138 => assert_eq!(IpType::T6, IpType::from_bytes(&bytes).unwrap()), - _ => assert_eq!(None, IpType::from_bytes(&bytes)), - } - } - quickcheck(with_bytes as fn(Vec)); - - // just in case - with_bytes(vec![]); - for i in 0x00 .. 0xff { - with_bytes(vec![i]); - } -} - -// IpType::parse_bytes() - -#[test] -fn ip_type_parse_bytes_rest_test() { - fn with_bytes(it: IpType, r_rest: Vec) { - let mut bytes = vec![it as u8]; - bytes.extend_from_slice(&r_rest); - - let (rest, _) = IpType::parse_bytes(&bytes) - .expect("IpType parsing failure."); - assert_eq!(&r_rest[..], rest); - } - quickcheck(with_bytes as fn(IpType, Vec)); -} - -// IpAddr - -#[derive(Clone, Debug)] -struct IpAddrWrap(IpAddr); - -impl Arbitrary for IpAddrWrap { - fn arbitrary(g: &mut G) -> Self { - let v4: bool = g.gen(); - - if v4 { - let res = IpAddr::V4(Ipv4Addr::new(g.gen(), g.gen(), - g.gen(), g.gen())); - IpAddrWrap(res) - } else { - let res = IpAddr::V6(Ipv6Addr::new(g.gen(), g.gen(), - g.gen(), g.gen(), - g.gen(), g.gen(), - g.gen(), g.gen())); - IpAddrWrap(res) - } - } -} - -// IpAddr::to_bytes() - -#[test] -fn ip_addr_to_bytes_test() { - fn with_ipv4(a: u8, b: u8, c: u8, d: u8) { - let a4 = Ipv4Addr::new(a, b, c, d); - let ab = IpAddr::V4(a4).to_bytes(); - assert_eq!(4, ab.len()); - assert_eq!(a, ab[0]); - assert_eq!(b, ab[1]); - assert_eq!(c, ab[2]); - assert_eq!(d, ab[3]); - } - quickcheck(with_ipv4 as fn(u8, u8, u8, u8)); - - fn with_ipv6(n1: u64, n2: u64) { - let (a, b, c, d) = u64_as_u16s(n1); - let (e, f, g, h) = u64_as_u16s(n2); - let a6 = Ipv6Addr::new(a, b, c, d, e, f, g, h); - let ab = IpAddr::V6(a6).to_bytes(); - assert_eq!(16, ab.len()); - assert_eq!(a, LittleEndian::read_u16(&ab[0..2])); - assert_eq!(b, LittleEndian::read_u16(&ab[2..4])); - assert_eq!(c, LittleEndian::read_u16(&ab[4..6])); - assert_eq!(d, LittleEndian::read_u16(&ab[6..8])); - assert_eq!(e, LittleEndian::read_u16(&ab[8..10])); - assert_eq!(f, LittleEndian::read_u16(&ab[10..12])); - assert_eq!(g, LittleEndian::read_u16(&ab[12..14])); - assert_eq!(h, LittleEndian::read_u16(&ab[14..16])); - } - quickcheck(with_ipv6 as fn(u64, u64)); -} - - -// Ipv6Addr::from_bytes() - -#[test] -fn ipv6_addr_from_bytes_test() { - fn with_bytes(b: Vec) { - if b.len() < 16 { - assert_eq!(None, Ipv6Addr::from_bytes(&b)); - } else { - let addr = Ipv6Addr::from_bytes(&b).unwrap(); - assert_eq!(&IpAddr::V6(addr).to_bytes()[..16], &b[..16]); - } - } - quickcheck(with_bytes as fn(Vec)); -} - -// Ipv4Addr::from_bytes() - -#[test] -fn ipv4_addr_from_bytes_test() { - fn with_bytes(b: Vec) { - if b.len() < 4 { - assert_eq!(None, Ipv4Addr::from_bytes(&b)); - } else { - let addr = Ipv4Addr::from_bytes(&b).unwrap(); - assert_eq!(&IpAddr::V4(addr).to_bytes()[..4], &b[..4]); - } - } - quickcheck(with_bytes as fn(Vec)); -} - -// Ipv[4,6]Addr::parse_bytes() - -#[test] -fn ip_addr_parse_bytes_rest_test() { - fn with_bytes(IpAddrWrap(ip_addr): IpAddrWrap, r_rest: Vec) { - let mut bytes = vec![]; - bytes.extend_from_slice(&ip_addr.to_bytes()); - bytes.extend_from_slice(&r_rest); - - let rest = match ip_addr { - IpAddr::V4(_) => { - let (rest, _) = Ipv4Addr::parse_bytes(&bytes).unwrap(); - rest - }, - IpAddr::V6(_) => { - let (rest, _) = Ipv6Addr::parse_bytes(&bytes).unwrap(); - rest - } - }; - - assert_eq!(&r_rest[..], rest); - } - quickcheck(with_bytes as fn(IpAddrWrap, Vec)); -} - - -// PackedNode:: - -/// Valid, random `PackedNode`. -impl Arbitrary for PackedNode { - fn arbitrary(g: &mut G) -> Self { - let ipv4: bool = g.gen(); - - let mut pk_bytes = [0; PUBLICKEYBYTES]; - g.fill_bytes(&mut pk_bytes); - let pk = PublicKey(pk_bytes); - - if ipv4 { - let addr = Ipv4Addr::new(g.gen(), g.gen(), g.gen(), g.gen()); - let saddr = SocketAddrV4::new(addr, g.gen()); - - PackedNode::new(g.gen(), SocketAddr::V4(saddr), &pk) - } else { - let addr = Ipv6Addr::new(g.gen(), g.gen(), g.gen(), g.gen(), - g.gen(), g.gen(), g.gen(), g.gen()); - let saddr = SocketAddrV6::new(addr, g.gen(), 0, 0); - - PackedNode::new(g.gen(), SocketAddr::V6(saddr), &pk) - } - } -} - -// PackedNode::new() - -#[test] -#[allow(non_snake_case)] -fn packed_node_new_test_ip_type_UDP_IPv4() { - let pk = PublicKey([0; PUBLICKEYBYTES]); - let addr = SocketAddr::V4("0.0.0.0:1".parse().unwrap()); - let info = PackedNode::new(true, addr, &pk); - assert_eq!(IpType::U4, info.ip_type()); - assert_eq!(&pk, info.pk()); - assert_eq!(addr, info.socket_addr()); -} - -#[test] -#[allow(non_snake_case)] -fn packed_node_new_test_ip_type_TCP_IPv4() { - let pk = PublicKey([0; PUBLICKEYBYTES]); - let addr = SocketAddr::V4("0.0.0.0:2".parse().unwrap()); - let info = PackedNode::new(false, addr, &pk); - assert_eq!(IpType::T4, info.ip_type()); - assert_eq!(&pk, info.pk()); - assert_eq!(addr, info.socket_addr()); -} - -#[test] -#[allow(non_snake_case)] -fn packed_node_new_test_ip_type_UDP_IPv6() { - let pk = PublicKey([0; PUBLICKEYBYTES]); - let addr = SocketAddr::V6("[::]:3".parse().unwrap()); - let info = PackedNode::new(true, addr, &pk); - assert_eq!(IpType::U6, info.ip_type()); - assert_eq!(&pk, info.pk()); - assert_eq!(addr, info.socket_addr()); -} - -#[test] -#[allow(non_snake_case)] -fn packed_node_new_test_ip_type_TCP_IPv6() { - let pk = PublicKey([0; PUBLICKEYBYTES]); - let addr = SocketAddr::V6("[::]:4".parse().unwrap()); - let info = PackedNode::new(false, addr, &pk); - assert_eq!(IpType::T6, info.ip_type()); - assert_eq!(&pk, info.pk()); - assert_eq!(addr, info.socket_addr()); -} - - -// PackedNode::ip() - -#[test] -fn packed_node_ip_test() { - let ipv4 = PackedNode::new(true, - SocketAddr::V4("0.0.0.0:0".parse().unwrap()), - &PublicKey([0; PUBLICKEYBYTES])); - assert!(ipv4.ip().is_ipv4()); - - let ipv6 = PackedNode::new(true, - SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from_str("::0").unwrap(), - 0, 0, 0)), - &PublicKey([0; PUBLICKEYBYTES])); - assert!(ipv6.ip().is_ipv6()); -} - - -// PackedNode::parse_bytes() - -#[test] -// check if always fails when there's not enough bytes for port -fn packed_node_parse_bytes_test_port() { - fn with_pn(pn: PackedNode) { - let bytes = &pn.to_bytes()[..]; - let bytes = &bytes[..bytes.len() - PUBLICKEYBYTES]; - assert!(PackedNode::parse_bytes(&bytes[..bytes.len() - 1]).is_incomplete()); - assert!(PackedNode::parse_bytes(&bytes[..bytes.len() - 2]).is_incomplete()); - } - quickcheck(with_pn as fn(PackedNode)); -} - - -// PackedNode::parse_bytes_multiple() - -#[test] -fn packed_node_parse_bytes_multiple_test() { - fn with_nodes(nodes: Vec, random_rest: Vec) { - let mut bytes = vec![]; - for n in &nodes { - bytes.extend_from_slice(&n.to_bytes()); - } - let mut expected_rest = vec![]; - if !random_rest.is_empty() { - expected_rest.push(0); // Incorrect IpType - expected_rest.extend_from_slice(&random_rest); - } - bytes.extend_from_slice(&expected_rest); - - let (rest, nodes2) = PackedNode::parse_bytes_multiple(&bytes).unwrap(); - - assert_eq!(nodes.len(), nodes2.len()); - assert_eq!(nodes, nodes2); - assert_eq!(&expected_rest[..], rest); - } - quickcheck(with_nodes as fn(Vec, Vec)); -} - -// PackedNode::parse_bytes_multiple_n() - -#[test] -fn packed_node_parse_bytes_multiple_n_test() { - fn with_nodes(nodes: Vec, random_rest: Vec) { - { - // should parse nodes - let mut bytes = vec![]; - for n in &nodes { - bytes.extend_from_slice(&n.to_bytes()); - } - bytes.extend_from_slice(&random_rest); - - let (rest, nodes2) = - PackedNode::parse_bytes_multiple_n(&bytes, nodes.len()) - .unwrap(); - - assert_eq!(nodes.len(), nodes2.len()); - assert_eq!(nodes, nodes2); - assert_eq!(&random_rest[..], rest); - } - - { - // should fail if not enough nodes - let mut bytes = vec![]; - for n in &nodes { - bytes.extend_from_slice(&n.to_bytes()); - } - bytes.push(0); // Incorrect IpType - bytes.extend_from_slice(&random_rest); - - assert!(PackedNode::parse_bytes_multiple_n(&bytes, nodes.len() + 1).is_err()); - } - - // should not parse too many nodes - if !nodes.is_empty() { - let mut bytes = vec![]; - for n in &nodes { - bytes.extend_from_slice(&n.to_bytes()); - } - bytes.extend_from_slice(&random_rest); - - let mut expected_rest = vec![]; - expected_rest.extend_from_slice(&nodes[nodes.len() - 1].to_bytes()); - expected_rest.extend_from_slice(&random_rest); - - - let (rest, nodes2) = - PackedNode::parse_bytes_multiple_n(&bytes, nodes.len() - 1) - .expect("Nodes parsing failed."); - - assert_eq!(nodes.len() - 1, nodes2.len()); - assert_eq!(&nodes[..nodes.len() - 1], &nodes2[..]); - assert_eq!(&expected_rest[..], rest); - } - } - quickcheck(with_nodes as fn(Vec, Vec)); -} - - - -// PackedNode::to_bytes() - -/// Returns all possible variants of `PackedNode` `ip_type`, in order -/// listed by `IpType` enum. -fn packed_node_protocol(saddr: SocketAddr, pk: &PublicKey) - -> (PackedNode, PackedNode) -{ - let u = PackedNode::new(true, saddr, pk); - let t = PackedNode::new(false, saddr, pk); - (u, t) -} - - -#[test] -// tests for various IPv4 – use quickcheck -fn packed_node_to_bytes_test_ipv4() { - fn with_random_saddr(a: u8, b: u8, c: u8, d: u8, port: u16) { - let pk = &PublicKey([0; PUBLICKEYBYTES]); - let saddr = SocketAddr::V4( - format!("{}.{}.{}.{}:{}", a, b, c, d, port) - .parse() - .expect("Failed to parse as IPv4!")); - - let (u, t) = packed_node_protocol(saddr, pk); - // check whether ip_type variant matches - assert!(u.to_bytes()[0] == IpType::U4 as u8); - assert!(t.to_bytes()[0] == IpType::T4 as u8); - - // check whether IP matches .. - // ..with UDP - assert!(u.to_bytes()[1] == a); - assert!(u.to_bytes()[2] == b); - assert!(u.to_bytes()[3] == c); - assert!(u.to_bytes()[4] == d); - // ..with TCP - assert!(t.to_bytes()[1] == a); - assert!(t.to_bytes()[2] == b); - assert!(t.to_bytes()[3] == c); - assert!(t.to_bytes()[4] == d); - - // check whether port matches - assert_eq!(port, BigEndian::read_u16(&u.to_bytes()[5..7])); - assert_eq!(port, BigEndian::read_u16(&t.to_bytes()[5..7])); - - // check whether length matches - assert!(u.to_bytes().len() == PACKED_NODE_IPV4_SIZE); - assert!(t.to_bytes().len() == PACKED_NODE_IPV4_SIZE); - } - quickcheck(with_random_saddr as fn(u8, u8, u8, u8, u16)); -} - -#[test] -fn packed_node_to_bytes_test_ipv6() { - fn with_random_saddr(num1: u64, num2: u64, flowinfo: u32, scope_id: u32, - port: u16) { - let pk = &PublicKey([0; PUBLICKEYBYTES]); - - let (a, b, c, d) = u64_as_u16s(num1); - let (e, f, g, h) = u64_as_u16s(num2); - let saddr = SocketAddr::V6( - SocketAddrV6::new( - Ipv6Addr::new(a, b, c, d, e, f, g, h), - port, flowinfo, scope_id)); - let (u, t) = packed_node_protocol(saddr, pk); - // check whether ip_type variant matches - assert_eq!(u.to_bytes()[0], IpType::U6 as u8); - assert_eq!(t.to_bytes()[0], IpType::T6 as u8); - - // check whether IP matches .. - // ..with UDP - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[1..3]), a); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[3..5]), b); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[5..7]), c); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[7..9]), d); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[9..11]), e); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[11..13]), f); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[13..15]), g); - assert_eq!(LittleEndian::read_u16(&u.to_bytes()[15..17]), h); - // ..with TCP - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[1..3]), a); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[3..5]), b); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[5..7]), c); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[7..9]), d); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[9..11]), e); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[11..13]), f); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[13..15]), g); - assert_eq!(LittleEndian::read_u16(&t.to_bytes()[15..17]), h); - - // check whether port matches - assert_eq!(port, BigEndian::read_u16(&u.to_bytes()[17..19])); - assert_eq!(port, BigEndian::read_u16(&t.to_bytes()[17..19])); - - // check whether length matches - assert!(u.to_bytes().len() == PACKED_NODE_IPV6_SIZE); - assert!(t.to_bytes().len() == PACKED_NODE_IPV6_SIZE); - } - quickcheck(with_random_saddr as fn(u64, u64, u32, u32, u16)); -} - -#[test] -/* test for serialization of random PKs - - this requires a workaround with loops and hops - i.e. supply to the - quickcheck 4 `u64` arguments, cast to arrays, put elements from arrays - into a single vec and use vec to create PK -*/ -fn packed_nodes_to_bytes_test_pk() { - fn with_pk(a: u64, b: u64, c: u64, d: u64) { - let saddr4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 1)); - let saddr6 = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from_str("::0").unwrap(), 1, 0, 0)); - - let pk = nums_to_pk(a, b, c, d); - let PublicKey(ref pk_bytes) = pk; - - let (u4, t4) = packed_node_protocol(saddr4, &pk); - assert_eq!(&u4.to_bytes()[7..], pk_bytes); - assert_eq!(&t4.to_bytes()[7..], pk_bytes); - - let (u6, t6) = packed_node_protocol(saddr6, &pk); - assert_eq!(&u6.to_bytes()[19..], pk_bytes); - assert_eq!(&t6.to_bytes()[19..], pk_bytes); - } - quickcheck(with_pk as fn(u64, u64, u64, u64)); -} - - -// PackedNode::from_bytes() - -#[test] -fn packed_nodes_from_bytes_test() { - fn fully_random(pn: PackedNode) { - assert_eq!(pn, PackedNode::from_bytes(&pn.to_bytes()[..]).unwrap()); - } - quickcheck(fully_random as fn(PackedNode)); -} - -#[test] -// test for fail when length is too small -fn packed_nodes_from_bytes_test_length_short() { - fn fully_random(pn: PackedNode) { - let pnb = pn.to_bytes(); - assert_eq!(None, PackedNode::from_bytes(&pnb[..(pnb.len() - 1)])); - if IpType::from_bytes(&pnb[1..]).is_none() { - assert_eq!(None, PackedNode::from_bytes(&pnb[1..])); - } - } - quickcheck(fully_random as fn(PackedNode)); -} - -#[test] -// test for when length is too big - should work, and parse only first bytes -fn packed_nodes_from_bytes_test_length_too_long() { - fn fully_random(pn: PackedNode, r_u8: Vec) { - let mut vec = Vec::with_capacity(PACKED_NODE_IPV6_SIZE); - vec.extend_from_slice(&pn.to_bytes()[..]); - vec.extend_from_slice(&r_u8); - assert_eq!(pn, PackedNode::from_bytes(&vec[..]).unwrap()); - } - quickcheck(fully_random as fn(PackedNode, Vec)); -} - -#[test] -// test for fail when first byte is not an `IpType` -fn packed_nodes_from_bytes_test_no_iptype() { - fn fully_random(pn: PackedNode, r_u8: u8) { - // not interested in valid options - if r_u8 == 2 || r_u8 == 10 || r_u8 == 130 || r_u8 == 138 { - return; - } - let mut vec = Vec::with_capacity(PACKED_NODE_IPV6_SIZE); - vec.push(r_u8); - vec.extend_from_slice(&pn.to_bytes()[1..]); - assert_eq!(None, PackedNode::from_bytes(&vec[..])); - } - quickcheck(fully_random as fn(PackedNode, u8)); -} - -#[test] -// test for when `IpType` doesn't match length -fn packed_nodes_from_bytes_test_wrong_iptype() { - fn fully_random(pn: PackedNode) { - let mut vec = Vec::with_capacity(PACKED_NODE_IPV6_SIZE); - match pn.ip_type() { - IpType::U4 => vec.push(IpType::U6 as u8), - IpType::T4 => vec.push(IpType::T6 as u8), - _ => return, - } - vec.extend_from_slice(&pn.to_bytes()[1..]); - - assert_eq!(None, PackedNode::from_bytes(&vec[..])); - } - quickcheck(fully_random as fn(PackedNode)); -} - -// PackedNode::parse_bytes() - -#[test] -// test for when length is too big - should work, and parse only first bytes -fn packed_nodes_parse_bytes_test_length_too_long() { - fn fully_random(pn: PackedNode, r_u8: Vec) { - let mut vec = Vec::with_capacity(PACKED_NODE_IPV6_SIZE); - vec.extend_from_slice(&pn.to_bytes()[..]); - vec.extend_from_slice(&r_u8); - - let (rest, result) = PackedNode::parse_bytes(&vec[..]) - .expect("PackedNode parsing failure."); - assert_eq!(pn, result); - assert_eq!(&r_u8[..], rest); - } - quickcheck(fully_random as fn(PackedNode, Vec)); -} - - -// GetNodes:: - -impl Arbitrary for GetNodes { - fn arbitrary(g: &mut G) -> Self { - let mut a: [u8; PUBLICKEYBYTES] = [0; PUBLICKEYBYTES]; - g.fill_bytes(&mut a); - GetNodes { pk: PublicKey(a), id: g.gen() } - } -} - -// GetNodes::new() - -#[test] -fn get_nodes_new_test() { - fn with_pk(a: u64, b: u64, c: u64, d: u64) { - let pk = nums_to_pk(a, b, c, d); - let gn = GetNodes::new(&pk); - assert_eq!(gn.pk, pk); - } - quickcheck(with_pk as fn(u64, u64, u64, u64)); -} - -// SendNodes::from_request() - -#[test] -fn get_nodes_response_test() { - fn with_req(req: GetNodes, kbucket: Kbucket) { - let nodes = kbucket.get_closest(&req.pk); - let sn = req.response(&kbucket); - if nodes.is_empty() { - assert_eq!(None, sn); - } else { - let sn = sn.expect("failed to create response"); - assert_eq!(req.id, sn.id); - assert_eq!(nodes, sn.nodes); - } - } - quickcheck(with_req as fn(GetNodes, Kbucket)); - with_req(GetNodes::new(&PublicKey([0; PUBLICKEYBYTES])), - Kbucket::new(1, &PublicKey([1; PUBLICKEYBYTES]))); -} - -// GetNodes::kind() - -#[test] -fn get_nodes_kind_test() { - fn with_gn(gn: GetNodes) { - assert_eq!(PacketKind::GetN, gn.kind()); - } - quickcheck(with_gn as fn(GetNodes)); -} - -// GetNodes::to_bytes() - -#[test] -fn get_nodes_to_bytes_test() { - fn with_gn(gn: GetNodes) { - let g_bytes = gn.to_bytes(); - let PublicKey(pk_bytes) = gn.pk; - assert_eq!(&pk_bytes, &g_bytes[..PUBLICKEYBYTES]); - assert_eq!(gn.id, NativeEndian::read_u64(&g_bytes[PUBLICKEYBYTES..])); - } - quickcheck(with_gn as fn(GetNodes)); -} - -// GetNodes::from_bytes() - -#[test] -fn get_nodes_from_bytes_test() { - fn with_bytes(bytes: Vec) { - if bytes.len() < GET_NODES_SIZE { - assert_eq!(None, GetNodes::from_bytes(&bytes)); - } else { - let gn = GetNodes::from_bytes(&bytes).unwrap(); - // ping_id as bytes should match "original" bytes - assert_eq!(NativeEndian::read_u64(&bytes[PUBLICKEYBYTES..GET_NODES_SIZE]), gn.id); - - let PublicKey(ref pk) = gn.pk; - assert_eq!(pk, &bytes[..PUBLICKEYBYTES]); - } - } - quickcheck(with_bytes as fn(Vec)); -} - -// GetNodes::parse_bytes() - -#[test] -fn get_nodes_parse_bytes_rest_test() { - fn with_bytes(bytes: Vec) { - if bytes.len() >= GET_NODES_SIZE { - let (rest, _) = GetNodes::parse_bytes(&bytes) - .expect("GetNodes parsing failed."); - assert_eq!(rest, &bytes[GET_NODES_SIZE..]); - } - } - quickcheck(with_bytes as fn(Vec)); -} - - -// SendNodes:: - -impl Arbitrary for SendNodes { - fn arbitrary(g: &mut G) -> Self { - let nodes = vec![Arbitrary::arbitrary(g); g.gen_range(1,4)]; - let id = g.gen(); - SendNodes { nodes: nodes, id: id } - } -} - -// SendNodes::with_nodes() - -#[test] -fn send_nodes_with_nodes_test() { - fn with_request(req: GetNodes, nodes: Vec) { - if nodes.len() > 4 || nodes.is_empty() { - assert_eq!(None, SendNodes::with_nodes(&req, nodes)); - } else { - let sn = SendNodes::with_nodes(&req, nodes.clone()).unwrap(); - assert_eq!(req.id, sn.id); - assert_eq!(nodes, sn.nodes); - } - } - quickcheck(with_request as fn(GetNodes, Vec)); -} - -// SendNodes::kind() - -#[test] -fn send_nodes_kind_test() { - fn with_sn(sn: SendNodes) { - assert_eq!(PacketKind::SendN, sn.kind()); - } - quickcheck(with_sn as fn(SendNodes)); -} - -// SendNodes::to_bytes() - -#[test] -fn send_nodes_to_bytes_test() { - // there should be at least 1 valid node; there can be up to 4 nodes - fn with_nodes(req: GetNodes, n1: PackedNode, n2: Option, - n3: Option, n4: Option) { - - let mut nodes = vec![n1]; - if let Some(n) = n2 { nodes.push(n); } - if let Some(n) = n3 { nodes.push(n); } - if let Some(n) = n4 { nodes.push(n); } - let sn_bytes = SendNodes::with_nodes(&req, nodes.clone()) - .unwrap().to_bytes(); - - // number of nodes should match - assert_eq!(nodes.len(), sn_bytes[0] as usize); - - // bytes before current PackedNode in serialized SendNodes - // starts from `1` since first byte of serialized SendNodes is number of - // nodes - let mut len_before = 1; - for node in &nodes { - let cur_len = node.to_bytes().len(); - assert_eq!(&node.to_bytes()[..], - &sn_bytes[len_before..(len_before + cur_len)]); - len_before += cur_len; - } - // ping id should be the same as in request - assert_eq!(req.id, NativeEndian::read_u64(&sn_bytes[len_before..])); - } - quickcheck(with_nodes as fn(GetNodes, PackedNode, Option, - Option, Option)); -} - - -// SendNodes::from_bytes() - -#[test] -fn send_nodes_from_bytes_test() { - fn with_nodes(nodes: Vec, r_u64: u64) { - let mut bytes = vec![nodes.len() as u8]; - for node in &nodes { - bytes.extend_from_slice(&node.to_bytes()); - } - // and ping id - bytes.write_u64::(r_u64).unwrap(); - - if nodes.len() > 4 || nodes.is_empty() { - assert_eq!(None, SendNodes::from_bytes(&bytes)); - } else { - let nodes2 = SendNodes::from_bytes(&bytes).unwrap(); - assert_eq!(&nodes, &nodes2.nodes); - assert_eq!(r_u64, nodes2.id); - } - } - quickcheck(with_nodes as fn(Vec, u64)); -} - -// SendNodes::parse_bytes() - -#[test] -fn send_nodes_parse_bytes_rest_test() { - fn with_nodes(nodes: Vec, r_u64: u64, r_rest: Vec) { - let mut bytes = vec![nodes.len() as u8]; - for node in &nodes { - bytes.extend_from_slice(&node.to_bytes()); - } - // and ping id - bytes.write_u64::(r_u64).unwrap(); - bytes.extend_from_slice(&r_rest); - - if nodes.len() <= 4 && !nodes.is_empty() { - let (rest, _) = SendNodes::parse_bytes(&bytes) - .unwrap(); - assert_eq!(&r_rest[..], rest); - } - } - quickcheck(with_nodes as fn(Vec, u64, Vec)); -} - - - -// DhtPacket:: - -impl Arbitrary for DhtPacket { - fn arbitrary(g: &mut G) -> Self { - let (pk, sk) = gen_keypair(); // "sender" keypair - let (r_pk, _) = gen_keypair(); // receiver PK - let precomputed = precompute(&r_pk, &sk); - let nonce = gen_nonce(); - - let choice = g.gen_range(0, 4); - match choice { - 0 => - DhtPacket::new(&precomputed, &pk, &nonce, &PingReq::arbitrary(g)), - 1 => - DhtPacket::new(&precomputed, &pk, &nonce, &PingResp::arbitrary(g)), - 2 => - DhtPacket::new(&precomputed, &pk, &nonce, &GetNodes::arbitrary(g)), - 3 => - DhtPacket::new(&precomputed, &pk, &nonce, &SendNodes::arbitrary(g)), - _ => panic!("Arbitrary for DhtPacket - should not have happened!") - } - } -} - - -// DhtPacket::get_payload() - -#[test] -fn dht_paket_get_payload_test() { - fn with_dht_packet

(dpt: P) - where P: DhtPacketT - { - let (alice_pk, alice_sk) = gen_keypair(); - let (bob_pk, bob_sk) = gen_keypair(); - let (_, eve_sk) = gen_keypair(); - let precomputed = precompute(&bob_pk, &alice_sk); - let nonce = gen_nonce(); - - let new_packet = DhtPacket::new(&precomputed, - &alice_pk, - &nonce, - &dpt); - - // eve can't decrypt it - assert_eq!(None, new_packet.get_payload::

(&eve_sk)); - - let bob_packet = new_packet.get_payload(&bob_sk).unwrap(); - assert_eq!(dpt, bob_packet); - } - quickcheck(with_dht_packet as fn(PingReq)); - quickcheck(with_dht_packet as fn(PingResp)); - quickcheck(with_dht_packet as fn(GetNodes)); - quickcheck(with_dht_packet as fn(SendNodes)); -} - -// DhtPacket::ping_resp() - -#[test] -fn dht_packet_ping_resp_test() { - fn with_dpt

(dpt: P) - where P: DhtPacketT - { - let (pk, sk) = gen_keypair(); - let prec = precompute(&pk, &sk); - let nonce = gen_nonce(); - - let response = DhtPacket::new(&prec, &pk, &nonce, &dpt) - .ping_resp(&sk, &prec, &pk); - - if dpt.kind() == PacketKind::PingReq { - // FIXME: assume that it's a correct response ;/ - } else { - assert_eq!(None, response); - } - } - quickcheck(with_dpt as fn(PingReq)); - quickcheck(with_dpt as fn(PingResp)); - quickcheck(with_dpt as fn(GetNodes)); - quickcheck(with_dpt as fn(SendNodes)); -} - -quickcheck! { - fn dht_packet_ping_resp_test_invalid(ping: PingReq) -> () { - // test whether a malformed DhtPacket can be parsed as PingReq - let (pk, sk) = gen_keypair(); - let prec = precompute(&pk, &sk); - let nonce = gen_nonce(); - - // create a malformed packet that contains PingResp even though - // it claims to be a PingReq - let ping = DhtPacket::new(&prec, &pk, &nonce, &ping) - .ping_resp(&sk, &prec, &pk).unwrap(); - let mut bytes = ping.to_bytes(); - bytes[0] = PacketKind::PingReq as u8; - // parsing as DhtPacket doesn't fail, since its contents are - // encrypted - let dhtp = DhtPacket::from_bytes(&bytes).unwrap(); - assert_eq!(PacketKind::PingReq, dhtp.kind()); - - assert!(dhtp.ping_resp(&sk, &prec, &pk).is_none()); - } -} - - -// DhtPacket::to_bytes() - -#[test] -fn dht_packet_to_bytes_test() { - fn with_dht_packet

(dpt: P) - where P: DhtPacketT - { - // Alice serializes & encrypts packet, Bob decrypts - let (alice_pk, alice_sk) = gen_keypair(); - let (bob_pk, bob_sk) = gen_keypair(); - let precomputed = precompute(&bob_pk, &alice_sk); - let nonce = gen_nonce(); - - let packet = DhtPacket::new(&precomputed, &alice_pk, &nonce, &dpt) - .to_bytes(); - - // check whether packet type was serialized correctly - let packet_type = dpt.kind() as u8; - assert_eq!(packet_type, packet[0]); - - // sender's PK - let PublicKey(send_pk) = alice_pk; - assert_eq!(send_pk, packet[1..(1 + PUBLICKEYBYTES)]); - - // nonce - let nonce_start = 1 + PUBLICKEYBYTES; - let nonce_end = nonce_start + NONCEBYTES; - let Nonce(nonce_bytes) = nonce; - assert_eq!(nonce_bytes, packet[nonce_start..nonce_end]); - - let decrypted = open(&packet[nonce_end..], &nonce, &alice_pk, &bob_sk).unwrap(); - assert_eq!(dpt, P::from_bytes(&decrypted).unwrap()); - } - quickcheck(with_dht_packet as fn(PingReq)); - quickcheck(with_dht_packet as fn(PingResp)); - quickcheck(with_dht_packet as fn(GetNodes)); - quickcheck(with_dht_packet as fn(SendNodes)); -} - -// DhtPacket::from_bytes() - -#[test] -fn dht_packet_from_bytes_test() { - fn with_packet(p: DhtPacket) { - let from_bytes = DhtPacket::from_bytes(&p.to_bytes()).unwrap(); - assert_eq!(p, from_bytes); - } - quickcheck(with_packet as fn(DhtPacket)); -} - -// DhtPacket::parse_bytes() - -#[test] -fn dht_packet_parse_bytes_rest_test() { - fn with_packet(p: DhtPacket, r_rest: Vec) { - let mut bytes = vec![]; - bytes.extend_from_slice(&p.to_bytes()); - bytes.extend_from_slice(&r_rest); - - let (rest, _) = DhtPacket::parse_bytes(&bytes) - .expect("DhtPacket parsing failed."); - assert!(rest.is_empty()); // all remaining bytes are in payload - } - quickcheck(with_packet as fn(DhtPacket, Vec)); -} - -// PublicKey::distance() - -#[test] -// TODO: possible to use quickcheck? -fn public_key_distance_test() { - let pk_0 = PublicKey([0; PUBLICKEYBYTES]); - let pk_1 = PublicKey([1; PUBLICKEYBYTES]); - let pk_2 = PublicKey([2; PUBLICKEYBYTES]); - let pk_ff = PublicKey([0xff; PUBLICKEYBYTES]); - let pk_fe = PublicKey([0xfe; PUBLICKEYBYTES]); - - assert_eq!(Ordering::Less, pk_0.distance(&pk_1, &pk_2)); - assert_eq!(Ordering::Equal, pk_2.distance(&pk_2, &pk_2)); - assert_eq!(Ordering::Less, pk_2.distance(&pk_0, &pk_1)); - assert_eq!(Ordering::Greater, pk_2.distance(&pk_ff, &pk_fe)); - assert_eq!(Ordering::Greater, pk_2.distance(&pk_ff, &pk_fe)); - assert_eq!(Ordering::Less, pk_fe.distance(&pk_ff, &pk_2)); -} - - -// kbucket_index() - -#[test] -fn kbucket_index_test() { - let pk1 = PublicKey([0b10_10_10_10; PUBLICKEYBYTES]); - let pk2 = PublicKey([0; PUBLICKEYBYTES]); - let pk3 = PublicKey([0b00_10_10_10; PUBLICKEYBYTES]); - assert_eq!(None, kbucket_index(&pk1, &pk1)); - assert_eq!(Some(0), kbucket_index(&pk1, &pk2)); - assert_eq!(Some(2), kbucket_index(&pk2, &pk3)); -} - - -// Bucket:: - -// Bucket::new() - -#[test] -fn bucket_new_test() { - fn check_with_capacity(num: Option, expected_capacity: usize) { - let bucket1 = Bucket::new(num); - assert_eq!(expected_capacity, bucket1.capacity()); - - // check if always the same with same parameters - let bucket2 = Bucket::new(num); - assert_eq!(bucket1, bucket2); - } - check_with_capacity(None, BUCKET_DEFAULT_SIZE); - check_with_capacity(Some(0), BUCKET_DEFAULT_SIZE); - - fn wrapped_check(num: u8) -> TestResult { - // check Some(n) where n > 0 - if num == 0 { - return TestResult::discard() - } - check_with_capacity(Some(num), num as usize); - TestResult::passed() - } - quickcheck(wrapped_check as fn(u8) -> TestResult); - wrapped_check(0); -} - -// Bucket::try_add() - -#[test] -fn bucket_try_add_test() { - fn with_nodes(n1: PackedNode, n2: PackedNode, n3: PackedNode, - n4: PackedNode, n5: PackedNode, n6: PackedNode, - n7: PackedNode, n8: PackedNode) { - let pk = PublicKey([0; PUBLICKEYBYTES]); - let mut bucket = Bucket::new(None); - assert_eq!(true, bucket.try_add(&pk, &n1)); - assert_eq!(true, bucket.try_add(&pk, &n2)); - assert_eq!(true, bucket.try_add(&pk, &n3)); - assert_eq!(true, bucket.try_add(&pk, &n4)); - assert_eq!(true, bucket.try_add(&pk, &n5)); - assert_eq!(true, bucket.try_add(&pk, &n6)); - assert_eq!(true, bucket.try_add(&pk, &n7)); - assert_eq!(true, bucket.try_add(&pk, &n8)); - - // updating bucket - assert_eq!(true, bucket.try_add(&pk, &n1)); - - // TODO: check whether adding a closest node will always work - } - quickcheck(with_nodes as fn(PackedNode, PackedNode, PackedNode, PackedNode, - PackedNode, PackedNode, PackedNode, PackedNode)); -} - -#[test] -fn bucket_1_capacity_try_add_test() { - fn with_nodes(n1: PackedNode, n2: PackedNode) -> TestResult { - let pk = PublicKey([0; PUBLICKEYBYTES]); - if pk.distance(n2.pk(), n1.pk()) != Ordering::Greater { - // n2 should be greater to check we can't add it - return TestResult::discard() - } - - let mut node = Bucket::new(Some(1)); - - assert_eq!(true, node.try_add(&pk, &n1)); - assert_eq!(false, node.try_add(&pk, &n2)); - - // updating node - assert_eq!(true, node.try_add(&pk, &n1)); - TestResult::passed() - } - quickcheck(with_nodes as fn(PackedNode, PackedNode) -> TestResult); -} - -// Bucket::remove() - -#[test] -fn bucket_remove_test() { - fn with_nodes(num: u8, bucket_size: u8, rng_num: usize) { - let mut rng = StdGen::new(ChaChaRng::new_unseeded(), rng_num); - - let base_pk = PublicKey([0; PUBLICKEYBYTES]); - let mut bucket = Bucket::new(Some(bucket_size)); - - let non_existent_node: PackedNode = Arbitrary::arbitrary(&mut rng); - bucket.remove(&base_pk, non_existent_node.pk()); // "removing" non-existent node - assert_eq!(true, bucket.is_empty()); - - let nodes = vec![Arbitrary::arbitrary(&mut rng); num as usize]; - for node in &nodes { - bucket.try_add(&base_pk, node); - } - if num == 0 { - // nothing was added - assert_eq!(true, bucket.is_empty()); - } else { - // some nodes were added - assert_eq!(false, bucket.is_empty()); - } - - for node in &nodes { - bucket.remove(&base_pk, node.pk()); - } - assert_eq!(true, bucket.is_empty()); - } - quickcheck(with_nodes as fn(u8, u8, usize)) -} - - -// Bucket::is_empty() - -#[test] -fn bucket_is_empty_test() { - fn with_pns(pns: Vec, p1: u64, p2: u64, p3: u64, p4: u64) -> TestResult { - if pns.len() > BUCKET_DEFAULT_SIZE { - // it's possible that not all nodes will be inserted if - // len > BUCKET_DEFAULT_SIZE - return TestResult::discard() - } - - let mut bucket = Bucket::new(None); - assert_eq!(true, bucket.is_empty()); - - let pk = nums_to_pk(p1, p2, p3, p4); - for n in &pns { - assert!(bucket.try_add(&pk, n)); - } - if !pns.is_empty() { - assert_eq!(false, bucket.is_empty()); - } - TestResult::passed() - } - quickcheck(with_pns as fn(Vec, u64, u64, u64, u64) -> TestResult); -} - - -// Kbucket:: - -impl Arbitrary for Kbucket { - fn arbitrary(g: &mut G) -> Self { - let mut pk = [0; PUBLICKEYBYTES]; - g.fill_bytes(&mut pk); - let pk = PublicKey([0; PUBLICKEYBYTES]); - - let mut kbucket = Kbucket::new(g.gen(), &pk); - - // might want to add some buckets - for _ in 0..(g.gen_range(0, KBUCKET_MAX_ENTRIES as usize * - BUCKET_DEFAULT_SIZE as usize * 2)) { - kbucket.try_add(&Arbitrary::arbitrary(g)); - } - kbucket - } -} - -// Kbucket::new() - -#[test] -fn kbucket_new_test() { - fn with_pk(a: u64, b: u64, c: u64, d: u64, buckets: u8) { - let pk = nums_to_pk(a, b, c, d); - let kbucket = Kbucket::new(buckets, &pk); - assert_eq!(buckets, kbucket.size()); - assert_eq!(pk, kbucket.pk()); - } - quickcheck(with_pk as fn(u64, u64, u64, u64, u8)); -} - -// Kbucket::size() - -#[test] -fn kbucket_size_test() { - let pk = PublicKey([0; PUBLICKEYBYTES]); - - let k0 = Kbucket::new(0, &pk); - assert_eq!(0, k0.size()); - - let k1 = Kbucket::new(1, &pk); - assert_eq!(1, k1.size()); - - let k255 = Kbucket::new(255, &pk); - assert_eq!(255, k255.size()); -} - -// Kbucket::try_add() - -#[test] -fn kbucket_try_add_test() { - fn with_pns(pns: Vec, n: u8, p1: u64, p2: u64, p3: u64, p4: u64) { - let pk = nums_to_pk(p1, p2, p3, p4); - let mut kbucket = Kbucket::new(n, &pk); - for node in pns { - // result may vary, so discard it - // TODO: can be done better? - kbucket.try_add(&node); - } - } - quickcheck(with_pns as fn(Vec, u8, u64, u64, u64, u64)); -} - -// Kbucket::remove() - -#[test] -fn kbucket_remove_test() { - fn with_nodes(nodes: Vec) -> TestResult { - if nodes.len() > BUCKET_DEFAULT_SIZE { - // it's possible that not all nodes will be inserted if - // len > BUCKET_DEFAULT_SIZE - return TestResult::discard() - } - - let pk = nums_to_pk(random_u64(), random_u64(), random_u64(), - random_u64()); - - let mut kbucket = Kbucket::new(KBUCKET_MAX_ENTRIES, &pk); - - // Fill Kbucked with nodes - for node in &nodes { - assert!(kbucket.try_add(node)); - } - if !nodes.is_empty() { - assert!(!kbucket.is_empty()); - } - - // Check for actual removing - for node in &nodes { - kbucket.remove(node.pk()); - } - assert!(kbucket.is_empty()); - TestResult::passed() - } - quickcheck(with_nodes as fn(Vec) -> TestResult); -} - -// Kbucket::get_closest() - -#[test] -fn kbucket_get_closest_test() { - fn with_kbucket(kb: Kbucket, a: u64, b: u64, c: u64, d: u64) { - let pk = nums_to_pk(a, b, c, d); - assert!(kb.get_closest(&pk).len() <= 4); - assert_eq!(kb.get_closest(&pk), kb.get_closest(&pk)); - } - quickcheck(with_kbucket as fn(Kbucket, u64, u64, u64, u64)); - - - fn with_nodes(n1: PackedNode, n2: PackedNode, n3: PackedNode, - n4: PackedNode, a: u64, b: u64, c: u64, d: u64) { - - let pk = nums_to_pk(a, b, c, d); - let mut kbucket = Kbucket::new(::std::u8::MAX, &pk); - - // check whether number of correct nodes that are returned is right - let correctness = |should, kbc: &Kbucket| { - assert_eq!(kbc.get_closest(&pk), kbc.get_closest(&kbc.pk())); - - let got_nodes = kbc.get_closest(&pk); - let mut got_correct = 0; - for node in got_nodes { - if node == n1 || node == n2 || node == n3 || node == n4 { - got_correct += 1; - } - } - assert_eq!(should, got_correct); - }; - - correctness(0, &kbucket); - - assert_eq!(true, kbucket.try_add(&n1)); - correctness(1, &kbucket); - assert_eq!(true, kbucket.try_add(&n2)); - correctness(2, &kbucket); - assert_eq!(true, kbucket.try_add(&n3)); - correctness(3, &kbucket); - assert_eq!(true, kbucket.try_add(&n4)); - correctness(4, &kbucket); - } - quickcheck(with_nodes as fn(PackedNode, PackedNode, PackedNode, - PackedNode, u64, u64, u64, u64)); -} - - -// NatPingReq:: - -// NatPingReq::new() - -#[test] -fn nat_ping_req_new_test() { - let p1 = NatPingReq::new(); - let p2 = NatPingReq::new(); - assert!(p1 != p2); - assert!(p1.id() != p2.id()); -} - -// NatPingReq::id() - -#[test] -fn nat_ping_req_id_test() { - let ping = NatPingReq::new(); - assert_eq!(ping.id(), ping.id()); -} - - - -// NatPingResp:: - -// NatPingResp::id() - -#[test] -fn nat_ping_resp_id_test() { - fn with_np(np: NatPingReq) { - assert_eq!(np.id(), NatPingResp::from(np).id()); - } - quickcheck(with_np as fn(NatPingReq)); -} - -// NatPingResp::to_bytes() - -#[test] -fn nat_ping_resp_to_bytes_test_cmp_to_req() { - fn with_np(p: NatPingReq) { - let pqb = p.to_bytes(); - let prb = NatPingResp::from(p).to_bytes(); - assert_eq!(pqb[0], prb[0]); - assert!(pqb[1] != prb[1]); - // `id` of the ping should not change - assert_eq!(pqb[2..], prb[2..]); - } - quickcheck(with_np as fn(NatPingReq)); -} - -macro_rules! impls_tests_for_nat_pings { - ($($np:ident($p:ident) $b_t:ident $f_t:ident $p_t:ident $d_t:ident)+) => ($( - impl Arbitrary for $np { - fn arbitrary(g: &mut G) -> Self { - $np(Arbitrary::arbitrary(g)) - } - } - - #[test] - fn $b_t() { - fn with_np(p: $np) { - let pb = p.to_bytes(); - assert_eq!(NAT_PING_SIZE, pb.len()); - // check the magic ping type value - assert_eq!(NAT_PING_TYPE, pb[0]); - assert_eq!(p.kind() as u8, pb[1]); - } - quickcheck(with_np as fn($np)); - } - - // ::from_bytes() - - #[test] - fn $f_t() { - fn with_bytes(bytes: Vec) { - if bytes.len() < NAT_PING_SIZE || - bytes[0] != NAT_PING_TYPE || - bytes[1] != PacketKind::$p as u8 - { - assert_eq!(None, $np::from_bytes(&bytes)); - } else { - let p = $np::from_bytes(&bytes) - .expect("De-serialization failed"); - - assert_eq!(p.id(), NativeEndian::read_u64(&bytes[2..NAT_PING_SIZE])); - } - } - quickcheck(with_bytes as fn(Vec)); - - // just in case - let mut ping = vec![NAT_PING_TYPE, PacketKind::$p as u8]; - ping.write_u64::(random_u64()) - .unwrap(); - with_bytes(ping); - } - - // ::parse_bytes() - - #[test] - fn $p_t() { - fn with_bytes(np: $np, r_rest: Vec) { - let mut bytes = vec![]; - bytes.extend_from_slice(&np.to_bytes()); - bytes.extend_from_slice(&r_rest); - - let (rest, _) = $np::parse_bytes(&bytes) - .unwrap(); - assert_eq!(&r_rest[..], rest); - } - quickcheck(with_bytes as fn($np, Vec)); - } - - // ::deref() - - #[test] - fn $d_t() { - fn with_ping(ping: $p) { - assert_eq!(*$np(ping), ping); - } - quickcheck(with_ping as fn($p)); - } - )+) -} -impls_tests_for_nat_pings!( - NatPingReq(PingReq) - nat_ping_req_to_bytes_test - nat_ping_req_from_bytes_test - nat_ping_req_parse_bytes_test - nat_ping_req_deref_test - NatPingResp(PingResp) - nat_ping_resp_to_bytes_test - nat_ping_resp_from_bytes_test - nat_ping_resp_parse_bytes_test - nat_ping_resp_deref_test -); - - - -// DhtRequest::new() - -#[test] -fn dht_request_new_test() { - // TODO: once DhtRequest will support more types, expand the test - fn with_req(req: R) - where R: DhtRequestT - { - let (alice_pk, alice_sk) = gen_keypair(); - let (bob_pk, _) = gen_keypair(); - let nonce = gen_nonce(); - let dr = DhtRequest::new(&alice_sk, &alice_pk, &bob_pk, &nonce, &req); - let dr2 = DhtRequest::new(&alice_sk, &alice_pk, &bob_pk, &nonce, &req); - assert_eq!(dr, dr2); - assert_eq!(dr.receiver, bob_pk); - assert_eq!(dr.sender, alice_pk); - - let nonce2 = gen_nonce(); - let dr3 = DhtRequest::new(&alice_sk, &alice_pk, &bob_pk, &nonce2, &req); - assert!(dr != dr3); - } - quickcheck(with_req as fn(NatPingReq)); - quickcheck(with_req as fn(NatPingResp)); -} - -// DhtRequest::get_request() - -#[test] -fn dht_request_get_request_test() { - fn with_req(req: R) - where R: DhtRequestT - { - let (alice_pk, alice_sk) = gen_keypair(); - let (bob_pk, bob_sk) = gen_keypair(); - let nonce = gen_nonce(); - let dreq = DhtRequest::new(&alice_sk, &alice_pk, &bob_pk, &nonce, &req); - - let received = dreq.get_request(&bob_sk).expect("Failed to get request"); - assert_eq!(req, received); - } - quickcheck(with_req as fn(NatPingReq)); - quickcheck(with_req as fn(NatPingResp)); -} diff --git a/src/toxcore/dht_old/example_dht_node.rs b/src/toxcore/dht_old/example_dht_node.rs deleted file mode 100644 index 78845ba01..000000000 --- a/src/toxcore/dht_old/example_dht_node.rs +++ /dev/null @@ -1,137 +0,0 @@ -/* - Copyright © 2017 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - - -// an example of DHT node with current code -// -// it's not supposed to be an actual example, more like try to find out -// which parts in zetox are still missing to make a DHT node easy to run -// -// to get some meaningful info, run it with `RUST_LOG` env variable -// set, e.g. `RUST_LOG="tox=debug,dht_node=debug"` - -extern crate futures; -extern crate env_logger; -#[macro_use] -extern crate log; -extern crate tox; -extern crate tokio_core; -extern crate tokio_timer; -extern crate rustc_serialize; - - -use futures::*; -// to get bytes from PK in hex and to make PK from them -use rustc_serialize::hex::FromHex; -use tokio_core::reactor::Core; -use tokio_timer::*; - -use std::net::SocketAddr; -use std::time::Duration; -use std::cell::RefCell; -use std::sync::Arc; - -use tox::toxcore::crypto_core::*; -use tox::toxcore::dht::*; -use tox::toxcore::dht_node::*; -use tox::toxcore::network::*; -use tox::toxcore::timeout::*; - -fn main() { - env_logger::init(); - - // the way RefCell is used below can result in a panic - let node = Arc::new(RefCell::new(DhtNode::new().unwrap())); - let mut core = Core::new().unwrap(); - let handle = core.handle(); - - let socket = bind_udp("::".parse().unwrap(), PORT_MIN..PORT_MAX, &handle) - .unwrap(); - - // get PK bytes of some "random" bootstrap node (Impyy's) - let bootstrap_pk_bytes = FromHex::from_hex( - "6FC41E2BD381D37E9748FC0E0328CE086AF9598BECC8FEB7DDF2E440475F300E") - .unwrap(); - // create PK from bytes - let bootstrap_pk = PublicKey::from_slice(&bootstrap_pk_bytes).unwrap(); - - //"51.15.37.145:33445".parse().unwrap() - let saddr: SocketAddr = "51.15.37.145:33445".parse().unwrap(); - let bootstrap_pn = PackedNode::new(true, saddr, &bootstrap_pk); - let (sink, stream) = socket.framed(ToxCodec).split(); - - let timer = Timer::default(); - // GetNodes timeout - let getn_time = timer.interval(Duration::from_secs(20)); - let eject_nodes = timer.interval(Duration::from_secs(1)); - - let send_tx = send_packets(sink); - - // bootstrap - // TODO: add method to DhtNode for adding timeouts ? - //let req_some_nodes = send_tx.clone() - // .send(node.borrow_mut().request_nodes(&bootstrap_pn).map) - // .then(|_| Ok(())); - //handle.spawn(req_some_nodes); - - // node will automatically "bootstrap", but bootstrapping is not - // reliable, restarting a few times might be necessary to join the - // network (bootstrapping from multiple nodes would help with that) - assert!(node.borrow_mut().try_add(&bootstrap_pn)); - - - //// handle incoming stuff - - let deal_with_it = receive_packets(stream).for_each(|(addr, packet)| { - debug!("Handling packet from {:?}", addr); - let resp = node.borrow_mut().handle_packet(&packet); - - if let Some(r) = resp { - let tx = send_tx.clone(); - handle.spawn(tx.send((addr, r)).then(|_| Ok(()))); - } - - Ok(()) - }); - - // make getn requests - let hc = core.handle(); - let send_tx = send_tx.clone(); - let getn_f = getn_time.then(|_| { - debug!("Requesting nodes 20s"); - let requests = node.borrow_mut().request_nodes_close(); - for req in requests { - let request_it = send_tx.clone() - .send(req) - .then(|_| Ok(())); - hc.spawn(request_it); - } - Ok::<(), ()>(()) - }).for_each(|_| Ok(())); - - - let eject = eject_nodes.then(|_| { - //debug!("Checking for timed out nodes"); - node.borrow_mut().remove_timed_out(RESPONSE_CHECK); - Ok(()) - }).for_each(|_| Ok(())); - - let joined = getn_f.join3(deal_with_it, eject); - drop(core.run(joined)); -} diff --git a/src/toxcore/dht_old/network.rs b/src/toxcore/dht_old/network.rs deleted file mode 100644 index aea3b405f..000000000 --- a/src/toxcore/dht_old/network.rs +++ /dev/null @@ -1,275 +0,0 @@ -/* - Copyright © 2016-2017 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - - -// ↓ FIXME expand doc -//! Networking part of the toxcore. - -// TODO: separate stuff managing DHT from network -// proper implementation of DHT should expose an interface that network -// implements - -use std::any::Any; -use std::rc::Rc; -use std::cell::RefCell; -use std::io::{ self, ErrorKind }; -use std::ops::{ Range, RangeFrom, RangeTo, RangeFull }; -use std::net::{ IpAddr, Ipv6Addr, SocketAddr, ToSocketAddrs }; -use std::collections::HashMap; - -use tokio_core::net::UdpSocket; -use tokio_core::reactor::Handle; - -use super::crypto_core::crypto_init; - - -/// Minimum default port which Tox will try to bind to. -pub const PORT_MIN: u16 = 33_445; -/// Maximum default port which Tox will try to bind to. -pub const PORT_MAX: u16 = 33_545; -/// Maximum size of a UDP packet that tox will handle. -// TODO: check if it's still the biggest packet size -pub const MAX_UDP_PACKET_SIZE: usize = 2048; - - -/** Type for functions that handle packets. - -- `addr` – sender address -- `data` – packet data -*/ -// TODO: move out of `network` ? -pub type PacketHandlerCallback = fn(Rc>, addr: SocketAddr, data: &[u8]) -> usize; - -// TODO: move out of `network` ? -struct PacketHandles { - object: Rc>, - function: PacketHandlerCallback -} - -// TODO: move out of `network` ? -impl PacketHandles { - #[inline] - fn handle(&self, addr: SocketAddr, data: &[u8]) { - (self.function)(Rc::clone(&self.object), addr, data); - } -} - -/// Networking Core. -pub struct NetworkingCore { - packethandles: HashMap, - sock: UdpSocket -} - -impl NetworkingCore { - /** Initialize networking by binding to specified socket IP, and a port in - supplied range. - - # Fails when - - - binding to IP address and every port in supplied range fails - - setting broadcast on socket fails - */ - pub fn new(ip: IpAddr, port_range: R, handle: &Handle) -> io::Result - where R: Into> - { - let PortRange(port_range) = port_range.into(); - - // TODO: network shouldn't fail due to crypto, remove this from network - // and put in more fitting place - if !crypto_init() { - return Err(io::Error::new(ErrorKind::Other, - "Crypto initialization failed.")); - } - - let sock = try!( - bind_udp(ip, port_range, handle) - .ok_or_else(|| io::Error::new(ErrorKind::AddrInUse, "Addr/Port in use.")) - ); - - if let IpAddr::V6(_) = ip { - // TODO Dual-stack: set only_v6 to false. - - let res = sock.join_multicast_v6(&Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x01), 0); - match res { - Ok(_) => debug!("Local multicast group FF02::1 joined successfully"), - Err(err) => debug!("Failed to activate local multicast membership. {}", err) - } - } - - // TODO set RCVBUF/SNDBUF/SIGPIPE - try!(sock.set_broadcast(true)); - - Ok(NetworkingCore { - packethandles: HashMap::new(), - sock: sock - }) - } - - /** Function to call when packet beginning with byte is received. - */ - // FIXME: docs - pub fn register(&mut self, byte: u8, cb: PacketHandlerCallback, object: Rc>) { - self.packethandles.insert(byte, PacketHandles { - object: object, - function: cb - }); - } - - /// Call this several times a second. - // TODO: polling isn't really optimal, move away from it - pub fn poll(&self) { - let mut data = [0; MAX_UDP_PACKET_SIZE]; - - while let Ok((size, addr)) = self.receive_packet(&mut data) { - if size < 1 { continue }; - - match self.packethandles.get(&data[0]) { - Some(handler) => handler.handle(addr, &data[..size]), - None => warn!("[{:x}] -- Packet has no handler", data[0]) - } - } - } - - /// Function to send packet (`data`) to SocketAddr. - pub fn send_packet(&self, addr: SocketAddr, data: &[u8]) -> io::Result { - // XXX need check target ip type? - let res = self.sock.send_to(data, &addr); - - // TODO debug - match res { - Ok(size) => debug!("send: [{} -> {}] {:?}", addr, size, data), - Err(ref err) => debug!("{}", err) - } - - res - } - - // TODO: convert docs to block style once rust gets fixed - /** Receive packet data into `&mut data`. - - Returns `Option<(length, addr)>` or `io::Error`. - - If successfull, `(received bytes length, sender socket addr)` are - returned. - */ - pub fn receive_packet(&self, data: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - let res = self.sock.recv_from(data); - - // TODO debug - // TODO: should be `trace!` instead? - match res { - Ok((size, addr)) => debug!("recv: [{} -> {}] {:?}", addr, size, data), - Err(ref err) => debug!("{}", err) - } - - res - } -} - -/** Bind to an UDP socket on `0.0.0.0` with a port in range [`PORT_MIN`] -(./constant.PORT_MIN.html):[`PORT_MAX`](./constant.PORT_MAX.html). - -Returns `None` if failed to bind to port within range. -*/ -pub fn bind_udp(ip: IpAddr, port_range: Range, handle: &Handle) - -> Option -{ - for port in port_range { - match (ip, port).to_socket_addrs().ok() - .and_then(|mut addrs| addrs.next()) - .ok_or_else(|| io::Error::new(ErrorKind::AddrNotAvailable, "Socket Addr Not Available.")) - .and_then(|addr| UdpSocket::bind(&addr, handle)) - { - Ok(s) => { - debug!(target: "Port", "Bind to port {} successful.", port); - return Some(s) - }, - Err(e) => trace!(target: "Port", "Bind to port {} unsuccessful: {}", - port, e), - } - } - error!(target: "Port", "Failed to bind to any port in range!"); - None // loop ended without "early" return – failed to bind -} - -/** Port Range, default is -[`PORT_MIN`](./constant.PORT_MIN.html)..[`PORT_MAX`](./constant.PORT_MAX.html). -*/ -#[derive(Clone, Debug, PartialEq)] -pub struct PortRange(pub Range); - -/** If one is 0 and the other is non-0, use the non-0 value as only port - -``` -# use tox::toxcore::network::PortRange; -assert_eq!(PortRange(33445..33446), (33445..).into()); -``` -*/ -impl From> for PortRange { - fn from(range: RangeFrom) -> PortRange { - let RangeFrom { start } = range; - PortRange(Range { start: start, end: start + 1 }) - } -} - -/** If one is 0 and the other is non-0, use the non-0 value as only port - -``` -# use tox::toxcore::network::PortRange; -assert_eq!(PortRange(33445..33446), (..33445).into()); -``` -*/ -impl From> for PortRange { - fn from(range: RangeTo) -> PortRange { - let RangeTo { end } = range; - PortRange(Range { start: end, end: end + 1 }) - } -} - -/** If both from and to are 0, use default port range - -``` -# use tox::toxcore::network::PortRange; -assert_eq!(PortRange(33445..33546), (..).into()); -``` -*/ -impl From for PortRange { - fn from(_: RangeFull) -> PortRange { - PortRange(Range { start: PORT_MIN, end: PORT_MAX + 1 }) - } -} - -/** If `from > to`, values are swapped. - -``` -# use tox::toxcore::network::PortRange; -assert_eq!(PortRange(33445..33546), (33445..33546).into()); -assert_eq!(PortRange(33445..33546), (33546..33445).into()); -``` -*/ -impl From> for PortRange { - fn from(range: Range) -> PortRange { - let Range { start, end } = range; - PortRange(if start > end { - Range { start: end, end: start } - } else { - Range { start: start, end: end } - }) - } -} diff --git a/src/toxcore/dht_old/network_tests.rs b/src/toxcore/dht_old/network_tests.rs deleted file mode 100644 index d5ed563f0..000000000 --- a/src/toxcore/dht_old/network_tests.rs +++ /dev/null @@ -1,88 +0,0 @@ -/* - Copyright © 2016-2017 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - -//! Tests for network module. - - -use tokio_core::reactor::Core; - -use std::thread; -use std::time::Duration; - -use toxcore::network::*; - -// NetworkingCore:: - -// NetworkingCore::new() - -#[test] -fn networking_core_new_test() { - let core = Core::new().unwrap(); - let handle = core.handle(); - NetworkingCore::new("::".parse().unwrap(), PORT_MIN..PORT_MAX, &handle) - .unwrap(); -} - - -// NetworkingCore::new() - -#[test] -fn networking_core_register_test() { - use std::rc::Rc; - use std::any::Any; - use std::cell::RefCell; - use std::net::SocketAddr; - - let core = Core::new().unwrap(); - let handle = core.handle(); - let mut net = NetworkingCore::new("::".parse().unwrap(), .., &handle).unwrap(); - fn callback(num: Rc>, _: SocketAddr, _: &[u8]) -> usize { - match num.borrow().downcast_ref::() { - Some(_) => unimplemented!(), - None => 0 - } - } - - net.register(99, callback, Rc::new(RefCell::new(1usize)) as Rc>); -} - -// bind_udp() - -#[test] -/* there's no way to reliably test whole range for both success and faliure; - at least as long as there's no assumption that there are no other instances - running. - - Thus test only whether binding to at least 50 ports works :/ -*/ -#[ignore] // TODO remove in favor of `Future` code -fn bind_udp_test() { - for _ in 0..50 { - thread::spawn(move || { - let core = Core::new().unwrap(); - let handle = core.handle(); - let socket = bind_udp("::".parse().unwrap(), 33_445..33_546, &handle); - match socket { - Some(_) => {}, - None => panic!("This should have worked; bind_udp()"), - } - thread::sleep(Duration::from_millis(100)); // probably enough? - }); - } -} diff --git a/src/toxcore/dht_old/packet_kind.rs b/src/toxcore/dht_old/packet_kind.rs deleted file mode 100644 index 909efc3b3..000000000 --- a/src/toxcore/dht_old/packet_kind.rs +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright © 2016 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - -/*! Data associated with the `PacketKind`. Used by most of other `toxcore` - modules. - - Used by: - - * [`dht`](../dht/index.html) -*/ - -use nom::le_u8; - -use toxcore::binary_io::*; - - -/** Top-level packet kind names and their associated numbers. - - According to https://zetok.github.io/tox-spec.html#packet-kind. -*/ -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum PacketKind { - /// [`Ping`](./struct.Ping.html) request number. - PingReq = 0, - /// [`Ping`](./struct.Ping.html) response number. - PingResp = 1, - /// [`GetNodes`](./struct.GetNodes.html) packet number. - GetN = 2, - /// [`SendNodes`](./struct.SendNodes.html) packet number. - SendN = 4, - /// Cookie Request. - CookieReq = 24, - /// Cookie Response. - CookieResp = 25, - /// Crypto Handshake. - CryptoHs = 26, - /// Crypto Data (general purpose packet for transporting encrypted data). - CryptoData = 27, - /// DHT Request. - DhtReq = 32, - /// LAN Discovery. - LanDisc = 33, - /// Onion Reuqest 0. - OnionReq0 = 128, - /// Onion Request 1. - OnionReq1 = 129, - /// Onion Request 2. - OnionReq2 = 130, - /// Announce Request. - AnnReq = 131, - /// Announce Response. - AnnResp = 132, - /// Onion Data Request. - OnionDataReq = 133, - /// Onion Data Response. - OnionDataResp = 134, - /// Onion Response 3. - OnionResp3 = 140, - /// Onion Response 2. - OnionResp2 = 141, - /// Onion Response 1. - OnionResp1 = 142, -} - -/** Parse first byte from provided `bytes` as `PacketKind`. - - Returns `None` if no bytes provided, or first byte doesn't match. -*/ -from_bytes!(PacketKind, switch!(le_u8, - 0 => value!(PacketKind::PingReq) | - 1 => value!(PacketKind::PingResp) | - 2 => value!(PacketKind::GetN) | - 4 => value!(PacketKind::SendN) | - 24 => value!(PacketKind::CookieReq) | - 25 => value!(PacketKind::CookieResp) | - 26 => value!(PacketKind::CryptoHs) | - 27 => value!(PacketKind::CryptoData) | - 32 => value!(PacketKind::DhtReq) | - 33 => value!(PacketKind::LanDisc) | - 128 => value!(PacketKind::OnionReq0) | - 129 => value!(PacketKind::OnionReq1) | - 130 => value!(PacketKind::OnionReq2) | - 131 => value!(PacketKind::AnnReq) | - 132 => value!(PacketKind::AnnResp) | - 133 => value!(PacketKind::OnionDataReq) | - 134 => value!(PacketKind::OnionDataResp) | - 140 => value!(PacketKind::OnionResp3) | - 141 => value!(PacketKind::OnionResp2) | - 142 => value!(PacketKind::OnionResp1) -)); diff --git a/src/toxcore/dht_old/packet_kind_tests.rs b/src/toxcore/dht_old/packet_kind_tests.rs deleted file mode 100644 index 97b1f2c18..000000000 --- a/src/toxcore/dht_old/packet_kind_tests.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* - Copyright © 2016 Zetok Zalbavar - - This file is part of Tox. - - Tox is libre software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Tox is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Tox. If not, see . -*/ - - -use toxcore::binary_io::*; -use toxcore::packet_kind::PacketKind; - -use quickcheck::{Arbitrary, Gen, quickcheck}; - -// PacketKind:: - -impl Arbitrary for PacketKind { - fn arbitrary(g: &mut G) -> Self { - *g.choose(&[PacketKind::PingReq, - PacketKind::PingResp, - PacketKind::GetN, - PacketKind::SendN, - PacketKind::CookieReq, - PacketKind::CookieResp, - PacketKind::CryptoHs, - PacketKind::CryptoData, - PacketKind::DhtReq, - PacketKind::LanDisc, - PacketKind::OnionReq0, - PacketKind::OnionReq1, - PacketKind::OnionReq2, - PacketKind::AnnReq, - PacketKind::AnnResp, - PacketKind::OnionDataReq, - PacketKind::OnionDataResp, - PacketKind::OnionResp3, - PacketKind::OnionResp2, - PacketKind::OnionResp1]) - .unwrap() - } -} - -// PacketKind::from_bytes - -#[test] -fn packet_kind_from_bytes_test() { - fn with_bytes(bytes: Vec) { - if bytes.is_empty() { - assert_eq!(None, PacketKind::from_bytes(&bytes)); - return - } - match bytes[0] { - 0x00 => assert_eq!(PacketKind::PingReq, PacketKind::from_bytes(&bytes).unwrap()), - 0x01 => assert_eq!(PacketKind::PingResp, PacketKind::from_bytes(&bytes).unwrap()), - 0x02 => assert_eq!(PacketKind::GetN, PacketKind::from_bytes(&bytes).unwrap()), - 0x04 => assert_eq!(PacketKind::SendN, PacketKind::from_bytes(&bytes).unwrap()), - 0x18 => assert_eq!(PacketKind::CookieReq, PacketKind::from_bytes(&bytes).unwrap()), - 0x19 => assert_eq!(PacketKind::CookieResp, PacketKind::from_bytes(&bytes).unwrap()), - 0x1a => assert_eq!(PacketKind::CryptoHs, PacketKind::from_bytes(&bytes).unwrap()), - 0x1b => assert_eq!(PacketKind::CryptoData, PacketKind::from_bytes(&bytes).unwrap()), - 0x20 => assert_eq!(PacketKind::DhtReq, PacketKind::from_bytes(&bytes).unwrap()), - 0x21 => assert_eq!(PacketKind::LanDisc, PacketKind::from_bytes(&bytes).unwrap()), - 0x80 => assert_eq!(PacketKind::OnionReq0, PacketKind::from_bytes(&bytes).unwrap()), - 0x81 => assert_eq!(PacketKind::OnionReq1, PacketKind::from_bytes(&bytes).unwrap()), - 0x82 => assert_eq!(PacketKind::OnionReq2, PacketKind::from_bytes(&bytes).unwrap()), - 0x83 => assert_eq!(PacketKind::AnnReq, PacketKind::from_bytes(&bytes).unwrap()), - 0x84 => assert_eq!(PacketKind::AnnResp, PacketKind::from_bytes(&bytes).unwrap()), - 0x85 => assert_eq!(PacketKind::OnionDataReq, PacketKind::from_bytes(&bytes).unwrap()), - 0x86 => assert_eq!(PacketKind::OnionDataResp, PacketKind::from_bytes(&bytes).unwrap()), - 0x8c => assert_eq!(PacketKind::OnionResp3, PacketKind::from_bytes(&bytes).unwrap()), - 0x8d => assert_eq!(PacketKind::OnionResp2, PacketKind::from_bytes(&bytes).unwrap()), - 0x8e => assert_eq!(PacketKind::OnionResp1, PacketKind::from_bytes(&bytes).unwrap()), - _ => assert_eq!(None, PacketKind::from_bytes(&bytes)), - } - } - quickcheck(with_bytes as fn(Vec)); - - // just in case - with_bytes(vec![]); - for i in 0x00 .. 0xff { - with_bytes(vec![i]); - } -} - -// PacketKind::parse_bytes() - -#[test] -fn user_status_parse_bytes_rest_test() { - fn with_bytes(sk: PacketKind, r_rest: Vec) { - let mut bytes = vec![sk as u8]; - bytes.extend_from_slice(&r_rest); - - let (rest, _) = PacketKind::parse_bytes(&bytes) - .expect("PacketKind parsing failure."); - assert_eq!(&r_rest[..], rest); - } - quickcheck(with_bytes as fn(PacketKind, Vec)); -}