Skip to content

Commit ea2be1a

Browse files
authored
Merge pull request #101 from nymtech/feature/panic_improvements
Feature/panic improvements
2 parents 28d8fbe + 1b787c9 commit ea2be1a

File tree

24 files changed

+305
-130
lines changed

24 files changed

+305
-130
lines changed

common/addressing/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ pub fn encoded_bytes_from_socket_address(address: SocketAddr) -> [u8; 32] {
6262
address_bytes
6363
}
6464

65-
pub fn socket_address_from_encoded_bytes(b: [u8; 32]) -> SocketAddr {
66-
let address_type: AddressType = b[0].try_into().unwrap();
65+
pub fn socket_address_from_encoded_bytes(b: [u8; 32]) -> Result<SocketAddr, AddressTypeError> {
66+
let address_type: AddressType = b[0].try_into()?;
6767

6868
let port: u16 = u16::from_be_bytes([b[1], b[2]]);
6969

@@ -76,5 +76,5 @@ pub fn socket_address_from_encoded_bytes(b: [u8; 32]) -> SocketAddr {
7676
}
7777
};
7878

79-
SocketAddr::new(ip, port)
79+
Ok(SocketAddr::new(ip, port))
8080
}

common/clients/directory-client/src/presence.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::net::ToSocketAddrs;
99
use topology::coco;
1010
use topology::mix;
1111
use topology::provider;
12+
use topology::provider::Node;
1213
use topology::NymTopology;
1314

1415
// special version of 'PartialEq' that does not care about last_seen field (where applicable)
@@ -62,16 +63,18 @@ impl TryInto<topology::mix::Node> for MixNodePresence {
6263
type Error = io::Error;
6364

6465
fn try_into(self) -> Result<topology::mix::Node, Self::Error> {
65-
let resolved_hostname = self.host.to_socket_addrs()?.next();
66-
if resolved_hostname.is_none() {
67-
return Err(io::Error::new(
68-
io::ErrorKind::Other,
69-
"no valid socket address",
70-
));
71-
}
66+
let resolved_hostname = match self.host.to_socket_addrs()?.next() {
67+
None => {
68+
return Err(io::Error::new(
69+
io::ErrorKind::Other,
70+
"no valid socket address",
71+
));
72+
}
73+
Some(host) => host,
74+
};
7275

7376
Ok(topology::mix::Node {
74-
host: resolved_hostname.unwrap(),
77+
host: resolved_hostname,
7578
pub_key: self.pub_key,
7679
layer: self.layer,
7780
last_seen: self.last_seen,
@@ -129,11 +132,13 @@ impl PresenceEq for MixProviderPresence {
129132
}
130133
}
131134

132-
impl Into<topology::provider::Node> for MixProviderPresence {
133-
fn into(self) -> topology::provider::Node {
134-
topology::provider::Node {
135-
client_listener: self.client_listener.parse().unwrap(),
136-
mixnet_listener: self.mixnet_listener.parse().unwrap(),
135+
impl TryInto<topology::provider::Node> for MixProviderPresence {
136+
type Error = std::net::AddrParseError;
137+
138+
fn try_into(self) -> Result<Node, Self::Error> {
139+
Ok(topology::provider::Node {
140+
client_listener: self.client_listener.parse()?,
141+
mixnet_listener: self.mixnet_listener.parse()?,
137142
pub_key: self.pub_key,
138143
registered_clients: self
139144
.registered_clients
@@ -142,7 +147,7 @@ impl Into<topology::provider::Node> for MixProviderPresence {
142147
.collect(),
143148
last_seen: self.last_seen,
144149
version: self.version,
145-
}
150+
})
146151
}
147152
}
148153

@@ -345,7 +350,7 @@ impl NymTopology for Topology {
345350
fn providers(&self) -> Vec<provider::Node> {
346351
self.mix_provider_nodes
347352
.iter()
348-
.map(|x| x.clone().into())
353+
.filter_map(|x| x.clone().try_into().ok())
349354
.collect()
350355
}
351356

common/clients/mix-client/src/packet.rs

+44-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,49 @@
11
use addressing;
2+
use addressing::AddressTypeError;
23
use sphinx::route::{Destination, DestinationAddressBytes, SURBIdentifier};
34
use sphinx::SphinxPacket;
45
use std::net::SocketAddr;
5-
use topology::NymTopology;
6+
use topology::{NymTopology, NymTopologyError};
67

78
pub const LOOP_COVER_MESSAGE_PAYLOAD: &[u8] = b"The cake is a lie!";
89
pub const LOOP_COVER_MESSAGE_AVERAGE_DELAY: f64 = 2.0;
910

11+
#[derive(Debug)]
12+
pub enum SphinxPacketEncapsulationError {
13+
NoValidProvidersError,
14+
InvalidTopologyError,
15+
SphinxEncapsulationError(sphinx::header::SphinxUnwrapError),
16+
InvalidFirstMixAddress,
17+
}
18+
19+
impl From<topology::NymTopologyError> for SphinxPacketEncapsulationError {
20+
fn from(_: NymTopologyError) -> Self {
21+
use SphinxPacketEncapsulationError::*;
22+
InvalidTopologyError
23+
}
24+
}
25+
26+
// it is correct error we're converting from, it just has an unfortunate name
27+
// related issue: https://github.com/nymtech/sphinx/issues/40
28+
impl From<sphinx::header::SphinxUnwrapError> for SphinxPacketEncapsulationError {
29+
fn from(err: sphinx::header::SphinxUnwrapError) -> Self {
30+
use SphinxPacketEncapsulationError::*;
31+
SphinxEncapsulationError(err)
32+
}
33+
}
34+
35+
impl From<AddressTypeError> for SphinxPacketEncapsulationError {
36+
fn from(_: AddressTypeError) -> Self {
37+
use SphinxPacketEncapsulationError::*;
38+
InvalidFirstMixAddress
39+
}
40+
}
41+
1042
pub fn loop_cover_message<T: NymTopology>(
1143
our_address: DestinationAddressBytes,
1244
surb_id: SURBIdentifier,
1345
topology: &T,
14-
) -> (SocketAddr, SphinxPacket) {
46+
) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> {
1547
let destination = Destination::new(our_address, surb_id);
1648

1749
encapsulate_message(
@@ -27,19 +59,24 @@ pub fn encapsulate_message<T: NymTopology>(
2759
message: Vec<u8>,
2860
topology: &T,
2961
average_delay: f64,
30-
) -> (SocketAddr, SphinxPacket) {
62+
) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> {
3163
let mut providers = topology.providers();
64+
if providers.len() == 0 {
65+
return Err(SphinxPacketEncapsulationError::NoValidProvidersError);
66+
}
67+
// unwrap is fine here as we asserted there is at least single provider
3268
let provider = providers.pop().unwrap().into();
3369

34-
let route = topology.route_to(provider).unwrap();
70+
let route = topology.route_to(provider)?;
3571

3672
let delays = sphinx::header::delays::generate(route.len(), average_delay);
3773

3874
// build the packet
39-
let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays).unwrap();
75+
let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays)?;
4076

77+
// we know the mix route must be valid otherwise we would have already returned an error
4178
let first_node_address =
42-
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes());
79+
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes())?;
4380

44-
(first_node_address, packet)
81+
Ok((first_node_address, packet))
4582
}
+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use rand_distr::{Distribution, Exp};
22

33
pub fn sample(average_delay: f64) -> f64 {
4+
// this is our internal code used by our traffic streams
5+
// the error is only thrown if average delay is less than 0, which will never happen
6+
// so call to unwrap is perfectly safe here
47
let exp = Exp::new(1.0 / average_delay).unwrap();
58
exp.sample(&mut rand::thread_rng())
69
}

common/clients/provider-client/src/lib.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl ProviderClient {
6666
pub async fn send_request(&self, bytes: Vec<u8>) -> Result<Vec<u8>, ProviderClientError> {
6767
let mut socket = tokio::net::TcpStream::connect(self.provider_network_address).await?;
6868

69-
socket.set_keepalive(Some(Duration::from_secs(2))).unwrap();
69+
socket.set_keepalive(Some(Duration::from_secs(2)))?;
7070
socket.write_all(&bytes[..]).await?;
7171
if let Err(e) = socket.shutdown(Shutdown::Write) {
7272
warn!("failed to close write part of the socket; err = {:?}", e)
@@ -82,11 +82,14 @@ impl ProviderClient {
8282
}
8383

8484
pub async fn retrieve_messages(&self) -> Result<Vec<Vec<u8>>, ProviderClientError> {
85-
if self.auth_token.is_none() {
86-
return Err(ProviderClientError::EmptyAuthTokenError);
87-
}
88-
89-
let pull_request = PullRequest::new(self.our_address, self.auth_token.unwrap());
85+
let auth_token = match self.auth_token {
86+
Some(token) => token,
87+
None => {
88+
return Err(ProviderClientError::EmptyAuthTokenError);
89+
}
90+
};
91+
92+
let pull_request = PullRequest::new(self.our_address, auth_token);
9093
let bytes = pull_request.to_bytes();
9194

9295
let response = self.send_request(bytes).await?;

common/crypto/src/encryption/x25519.rs

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ impl MixnetEncryptionPrivateKey for PrivateKey {
5656
fn from_bytes(b: &[u8]) -> Self {
5757
let mut bytes = [0; 32];
5858
bytes.copy_from_slice(&b[..]);
59+
// due to trait restriction we have no choice but to panic if this fails
5960
let key = Scalar::from_canonical_bytes(bytes).unwrap();
6061
Self(key)
6162
}

common/healthcheck/src/path_check.rs

+27-7
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,23 @@ impl PathChecker {
153153
let mut provider_messages = Vec::new();
154154
for provider_client in self.provider_clients.values() {
155155
// if it was none all associated paths were already marked as unhealthy
156-
if provider_client.is_some() {
157-
let pc = provider_client.as_ref().unwrap();
158-
provider_messages.extend(self.resolve_pending_provider_checks(pc).await);
159-
}
156+
let pc = match provider_client {
157+
Some(pc) => pc,
158+
None => continue,
159+
};
160+
161+
provider_messages.extend(self.resolve_pending_provider_checks(pc).await);
160162
}
161163

162164
self.update_path_statuses(provider_messages);
163165
}
164166

165167
pub(crate) async fn send_test_packet(&mut self, path: &Vec<SphinxNode>, iteration: u8) {
168+
if path.len() == 0 {
169+
warn!("trying to send test packet through an empty path!");
170+
return;
171+
}
172+
166173
debug!("Checking path: {:?} ({})", path, iteration);
167174
let path_identifier = PathChecker::unique_path_key(path, iteration);
168175

@@ -171,7 +178,13 @@ impl PathChecker {
171178
// does provider exist?
172179
let provider_client = self
173180
.provider_clients
174-
.get(&path.last().unwrap().pub_key.to_bytes())
181+
.get(
182+
&path
183+
.last()
184+
.expect("We checked the path to contain at least one entry")
185+
.pub_key
186+
.to_bytes(),
187+
)
175188
.unwrap();
176189

177190
if provider_client.is_none() {
@@ -186,10 +199,15 @@ impl PathChecker {
186199
return;
187200
}
188201

189-
let layer_one_mix = path.first().unwrap();
202+
let layer_one_mix = path
203+
.first()
204+
.expect("We checked the path to contain at least one entry");
190205
let first_node_key = layer_one_mix.pub_key.to_bytes();
206+
207+
// we generated the bytes data so unwrap is fine
191208
let first_node_address =
192-
addressing::socket_address_from_encoded_bytes(layer_one_mix.address.to_bytes());
209+
addressing::socket_address_from_encoded_bytes(layer_one_mix.address.to_bytes())
210+
.unwrap();
193211

194212
let first_node_client = self
195213
.layer_one_clients
@@ -208,10 +226,12 @@ impl PathChecker {
208226
return;
209227
}
210228

229+
// we already checked for 'None' case
211230
let first_node_client = first_node_client.as_ref().unwrap();
212231

213232
let delays: Vec<_> = path.iter().map(|_| Delay::new(0)).collect();
214233

234+
// all of the data used to create the packet was created by us
215235
let packet = sphinx::SphinxPacket::new(
216236
path_identifier.clone(),
217237
&path[..],

common/healthcheck/src/result.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ pub struct HealthCheckResult(Vec<NodeScore>);
1414
impl std::fmt::Display for HealthCheckResult {
1515
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
1616
write!(f, "NETWORK HEALTH\n==============\n")?;
17-
self.0
18-
.iter()
19-
.for_each(|score| write!(f, "{}\n", score).unwrap());
17+
for score in self.0.iter() {
18+
write!(f, "{}\n", score)?
19+
}
2020
Ok(())
2121
}
2222
}

0 commit comments

Comments
 (0)