Commit 2c513166 authored by Nick Mathewson's avatar Nick Mathewson 🦞
Browse files

Merge branch 'main' into 'retriable'

# Conflicts:
#   doc/semver_status.md
parents 36440a95 314f5707
Loading
Loading
Loading
Loading
+93 −3
Original line number Original line Diff line number Diff line
@@ -229,10 +229,10 @@ mod test {
    };
    };
    use pk::ed25519::Ed25519Identity;
    use pk::ed25519::Ed25519Identity;
    use pk::rsa::RsaIdentity;
    use pk::rsa::RsaIdentity;
    use std::net::SocketAddr;
    use std::time::{Duration, SystemTime};
    use std::time::{Duration, SystemTime};
    use std::{net::SocketAddr, str::FromStr};
    use tor_proto::channel::Channel;
    use tor_proto::channel::Channel;
    use tor_rtcompat::{test_with_one_runtime, TcpListener};
    use tor_rtcompat::{test_with_one_runtime, SleepProviderExt, TcpListener};
    use tor_rtmock::{io::LocalStream, net::MockNetwork, MockSleepRuntime};
    use tor_rtmock::{io::LocalStream, net::MockNetwork, MockSleepRuntime};


    // Make sure that the builder can build a real channel.  To test
    // Make sure that the builder can build a real channel.  To test
@@ -298,10 +298,100 @@ mod test {
            let chan = r1.unwrap();
            let chan = r1.unwrap();
            assert_eq!(chan.ident(), &ed);
            assert_eq!(chan.ident(), &ed);
            assert!(chan.is_usable());
            assert!(chan.is_usable());
            // In theory, time could pass here, so we can't just use
            // "assert_eq!(dur_unused, dur_unused2)".
            let dur_unused = Channel::duration_unused(&chan);
            let dur_unused_2 = AbstractChannel::duration_unused(&chan);
            let dur_unused_3 = Channel::duration_unused(&chan);
            assert!(dur_unused.unwrap() <= dur_unused_2.unwrap());
            assert!(dur_unused_2.unwrap() <= dur_unused_3.unwrap());

            r2.unwrap();
            r2.unwrap();
            Ok(())
            Ok(())
        })
        })
    }
    }


    #[test]
    fn test_connect_one() {
        let client_addr = "192.0.1.16".parse().unwrap();
        // We'll put a "relay" at this address
        let addr1 = SocketAddr::from_str("192.0.2.17:443").unwrap();
        // We'll put nothing at this address, to generate errors.
        let addr2 = SocketAddr::from_str("192.0.3.18:443").unwrap();
        // Well put a black hole at this address, to generate timeouts.
        let addr3 = SocketAddr::from_str("192.0.4.19:443").unwrap();
        // We'll put a "relay" at this address too
        let addr4 = SocketAddr::from_str("192.0.9.9:443").unwrap();

        test_with_one_runtime!(|rt| async move {
            // Stub out the internet so that this connection can work.
            let network = MockNetwork::new();

            // Set up a client and server runtime with a given IP
            let client_rt = network
                .builder()
                .add_address(client_addr)
                .runtime(rt.clone());
            let server_rt = network
                .builder()
                .add_address(addr1.ip())
                .add_address(addr4.ip())
                .runtime(rt.clone());
            let _listener = server_rt.mock_net().listen(&addr1).await.unwrap();
            let _listener2 = server_rt.mock_net().listen(&addr4).await.unwrap();
            // TODO: Because this test doesn't mock time, there will actually be
            // delays as we wait for connections to this address to time out. It
            // would be good to use MockSleepProvider instead, once we figure
            // out how to make it both reliable and convenient.
            network.add_blackhole(addr3).unwrap();

            // No addresses? Can't succeed.
            let failure = connect_to_one(&client_rt, &[]).await;
            assert!(failure.is_err());

            // Connect to a set of addresses including addr1? That's a success.
            for addresses in [
                &[addr1][..],
                &[addr1, addr2][..],
                &[addr2, addr1][..],
                &[addr1, addr3][..],
                &[addr3, addr1][..],
                &[addr1, addr2, addr3][..],
                &[addr3, addr2, addr1][..],
            ] {
                let (_conn, addr) = connect_to_one(&client_rt, addresses).await.unwrap();
                assert_eq!(addr, addr1);
            }

            // Connect to a set of addresses including addr2 but not addr1?
            // That's an error of one kind or another.
            for addresses in [
                &[addr2][..],
                &[addr2, addr3][..],
                &[addr3, addr2][..],
                &[addr3][..],
            ] {
                let expect_timeout = addresses.contains(&addr3);
                let failure = rt
                    .timeout(
                        Duration::from_millis(300),
                        connect_to_one(&client_rt, addresses),
                    )
                    .await;
                if expect_timeout {
                    assert!(failure.is_err());
                } else {
                    assert!(failure.unwrap().is_err());
                }
            }

            // Connect to addr1 and addr4?  The first one should win.
            let (_conn, addr) = connect_to_one(&client_rt, &[addr1, addr4]).await.unwrap();
            assert_eq!(addr, addr1);
            let (_conn, addr) = connect_to_one(&client_rt, &[addr4, addr1]).await.unwrap();
            assert_eq!(addr, addr4);
        });
    }

    // TODO: Write tests for timeout logic, once there is smarter logic.
    // TODO: Write tests for timeout logic, once there is smarter logic.
}
}
+5 −0
Original line number Original line Diff line number Diff line
@@ -351,6 +351,7 @@ mod test {
        assert_float_eq!(s1.frac(), 0.0, abs <= TOL);
        assert_float_eq!(s1.frac(), 0.0, abs <= TOL);
        assert!(s1.eq(&s1));
        assert!(s1.eq(&s1));
        assert!(s1.blockage().is_none());
        assert!(s1.blockage().is_none());
        assert!(!s1.usable());


        let s2 = ConnStatus {
        let s2 = ConnStatus {
            online: Some(false),
            online: Some(false),
@@ -365,6 +366,7 @@ mod test {
            s2.blockage().unwrap().to_string(),
            s2.blockage().unwrap().to_string(),
            "unable to connect to the internet"
            "unable to connect to the internet"
        );
        );
        assert!(!s2.usable());


        let s3 = ConnStatus {
        let s3 = ConnStatus {
            online: Some(true),
            online: Some(true),
@@ -374,6 +376,7 @@ mod test {
        assert_float_eq!(s3.frac(), 0.5, abs <= TOL);
        assert_float_eq!(s3.frac(), 0.5, abs <= TOL);
        assert_eq!(s3.blockage(), None);
        assert_eq!(s3.blockage(), None);
        assert!(!s3.eq(&s1));
        assert!(!s3.eq(&s1));
        assert!(!s3.usable());


        let s4 = ConnStatus {
        let s4 = ConnStatus {
            online: Some(true),
            online: Some(true),
@@ -390,6 +393,7 @@ mod test {
        assert!(!s4.eq(&s2));
        assert!(!s4.eq(&s2));
        assert!(!s4.eq(&s3));
        assert!(!s4.eq(&s3));
        assert!(s4.eq(&s4));
        assert!(s4.eq(&s4));
        assert!(!s4.usable());


        let s5 = ConnStatus {
        let s5 = ConnStatus {
            online: Some(true),
            online: Some(true),
@@ -400,6 +404,7 @@ mod test {
        assert!(s5.blockage().is_none());
        assert!(s5.blockage().is_none());
        assert!(s5.eq(&s5));
        assert!(s5.eq(&s5));
        assert!(!s5.eq(&s4));
        assert!(!s5.eq(&s4));
        assert!(s5.usable());
    }
    }


    #[test]
    #[test]
+28 −0
Original line number Original line Diff line number Diff line
@@ -203,16 +203,44 @@ mod test {
        let eu = SystemTime::UNIX_EPOCH + one_day * 8705;
        let eu = SystemTime::UNIX_EPOCH + one_day * 8705;
        let za = SystemTime::UNIX_EPOCH + one_day * 8882;
        let za = SystemTime::UNIX_EPOCH + one_day * 8882;


        // check_valid_at
        let tr = TimerangeBound::new("Hello world", cz_sk..eu);
        let tr = TimerangeBound::new("Hello world", cz_sk..eu);
        assert!(tr.check_valid_at(&za).is_err());
        assert!(tr.check_valid_at(&za).is_err());


        let tr = TimerangeBound::new("Hello world", cz_sk..za);
        let tr = TimerangeBound::new("Hello world", cz_sk..za);
        assert_eq!(tr.check_valid_at(&eu), Ok("Hello world"));
        assert_eq!(tr.check_valid_at(&eu), Ok("Hello world"));


        // check_valid_now
        let tr = TimerangeBound::new("hello world", de..);
        let tr = TimerangeBound::new("hello world", de..);
        assert_eq!(tr.check_valid_now(), Ok("hello world"));
        assert_eq!(tr.check_valid_now(), Ok("hello world"));


        let tr = TimerangeBound::new("hello world", ..za);
        let tr = TimerangeBound::new("hello world", ..za);
        assert!(tr.check_valid_now().is_err());
        assert!(tr.check_valid_now().is_err());

        // Now try check_valid_at_opt() api
        let tr = TimerangeBound::new("hello world", de..);
        assert_eq!(tr.check_valid_at_opt(None), Ok("hello world"));
        let tr = TimerangeBound::new("hello world", de..);
        assert_eq!(
            tr.check_valid_at_opt(Some(SystemTime::now())),
            Ok("hello world")
        );
        let tr = TimerangeBound::new("hello world", ..za);
        assert!(tr.check_valid_at_opt(None).is_err());
    }

    #[cfg(feature = "experimental-api")]
    #[test]
    fn test_dangerous() {
        let t1 = SystemTime::now();
        let t2 = t1 + Duration::from_secs(60 * 525600);
        let tr = TimerangeBound::new("cups of coffee", t1..=t2);

        assert_eq!(tr.dangerously_peek(), &"cups of coffee");

        let (a, b) = tr.dangerously_into_parts();
        assert_eq!(a, "cups of coffee");
        assert_eq!(b.0, Bound::Included(t1));
        assert_eq!(b.1, Bound::Included(t2));
    }
    }
}
}
+29 −6
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@ type ConnReceiver = mpsc::Receiver<(LocalStream, SocketAddr)>;
/// its own view of its address(es) on the network.
/// its own view of its address(es) on the network.
pub struct MockNetwork {
pub struct MockNetwork {
    /// A map from address to the entries about listeners there.
    /// A map from address to the entries about listeners there.
    listening: Mutex<HashMap<SocketAddr, ListenerEntry>>,
    listening: Mutex<HashMap<SocketAddr, AddrBehavior>>,
}
}


/// The `MockNetwork`'s view of a listener.
/// The `MockNetwork`'s view of a listener.
@@ -55,6 +55,15 @@ struct ListenerEntry {
    tls_cert: Option<Vec<u8>>,
    tls_cert: Option<Vec<u8>>,
}
}


/// A possible non-error behavior from an address
#[derive(Clone)]
enum AddrBehavior {
    /// There's a listener at this address, which would like to reply.
    Listener(ListenerEntry),
    /// All connections sent to this address will time out.
    Timeout,
}

/// A view of a single host's access to a MockNetwork.
/// A view of a single host's access to a MockNetwork.
///
///
/// Each simulated host has its own addresses that it's allowed to listen on,
/// Each simulated host has its own addresses that it's allowed to listen on,
@@ -159,6 +168,16 @@ impl MockNetwork {
        }
        }
    }
    }


    /// Add a "black hole" at the given address, where all traffic will time out.
    pub fn add_blackhole(&self, address: SocketAddr) -> IoResult<()> {
        let mut listener_map = self.listening.lock().expect("Poisoned lock for listener");
        if listener_map.contains_key(&address) {
            return Err(err(ErrorKind::AddrInUse));
        }
        listener_map.insert(address, AddrBehavior::Timeout);
        Ok(())
    }

    /// Tell the listener at `target_addr` (if any) about an incoming
    /// Tell the listener at `target_addr` (if any) about an incoming
    /// connection from `source_addr` at `peer_stream`.
    /// connection from `source_addr` at `peer_stream`.
    ///
    ///
@@ -177,13 +196,17 @@ impl MockNetwork {
            let listener_map = self.listening.lock().expect("Poisoned lock for listener");
            let listener_map = self.listening.lock().expect("Poisoned lock for listener");
            listener_map.get(&target_addr).map(Clone::clone)
            listener_map.get(&target_addr).map(Clone::clone)
        };
        };
        if let Some(mut entry) = entry {
        match entry {
            Some(AddrBehavior::Listener(mut entry)) => {
                if entry.send.send((peer_stream, source_addr)).await.is_ok() {
                if entry.send.send((peer_stream, source_addr)).await.is_ok() {
                    return Ok(entry.tls_cert);
                    return Ok(entry.tls_cert);
                }
                }
        }
                Err(err(ErrorKind::ConnectionRefused))
                Err(err(ErrorKind::ConnectionRefused))
            }
            }
            Some(AddrBehavior::Timeout) => futures::future::pending().await,
            None => Err(err(ErrorKind::ConnectionRefused)),
        }
    }


    /// Register a listener at `addr` and return the ConnReceiver
    /// Register a listener at `addr` and return the ConnReceiver
    /// that it should use for connections.
    /// that it should use for connections.
@@ -203,7 +226,7 @@ impl MockNetwork {


        let entry = ListenerEntry { send, tls_cert };
        let entry = ListenerEntry { send, tls_cert };


        listener_map.insert(addr, entry);
        listener_map.insert(addr, AddrBehavior::Listener(entry));


        Ok(recv)
        Ok(recv)
    }
    }
+0 −15
Original line number Original line Diff line number Diff line
@@ -19,15 +19,6 @@ pub enum SocksVersion {
    V5,
    V5,
}
}


impl From<SocksVersion> for u8 {
    fn from(v: SocksVersion) -> u8 {
        match v {
            SocksVersion::V4 => 4,
            SocksVersion::V5 => 5,
        }
    }
}

impl TryFrom<u8> for SocksVersion {
impl TryFrom<u8> for SocksVersion {
    type Error = Error;
    type Error = Error;
    fn try_from(v: u8) -> Result<SocksVersion> {
    fn try_from(v: u8) -> Result<SocksVersion> {
@@ -184,12 +175,6 @@ impl AsRef<str> for SocksHostname {
    }
    }
}
}


impl From<SocksHostname> for String {
    fn from(s: SocksHostname) -> String {
        s.0
    }
}

impl SocksRequest {
impl SocksRequest {
    /// Create a SocksRequest with a given set of fields.
    /// Create a SocksRequest with a given set of fields.
    ///
    ///
Loading