Some fixes/improvements

This commit is contained in:
Dennis Schwerdel 2020-11-03 18:49:35 +01:00
parent 35bdfafabf
commit 150f219e04
10 changed files with 104 additions and 78 deletions

View File

@ -7,10 +7,13 @@ This project follows [semantic versioning](http://semver.org).
- [changed] Changed documentation - [changed] Changed documentation
- [changed] Updated dependencies - [changed] Updated dependencies
- [changed] Retrying connections for 120 secs - [changed] Retrying connections for 120 secs
- [changed] Resetting own addresses periodically
- [changed] Using smallvec everywhere
- [fixed] Fixed corner case with lost init message - [fixed] Fixed corner case with lost init message
- [fixed] Do not reconnect to timed out pending connections - [fixed] Do not reconnect to timed out pending connections
- [fixed] Most specific claims beat less specific claims - [fixed] Most specific claims beat less specific claims
- [fixed] Count all invalid protocol traffic - [fixed] Count all invalid protocol traffic
- [fixed] Fixed compile with musl
### v2.0.0 (2020-10-30) ### v2.0.0 (2020-10-30)

8
Cargo.lock generated
View File

@ -96,9 +96,9 @@ dependencies = [
[[package]] [[package]]
name = "const_fn" name = "const_fn"
version = "0.4.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
[[package]] [[package]]
name = "daemonize" name = "daemonize"
@ -279,9 +279,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.9" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]] [[package]]
name = "privdrop" name = "privdrop"

View File

@ -21,6 +21,7 @@ use std::{
}; };
use super::util::{from_base62, to_base62, Encoder, TimeSource}; use super::util::{from_base62, to_base62, Encoder, TimeSource};
use smallvec::SmallVec;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
@ -33,8 +34,8 @@ fn base_62_sanitize(data: &str) -> String {
data.chars().filter(|c| c.is_ascii_alphanumeric()).collect() data.chars().filter(|c| c.is_ascii_alphanumeric()).collect()
} }
fn sha512(data: &[u8]) -> Vec<u8> { fn sha512(data: &[u8]) -> SmallVec<[u8; 64]> {
digest::digest(&digest::SHA512, data).as_ref().to_vec() digest::digest(&digest::SHA512, data).as_ref().into()
} }
struct FutureResult<T> { struct FutureResult<T> {
@ -62,8 +63,8 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
((TS::now() / 3600) & 0xffff) as u16 ((TS::now() / 3600) & 0xffff) as u16
} }
fn get_keystream(&self, type_: u8, seed: u8, iter: u8) -> Vec<u8> { fn get_keystream(&self, type_: u8, seed: u8, iter: u8) -> SmallVec<[u8; 64]> {
let mut data = Vec::new(); let mut data = SmallVec::<[u8; 128]>::new();
data.extend_from_slice(&[type_, seed, iter]); data.extend_from_slice(&[type_, seed, iter]);
data.extend_from_slice(&self.shared_key); data.extend_from_slice(&self.shared_key);
sha512(&data) sha512(&data)
@ -92,6 +93,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
fn end(&self) -> String { fn end(&self) -> String {
to_base62(&self.get_keystream(TYPE_END, 0, 0))[0..5].to_string() to_base62(&self.get_keystream(TYPE_END, 0, 0))[0..5].to_string()
} }
fn encrypt_data(&self, data: &mut Vec<u8>) { fn encrypt_data(&self, data: &mut Vec<u8>) {
// Note: the 1 byte seed is only meant to protect from random changes, // Note: the 1 byte seed is only meant to protect from random changes,
// not malicious ones. For full protection, at least 8 bytes (~12 // not malicious ones. For full protection, at least 8 bytes (~12
@ -115,8 +117,8 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
// Add timestamp // Add timestamp
data.extend_from_slice(&Self::now_hour_16().to_be_bytes()); data.extend_from_slice(&Self::now_hour_16().to_be_bytes());
// Split addresses into v4 and v6 // Split addresses into v4 and v6
let mut v4addrs = Vec::new(); let mut v4addrs = SmallVec::<[SocketAddrV4; 256]>::new();
let mut v6addrs = Vec::new(); let mut v6addrs = SmallVec::<[SocketAddrV6; 256]>::new();
for p in peers { for p in peers {
match *p { match *p {
SocketAddr::V4(addr) => v4addrs.push(addr), SocketAddr::V4(addr) => v4addrs.push(addr),

View File

@ -43,6 +43,7 @@ pub type Hash = BuildHasherDefault<FnvHasher>;
const MAX_RECONNECT_INTERVAL: u16 = 3600; const MAX_RECONNECT_INTERVAL: u16 = 3600;
const RESOLVE_INTERVAL: Time = 300; const RESOLVE_INTERVAL: Time = 300;
pub const STATS_INTERVAL: Time = 60; pub const STATS_INTERVAL: Time = 60;
const OWN_ADDRESS_RESET_INTERVAL: Time = 300;
const SPACE_BEFORE: usize = 100; const SPACE_BEFORE: usize = 100;
struct PeerData { struct PeerData {
@ -56,7 +57,7 @@ struct PeerData {
#[derive(Clone)] #[derive(Clone)]
pub struct ReconnectEntry { pub struct ReconnectEntry {
address: Option<(String, Time)>, address: Option<(String, Time)>,
resolved: Vec<SocketAddr>, resolved: SmallVec<[SocketAddr; 3]>,
tries: u16, tries: u16,
timeout: u16, timeout: u16,
next: Time, next: Time,
@ -70,8 +71,8 @@ pub struct GenericCloud<D: Device, P: Protocol, S: Socket, TS: TimeSource> {
learning: bool, learning: bool,
broadcast: bool, broadcast: bool,
peers: HashMap<SocketAddr, PeerData, Hash>, peers: HashMap<SocketAddr, PeerData, Hash>,
reconnect_peers: Vec<ReconnectEntry>, reconnect_peers: SmallVec<[ReconnectEntry; 3]>,
own_addresses: Vec<SocketAddr>, own_addresses: SmallVec<[SocketAddr; 3]>,
pending_inits: HashMap<SocketAddr, PeerCrypto<NodeInfo>, Hash>, pending_inits: HashMap<SocketAddr, PeerCrypto<NodeInfo>, Hash>,
table: ClaimTable<TS>, table: ClaimTable<TS>,
socket: S, socket: S,
@ -86,6 +87,7 @@ pub struct GenericCloud<D: Device, P: Protocol, S: Socket, TS: TimeSource> {
next_housekeep: Time, next_housekeep: Time,
next_stats_out: Time, next_stats_out: Time,
next_beacon: Time, next_beacon: Time,
next_own_address_reset: Time,
port_forwarding: Option<PortForwarding>, port_forwarding: Option<PortForwarding>,
traffic: TrafficStats, traffic: TrafficStats,
beacon_serializer: BeaconSerializer<TS>, beacon_serializer: BeaconSerializer<TS>,
@ -137,8 +139,8 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
learning, learning,
broadcast, broadcast,
pending_inits: HashMap::default(), pending_inits: HashMap::default(),
reconnect_peers: Vec::new(), reconnect_peers: SmallVec::new(),
own_addresses: Vec::new(), own_addresses: SmallVec::new(),
peer_timeout_publish: config.peer_timeout as u16, peer_timeout_publish: config.peer_timeout as u16,
table: ClaimTable::new(config.switch_timeout as Duration, config.peer_timeout as Duration), table: ClaimTable::new(config.switch_timeout as Duration, config.peer_timeout as Duration),
socket, socket,
@ -150,6 +152,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
next_housekeep: now, next_housekeep: now,
next_stats_out: now + STATS_INTERVAL, next_stats_out: now + STATS_INTERVAL,
next_beacon: now, next_beacon: now,
next_own_address_reset: now + OWN_ADDRESS_RESET_INTERVAL,
port_forwarding, port_forwarding,
traffic: TrafficStats::default(), traffic: TrafficStats::default(),
beacon_serializer: BeaconSerializer::new(beacon_key), beacon_serializer: BeaconSerializer::new(beacon_key),
@ -214,16 +217,14 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
self.send_to(addr, msg) self.send_to(addr, msg)
} }
/// Returns the self-perceived addresses (IPv4 and IPv6) of this node pub fn reset_own_addresses(&mut self) -> io::Result<()> {
/// self.own_addresses.clear();
/// Note that those addresses could be private addresses that are not reachable by other nodes, self.own_addresses.push(self.socket.address().map(mapped_addr)?);
/// or only some other nodes inside the same network. if let Some(ref pfw) = self.port_forwarding {
/// self.own_addresses.push(pfw.get_internal_ip().into());
/// # Errors self.own_addresses.push(pfw.get_external_ip().into());
/// Returns an IOError if the underlying system call fails }
#[allow(dead_code)] Ok(())
pub fn address(&self) -> io::Result<SocketAddr> {
Ok(self.socket.address().map(mapped_addr)?)
} }
/// Returns the number of peers /// Returns the number of peers
@ -246,7 +247,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
Ok(addrs) => addrs, Ok(addrs) => addrs,
Err(err) => { Err(err) => {
warn!("Failed to resolve {}: {:?}", add, err); warn!("Failed to resolve {}: {:?}", add, err);
vec![] smallvec![]
} }
}; };
self.reconnect_peers.push(ReconnectEntry { self.reconnect_peers.push(ReconnectEntry {
@ -268,7 +269,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
/// # Errors /// # Errors
/// This method returns `Error::NameError` if the address is a name that fails to resolve. /// This method returns `Error::NameError` if the address is a name that fails to resolve.
pub fn connect<Addr: ToSocketAddrs + fmt::Debug + Clone>(&mut self, addr: Addr) -> Result<(), Error> { pub fn connect<Addr: ToSocketAddrs + fmt::Debug + Clone>(&mut self, addr: Addr) -> Result<(), Error> {
let addrs = resolve(&addr)?.into_iter().map(mapped_addr).collect::<Vec<_>>(); let addrs = resolve(&addr)?.into_iter().map(mapped_addr).collect::<SmallVec<[SocketAddr; 3]>>();
for addr in &addrs { for addr in &addrs {
if self.own_addresses.contains(addr) if self.own_addresses.contains(addr)
|| self.peers.contains_key(addr) || self.peers.contains_key(addr)
@ -305,7 +306,10 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
fn connect_sock(&mut self, addr: SocketAddr) -> Result<(), Error> { fn connect_sock(&mut self, addr: SocketAddr) -> Result<(), Error> {
let addr = mapped_addr(addr); let addr = mapped_addr(addr);
if self.peers.contains_key(&addr) || self.own_addresses.contains(&addr) { if self.peers.contains_key(&addr)
|| self.own_addresses.contains(&addr)
|| self.pending_inits.contains_key(&addr)
{
return Ok(()) return Ok(())
} }
debug!("Connecting to {:?}", addr); debug!("Connecting to {:?}", addr);
@ -320,7 +324,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
fn crypto_housekeep(&mut self) -> Result<(), Error> { fn crypto_housekeep(&mut self) -> Result<(), Error> {
let mut msg = MsgBuffer::new(SPACE_BEFORE); let mut msg = MsgBuffer::new(SPACE_BEFORE);
let mut del: SmallVec<[SocketAddr; 4]> = smallvec![]; let mut del: SmallVec<[SocketAddr; 4]> = smallvec![];
for addr in self.pending_inits.keys().copied().collect::<Vec<_>>() { for addr in self.pending_inits.keys().copied().collect::<SmallVec<[SocketAddr; 4]>>() {
msg.clear(); msg.clear();
match self.pending_inits.get_mut(&addr).unwrap().every_second(&mut msg) { match self.pending_inits.get_mut(&addr).unwrap().every_second(&mut msg) {
Err(_) => del.push(addr), Err(_) => del.push(addr),
@ -329,7 +333,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
Ok(_) => unreachable!() Ok(_) => unreachable!()
} }
} }
for addr in self.peers.keys().copied().collect::<Vec<_>>() { for addr in self.peers.keys().copied().collect::<SmallVec<[SocketAddr; 16]>>() {
msg.clear(); msg.clear();
match self.peers.get_mut(&addr).unwrap().crypto.every_second(&mut msg) { match self.peers.get_mut(&addr).unwrap().crypto.every_second(&mut msg) {
Err(_) => del.push(addr), Err(_) => del.push(addr),
@ -347,39 +351,8 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
Ok(()) Ok(())
} }
fn housekeep(&mut self) -> Result<(), Error> { fn reconnect_to_peers(&mut self) -> Result<(), Error> {
let now = TS::now(); let now = TS::now();
let mut buffer = MsgBuffer::new(SPACE_BEFORE);
let mut del: Vec<SocketAddr> = Vec::new();
for (&addr, ref data) in &self.peers {
if data.timeout < now {
del.push(addr);
}
}
for addr in del {
info!("Forgot peer {} due to timeout", addr_nice(addr));
self.peers.remove(&addr);
self.table.remove_claims(addr);
self.connect_sock(addr)?; // Try to reconnect
}
self.table.housekeep();
self.crypto_housekeep()?;
// Periodically extend the port-forwarding
if let Some(ref mut pfw) = self.port_forwarding {
pfw.check_extend();
}
// Periodically send peer list to peers
let now = TS::now();
if self.next_peers <= now {
debug!("Send peer list to all peers");
let info = self.create_node_info();
info.encode(&mut buffer);
self.broadcast_msg(MESSAGE_TYPE_NODE_INFO, &mut buffer)?;
// Reschedule for next update
let min_peer_timeout = self.peers.iter().map(|p| p.1.peer_timeout).min().unwrap_or(DEFAULT_PEER_TIMEOUT);
let interval = min(self.update_freq as u16, max(min_peer_timeout / 2 - 60, 1));
self.next_peers = now + Time::from(interval);
}
// Connect to those reconnect_peers that are due // Connect to those reconnect_peers that are due
for entry in self.reconnect_peers.clone() { for entry in self.reconnect_peers.clone() {
if entry.next > now { if entry.next > now {
@ -424,6 +397,48 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
entry.next = now + Time::from(entry.timeout); entry.next = now + Time::from(entry.timeout);
} }
self.reconnect_peers.retain(|e| e.final_timeout.unwrap_or(now) >= now); self.reconnect_peers.retain(|e| e.final_timeout.unwrap_or(now) >= now);
Ok(())
}
fn housekeep(&mut self) -> Result<(), Error> {
let now = TS::now();
let mut buffer = MsgBuffer::new(SPACE_BEFORE);
let mut del: SmallVec<[SocketAddr; 3]> = SmallVec::new();
for (&addr, ref data) in &self.peers {
if data.timeout < now {
del.push(addr);
}
}
for addr in del {
info!("Forgot peer {} due to timeout", addr_nice(addr));
self.peers.remove(&addr);
self.table.remove_claims(addr);
self.connect_sock(addr)?; // Try to reconnect
}
self.table.housekeep();
self.crypto_housekeep()?;
// Periodically extend the port-forwarding
if let Some(ref mut pfw) = self.port_forwarding {
pfw.check_extend();
}
let now = TS::now();
// Periodically reset own peers
if self.next_own_address_reset <= now {
self.reset_own_addresses().map_err(|err| Error::SocketIo("Failed to get own addresses", err))?;
self.next_own_address_reset = now + OWN_ADDRESS_RESET_INTERVAL;
}
// Periodically send peer list to peers
if self.next_peers <= now {
debug!("Send peer list to all peers");
let info = self.create_node_info();
info.encode(&mut buffer);
self.broadcast_msg(MESSAGE_TYPE_NODE_INFO, &mut buffer)?;
// Reschedule for next update
let min_peer_timeout = self.peers.iter().map(|p| p.1.peer_timeout).min().unwrap_or(DEFAULT_PEER_TIMEOUT);
let interval = min(self.update_freq as u16, max(min_peer_timeout / 2 - 60, 1));
self.next_peers = now + Time::from(interval);
}
self.reconnect_to_peers()?;
if self.next_stats_out < now { if self.next_stats_out < now {
// Write out the statistics // Write out the statistics
self.write_out_stats().map_err(|err| Error::FileIo("Failed to write stats file", err))?; self.write_out_stats().map_err(|err| Error::FileIo("Failed to write stats file", err))?;
@ -448,7 +463,8 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
/// Stores the beacon /// Stores the beacon
fn store_beacon(&mut self) -> Result<(), Error> { fn store_beacon(&mut self) -> Result<(), Error> {
if let Some(ref path) = self.config.beacon_store { if let Some(ref path) = self.config.beacon_store {
let peers: Vec<_> = self.own_addresses.choose_multiple(&mut thread_rng(), 3).cloned().collect(); let peers: SmallVec<[SocketAddr; 3]> =
self.own_addresses.choose_multiple(&mut thread_rng(), 3).cloned().collect();
if let Some(path) = path.strip_prefix('|') { if let Some(path) = path.strip_prefix('|') {
self.beacon_serializer self.beacon_serializer
.write_to_cmd(&peers, path) .write_to_cmd(&peers, path)
@ -762,9 +778,8 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
} }
fn initialize(&mut self) { fn initialize(&mut self) {
match self.address() { if let Err(err) = self.reset_own_addresses() {
Err(err) => error!("Failed to obtain local addresses: {}", err), error!("Failed to obtain local addresses: {}", err)
Ok(addr) => self.own_addresses.push(addr)
} }
} }

View File

@ -229,7 +229,7 @@ impl InitMsg {
let pos = r.position() as usize; let pos = r.position() as usize;
let signature_len = r.read_u8().map_err(|_| Error::Parse("Init message too short"))? as usize; let signature_len = r.read_u8().map_err(|_| Error::Parse("Init message too short"))? as usize;
let mut signature = vec![0; signature_len]; let mut signature: SmallVec<[u8; 32]> = smallvec![0; signature_len];
r.read_exact(&mut signature).map_err(|_| Error::Parse("Init message too short"))?; r.read_exact(&mut signature).map_err(|_| Error::Parse("Init message too short"))?;
let signed_data = &r.into_inner()[0..pos]; let signed_data = &r.into_inner()[0..pos];

View File

@ -254,7 +254,7 @@ impl TunTapDevice {
+ crypto::EXTRA_LEN + crypto::TAG_LEN /* crypto overhead */ + crypto::EXTRA_LEN + crypto::TAG_LEN /* crypto overhead */
+ 1 /* message type header */ + 1 /* message type header */
+ match self.type_ { + match self.type_ {
Type::Tap => 12, /* inner ethernet header */ Type::Tap => 14, /* inner ethernet header */
Type::Tun | Type::Dummy => 0 Type::Tun | Type::Dummy => 0
} }
} }

View File

@ -40,9 +40,9 @@ pub struct NodeInfo {
} }
impl NodeInfo { impl NodeInfo {
const PART_NODEID: u8 = 4;
const PART_CLAIMS: u8 = 2; const PART_CLAIMS: u8 = 2;
const PART_END: u8 = 0; const PART_END: u8 = 0;
const PART_NODEID: u8 = 4;
const PART_PEERS: u8 = 1; const PART_PEERS: u8 = 1;
const PART_PEER_TIMEOUT: u8 = 3; const PART_PEER_TIMEOUT: u8 = 3;
@ -133,8 +133,8 @@ impl NodeInfo {
fn encode_peer_list_part<W: Write>(&self, mut out: W) -> Result<(), io::Error> { fn encode_peer_list_part<W: Write>(&self, mut out: W) -> Result<(), io::Error> {
for p in &self.peers { for p in &self.peers {
let mut addr_ipv4 = vec![]; let mut addr_ipv4: SmallVec<[SocketAddrV4; 16]> = smallvec![];
let mut addr_ipv6 = vec![]; let mut addr_ipv6: SmallVec<[SocketAddrV6; 16]> = smallvec![];
for a in &p.addrs { for a in &p.addrs {
match a { match a {
SocketAddr::V4(addr) => addr_ipv4.push(*addr), SocketAddr::V4(addr) => addr_ipv4.push(*addr),
@ -186,9 +186,7 @@ impl NodeInfo {
let len; let len;
{ {
let mut cursor = Cursor::new(buffer.buffer()); let mut cursor = Cursor::new(buffer.buffer());
Self::encode_part(&mut cursor, Self::PART_NODEID, |cursor| { Self::encode_part(&mut cursor, Self::PART_NODEID, |cursor| cursor.write_all(&self.node_id))?;
cursor.write_all(&self.node_id)
})?;
Self::encode_part(&mut cursor, Self::PART_PEERS, |cursor| self.encode_peer_list_part(cursor))?; Self::encode_part(&mut cursor, Self::PART_PEERS, |cursor| self.encode_peer_list_part(cursor))?;
Self::encode_part(&mut cursor, Self::PART_CLAIMS, |mut cursor| { Self::encode_part(&mut cursor, Self::PART_CLAIMS, |mut cursor| {
for c in &self.claims { for c in &self.claims {

View File

@ -103,7 +103,6 @@ mod internal {
} }
} }
} else { } else {
gateway.remove_port(PortMappingProtocol::UDP, port).ok();
match gateway.add_port(PortMappingProtocol::UDP, port, addr, LEASE_TIME, DESCRIPTION) { match gateway.add_port(PortMappingProtocol::UDP, port, addr, LEASE_TIME, DESCRIPTION) {
Ok(()) => Ok((port, LEASE_TIME)), Ok(()) => Ok((port, LEASE_TIME)),
Err(AddPortError::OnlyPermanentLeasesSupported) => { Err(AddPortError::OnlyPermanentLeasesSupported) => {
@ -150,6 +149,14 @@ mod internal {
Err(err) => debug!("Port-forwarding: failed to deactivate port forwarding: {}", err) Err(err) => debug!("Port-forwarding: failed to deactivate port forwarding: {}", err)
} }
} }
pub fn get_internal_ip(&self) -> SocketAddrV4 {
self.internal_addr
}
pub fn get_external_ip(&self) -> SocketAddrV4 {
self.external_addr
}
} }
impl Drop for PortForwarding { impl Drop for PortForwarding {

View File

@ -118,7 +118,7 @@ impl FromStr for Address {
} }
return Ok(Address { data: res, len: 16 }) return Ok(Address { data: res, len: 16 })
} }
let parts: Vec<&str> = text.split(':').collect(); let parts: SmallVec<[&str; 10]> = text.split(':').collect();
if parts.len() == 6 { if parts.len() == 6 {
let mut bytes = [0; 16]; let mut bytes = [0; 16];
for i in 0..6 { for i in 0..6 {

View File

@ -14,6 +14,7 @@ use crate::error::Error;
use signal::{trap::Trap, Signal}; use signal::{trap::Trap, Signal};
use std::time::Instant; use std::time::Instant;
use smallvec::SmallVec;
pub type Duration = u32; pub type Duration = u32;
@ -216,10 +217,10 @@ pub fn get_internal_ip() -> Ipv4Addr {
#[allow(unknown_lints, clippy::needless_pass_by_value)] #[allow(unknown_lints, clippy::needless_pass_by_value)]
pub fn resolve<Addr: ToSocketAddrs + fmt::Debug>(addr: Addr) -> Result<Vec<SocketAddr>, Error> { pub fn resolve<Addr: ToSocketAddrs + fmt::Debug>(addr: Addr) -> Result<SmallVec<[SocketAddr; 3]>, Error> {
let addrs = addr.to_socket_addrs().map_err(|_| Error::NameUnresolvable(format!("{:?}", addr)))?; let addrs = addr.to_socket_addrs().map_err(|_| Error::NameUnresolvable(format!("{:?}", addr)))?;
// Remove duplicates in addrs (why are there duplicates???) // Remove duplicates in addrs (why are there duplicates???)
let mut addrs = addrs.collect::<Vec<_>>(); let mut addrs = addrs.collect::<SmallVec<_>>();
// Try IPv4 first as it usually is faster // Try IPv4 first as it usually is faster
addrs.sort_by_key(|addr| { addrs.sort_by_key(|addr| {
match *addr { match *addr {