diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3e06479b9..7671ae150 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2470,7 +2470,6 @@ dependencies = [ "ip_network_table", "itertools 0.13.0", "lru", - "phoenix-channel", "proptest", "proptest-state-machine", "rand 0.8.5", diff --git a/rust/connlib/tunnel/Cargo.toml b/rust/connlib/tunnel/Cargo.toml index 650078c58..873cadf52 100644 --- a/rust/connlib/tunnel/Cargo.toml +++ b/rust/connlib/tunnel/Cargo.toml @@ -41,7 +41,6 @@ uuid = { version = "1.10", default-features = false, features = ["std", "v4"] } derivative = "2.2.0" firezone-relay = { workspace = true, features = ["proptest"] } ip-packet = { workspace = true, features = ["proptest"] } -phoenix-channel = { workspace = true } proptest-state-machine = "0.3" rand = "0.8" serde_json = "1.0" diff --git a/rust/connlib/tunnel/src/client.rs b/rust/connlib/tunnel/src/client.rs index b22161a70..2184c75b1 100644 --- a/rust/connlib/tunnel/src/client.rs +++ b/rust/connlib/tunnel/src/client.rs @@ -1,8 +1,14 @@ +mod resource; + +pub(crate) use resource::{CidrResource, Resource}; +#[cfg(all(feature = "proptest", test))] +pub(crate) use resource::{DnsResource, InternetResource}; + use crate::dns::StubResolver; +use crate::messages::client::ResourceDescription; use crate::messages::ResolveRequest; use crate::messages::{ - client::ResourceDescription, client::ResourceDescriptionCidr, Answer, DnsServer, - Interface as InterfaceConfig, IpDnsServer, Key, Offer, Relay, + Answer, DnsServer, Interface as InterfaceConfig, IpDnsServer, Key, Offer, Relay, }; use crate::peer_store::PeerStore; use crate::{dns, TunConfig}; @@ -72,7 +78,12 @@ const MAX_REMEMBERED_GATEWAYS: NonZeroUsize = unsafe { NonZeroUsize::new_uncheck impl ClientTunnel { pub fn set_resources(&mut self, resources: Vec) { - self.role_state.set_resources(resources); + self.role_state.set_resources( + resources + .into_iter() + .filter_map(Resource::from_description) + .collect(), + ); self.role_state .buffered_events @@ -97,6 +108,10 @@ impl ClientTunnel { /// Adds a the given resource to the tunnel. pub fn add_resource(&mut self, resource: ResourceDescription) { + let Some(resource) = Resource::from_description(resource) else { + return; + }; + self.role_state.add_resource(resource); self.role_state @@ -207,11 +222,11 @@ pub struct ClientState { sites_status: HashMap, /// All CIDR resources we know about, indexed by the IP range they cover (like `1.1.0.0/8`). - active_cidr_resources: IpNetworkTable, + active_cidr_resources: IpNetworkTable, /// `Some` if the Internet resource is enabled. internet_resource: Option, /// All resources indexed by their ID. - resources_by_id: BTreeMap, + resources_by_id: BTreeMap, /// The DNS resolvers configured on the system outside of connlib. system_resolvers: Vec, @@ -329,7 +344,7 @@ impl ClientState { .collect_vec() } - fn resource_status(&self, resource: &ResourceDescription) -> ResourceStatus { + fn resource_status(&self, resource: &Resource) -> ResourceStatus { if resource.sites().iter().any(|s| { self.sites_status .get(&s.id) @@ -389,10 +404,7 @@ impl ClientState { } fn is_dns_resource(&self, resource: &ResourceId) -> bool { - matches!( - self.resources_by_id.get(resource), - Some(ResourceDescription::Dns(_)) - ) + matches!(self.resources_by_id.get(resource), Some(Resource::Dns(_))) } fn is_cidr_resource_connected(&self, resource: &ResourceId) -> bool { @@ -940,11 +952,11 @@ impl ClientState { self.maybe_update_tun_config(new_tun_config); } - fn recalculate_active_cidr_resources(&self) -> IpNetworkTable { - let mut active_cidr_resources = IpNetworkTable::::new(); + fn recalculate_active_cidr_resources(&self) -> IpNetworkTable { + let mut active_cidr_resources = IpNetworkTable::::new(); for resource in self.resources_by_id.values() { - let ResourceDescription::Cidr(resource) = resource else { + let Resource::Cidr(resource) = resource else { continue; }; @@ -1080,7 +1092,7 @@ impl ClientState { /// /// TODO: Add a test that asserts the above. /// That is tricky because we need to assert on state deleted by [`ClientState::remove_resource`] and check that it did in fact not get deleted. - pub(crate) fn set_resources(&mut self, new_resources: Vec) { + pub(crate) fn set_resources(&mut self, new_resources: Vec) { let current_resource_ids = self .resources_by_id .keys() @@ -1103,7 +1115,7 @@ impl ClientState { self.maybe_update_tun_routes(); } - pub(crate) fn add_resource(&mut self, new_resource: ResourceDescription) { + pub(crate) fn add_resource(&mut self, new_resource: Resource) { if let Some(resource) = self.resources_by_id.get(&new_resource.id()) { if resource.has_different_address(&new_resource) { self.remove_resource(resource.id()); @@ -1118,10 +1130,8 @@ impl ClientState { } let added = match &new_resource { - ResourceDescription::Dns(dns) => { - self.stub_resolver.add_resource(dns.id, dns.address.clone()) - } - ResourceDescription::Cidr(cidr) => { + Resource::Dns(dns) => self.stub_resolver.add_resource(dns.id, dns.address.clone()), + Resource::Cidr(cidr) => { let existing = self.active_cidr_resources.exact_match(cidr.address); match existing { @@ -1129,7 +1139,7 @@ impl ClientState { None => true, } } - ResourceDescription::Internet(resource) => { + Resource::Internet(resource) => { self.internet_resource.replace(resource.id) != Some(resource.id) } }; @@ -1160,9 +1170,9 @@ impl ClientState { }; match resource { - ResourceDescription::Dns(_) => self.stub_resolver.remove_resource(id), - ResourceDescription::Cidr(_) => {} - ResourceDescription::Internet(_) => { + Resource::Dns(_) => self.stub_resolver.remove_resource(id), + Resource::Cidr(_) => {} + Resource::Internet(_) => { if self.internet_resource.is_some_and(|r_id| r_id == id) { self.internet_resource = None; } @@ -1279,11 +1289,11 @@ fn peer_by_resource_mut<'p>( } fn get_addresses_for_awaiting_resource( - desc: &ResourceDescription, + desc: &Resource, awaiting_connection_details: &AwaitingConnectionDetails, ) -> Vec { match desc { - ResourceDescription::Dns(_) => awaiting_connection_details + Resource::Dns(_) => awaiting_connection_details .domain .as_ref() .expect("for dns resources the awaiting connection should have an ip") @@ -1292,8 +1302,8 @@ fn get_addresses_for_awaiting_resource( .copied() .map_into() .collect_vec(), - ResourceDescription::Cidr(r) => vec![r.address], - ResourceDescription::Internet(_) => vec![ + Resource::Cidr(r) => vec![r.address], + Resource::Internet(_) => vec![ Ipv4Network::DEFAULT_ROUTE.into(), Ipv6Network::DEFAULT_ROUTE.into(), ], @@ -1595,21 +1605,21 @@ mod tests { #[cfg(all(test, feature = "proptest"))] mod proptests { use super::*; - use crate::messages::client::ResourceDescriptionDns; use crate::proptest::*; use connlib_model::ResourceView; use prop::collection; use proptest::prelude::*; + use resource::DnsResource; #[test_strategy::proptest] fn cidr_resources_are_turned_into_routes( - #[strategy(cidr_resource())] resource1: ResourceDescriptionCidr, - #[strategy(cidr_resource())] resource2: ResourceDescriptionCidr, + #[strategy(cidr_resource())] resource1: CidrResource, + #[strategy(cidr_resource())] resource2: CidrResource, ) { let mut client_state = ClientState::for_test(); - client_state.add_resource(ResourceDescription::Cidr(resource1.clone())); - client_state.add_resource(ResourceDescription::Cidr(resource2.clone())); + client_state.add_resource(Resource::Cidr(resource1.clone())); + client_state.add_resource(Resource::Cidr(resource2.clone())); assert_eq!( hashset(client_state.routes()), @@ -1619,14 +1629,14 @@ mod proptests { #[test_strategy::proptest] fn added_resources_show_up_as_resoucres( - #[strategy(cidr_resource())] resource1: ResourceDescriptionCidr, - #[strategy(dns_resource())] resource2: ResourceDescriptionDns, - #[strategy(cidr_resource())] resource3: ResourceDescriptionCidr, + #[strategy(cidr_resource())] resource1: CidrResource, + #[strategy(dns_resource())] resource2: DnsResource, + #[strategy(cidr_resource())] resource3: CidrResource, ) { let mut client_state = ClientState::for_test(); - client_state.add_resource(ResourceDescription::Cidr(resource1.clone())); - client_state.add_resource(ResourceDescription::Dns(resource2.clone())); + client_state.add_resource(Resource::Cidr(resource1.clone())); + client_state.add_resource(Resource::Dns(resource2.clone())); assert_eq!( hashset(client_state.resources()), @@ -1636,7 +1646,7 @@ mod proptests { ]) ); - client_state.add_resource(ResourceDescription::Cidr(resource3.clone())); + client_state.add_resource(Resource::Cidr(resource3.clone())); assert_eq!( hashset(client_state.resources()), @@ -1650,18 +1660,18 @@ mod proptests { #[test_strategy::proptest] fn adding_same_resource_with_different_address_updates_the_address( - #[strategy(cidr_resource())] resource: ResourceDescriptionCidr, + #[strategy(cidr_resource())] resource: CidrResource, #[strategy(any_ip_network(8))] new_address: IpNetwork, ) { let mut client_state = ClientState::for_test(); - client_state.add_resource(ResourceDescription::Cidr(resource.clone())); + client_state.add_resource(Resource::Cidr(resource.clone())); - let updated_resource = ResourceDescriptionCidr { + let updated_resource = CidrResource { address: new_address, ..resource }; - client_state.add_resource(ResourceDescription::Cidr(updated_resource.clone())); + client_state.add_resource(Resource::Cidr(updated_resource.clone())); assert_eq!( hashset(client_state.resources()), @@ -1677,13 +1687,13 @@ mod proptests { #[test_strategy::proptest] fn adding_cidr_resource_with_same_id_as_dns_resource_replaces_dns_resource( - #[strategy(dns_resource())] resource: ResourceDescriptionDns, + #[strategy(dns_resource())] resource: DnsResource, #[strategy(any_ip_network(8))] address: IpNetwork, ) { let mut client_state = ClientState::for_test(); - client_state.add_resource(ResourceDescription::Dns(resource.clone())); + client_state.add_resource(Resource::Dns(resource.clone())); - let dns_as_cidr_resource = ResourceDescriptionCidr { + let dns_as_cidr_resource = CidrResource { address, id: resource.id, name: resource.name, @@ -1691,7 +1701,7 @@ mod proptests { sites: resource.sites, }; - client_state.add_resource(ResourceDescription::Cidr(dns_as_cidr_resource.clone())); + client_state.add_resource(Resource::Cidr(dns_as_cidr_resource.clone())); assert_eq!( hashset(client_state.resources()), @@ -1707,12 +1717,12 @@ mod proptests { #[test_strategy::proptest] fn resources_can_be_removed( - #[strategy(dns_resource())] dns_resource: ResourceDescriptionDns, - #[strategy(cidr_resource())] cidr_resource: ResourceDescriptionCidr, + #[strategy(dns_resource())] dns_resource: DnsResource, + #[strategy(cidr_resource())] cidr_resource: CidrResource, ) { let mut client_state = ClientState::for_test(); - client_state.add_resource(ResourceDescription::Dns(dns_resource.clone())); - client_state.add_resource(ResourceDescription::Cidr(cidr_resource.clone())); + client_state.add_resource(Resource::Dns(dns_resource.clone())); + client_state.add_resource(Resource::Cidr(cidr_resource.clone())); client_state.remove_resource(dns_resource.id); @@ -1735,18 +1745,18 @@ mod proptests { #[test_strategy::proptest] fn resources_can_be_replaced( - #[strategy(dns_resource())] dns_resource1: ResourceDescriptionDns, - #[strategy(dns_resource())] dns_resource2: ResourceDescriptionDns, - #[strategy(cidr_resource())] cidr_resource1: ResourceDescriptionCidr, - #[strategy(cidr_resource())] cidr_resource2: ResourceDescriptionCidr, + #[strategy(dns_resource())] dns_resource1: DnsResource, + #[strategy(dns_resource())] dns_resource2: DnsResource, + #[strategy(cidr_resource())] cidr_resource1: CidrResource, + #[strategy(cidr_resource())] cidr_resource2: CidrResource, ) { let mut client_state = ClientState::for_test(); - client_state.add_resource(ResourceDescription::Dns(dns_resource1)); - client_state.add_resource(ResourceDescription::Cidr(cidr_resource1)); + client_state.add_resource(Resource::Dns(dns_resource1)); + client_state.add_resource(Resource::Cidr(cidr_resource1)); client_state.set_resources(vec![ - ResourceDescription::Dns(dns_resource2.clone()), - ResourceDescription::Cidr(cidr_resource2.clone()), + Resource::Dns(dns_resource2.clone()), + Resource::Cidr(cidr_resource2.clone()), ]); assert_eq!( @@ -1764,8 +1774,8 @@ mod proptests { #[test_strategy::proptest] fn setting_gateway_online_sets_all_related_resources_online( - #[strategy(resources_sharing_n_sites(1))] resources_online: Vec, - #[strategy(resources_sharing_n_sites(1))] resources_unknown: Vec, + #[strategy(resources_sharing_n_sites(1))] resources_online: Vec, + #[strategy(resources_sharing_n_sites(1))] resources_unknown: Vec, #[strategy(gateway_id())] gateway: GatewayId, ) { let mut client_state = ClientState::for_test(); @@ -1801,7 +1811,7 @@ mod proptests { #[test_strategy::proptest] fn disconnecting_gateway_sets_related_resources_unknown( - #[strategy(resources_sharing_n_sites(1))] resources: Vec, + #[strategy(resources_sharing_n_sites(1))] resources: Vec, #[strategy(gateway_id())] gateway: GatewayId, ) { let mut client_state = ClientState::for_test(); @@ -1829,8 +1839,8 @@ mod proptests { #[test_strategy::proptest] fn setting_resource_offline_doesnt_set_all_related_resources_offline( - #[strategy(resources_sharing_n_sites(2))] multi_site_resources: Vec, - #[strategy(resource())] single_site_resource: ResourceDescription, + #[strategy(resources_sharing_n_sites(2))] multi_site_resources: Vec, + #[strategy(resource())] single_site_resource: Resource, ) { let mut client_state = ClientState::for_test(); client_state.add_resource(single_site_resource.clone()); @@ -1870,22 +1880,20 @@ mod proptests { HashSet::from_iter(val.into_iter().map(|b| b.to_owned())) } - fn resource() -> impl Strategy { + fn resource() -> impl Strategy { crate::proptest::resource(site().prop_map(|s| vec![s])) } - fn cidr_resource() -> impl Strategy { + fn cidr_resource() -> impl Strategy { crate::proptest::cidr_resource(any_ip_network(8), site().prop_map(|s| vec![s])) } - fn dns_resource() -> impl Strategy { + fn dns_resource() -> impl Strategy { crate::proptest::dns_resource(site().prop_map(|s| vec![s])) } // Generate resources sharing 1 site - fn resources_sharing_n_sites( - num_sites: usize, - ) -> impl Strategy> { + fn resources_sharing_n_sites(num_sites: usize) -> impl Strategy> { collection::vec(site(), num_sites) .prop_flat_map(|sites| collection::vec(crate::proptest::resource(Just(sites)), 1..=100)) } diff --git a/rust/connlib/tunnel/src/client/resource.rs b/rust/connlib/tunnel/src/client/resource.rs new file mode 100644 index 000000000..b5446cbec --- /dev/null +++ b/rust/connlib/tunnel/src/client/resource.rs @@ -0,0 +1,206 @@ +//! Internal model of resources as used by connlib's client code. + +use std::collections::BTreeSet; + +use connlib_model::{ + CidrResourceView, DnsResourceView, InternetResourceView, ResourceId, ResourceStatus, + ResourceView, Site, +}; +use ip_network::IpNetwork; +use itertools::Itertools as _; + +use crate::messages::client::{ + ResourceDescription, ResourceDescriptionCidr, ResourceDescriptionDns, + ResourceDescriptionInternet, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Resource { + Dns(DnsResource), + Cidr(CidrResource), + Internet(InternetResource), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct DnsResource { + /// Resource's id. + pub id: ResourceId, + /// Internal resource's domain name. + pub address: String, + /// Name of the resource. + /// + /// Used only for display. + pub name: String, + + pub address_description: Option, + pub sites: Vec, +} + +/// Description of a resource that maps to a CIDR. +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct CidrResource { + /// Resource's id. + pub id: ResourceId, + /// CIDR that this resource points to. + pub address: IpNetwork, + /// Name of the resource. + /// + /// Used only for display. + pub name: String, + + pub address_description: Option, + pub sites: Vec, +} + +/// Description of an internet resource. +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct InternetResource { + /// Name of the resource. + /// + /// Used only for display. + pub name: String, + /// Resource's id. + pub id: ResourceId, + /// Sites for the internet resource + pub sites: Vec, +} + +impl Resource { + pub fn from_description(resource: ResourceDescription) -> Option { + match resource { + ResourceDescription::Dns(i) => Some(Resource::Dns(DnsResource::from_description(i))), + ResourceDescription::Cidr(i) => Some(Resource::Cidr(CidrResource::from_description(i))), + ResourceDescription::Internet(i) => { + Some(Resource::Internet(InternetResource::from_description(i))) + } + ResourceDescription::Unknown => None, + } + } + + #[cfg(all(feature = "proptest", test))] + pub fn into_dns(self) -> Option { + match self { + Resource::Dns(d) => Some(d), + Resource::Cidr(_) | Resource::Internet(_) => None, + } + } + + pub fn address_string(&self) -> Option { + match self { + Resource::Dns(d) => Some(d.address.clone()), + Resource::Cidr(c) => Some(c.address.to_string()), + Resource::Internet(_) => None, + } + } + + pub fn sites_string(&self) -> String { + self.sites().iter().map(|s| &s.name).join("|") + } + + pub fn id(&self) -> ResourceId { + match self { + Resource::Dns(r) => r.id, + Resource::Cidr(r) => r.id, + Resource::Internet(r) => r.id, + } + } + + pub fn sites(&self) -> BTreeSet<&Site> { + match self { + Resource::Dns(r) => BTreeSet::from_iter(r.sites.iter()), + Resource::Cidr(r) => BTreeSet::from_iter(r.sites.iter()), + Resource::Internet(r) => BTreeSet::from_iter(r.sites.iter()), + } + } + + /// What the GUI clients should show as the user-friendly display name, e.g. `Firezone GitHub` + pub fn name(&self) -> &str { + match self { + Resource::Dns(r) => &r.name, + Resource::Cidr(r) => &r.name, + Resource::Internet(_) => "Internet", + } + } + + pub fn has_different_address(&self, other: &Resource) -> bool { + match (self, other) { + (Resource::Dns(dns_a), Resource::Dns(dns_b)) => dns_a.address != dns_b.address, + (Resource::Cidr(cidr_a), Resource::Cidr(cidr_b)) => cidr_a.address != cidr_b.address, + (Resource::Internet(_), Resource::Internet(_)) => false, + _ => true, + } + } + + pub fn with_status(self, status: ResourceStatus) -> ResourceView { + match self { + Resource::Dns(r) => ResourceView::Dns(r.with_status(status)), + Resource::Cidr(r) => ResourceView::Cidr(r.with_status(status)), + Resource::Internet(r) => ResourceView::Internet(r.with_status(status)), + } + } +} + +impl CidrResource { + pub fn from_description(resource: ResourceDescriptionCidr) -> Self { + Self { + id: resource.id, + address: resource.address, + name: resource.name, + address_description: resource.address_description, + sites: resource.sites, + } + } + + pub fn with_status(self, status: ResourceStatus) -> CidrResourceView { + CidrResourceView { + id: self.id, + address: self.address, + name: self.name, + address_description: self.address_description, + sites: self.sites, + status, + } + } +} + +impl InternetResource { + pub fn from_description(resource: ResourceDescriptionInternet) -> Self { + Self { + name: resource.name, + id: resource.id, + sites: resource.sites, + } + } + + pub fn with_status(self, status: ResourceStatus) -> InternetResourceView { + InternetResourceView { + name: self.name, + id: self.id, + sites: self.sites, + status, + } + } +} + +impl DnsResource { + pub fn from_description(resource: ResourceDescriptionDns) -> Self { + Self { + id: resource.id, + address: resource.address, + name: resource.name, + address_description: resource.address_description, + sites: resource.sites, + } + } + + pub fn with_status(self, status: ResourceStatus) -> DnsResourceView { + DnsResourceView { + id: self.id, + address: self.address, + name: self.name, + address_description: self.address_description, + sites: self.sites, + status, + } + } +} diff --git a/rust/connlib/tunnel/src/messages/client.rs b/rust/connlib/tunnel/src/messages/client.rs index 978a0b151..b94ef28bc 100644 --- a/rust/connlib/tunnel/src/messages/client.rs +++ b/rust/connlib/tunnel/src/messages/client.rs @@ -3,17 +3,13 @@ use crate::messages::{ GatewayResponse, Interface, Key, Relay, RelaysPresence, RequestConnection, ReuseConnection, }; -use connlib_model::{ - CidrResourceView, DnsResourceView, GatewayId, InternetResourceView, ResourceId, ResourceStatus, - ResourceView, Site, SiteId, -}; +use connlib_model::{GatewayId, ResourceId, Site, SiteId}; use ip_network::IpNetwork; -use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{collections::BTreeSet, net::IpAddr}; /// Description of a resource that maps to a DNS record. -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Deserialize)] pub struct ResourceDescriptionDns { /// Resource's id. pub id: ResourceId, @@ -29,21 +25,8 @@ pub struct ResourceDescriptionDns { pub sites: Vec, } -impl ResourceDescriptionDns { - pub fn with_status(self, status: ResourceStatus) -> DnsResourceView { - DnsResourceView { - id: self.id, - address: self.address, - name: self.name, - address_description: self.address_description, - sites: self.sites, - status, - } - } -} - /// Description of a resource that maps to a CIDR. -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Deserialize)] pub struct ResourceDescriptionCidr { /// Resource's id. pub id: ResourceId, @@ -59,25 +42,12 @@ pub struct ResourceDescriptionCidr { pub sites: Vec, } -impl ResourceDescriptionCidr { - pub fn with_status(self, status: ResourceStatus) -> CidrResourceView { - CidrResourceView { - id: self.id, - address: self.address, - name: self.name, - address_description: self.address_description, - sites: self.sites, - status, - } - } -} - fn internet_resource_name() -> String { "Internet Resource".to_string() } /// Description of an internet resource. -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Deserialize)] pub struct ResourceDescriptionInternet { /// Name of the resource. /// @@ -91,110 +61,17 @@ pub struct ResourceDescriptionInternet { pub sites: Vec, } -impl ResourceDescriptionInternet { - pub fn with_status(self, status: ResourceStatus) -> InternetResourceView { - InternetResourceView { - name: self.name, - id: self.id, - sites: self.sites, - status, - } - } -} - -impl ResourceDescription { - pub fn address_string(&self) -> Option { - match self { - ResourceDescription::Dns(d) => Some(d.address.clone()), - ResourceDescription::Cidr(c) => Some(c.address.to_string()), - ResourceDescription::Internet(_) => None, - } - } - - pub fn sites_string(&self) -> String { - self.sites().iter().map(|s| &s.name).join("|") - } - - pub fn id(&self) -> ResourceId { - match self { - ResourceDescription::Dns(r) => r.id, - ResourceDescription::Cidr(r) => r.id, - ResourceDescription::Internet(r) => r.id, - } - } - - pub fn sites(&self) -> BTreeSet<&Site> { - match self { - ResourceDescription::Dns(r) => BTreeSet::from_iter(r.sites.iter()), - ResourceDescription::Cidr(r) => BTreeSet::from_iter(r.sites.iter()), - ResourceDescription::Internet(r) => BTreeSet::from_iter(r.sites.iter()), - } - } - - pub fn sites_mut(&mut self) -> &mut Vec { - match self { - ResourceDescription::Dns(r) => &mut r.sites, - ResourceDescription::Cidr(r) => &mut r.sites, - ResourceDescription::Internet(r) => &mut r.sites, - } - } - - /// What the GUI clients should show as the user-friendly display name, e.g. `Firezone GitHub` - pub fn name(&self) -> &str { - match self { - ResourceDescription::Dns(r) => &r.name, - ResourceDescription::Cidr(r) => &r.name, - ResourceDescription::Internet(_) => "Internet", - } - } - - pub fn has_different_address(&self, other: &ResourceDescription) -> bool { - match (self, other) { - (ResourceDescription::Dns(dns_a), ResourceDescription::Dns(dns_b)) => { - dns_a.address != dns_b.address - } - (ResourceDescription::Cidr(cidr_a), ResourceDescription::Cidr(cidr_b)) => { - cidr_a.address != cidr_b.address - } - (ResourceDescription::Internet(_), ResourceDescription::Internet(_)) => false, - _ => true, - } - } - - pub fn with_status(self, status: ResourceStatus) -> ResourceView { - match self { - ResourceDescription::Dns(r) => ResourceView::Dns(r.with_status(status)), - ResourceDescription::Cidr(r) => ResourceView::Cidr(r.with_status(status)), - ResourceDescription::Internet(r) => ResourceView::Internet(r.with_status(status)), - } - } -} - -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum ResourceDescription { Dns(ResourceDescriptionDns), Cidr(ResourceDescriptionCidr), Internet(ResourceDescriptionInternet), + #[serde(other)] + Unknown, // Important for forwards-compatibility with future resource types. } -impl ResourceDescription { - pub fn into_dns(self) -> Option { - match self { - ResourceDescription::Dns(d) => Some(d), - ResourceDescription::Cidr(_) | ResourceDescription::Internet(_) => None, - } - } - - pub fn into_cidr(self) -> Option { - match self { - ResourceDescription::Cidr(c) => Some(c), - ResourceDescription::Dns(_) | ResourceDescription::Internet(_) => None, - } - } -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[derive(Debug, Deserialize)] pub struct InitClient { pub interface: Interface, #[serde(default)] @@ -203,12 +80,12 @@ pub struct InitClient { pub relays: Vec, } -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[derive(Debug, Deserialize)] pub struct ConfigUpdate { pub interface: Interface, } -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize)] pub struct ConnectionDetails { pub resource_id: ResourceId, pub gateway_id: GatewayId, @@ -217,7 +94,7 @@ pub struct ConnectionDetails { pub site_id: SiteId, } -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize)] pub struct Connect { pub gateway_payload: GatewayResponse, pub resource_id: ResourceId, @@ -227,7 +104,7 @@ pub struct Connect { // These messages are the messages that can be received // by a client. -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case", tag = "event", content = "payload")] pub enum IngressMessages { Init(InitClient), @@ -244,7 +121,7 @@ pub enum IngressMessages { RelaysPresence(RelaysPresence), } -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[derive(Debug, Serialize)] pub struct GatewaysIceCandidates { /// The list of gateway IDs these candidates will be broadcast to. pub gateway_ids: Vec, @@ -252,7 +129,7 @@ pub struct GatewaysIceCandidates { pub candidates: BTreeSet, } -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[derive(Debug, Deserialize)] pub struct GatewayIceCandidates { /// Gateway's id the ice candidates are from pub gateway_id: GatewayId, @@ -261,7 +138,7 @@ pub struct GatewayIceCandidates { } /// The replies that can arrive from the channel by a client -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize)] #[serde(untagged)] pub enum ReplyMessages { ConnectionDetails(ConnectionDetails), @@ -269,7 +146,7 @@ pub enum ReplyMessages { } // These messages can be sent from a client to a control pane -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "snake_case", tag = "event", content = "payload")] // enum_variant_names: These are the names in the portal! pub enum EgressMessages { @@ -288,10 +165,6 @@ pub enum EgressMessages { #[cfg(test)] mod tests { use super::*; - use crate::messages::{DnsServer, IpDnsServer, Turn}; - use chrono::DateTime; - use connlib_model::Site; - use phoenix_channel::{OutboundRequestId, PhoenixMessage}; #[test] fn can_deserialize_internet_resource() { @@ -328,91 +201,109 @@ mod tests { } #[test] - fn broadcast_ice_candidates() { - let message = r#"{"topic":"client","event":"broadcast_ice_candidates","payload":{"gateway_ids":["b3d34a15-55ab-40df-994b-a838e75d65d7"],"candidates":["candidate:7031633958891736544 1 udp 50331391 35.244.108.190 53909 typ relay"]},"ref":6}"#; - let expected = PhoenixMessage::new_message( - "client", - EgressMessages::BroadcastIceCandidates(GatewaysIceCandidates { - gateway_ids: vec!["b3d34a15-55ab-40df-994b-a838e75d65d7".parse().unwrap()], - candidates: BTreeSet::from([ - "candidate:7031633958891736544 1 udp 50331391 35.244.108.190 53909 typ relay" - .to_owned(), - ]), - }), - Some(OutboundRequestId::for_test(6)), - ); - - let ingress_message = serde_json::from_str::>(message).unwrap(); - - assert_eq!(ingress_message, expected); - } - - #[test] - fn invalidate_ice_candidates_message() { - let msg = r#"{"event":"invalidate_ice_candidates","ref":null,"topic":"client","payload":{"candidates":["candidate:7854631899965427361 1 udp 1694498559 172.28.0.100 47717 typ srflx"],"gateway_id":"2b1524e6-239e-4570-bc73-70a188e12101"}}"#; - let expected = IngressMessages::InvalidateIceCandidates(GatewayIceCandidates { - gateway_id: "2b1524e6-239e-4570-bc73-70a188e12101".parse().unwrap(), - candidates: vec![ - "candidate:7854631899965427361 1 udp 1694498559 172.28.0.100 47717 typ srflx" - .to_owned(), - ], - }); - - let actual = serde_json::from_str::(msg).unwrap(); - - assert_eq!(actual, expected); - } - - #[test] - fn connection_ready_deserialization() { - let message = r#"{ - "ref": 0, - "topic": "client", - "event": "phx_reply", - "payload": { - "status": "ok", - "response": { - "resource_id": "ea6570d1-47c7-49d2-9dc3-efff1c0c9e0b", - "gateway_public_key": "dvy0IwyxAi+txSbAdT7WKgf7K4TekhKzrnYwt5WfbSM=", - "gateway_payload": { - "ConnectionAccepted":{ - "domain_response":{ - "address":[ - "2607:f8b0:4008:804::200e", - "142.250.64.206" - ], - "domain":"google.com" - }, - "ice_parameters":{ - "username":"tGeqOjtGuPzPpuOx", - "password":"pMAxxTgHHSdpqHRzHGNvuNsZinLrMxwe" - } - } - }, - "persistent_keepalive": 25 - } + fn can_deserialize_unknown_resource() { + let resources = r#"[ + { + "id": "73037362-715d-4a83-a749-f18eadd970e6", + "type": "cidr", + "name": "172.172.0.0/16", + "address": "172.172.0.0/16", + "address_description": "cidr resource", + "gateway_groups": [{"name": "test", "id": "bf56f32d-7b2c-4f5d-a784-788977d014a4"}] + }, + { + "id": "03000143-e25e-45c7-aafb-144990e57dcd", + "type": "dns", + "name": "gitlab.mycorp.com", + "address": "gitlab.mycorp.com", + "address_description": "dns resource", + "gateway_groups": [{"name": "test", "id": "bf56f32d-7b2c-4f5d-a784-788977d014a4"}] + }, + { + "id": "1106047c-cd5d-4151-b679-96b93da7383b", + "type": "internet", + "name": "Internet Resource", + "gateway_groups": [{"name": "test", "id": "eb94482a-94f4-47cb-8127-14fb3afa5516"}], + "not": "relevant", + "some_other": [ + "field" + ] + }, + { + "type": "what_is_this" } - }"#; - let _: PhoenixMessage = - serde_json::from_str(message).unwrap(); + ]"#; + + serde_json::from_str::>(resources).unwrap(); } #[test] - fn config_updated() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::ConfigChanged(ConfigUpdate { - interface: Interface { - ipv4: "100.67.138.25".parse().unwrap(), - ipv6: "fd00:2021:1111::e:65ea".parse().unwrap(), - upstream_dns: vec![DnsServer::IpPort(IpDnsServer { - address: "1.1.1.1:53".parse().unwrap(), - })], - }, - }), - None, - ); - let message = r#" + fn can_deserialize_ice_candidates_message() { + let json = r#"{"topic":"client","event":"ice_candidates","payload":{"gateway_id":"b3d34a15-55ab-40df-994b-a838e75d65d7","candidates":["candidate:7031633958891736544 1 udp 50331391 35.244.108.190 53909 typ relay"]},"ref":6}"#; + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::IceCandidates(_))); + } + + #[test] + fn can_deserialize_invalidate_ice_candidates_message() { + let json = r#"{"event":"invalidate_ice_candidates","ref":null,"topic":"client","payload":{"candidates":["candidate:7854631899965427361 1 udp 1694498559 172.28.0.100 47717 typ srflx"],"gateway_id":"2b1524e6-239e-4570-bc73-70a188e12101"}}"#; + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!( + message, + IngressMessages::InvalidateIceCandidates(_) + )); + } + + #[test] + fn can_deserialize_connect_reply() { + let json = r#"{ + "resource_id": "ea6570d1-47c7-49d2-9dc3-efff1c0c9e0b", + "gateway_public_key": "dvy0IwyxAi+txSbAdT7WKgf7K4TekhKzrnYwt5WfbSM=", + "gateway_payload": { + "ConnectionAccepted":{ + "domain_response":{ + "address":[ + "2607:f8b0:4008:804::200e", + "142.250.64.206" + ], + "domain":"google.com" + }, + "ice_parameters":{ + "username":"tGeqOjtGuPzPpuOx", + "password":"pMAxxTgHHSdpqHRzHGNvuNsZinLrMxwe" + } + } + }, + "persistent_keepalive": 25 + }"#; + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, ReplyMessages::Connect(_))) + } + + #[test] + fn can_deserialize_connection_details_reply() { + let json = r#" + { + "resource_id": "f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3", + "gateway_id": "73037362-715d-4a83-a749-f18eadd970e6", + "gateway_remote_ip": "172.28.0.1", + "gateway_group_id": "bf56f32d-7b2c-4f5d-a784-788977d014a4" + }"#; + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, ReplyMessages::ConnectionDetails(_))); + } + + #[test] + fn can_deserialize_config_changed_message() { + let json = r#" { "event": "config_changed", "ref": null, @@ -431,68 +322,15 @@ mod tests { } } "#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::ConfigChanged(_))) } #[test] - fn init_phoenix_message() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::Init(InitClient { - interface: Interface { - ipv4: "100.72.112.111".parse().unwrap(), - ipv6: "fd00:2021:1111::13:efb9".parse().unwrap(), - upstream_dns: vec![], - }, - resources: vec![ - ResourceDescription::Cidr(ResourceDescriptionCidr { - id: "73037362-715d-4a83-a749-f18eadd970e6".parse().unwrap(), - address: "172.172.0.0/16".parse().unwrap(), - name: "172.172.0.0/16".to_string(), - address_description: Some("cidr resource".to_string()), - sites: vec![Site { - name: "test".to_string(), - id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }], - }), - ResourceDescription::Cidr(ResourceDescriptionCidr { - id: "73037362-715d-4a83-a749-f18eadd970e7".parse().unwrap(), - address: "172.173.0.0/16".parse().unwrap(), - name: "172.173.0.0/16".to_string(), - address_description: None, - sites: vec![Site { - name: "test".to_string(), - id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }], - }), - ResourceDescription::Dns(ResourceDescriptionDns { - id: "03000143-e25e-45c7-aafb-144990e57dcd".parse().unwrap(), - address: "gitlab.mycorp.com".to_string(), - name: "gitlab.mycorp.com".to_string(), - address_description: Some("dns resource".to_string()), - sites: vec![Site { - name: "test".to_string(), - id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }], - }), - ResourceDescription::Dns(ResourceDescriptionDns { - id: "03000143-e25e-45c7-aafb-144990e57dce".parse().unwrap(), - address: "github.mycorp.com".to_string(), - name: "github.mycorp.com".to_string(), - address_description: None, - sites: vec![Site { - name: "test".to_string(), - id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }], - }), - ], - relays: vec![], - }), - None, - ); - let message = r#"{ + fn can_deserialize_init_message() { + let json = r#"{ "event": "init", "payload": { "interface": { @@ -539,320 +377,52 @@ mod tests { "ref": null, "topic": "client" }"#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::Init(_))); } #[test] - fn messages_ignore_additional_fields() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::Init(InitClient { - interface: Interface { - ipv4: "100.72.112.111".parse().unwrap(), - ipv6: "fd00:2021:1111::13:efb9".parse().unwrap(), - upstream_dns: vec![], - }, - resources: vec![ - ResourceDescription::Cidr(ResourceDescriptionCidr { - id: "73037362-715d-4a83-a749-f18eadd970e6".parse().unwrap(), - address: "172.172.0.0/16".parse().unwrap(), - name: "172.172.0.0/16".to_string(), - address_description: Some("cidr resource".to_string()), - sites: vec![Site { - name: "test".to_string(), - id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }], - }), - ResourceDescription::Dns(ResourceDescriptionDns { - id: "03000143-e25e-45c7-aafb-144990e57dcd".parse().unwrap(), - address: "gitlab.mycorp.com".to_string(), - name: "gitlab.mycorp.com".to_string(), - address_description: Some("dns resource".to_string()), - sites: vec![Site { - name: "test".to_string(), - id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }], - }), - ], - relays: vec![], - }), - None, - ); - let message = r#"{ - "event": "init", - "payload": { - "interface": { - "ipv4": "100.72.112.111", - "ipv6": "fd00:2021:1111::13:efb9", - "upstream_dns": [], - "extra_config": "foo" - }, - "resources": [ - { - "address": "172.172.0.0/16", - "id": "73037362-715d-4a83-a749-f18eadd970e6", - "name": "172.172.0.0/16", - "type": "cidr", - "address_description": "cidr resource", - "gateway_groups": [{"name": "test", "id": "bf56f32d-7b2c-4f5d-a784-788977d014a4"}], - "not": "relevant" - }, - { - "address": "gitlab.mycorp.com", - "id": "03000143-e25e-45c7-aafb-144990e57dcd", - "ipv4": "100.126.44.50", - "ipv6": "fd00:2021:1111::e:7758", - "name": "gitlab.mycorp.com", - "type": "dns", - "address_description": "dns resource", - "gateway_groups": [{"name": "test", "id": "bf56f32d-7b2c-4f5d-a784-788977d014a4"}], - "not": "relevant" - } - ] - }, - "ref": null, - "topic": "client" - }"#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn messages_ignore_additional_bool_fields() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::Init(InitClient { - interface: Interface { - ipv4: "100.72.112.111".parse().unwrap(), - ipv6: "fd00:2021:1111::13:efb9".parse().unwrap(), - upstream_dns: vec![], - }, - resources: vec![], - relays: vec![], - }), - None, - ); - let message = r#"{ - "event": "init", - "payload": { - "interface": { - "ipv4": "100.72.112.111", - "ipv6": "fd00:2021:1111::13:efb9", - "upstream_dns": [], - "additional": true - }, - "resources": [] - }, - "ref": null, - "topic": "client" - }"#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn messages_ignore_additional_number_fields() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::Init(InitClient { - interface: Interface { - ipv4: "100.72.112.111".parse().unwrap(), - ipv6: "fd00:2021:1111::13:efb9".parse().unwrap(), - upstream_dns: vec![], - }, - resources: vec![], - relays: vec![], - }), - None, - ); - let message = r#"{ - "event": "init", - "payload": { - "interface": { - "ipv4": "100.72.112.111", - "ipv6": "fd00:2021:1111::13:efb9", - "upstream_dns": [], - "additional": 0.3 - }, - "resources": [] - }, - "ref": null, - "topic": "client" - }"#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn messages_ignore_additional_object_fields() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::Init(InitClient { - interface: Interface { - ipv4: "100.72.112.111".parse().unwrap(), - ipv6: "fd00:2021:1111::13:efb9".parse().unwrap(), - upstream_dns: vec![], - }, - resources: vec![], - relays: vec![], - }), - None, - ); - let message = r#"{ - "event": "init", - "payload": { - "interface": { - "ipv4": "100.72.112.111", - "ipv6": "fd00:2021:1111::13:efb9", - "upstream_dns": [], - "additional": { "ignored": "field" } - }, - "resources": [] - }, - "ref": null, - "topic": "client" - }"#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn messages_ignore_additional_array_fields() { - let m = PhoenixMessage::new_message( - "client", - IngressMessages::Init(InitClient { - interface: Interface { - ipv4: "100.72.112.111".parse().unwrap(), - ipv6: "fd00:2021:1111::13:efb9".parse().unwrap(), - upstream_dns: vec![], - }, - resources: vec![], - relays: vec![], - }), - None, - ); - let message = r#"{ - "event": "init", - "payload": { - "interface": { - "ipv4": "100.72.112.111", - "ipv6": "fd00:2021:1111::13:efb9", - "upstream_dns": [], - "additional": [true, false] - }, - "resources": [] - }, - "ref": null, - "topic": "client" - }"#; - let ingress_message: PhoenixMessage = - serde_json::from_str(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn list_relays_message() { - let m = PhoenixMessage::::new_message( - "client", - EgressMessages::PrepareConnection { - resource_id: "f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3".parse().unwrap(), - connected_gateway_ids: BTreeSet::new(), - }, - None, - ); - let message = r#" + fn can_deserialize_relay_presence() { + let json = r#" { - "event": "prepare_connection", + "event": "relays_presence", + "ref": null, + "topic": "client", "payload": { - "resource_id": "f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3", - "connected_gateway_ids": [] - }, - "ref":null, - "topic": "client" - } - "#; - let egress_message = serde_json::from_str(message).unwrap(); - assert_eq!(m, egress_message); - } - - #[test] - fn connection_details_reply() { - let m = PhoenixMessage::::new_ok_reply( - "client", - ReplyMessages::ConnectionDetails(ConnectionDetails { - gateway_id: "73037362-715d-4a83-a749-f18eadd970e6".parse().unwrap(), - gateway_remote_ip: "172.28.0.1".parse().unwrap(), - resource_id: "f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3".parse().unwrap(), - site_id: "bf56f32d-7b2c-4f5d-a784-788977d014a4".parse().unwrap(), - }), - None, - ); - let message = r#" - { - "ref":null, - "topic":"client", - "event": "phx_reply", - "payload": { - "response": { - "resource_id": "f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3", - "gateway_id": "73037362-715d-4a83-a749-f18eadd970e6", - "gateway_remote_ip": "172.28.0.1", - "gateway_group_id": "bf56f32d-7b2c-4f5d-a784-788977d014a4" - }, - "status":"ok" + "disconnected_ids": [ + "e95f9517-2152-4677-a16a-fbb2687050a3", + "b0724bd1-a8cc-4faf-88cd-f21159cfec47" + ], + "connected": [ + { + "id": "0a133356-7a9e-4b9a-b413-0d95a5720fd8", + "type": "turn", + "username": "1719367575:ZQHcVGkdnfgGmcP1", + "password": "ZWYiBeFHOJyYq0mcwAXjRpcuXIJJpzWlOXVdxwttrWg", + "addr": "172.28.0.101:3478", + "expires_at": 1719367575 + } + ] } - }"#; - let reply_message = serde_json::from_str(message).unwrap(); - assert_eq!(m, reply_message); + } + "#; + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::RelaysPresence(_))); } #[test] - fn relays_presence() { - let message = r#" - { - "event": "relays_presence", - "ref": null, - "topic": "client", - "payload": { - "disconnected_ids": [ - "e95f9517-2152-4677-a16a-fbb2687050a3", - "b0724bd1-a8cc-4faf-88cd-f21159cfec47" - ], - "connected": [ - { - "id": "0a133356-7a9e-4b9a-b413-0d95a5720fd8", - "type": "turn", - "username": "1719367575:ZQHcVGkdnfgGmcP1", - "password": "ZWYiBeFHOJyYq0mcwAXjRpcuXIJJpzWlOXVdxwttrWg", - "addr": "172.28.0.101:3478", - "expires_at": 1719367575 - } - ] - } - } - "#; - let expected = IngressMessages::RelaysPresence(RelaysPresence { - disconnected_ids: vec![ - "e95f9517-2152-4677-a16a-fbb2687050a3".parse().unwrap(), - "b0724bd1-a8cc-4faf-88cd-f21159cfec47".parse().unwrap(), - ], - connected: vec![Relay::Turn(Turn { - id: "0a133356-7a9e-4b9a-b413-0d95a5720fd8".parse().unwrap(), - expires_at: DateTime::from_timestamp(1719367575, 0).unwrap(), - addr: "172.28.0.101:3478".parse().unwrap(), - username: "1719367575:ZQHcVGkdnfgGmcP1".to_owned(), - password: "ZWYiBeFHOJyYq0mcwAXjRpcuXIJJpzWlOXVdxwttrWg".to_owned(), - })], - }); + fn serialize_prepare_connection_message() { + let message = EgressMessages::PrepareConnection { + resource_id: "f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3".parse().unwrap(), + connected_gateway_ids: BTreeSet::new(), + }; + let expected_json = r#"{"event":"prepare_connection","payload":{"resource_id":"f16ecfa0-a94f-4bfd-a2ef-1cc1f2ef3da3","connected_gateway_ids":[]}}"#; + let actual_json = serde_json::to_string(&message).unwrap(); - let ingress_message = serde_json::from_str::(message).unwrap(); - - assert_eq!(ingress_message, expected); + assert_eq!(actual_json, expected_json); } } diff --git a/rust/connlib/tunnel/src/messages/gateway.rs b/rust/connlib/tunnel/src/messages/gateway.rs index 5db9b22b8..0e015223b 100644 --- a/rust/connlib/tunnel/src/messages/gateway.rs +++ b/rust/connlib/tunnel/src/messages/gateway.rs @@ -15,7 +15,7 @@ use std::{ pub type Filters = Vec; /// Description of a resource that maps to a DNS record. -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Clone)] pub struct ResourceDescriptionDns { /// Resource's id. pub id: ResourceId, @@ -30,7 +30,7 @@ pub struct ResourceDescriptionDns { } /// Description of a resource that maps to a CIDR. -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Clone)] pub struct ResourceDescriptionCidr { /// Resource's id. pub id: ResourceId, @@ -45,12 +45,12 @@ pub struct ResourceDescriptionCidr { } /// Description of an Internet resource. -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Clone)] pub struct ResourceDescriptionInternet { pub id: ResourceId, } -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Clone)] #[serde(tag = "type", rename_all = "snake_case")] pub enum ResourceDescription { Dns(ResourceDescriptionDns), @@ -104,14 +104,14 @@ impl ResourceDescription { } } -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize, Clone)] pub struct ClientPayload { pub ice_parameters: Offer, pub domain: Option, } // TODO: Should this have a resource? -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[derive(Debug, Deserialize, Clone)] pub struct InitGateway { pub interface: Interface, pub config: Config, @@ -125,14 +125,14 @@ pub struct Config { pub ipv6_masquerade_enabled: bool, } -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize, Clone)] pub struct Client { pub id: ClientId, pub payload: ClientPayload, pub peer: Peer, } -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize, Clone)] pub struct RequestConnection { pub resource: ResourceDescription, pub client: Client, @@ -142,12 +142,12 @@ pub struct RequestConnection { pub expires_at: Option>, } -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct RemoveResource { pub id: ResourceId, } -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize, Clone)] pub struct AllowAccess { pub client_id: ClientId, pub resource: ResourceDescription, @@ -162,7 +162,7 @@ pub struct AllowAccess { pub client_ipv6: Ipv6Addr, } -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Clone)] pub struct RejectAccess { pub client_id: ClientId, pub resource_id: ResourceId, @@ -170,7 +170,7 @@ pub struct RejectAccess { // These messages are the messages that can be received // either by a client or a gateway by the client. -#[derive(Debug, Deserialize, Clone, PartialEq)] +#[derive(Debug, Deserialize, Clone)] #[serde(rename_all = "snake_case", tag = "event", content = "payload")] pub enum IngressMessages { RequestConnection(RequestConnection), @@ -184,7 +184,7 @@ pub enum IngressMessages { } /// A client's ice candidate message. -#[derive(Debug, Serialize, Clone, PartialEq, Eq)] +#[derive(Debug, Serialize, Clone)] pub struct ClientsIceCandidates { /// Client's id the ice candidates are meant for pub client_ids: Vec, @@ -193,7 +193,7 @@ pub struct ClientsIceCandidates { } /// A client's ice candidate message. -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Clone)] pub struct ClientIceCandidates { /// Client's id the ice candidates came from pub client_id: ClientId, @@ -221,8 +221,6 @@ pub struct ConnectionReady { #[cfg(test)] mod tests { use super::*; - use crate::messages::Turn; - use phoenix_channel::PhoenixMessage; #[test] fn can_deserialize_udp_filter() { @@ -317,8 +315,8 @@ mod tests { } #[test] - fn request_connection_message() { - let message = r#"{ + fn can_deserialize_request_connection_messages() { + let json = r#"{ "ref": null, "topic": "gateway", "event": "request_connection", @@ -368,13 +366,15 @@ mod tests { ] } }"#; - // TODO: We are just testing we can deserialize for now. - let _: PhoenixMessage = serde_json::from_str(message).unwrap(); + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::RequestConnection(_))); } #[test] - fn request_connection_with_payload() { - let message = r#"{ + fn can_deserialize_legacy_request_connection_message() { + let json = r#"{ "event": "request_connection", "ref": null, "topic": "gateway", @@ -460,223 +460,45 @@ mod tests { "flow_id": "b944e68a-c936-4a81-bd8d-88c45efdcb2c" } }"#; - let _: PhoenixMessage = serde_json::from_str(message).unwrap(); + + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::RequestConnection(_))); } #[test] - fn invalidate_ice_candidates_message() { - let msg = r#"{"event":"invalidate_ice_candidates","ref":null,"topic":"gateway","payload":{"candidates":["candidate:7854631899965427361 1 udp 1694498559 172.28.0.100 47717 typ srflx"],"client_id":"2b1524e6-239e-4570-bc73-70a188e12101"}}"#; - let expected = IngressMessages::InvalidateIceCandidates(ClientIceCandidates { - client_id: "2b1524e6-239e-4570-bc73-70a188e12101".parse().unwrap(), - candidates: vec![ - "candidate:7854631899965427361 1 udp 1694498559 172.28.0.100 47717 typ srflx" - .to_owned(), - ], - }); + fn can_deserialize_invalidate_ice_candidates_message() { + let json = r#"{"event":"invalidate_ice_candidates","ref":null,"topic":"gateway","payload":{"candidates":["candidate:7854631899965427361 1 udp 1694498559 172.28.0.100 47717 typ srflx"],"client_id":"2b1524e6-239e-4570-bc73-70a188e12101"}}"#; - let actual = serde_json::from_str::(msg).unwrap(); + let message = serde_json::from_str::(json).unwrap(); - assert_eq!(actual, expected); + assert!(matches!( + message, + IngressMessages::InvalidateIceCandidates(_) + )); } #[test] - fn init_phoenix_message() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); + fn can_deserialize_init_message() { + let json = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let message = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::Init(_))); } #[test] - fn additional_fields_are_ignore() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); + fn can_deserialize_resource_updated_message() { + let json = r#"{"event":"resource_updated","ref":null,"topic":"gateway","payload":{"id":"57f9ebbb-21d5-4f9f-bf86-b25122fc7a43","name":"?.httpbin","type":"dns","address":"?.httpbin","filters":[{"protocol":"icmp"},{"protocol":"tcp"}]}}"#; - let message = r#"{"event":"init","ref":null,"topic":"gateway","irrelevant":"field","payload":{"more":"info","interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true,"ignored":"field"}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); + let message = serde_json::from_str::(json).unwrap(); + + assert!(matches!(message, IngressMessages::ResourceUpdated(_))); } #[test] - fn additional_null_fields_are_ignored() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); - - let message = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"additional":null,"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn additional_number_fields_are_ignored() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); - - let message = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"additional":0.3,"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn additional_boolean_fields_are_ignored() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); - - let message = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"additional":true,"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn additional_object_fields_are_ignored() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); - - let message = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"additional":{"ignored":"field"},"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn additional_array_fields_are_ignored() { - let m = PhoenixMessage::new_message( - "gateway", - IngressMessages::Init(InitGateway { - interface: Interface { - ipv4: "100.115.164.78".parse().unwrap(), - ipv6: "fd00:2021:1111::2c:f6ab".parse().unwrap(), - upstream_dns: vec![], - }, - config: Config { - ipv4_masquerade_enabled: true, - ipv6_masquerade_enabled: true, - }, - relays: vec![], - }), - None, - ); - - let message = r#"{"event":"init","ref":null,"topic":"gateway","payload":{"additional":[true,false],"interface":{"ipv6":"fd00:2021:1111::2c:f6ab","ipv4":"100.115.164.78"},"config":{"ipv4_masquerade_enabled":true,"ipv6_masquerade_enabled":true}}}"#; - let ingress_message = - serde_json::from_str::>(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn resource_updated() { - let message = r#"{"event":"resource_updated","ref":null,"topic":"gateway","payload":{"id":"57f9ebbb-21d5-4f9f-bf86-b25122fc7a43","name":"?.httpbin","type":"dns","address":"?.httpbin","filters":[{"protocol":"icmp"},{"protocol":"tcp"}]}}"#; - let m = - IngressMessages::ResourceUpdated(ResourceDescription::Dns(ResourceDescriptionDns { - id: "57f9ebbb-21d5-4f9f-bf86-b25122fc7a43".parse().unwrap(), - address: "?.httpbin".to_string(), - name: "?.httpbin".to_string(), - filters: vec![ - Filter::Icmp, - Filter::Tcp(PortRange { - port_range_end: 65535, - port_range_start: 0, - }), - ], - })); - let ingress_message = serde_json::from_str::(message).unwrap(); - assert_eq!(m, ingress_message); - } - - #[test] - fn relays_presence() { - let message = r#" + fn can_deserialize_relays_presence_message() { + let json = r#" { "event": "relays_presence", "ref": null, @@ -699,22 +521,9 @@ mod tests { } } "#; - let expected = IngressMessages::RelaysPresence(RelaysPresence { - disconnected_ids: vec![ - "e95f9517-2152-4677-a16a-fbb2687050a3".parse().unwrap(), - "b0724bd1-a8cc-4faf-88cd-f21159cfec47".parse().unwrap(), - ], - connected: vec![Relay::Turn(Turn { - id: "0a133356-7a9e-4b9a-b413-0d95a5720fd8".parse().unwrap(), - expires_at: DateTime::from_timestamp(1719367575, 0).unwrap(), - addr: "172.28.0.101:3478".parse().unwrap(), - username: "1719367575:ZQHcVGkdnfgGmcP1".to_owned(), - password: "ZWYiBeFHOJyYq0mcwAXjRpcuXIJJpzWlOXVdxwttrWg".to_owned(), - })], - }); - let ingress_message = serde_json::from_str::(message).unwrap(); + let message = serde_json::from_str::(json).unwrap(); - assert_eq!(ingress_message, expected); + assert!(matches!(message, IngressMessages::RelaysPresence(_))); } } diff --git a/rust/connlib/tunnel/src/proptest.rs b/rust/connlib/tunnel/src/proptest.rs index fbce6a11a..9daf838a9 100644 --- a/rust/connlib/tunnel/src/proptest.rs +++ b/rust/connlib/tunnel/src/proptest.rs @@ -1,7 +1,3 @@ -use crate::messages::client::{ - ResourceDescription, ResourceDescriptionCidr, ResourceDescriptionDns, - ResourceDescriptionInternet, -}; use connlib_model::{ClientId, GatewayId, RelayId, ResourceId, Site, SiteId}; use ip_network::{IpNetwork, Ipv4Network, Ipv6Network}; use proptest::{ @@ -14,25 +10,23 @@ use std::{ ops::Range, }; +use crate::client::{CidrResource, DnsResource, InternetResource, Resource}; + pub fn resource( sites: impl Strategy> + Clone + 'static, -) -> impl Strategy { +) -> impl Strategy { any::().prop_flat_map(move |is_dns| { if is_dns { - dns_resource(sites.clone()) - .prop_map(ResourceDescription::Dns) - .boxed() + dns_resource(sites.clone()).prop_map(Resource::Dns).boxed() } else { cidr_resource(any_ip_network(8), sites.clone()) - .prop_map(ResourceDescription::Cidr) + .prop_map(Resource::Cidr) .boxed() } }) } -pub fn dns_resource( - sites: impl Strategy>, -) -> impl Strategy { +pub fn dns_resource(sites: impl Strategy>) -> impl Strategy { ( resource_id(), resource_name(), @@ -40,21 +34,21 @@ pub fn dns_resource( address_description(), sites, ) - .prop_map(move |(id, name, address, address_description, sites)| { - ResourceDescriptionDns { + .prop_map( + move |(id, name, address, address_description, sites)| DnsResource { id, address, name, sites, address_description, - } - }) + }, + ) } pub fn cidr_resource( ip_network: impl Strategy, sites: impl Strategy>, -) -> impl Strategy { +) -> impl Strategy { ( resource_id(), resource_name(), @@ -62,21 +56,21 @@ pub fn cidr_resource( address_description(), sites, ) - .prop_map(move |(id, name, address, address_description, sites)| { - ResourceDescriptionCidr { + .prop_map( + move |(id, name, address, address_description, sites)| CidrResource { id, address, name, sites, address_description, - } - }) + }, + ) } pub fn internet_resource( sites: impl Strategy>, -) -> impl Strategy { - (resource_id(), sites).prop_map(move |(id, sites)| ResourceDescriptionInternet { +) -> impl Strategy { + (resource_id(), sites).prop_map(move |(id, sites)| InternetResource { name: "Internet Resource".to_string(), id, sites, diff --git a/rust/connlib/tunnel/src/tests/reference.rs b/rust/connlib/tunnel/src/tests/reference.rs index ced847d8e..06ea6a17f 100644 --- a/rust/connlib/tunnel/src/tests/reference.rs +++ b/rust/connlib/tunnel/src/tests/reference.rs @@ -2,11 +2,8 @@ use super::{ composite_strategy::CompositeStrategy, sim_client::*, sim_dns::*, sim_gateway::*, sim_net::*, strategies::*, stub_portal::StubPortal, transition::*, }; +use crate::{client, DomainName, StaticSecret}; use crate::{dns::is_subdomain, proptest::relay_id}; -use crate::{ - messages::client::{self, ResourceDescription}, - DomainName, StaticSecret, -}; use connlib_model::{GatewayId, RelayId, ResourceId}; use domain::base::Rtype; use proptest::{prelude::*, sample}; @@ -293,7 +290,7 @@ impl ReferenceStateMachine for ReferenceState { match transition { Transition::ActivateResource(resource) => { state.client.exec_mut(|client| match resource { - client::ResourceDescription::Dns(r) => { + client::Resource::Dns(r) => { client.add_dns_resource(r.clone()); // TODO: PRODUCTION CODE CANNOT DO THIS. @@ -306,10 +303,8 @@ impl ReferenceStateMachine for ReferenceState { true }); } - client::ResourceDescription::Cidr(r) => client.add_cidr_resource(r.clone()), - client::ResourceDescription::Internet(r) => { - client.add_internet_resource(r.clone()) - } + client::Resource::Cidr(r) => client.add_cidr_resource(r.clone()), + client::Resource::Internet(r) => client.add_internet_resource(r.clone()), }); } Transition::DeactivateResource(id) => { @@ -659,7 +654,7 @@ impl ReferenceState { .collect() } - fn all_resources_not_known_to_client(&self) -> Vec { + fn all_resources_not_known_to_client(&self) -> Vec { let mut all_resources = self.portal.all_resources(); all_resources.retain(|r| !self.client.inner().has_resource(r.id())); diff --git a/rust/connlib/tunnel/src/tests/sim_client.rs b/rust/connlib/tunnel/src/tests/sim_client.rs index 88b918eef..5f33a751e 100644 --- a/rust/connlib/tunnel/src/tests/sim_client.rs +++ b/rust/connlib/tunnel/src/tests/sim_client.rs @@ -7,13 +7,8 @@ use super::{ IcmpIdentifier, IcmpSeq, QueryId, }; use crate::{ - messages::{ - client::{ - ResourceDescription, ResourceDescriptionCidr, ResourceDescriptionDns, - ResourceDescriptionInternet, - }, - DnsServer, Interface, - }, + client::{CidrResource, DnsResource, InternetResource, Resource}, + messages::{DnsServer, Interface}, DomainName, }; use crate::{proptest::*, ClientState}; @@ -287,7 +282,7 @@ pub struct RefClient { /// /// When reconnecting to the portal, we simulate them being re-added in the same order. #[derivative(Debug = "ignore")] - resources: Vec, + resources: Vec, #[derivative(Debug = "ignore")] internet_resource: Option, @@ -374,7 +369,7 @@ impl RefClient { fn recalculate_cidr_routes(&mut self) -> IpNetworkTable { let mut table = IpNetworkTable::::new(); for resource in self.resources.iter().sorted_by_key(|r| r.id()) { - let ResourceDescription::Cidr(resource) = resource else { + let Resource::Cidr(resource) = resource else { continue; }; @@ -401,10 +396,9 @@ impl RefClient { self.connected_gateways.clear(); } - pub(crate) fn add_internet_resource(&mut self, r: ResourceDescriptionInternet) { + pub(crate) fn add_internet_resource(&mut self, r: InternetResource) { self.internet_resource = Some(r.id); - self.resources - .push(ResourceDescription::Internet(r.clone())); + self.resources.push(Resource::Internet(r.clone())); if self.disabled_resources.contains(&r.id) { return; @@ -414,8 +408,8 @@ impl RefClient { self.ipv6_routes.insert(r.id, Ipv6Network::DEFAULT_ROUTE); } - pub(crate) fn add_cidr_resource(&mut self, r: ResourceDescriptionCidr) { - self.resources.push(ResourceDescription::Cidr(r.clone())); + pub(crate) fn add_cidr_resource(&mut self, r: CidrResource) { + self.resources.push(Resource::Cidr(r.clone())); self.cidr_resources = self.recalculate_cidr_routes(); if self.disabled_resources.contains(&r.id) { @@ -432,8 +426,8 @@ impl RefClient { } } - pub(crate) fn add_dns_resource(&mut self, r: ResourceDescriptionDns) { - self.resources.push(ResourceDescription::Dns(r)); + pub(crate) fn add_dns_resource(&mut self, r: DnsResource) { + self.resources.push(Resource::Dns(r)); } /// Re-adds all resources in the order they have been initially added. @@ -443,9 +437,9 @@ impl RefClient { for resource in mem::take(&mut self.resources) { match resource { - ResourceDescription::Dns(d) => self.add_dns_resource(d), - ResourceDescription::Cidr(c) => self.add_cidr_resource(c), - ResourceDescription::Internet(i) => self.add_internet_resource(i), + Resource::Dns(d) => self.add_dns_resource(d), + Resource::Cidr(c) => self.add_cidr_resource(c), + Resource::Internet(i) => self.add_internet_resource(i), } } } @@ -838,7 +832,7 @@ impl RefClient { self.resources.iter().any(|r| r.id() == resource_id) } - pub(crate) fn all_resources(&self) -> Vec { + pub(crate) fn all_resources(&self) -> Vec { self.resources.clone() } diff --git a/rust/connlib/tunnel/src/tests/strategies.rs b/rust/connlib/tunnel/src/tests/strategies.rs index 0bc8cd2a9..1403b0e19 100644 --- a/rust/connlib/tunnel/src/tests/strategies.rs +++ b/rust/connlib/tunnel/src/tests/strategies.rs @@ -4,15 +4,9 @@ use super::{ sim_relay::ref_relay_host, stub_portal::StubPortal, }; -use crate::client::{IPV4_RESOURCES, IPV6_RESOURCES}; +use crate::client::{CidrResource, DnsResource, InternetResource, IPV4_RESOURCES, IPV6_RESOURCES}; use crate::proptest::*; -use crate::{ - messages::{ - client::{ResourceDescriptionCidr, ResourceDescriptionDns, ResourceDescriptionInternet}, - DnsServer, - }, - DomainName, -}; +use crate::{messages::DnsServer, DomainName}; use connlib_model::{RelayId, Site}; use ip_network::{IpNetwork, Ipv4Network, Ipv6Network}; use itertools::Itertools; @@ -152,7 +146,7 @@ fn any_site(sites: BTreeSet) -> impl Strategy { fn cidr_resource_outside_reserved_ranges( sites: impl Strategy, -) -> impl Strategy { +) -> impl Strategy { cidr_resource(any_ip_network(8), sites.prop_map(|s| vec![s])) .prop_filter( "tests doesn't support yet CIDR resources overlapping DNS resources", @@ -169,22 +163,20 @@ fn cidr_resource_outside_reserved_ranges( .prop_filter("resource must not be in the documentation range because we use those for host addresses and DNS IPs", |r| !r.address.is_documentation()) } -fn internet_resource( - site: impl Strategy, -) -> impl Strategy { +fn internet_resource(site: impl Strategy) -> impl Strategy { crate::proptest::internet_resource(site.prop_map(|s| vec![s])) } fn non_wildcard_dns_resource( site: impl Strategy, -) -> impl Strategy { +) -> impl Strategy { dns_resource(site.prop_map(|s| vec![s])) } fn star_wildcard_dns_resource( site: impl Strategy, -) -> impl Strategy { - dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| ResourceDescriptionDns { +) -> impl Strategy { + dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| DnsResource { address: format!("*.{}", r.address), ..r }) @@ -192,8 +184,8 @@ fn star_wildcard_dns_resource( fn double_star_wildcard_dns_resource( site: impl Strategy, -) -> impl Strategy { - dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| ResourceDescriptionDns { +) -> impl Strategy { + dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| DnsResource { address: format!("**.{}", r.address), ..r }) diff --git a/rust/connlib/tunnel/src/tests/stub_portal.rs b/rust/connlib/tunnel/src/tests/stub_portal.rs index 0fb409569..acd577410 100644 --- a/rust/connlib/tunnel/src/tests/stub_portal.rs +++ b/rust/connlib/tunnel/src/tests/stub_portal.rs @@ -4,9 +4,9 @@ use super::{ sim_net::Host, strategies::{resolved_ips, subdomain_records}, }; -use crate::proptest::*; +use crate::{client, proptest::*}; use crate::{ - messages::{client, gateway, DnsServer}, + messages::{gateway, DnsServer}, DomainName, }; use connlib_model::GatewayId; @@ -31,9 +31,11 @@ pub(crate) struct StubPortal { #[derivative(Debug = "ignore")] sites_by_resource: BTreeMap, - cidr_resources: BTreeMap, - dns_resources: BTreeMap, - internet_resource: client::ResourceDescriptionInternet, + + // TODO: Maybe these should use the `messages` types to cover the conversions and to model that that is what we receive from the portal? + cidr_resources: BTreeMap, + dns_resources: BTreeMap, + internet_resource: client::InternetResource, #[derivative(Debug = "ignore")] gateway_selector: Selector, @@ -43,9 +45,9 @@ impl StubPortal { pub(crate) fn new( gateways_by_site: BTreeMap>, gateway_selector: Selector, - cidr_resources: BTreeSet, - dns_resources: BTreeSet, - internet_resource: client::ResourceDescriptionInternet, + cidr_resources: BTreeSet, + dns_resources: BTreeSet, + internet_resource: client::InternetResource, ) -> Self { let cidr_resources = cidr_resources .into_iter() @@ -98,18 +100,18 @@ impl StubPortal { } } - pub(crate) fn all_resources(&self) -> Vec { + pub(crate) fn all_resources(&self) -> Vec { self.cidr_resources .values() .cloned() - .map(client::ResourceDescription::Cidr) + .map(client::Resource::Cidr) .chain( self.dns_resources .values() .cloned() - .map(client::ResourceDescription::Dns), + .map(client::Resource::Dns), ) - .chain(iter::once(client::ResourceDescription::Internet( + .chain(iter::once(client::Resource::Internet( self.internet_resource.clone(), ))) .collect() diff --git a/rust/connlib/tunnel/src/tests/sut.rs b/rust/connlib/tunnel/src/tests/sut.rs index 6bf6af280..c9e4a0475 100644 --- a/rust/connlib/tunnel/src/tests/sut.rs +++ b/rust/connlib/tunnel/src/tests/sut.rs @@ -7,9 +7,9 @@ use super::sim_net::{Host, HostId, RoutingTable}; use super::sim_relay::SimRelay; use super::stub_portal::StubPortal; use super::transition::DnsQuery; +use crate::client::Resource; use crate::dns::is_subdomain; use crate::gateway::DnsResourceNatEntry; -use crate::messages::client::ResourceDescription; use crate::tests::assertions::*; use crate::tests::flux_capacitor::FluxCapacitor; use crate::tests::transition::Transition; @@ -121,7 +121,7 @@ impl TunnelTest { state.client.exec_mut(|c| { // Flush DNS. match &resource { - ResourceDescription::Dns(r) => { + Resource::Dns(r) => { c.dns_records.retain(|domain, _| { if is_subdomain(domain, &r.address) { return false; @@ -130,8 +130,8 @@ impl TunnelTest { true }); } - ResourceDescription::Cidr(_) => {} - ResourceDescription::Internet(_) => {} + Resource::Cidr(_) => {} + Resource::Internet(_) => {} } c.sut.add_resource(resource); diff --git a/rust/connlib/tunnel/src/tests/transition.rs b/rust/connlib/tunnel/src/tests/transition.rs index 7d11c4a49..242108d81 100644 --- a/rust/connlib/tunnel/src/tests/transition.rs +++ b/rust/connlib/tunnel/src/tests/transition.rs @@ -1,11 +1,11 @@ use crate::{ - client::{IPV4_RESOURCES, IPV6_RESOURCES}, + client::{Resource, IPV4_RESOURCES, IPV6_RESOURCES}, proptest::{host_v4, host_v6}, }; use connlib_model::RelayId; use super::sim_net::{any_ip_stack, any_port, Host}; -use crate::messages::{client::ResourceDescription, DnsServer}; +use crate::messages::DnsServer; use connlib_model::{DomainName, ResourceId}; use domain::base::Rtype; use prop::collection; @@ -21,7 +21,7 @@ use std::{ #[expect(clippy::large_enum_variant)] pub(crate) enum Transition { /// Activate a resource on the client. - ActivateResource(ResourceDescription), + ActivateResource(Resource), /// Deactivate a resource on the client. DeactivateResource(ResourceId), /// Client-side disable resource