diff --git a/src/beacon.rs b/src/beacon.rs index b1319e9..191256a 100644 --- a/src/beacon.rs +++ b/src/beacon.rs @@ -12,11 +12,12 @@ use std::fs::{self, Permissions, File}; use std::os::unix::fs::PermissionsExt; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; +use std::marker::PhantomData; use std::mem; use std::thread; use std::process::{Command, Stdio}; -use super::util::{now, Encoder}; +use super::util::{Encoder, TimeSource}; use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr}; @@ -33,34 +34,36 @@ fn sha512(data: &[u8]) -> Vec { digest::digest(&digest::SHA512, data).as_ref().iter().map(|b| *b).collect() } -fn now_hour_16() -> u16 { - ((now() / 3600) & 0xffff) as u16 -} - struct FutureResult { has_result: AtomicBool, result: Mutex } #[derive(Clone)] -pub struct BeaconSerializer { +pub struct BeaconSerializer { magic: Vec, shared_key: Vec, future_peers: Arc>>, + _dummy_ts: PhantomData } -impl BeaconSerializer { +impl BeaconSerializer { pub fn new(magic: &[u8], shared_key: &[u8]) -> Self { - BeaconSerializer { + Self { magic: magic.to_owned(), shared_key: shared_key.to_owned(), future_peers: Arc::new(FutureResult { has_result: AtomicBool::new(false), result: Mutex::new(Vec::new()) - }) + }), + _dummy_ts: PhantomData } } + fn now_hour_16() -> u16 { + ((TS::now() / 3600) & 0xffff) as u16 + } + fn get_keystream(&self, type_: u8, seed: u8, iter: u8) -> Vec { let mut data = Vec::new(); data.extend_from_slice(&[type_, seed, iter]); @@ -110,10 +113,10 @@ impl BeaconSerializer { seed == sha512(data as &[u8])[0] } - fn peerlist_encode(&self, peers: &[SocketAddr], now_hour: u16) -> String { + fn peerlist_encode(&self, peers: &[SocketAddr]) -> String { let mut data = Vec::new(); // Add timestamp - data.extend_from_slice(&now_hour.to_be_bytes()); + data.extend_from_slice(&Self::now_hour_16().to_be_bytes()); // Split addresses into v4 and v6 let mut v4addrs = Vec::new(); let mut v6addrs = Vec::new(); @@ -151,7 +154,7 @@ impl BeaconSerializer { base_62::encode(&data) } - fn peerlist_decode(&self, data: &str, ttl_hours: Option, now_hour: u16) -> Vec { + fn peerlist_decode(&self, data: &str, ttl_hours: Option) -> Vec { let mut data = base_62::decode(data).expect("Invalid input"); let mut peers = Vec::new(); let mut pos = 0; @@ -163,7 +166,7 @@ impl BeaconSerializer { } let then = Wrapping(Encoder::read_u16(&data[pos..=pos+1])); if let Some(ttl) = ttl_hours { - let now = Wrapping(now_hour); + let now = Wrapping(Self::now_hour_16()); if now - then > Wrapping(ttl) && then - now > Wrapping(ttl) { return peers } @@ -199,12 +202,8 @@ impl BeaconSerializer { peers } - fn encode_internal(&self, peers: &[SocketAddr], now_hour: u16) -> String { - format!("{}{}{}", self.begin(), self.peerlist_encode(peers, now_hour), self.end()) - } - pub fn encode(&self, peers: &[SocketAddr]) -> String { - self.encode_internal(peers, now_hour_16()) + format!("{}{}{}", self.begin(), self.peerlist_encode(peers), self.end()) } pub fn write_to_file>(&self, peers: &[SocketAddr], path: P) -> Result<(), io::Error> { @@ -218,7 +217,7 @@ impl BeaconSerializer { pub fn write_to_cmd(&self, peers: &[SocketAddr], cmd: &str) -> Result<(), io::Error> { let begin = self.begin(); - let data = self.peerlist_encode(peers, now_hour_16()); + let data = self.peerlist_encode(peers); let end = self.end(); let beacon = format!("{}{}{}", begin, data, end); debug!("Calling beacon command: {}", cmd); @@ -236,7 +235,7 @@ impl BeaconSerializer { Ok(()) } - fn decode_internal(&self, data: &str, ttl_hours: Option, now_hour: u16) -> Vec { + pub fn decode(&self, data: &str, ttl_hours: Option) -> Vec { let data = base_62_sanitize(data); let mut peers = Vec::new(); let begin = self.begin(); @@ -247,7 +246,7 @@ impl BeaconSerializer { let start_pos = pos + begin.len(); if let Some(found) = data[pos..].find(&end) { let end_pos = pos + found; - peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos], ttl_hours, now_hour)); + peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos], ttl_hours)); pos = start_pos } else { break @@ -256,10 +255,6 @@ impl BeaconSerializer { peers } - pub fn decode(&self, data: &str, ttl_hours: Option) -> Vec { - self.decode_internal(data, ttl_hours, now_hour_16()) - } - pub fn read_from_file>(&self, path: P, ttl_hours: Option) -> Result, io::Error> { let mut f = try!(File::open(&path)); let mut contents = String::new(); @@ -306,94 +301,112 @@ impl BeaconSerializer { #[cfg(test)] use std::str::FromStr; #[cfg(test)] use std::time::Duration; #[cfg(test)] use tempfile; +#[cfg(test)] use ::util::MockTimeSource; #[test] fn encode() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); - assert_eq!("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", ser.encode_internal(&peers, 2000)); + assert_eq!("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", ser.encode(&peers)); peers.push(SocketAddr::from_str("[::1]:5678").unwrap()); - assert_eq!("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", ser.encode_internal(&peers, 2000)); + assert_eq!("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", ser.encode(&peers)); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:54").unwrap()); - assert_eq!("juWwKIgSqTammVFRNoIVzLPO0BEO55LN", ser.encode_internal(&peers, 2000)); + assert_eq!("juWwKIgSqTammVFRNoIVzLPO0BEO55LN", ser.encode(&peers)); } #[test] fn decode() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); - assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None, 2000))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None))); peers.push(SocketAddr::from_str("[::1]:5678").unwrap()); - assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", None, 2000))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", None))); } #[test] fn decode_split() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); - assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("juWwK-hj.VT:Yj bw\tJj\ntY(AZ)lM[fE]j7üIDäO55LN", None, 2000))); - assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("j -, \nuW--wKhjVTYjbwJjtYAZlMfEj7IDO(5}5ÖÄÜ\nLN", None, 2000))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwK-hj.VT:Yj bw\tJj\ntY(AZ)lM[fE]j7üIDäO55LN", None))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("j -, \nuW--wKhjVTYjbwJjtYAZlMfEj7IDO(5}5ÖÄÜ\nLN", None))); } #[test] fn decode_offset() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); - assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("Hello World: juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN! End of the World", None, 2000))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("Hello World: juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN! End of the World", None))); } #[test] fn decode_multiple() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); - assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("juWwKkBEVBp9SsDiN3BO55LN juWwKtGGPQz1gXIBd68O55LN", None, 2000))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKkBEVBp9SsDiN3BO55LN juWwKtGGPQz1gXIBd68O55LN", None))); } #[test] fn decode_ttl() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None, 2000).len()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None, 2100).len()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None, 2005).len()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None, 1995).len()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24), 2000).len()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24), 1995).len()); - assert_eq!(2, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24), 2005).len()); - assert_eq!(0, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24), 2100).len()); - assert_eq!(0, ser.decode_internal("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24), 1900).len()); + MockTimeSource::set_time(2000*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len()); + MockTimeSource::set_time(2100*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len()); + MockTimeSource::set_time(2005*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len()); + MockTimeSource::set_time(1995*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len()); + MockTimeSource::set_time(2000*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len()); + MockTimeSource::set_time(1995*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len()); + MockTimeSource::set_time(2005*3600); + assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len()); + MockTimeSource::set_time(2100*3600); + assert_eq!(0, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len()); + MockTimeSource::set_time(1900*3600); + assert_eq!(0, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len()); } #[test] fn decode_invalid() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); - assert_eq!(0, ser.decode_internal("", None, 2000).len()); - assert_eq!(0, ser.decode_internal("juWwKO55LN", None, 2000).len()); - assert_eq!(0, ser.decode_internal("juWwK--", None, 2000).len()); - assert_eq!(0, ser.decode_internal("--O55LN", None, 2000).len()); - assert_eq!(0, ser.decode_internal("juWwKhjVTYjbwJjtYAZXMfEj7IDO55LN", None, 2000).len()); - assert_eq!(2, ser.decode_internal("SGrivjuWwKhjVTYjbwJjtYAZlMfEj7IDO55LNjuWwK", None, 2000).len()); - assert_eq!(2, ser.decode_internal("juWwKjuWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None, 2000).len()); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); + assert_eq!(0, ser.decode("", None).len()); + assert_eq!(0, ser.decode("juWwKO55LN", None).len()); + assert_eq!(0, ser.decode("juWwK--", None).len()); + assert_eq!(0, ser.decode("--O55LN", None).len()); + assert_eq!(0, ser.decode("juWwKhjVTYjbwJjtYAZXMfEj7IDO55LN", None).len()); + assert_eq!(2, ser.decode("SGrivjuWwKhjVTYjbwJjtYAZlMfEj7IDO55LNjuWwK", None).len()); + assert_eq!(2, ser.decode("juWwKjuWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len()); } #[test] fn encode_decode() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); @@ -404,7 +417,8 @@ fn encode_decode() { #[test] fn encode_decode_file() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); @@ -417,7 +431,8 @@ fn encode_decode_file() { #[test] fn encode_decode_cmd() { - let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + MockTimeSource::set_time(2000*3600); + let ser = BeaconSerializer::::new(b"vpnc", b"mysecretkey"); let mut peers = Vec::new(); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); diff --git a/src/cloud.rs b/src/cloud.rs index 965d78e..004fcc8 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -21,7 +21,7 @@ use super::device::Device; use super::udpmessage::{encode, decode, Message}; use super::crypto::Crypto; use super::port_forwarding::PortForwarding; -use super::util::{now, Time, Duration, resolve, CtrlC}; +use super::util::{TimeSource, Time, Duration, resolve, CtrlC}; use super::poll::{WaitImpl, WaitResult}; use super::traffic::TrafficStats; use super::beacon::BeaconSerializer; @@ -40,25 +40,27 @@ struct PeerData { alt_addrs: Vec, } -struct PeerList { +struct PeerList { timeout: Duration, peers: HashMap, nodes: HashMap, - addresses: HashMap + addresses: HashMap, + _dummy_ts: PhantomData } -impl PeerList { - fn new(timeout: Duration) -> PeerList { +impl PeerList { + fn new(timeout: Duration) -> PeerList { PeerList{ peers: HashMap::default(), timeout, nodes: HashMap::default(), - addresses: HashMap::default() + addresses: HashMap::default(), + _dummy_ts: PhantomData } } fn timeout(&mut self) -> Vec { - let now = now(); + let now = TS::now(); let mut del: Vec = Vec::new(); for (&addr, ref data) in &self.peers { if data.timeout < now { @@ -104,7 +106,7 @@ impl PeerList { if self.nodes.insert(node_id, addr).is_none() { info!("New peer: {}", addr); self.peers.insert(addr, PeerData { - timeout: now() + Time::from(self.timeout), + timeout: TS::now() + Time::from(self.timeout), node_id, alt_addrs: vec![] }); @@ -115,7 +117,7 @@ impl PeerList { #[inline] fn refresh(&mut self, addr: &SocketAddr) { if let Some(ref mut data) = self.peers.get_mut(addr) { - data.timeout = now()+Time::from(self.timeout); + data.timeout = TS::now()+Time::from(self.timeout); } } @@ -180,8 +182,9 @@ impl PeerList { #[inline] fn write_out(&self, out: &mut W) -> Result<(), io::Error> { try!(writeln!(out, "Peers:")); + let now = TS::now(); for (addr, data) in &self.peers { - try!(writeln!(out, " - {} (ttl: {} s)", addr, data.timeout-now())); + try!(writeln!(out, " - {} (ttl: {} s)", addr, data.timeout-now)); } Ok(()) } @@ -198,11 +201,11 @@ pub struct ReconnectEntry { } -pub struct GenericCloud { +pub struct GenericCloud { config: Config, magic: HeaderMagic, node_id: NodeId, - peers: PeerList, + peers: PeerList, addresses: Vec, learning: bool, broadcast: bool, @@ -221,11 +224,12 @@ pub struct GenericCloud { next_beacon: Time, port_forwarding: Option, traffic: TrafficStats, - beacon_serializer: BeaconSerializer, + beacon_serializer: BeaconSerializer, _dummy_p: PhantomData

, + _dummy_ts: PhantomData } -impl GenericCloud { +impl GenericCloud { pub fn new(config: &Config, device: Device, table: T, learning: bool, broadcast: bool, addresses: Vec, crypto: Crypto, port_forwarding: Option @@ -238,6 +242,7 @@ impl GenericCloud { Ok(socket) => socket, Err(err) => fail!("Failed to open ipv6 address ::{}: {}", config.port, err) }; + let now = TS::now(); let mut res = GenericCloud{ magic: config.get_magic(), node_id: random(), @@ -251,18 +256,19 @@ impl GenericCloud { socket4, socket6, device, - next_peerlist: now(), + next_peerlist: now, update_freq: config.get_keepalive(), buffer_out: [0; 64*1024], - next_housekeep: now(), - next_stats_out: now() + STATS_INTERVAL, - next_beacon: now(), + next_housekeep: now, + next_stats_out: now + STATS_INTERVAL, + next_beacon: now, port_forwarding, traffic: TrafficStats::default(), beacon_serializer: BeaconSerializer::new(&config.get_magic(), crypto.get_key()), crypto, config: config.clone(), _dummy_p: PhantomData, + _dummy_ts: PhantomData }; res.initialize(); return res @@ -344,13 +350,14 @@ impl GenericCloud { /// This method adds a peer to the list of nodes to reconnect to. A periodic task will try to /// connect to the peer if it is not already connected. pub fn add_reconnect_peer(&mut self, add: String) { + let now = TS::now(); self.reconnect_peers.push(ReconnectEntry { address: add, tries: 0, timeout: 1, resolved: vec![], - next_resolve: now(), - next: now() + next_resolve: now, + next: now }) } @@ -431,7 +438,7 @@ impl GenericCloud { pfw.check_extend(); } // Periodically send peer list to peers - let now = now(); + let now = TS::now(); if self.next_peerlist <= now { debug!("Send peer list to all peers"); let mut peer_num = self.peers.len(); @@ -760,7 +767,7 @@ impl GenericCloud { WaitResult::SocketV6 => self.handle_socket_v6_event(&mut buffer), WaitResult::Device => self.handle_device_event(&mut buffer) } - if self.next_housekeep < now() { + if self.next_housekeep < TS::now() { poll_error = false; if ctrlc.was_pressed() { break @@ -768,7 +775,7 @@ impl GenericCloud { if let Err(e) = self.housekeep() { error!("Error: {}", e) } - self.next_housekeep = now() + 1 + self.next_housekeep = TS::now() + 1 } } info!("Shutting down..."); diff --git a/src/ethernet.rs b/src/ethernet.rs index 9845190..809c01e 100644 --- a/src/ethernet.rs +++ b/src/ethernet.rs @@ -7,11 +7,12 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::io::{self, Write}; +use std::marker::PhantomData; use fnv::FnvHasher; use super::types::{Error, Table, Protocol, Address}; -use super::util::{now, Time, Duration}; +use super::util::{TimeSource, Time, Duration, MockTimeSource}; /// An ethernet frame dissector /// @@ -68,26 +69,27 @@ type Hash = BuildHasherDefault; /// /// This table is a simple hash map between an address and the destination peer. It learns /// addresses as they are seen and forgets them after some time. -pub struct SwitchTable { +pub struct SwitchTable { /// The table storing the actual mapping table: HashMap, /// Timeout period for forgetting learnt addresses timeout: Duration, // Timeout period for not overwriting learnt addresses protection_period: Duration, + _dummy_ts: PhantomData } -impl SwitchTable { +impl SwitchTable { /// Creates a new switch table pub fn new(timeout: Duration, protection_period: Duration) -> Self { - SwitchTable{table: HashMap::default(), timeout, protection_period} + Self{table: HashMap::default(), timeout, protection_period, _dummy_ts: PhantomData} } } -impl Table for SwitchTable { +impl Table for SwitchTable { /// Forget addresses that have not been seen for the configured timeout fn housekeep(&mut self) { - let now = now(); + let now = TS::now(); let mut del: Vec

= Vec::new(); for (key, val) in &self.table { if val.timeout < now { @@ -102,7 +104,7 @@ impl Table for SwitchTable { /// Write out the table fn write_out(&self, out: &mut W) -> Result<(), io::Error> { - let now = now(); + let now = TS::now(); try!(writeln!(out, "Switch table:")); for (addr, val) in &self.table { try!(writeln!(out, " - {} => {} (ttl: {} s)", addr, val.address, val.timeout - now)); @@ -113,7 +115,7 @@ impl Table for SwitchTable { /// Learns the given address, inserting it in the hash map #[inline] fn learn(&mut self, key: Address, _prefix_len: Option, addr: SocketAddr) { - let deadline = now() + Time::from(self.timeout); + let deadline = TS::now() + Time::from(self.timeout); match self.table.entry(key) { Entry::Vacant(entry) => { entry.insert(SwitchTableValue{address: addr, timeout: deadline}); @@ -163,7 +165,6 @@ impl Table for SwitchTable { #[cfg(test)] use std::str::FromStr; #[cfg(test)] use std::net::ToSocketAddrs; -#[cfg(test)] use std::thread; #[test] fn decode_frame_without_vlan() { @@ -192,17 +193,19 @@ fn decode_invalid_frame() { #[test] fn switch() { - let mut table = SwitchTable::new(10, 1); + MockTimeSource::set_time(1000); + let mut table = SwitchTable::::new(10, 1); let addr = Address::from_str("12:34:56:78:90:ab").unwrap(); let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap(); let peer2 = "1.2.3.5:7890".to_socket_addrs().unwrap().next().unwrap(); assert!(table.lookup(&addr).is_none()); + MockTimeSource::set_time(1000); table.learn(addr.clone(), None, peer.clone()); assert_eq!(table.lookup(&addr), Some(peer)); - // Do not override within 1 seconds + MockTimeSource::set_time(1000); table.learn(addr.clone(), None, peer2.clone()); assert_eq!(table.lookup(&addr), Some(peer)); - thread::sleep(std::time::Duration::from_secs(1)); + MockTimeSource::set_time(1010); table.learn(addr.clone(), None, peer2.clone()); assert_eq!(table.lookup(&addr), Some(peer2)); } diff --git a/src/main.rs b/src/main.rs index 96a9566..3208a38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,7 @@ use std::process::Command; use std::fs::File; use std::path::Path; use std::io::{self, Write}; +use std::net::UdpSocket; use device::{Device, Type}; use ethernet::SwitchTable; @@ -56,9 +57,8 @@ use types::{Mode, Range, Protocol, HeaderMagic, Error}; use cloud::GenericCloud; use crypto::{Crypto, CryptoMethod}; use port_forwarding::PortForwarding; -use util::Duration; +use util::{Duration, SystemTimeSource}; use config::Config; -use std::net::UdpSocket; const VERSION: u8 = 1; @@ -156,25 +156,25 @@ fn run_script(script: &str, ifname: &str) { } enum AnyTable { - Switch(SwitchTable), + Switch(SwitchTable), Routing(RoutingTable) } enum AnyCloud { - Switch(GenericCloud), - Routing(GenericCloud) + Switch(GenericCloud, UdpSocket, SystemTimeSource>), + Routing(GenericCloud) } impl AnyCloud

{ - #[allow(unknown_lints,clippy::too_many_arguments)] + #[allow(unknown_lints, clippy::too_many_arguments)] fn new(config: &Config, device: Device, table: AnyTable, learning: bool, broadcast: bool, addresses: Vec, crypto: Crypto, port_forwarding: Option) -> Self { match table { - AnyTable::Switch(t) => AnyCloud::Switch(GenericCloud::::new( + AnyTable::Switch(t) => AnyCloud::Switch(GenericCloud::, UdpSocket, SystemTimeSource>::new( config, device,t, learning, broadcast, addresses, crypto, port_forwarding )), - AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::::new( + AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::::new( config, device,t, learning, broadcast, addresses, crypto, port_forwarding )) } diff --git a/src/port_forwarding.rs b/src/port_forwarding.rs index 2c6834c..f6dfc73 100644 --- a/src/port_forwarding.rs +++ b/src/port_forwarding.rs @@ -7,7 +7,7 @@ use std::io; use igd::*; -use super::util::{Time, now}; +use super::util::{SystemTimeSource, Time, TimeSource}; const LEASE_TIME: u32 = 300; const DESCRIPTION: &str = "VpnCloud"; @@ -90,7 +90,7 @@ impl PortForwarding { }; info!("Port-forwarding: sucessfully activated port forward on {}, timeout: {}", external_addr, timeout); let next_extension = if timeout > 0 { - Some(now() + Time::from(timeout) - 60) + Some(SystemTimeSource::now() + Time::from(timeout) - 60) } else { None }; @@ -104,7 +104,7 @@ impl PortForwarding { pub fn check_extend(&mut self) { if let Some(deadline) = self.next_extension { - if deadline > now() { + if deadline > SystemTimeSource::now() { return } } else { @@ -114,7 +114,7 @@ impl PortForwarding { Ok(()) => debug!("Port-forwarding: extended port forwarding"), Err(err) => error!("Port-forwarding: failed to extend port forwarding: {}", err) }; - self.next_extension = Some(now() + Time::from(LEASE_TIME) - 60); + self.next_extension = Some(SystemTimeSource::now() + Time::from(LEASE_TIME) - 60); } fn deactivate(&self) { diff --git a/src/util.rs b/src/util.rs index 5267aab..553f7f3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,6 +4,7 @@ use std::net::{SocketAddr, ToSocketAddrs}; use std::fmt; +use std::sync::atomic::{AtomicIsize, Ordering}; use super::types::Error; @@ -20,19 +21,6 @@ use std::time::Instant; pub type Duration = u32; pub type Time = i64; -#[inline] -#[cfg(target_os = "linux")] -pub fn now() -> Time { - let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - unsafe { libc::clock_gettime(6, &mut tv); } - tv.tv_sec as Time -} - -#[inline] -#[cfg(not(target_os = "linux"))] -pub fn now() -> Time { - time::get_time().sec -} const HEX_CHARS: &[u8] = b"0123456789abcdef"; @@ -188,4 +176,45 @@ impl CtrlC { pub fn was_pressed(&self) -> bool { self.trap.wait(self.dummy_time).is_some() } +} + + +pub trait TimeSource: Sync + Copy + Send + 'static { + fn now() -> Time; +} + +#[derive(Clone, Copy)] +pub struct SystemTimeSource; + +impl TimeSource for SystemTimeSource { + #[cfg(target_os = "linux")] + fn now() -> Time { + let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + unsafe { libc::clock_gettime(6, &mut tv); } + tv.tv_sec as Time + } + + #[cfg(not(target_os = "linux"))] + fn now() -> Time { + time::get_time().sec + } +} + +thread_local! { + static MOCK_TIME: AtomicIsize = AtomicIsize::new(0); +} + +#[derive(Clone, Copy)] +pub struct MockTimeSource; + +impl MockTimeSource { + pub fn set_time(time: Time) { + MOCK_TIME.with(|t| t.store(time as isize, Ordering::SeqCst)) + } +} + +impl TimeSource for MockTimeSource { + fn now() -> Time { + MOCK_TIME.with(|t| t.load(Ordering::SeqCst) as Time) + } } \ No newline at end of file