mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
refactor(connlib): introduce dedicated Resource model (#6920)
Following up from #6919, this PR introduces a dedicated, internal model for resources as to how the client uses them. This separation serves several purposes: 1. It allows us to introduce an `Unknown` resource type, ensuring forwards-compatibility with future resource types. 2. It allows us to remove trait implementations like `PartialEq` or `PartialOrd` from the message types. With #6732, the messages will include types like `SecretKey`s which cannot be compared. 3. A decoupling of serialisation and domain models is good practice in general and has long been overdue.
This commit is contained in:
1
rust/Cargo.lock
generated
1
rust/Cargo.lock
generated
@@ -2470,7 +2470,6 @@ dependencies = [
|
||||
"ip_network_table",
|
||||
"itertools 0.13.0",
|
||||
"lru",
|
||||
"phoenix-channel",
|
||||
"proptest",
|
||||
"proptest-state-machine",
|
||||
"rand 0.8.5",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<ResourceDescription>) {
|
||||
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<SiteId, ResourceStatus>,
|
||||
|
||||
/// All CIDR resources we know about, indexed by the IP range they cover (like `1.1.0.0/8`).
|
||||
active_cidr_resources: IpNetworkTable<ResourceDescriptionCidr>,
|
||||
active_cidr_resources: IpNetworkTable<CidrResource>,
|
||||
/// `Some` if the Internet resource is enabled.
|
||||
internet_resource: Option<ResourceId>,
|
||||
/// All resources indexed by their ID.
|
||||
resources_by_id: BTreeMap<ResourceId, ResourceDescription>,
|
||||
resources_by_id: BTreeMap<ResourceId, Resource>,
|
||||
|
||||
/// The DNS resolvers configured on the system outside of connlib.
|
||||
system_resolvers: Vec<IpAddr>,
|
||||
@@ -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<ResourceDescriptionCidr> {
|
||||
let mut active_cidr_resources = IpNetworkTable::<ResourceDescriptionCidr>::new();
|
||||
fn recalculate_active_cidr_resources(&self) -> IpNetworkTable<CidrResource> {
|
||||
let mut active_cidr_resources = IpNetworkTable::<CidrResource>::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<ResourceDescription>) {
|
||||
pub(crate) fn set_resources(&mut self, new_resources: Vec<Resource>) {
|
||||
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<IpNetwork> {
|
||||
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<ResourceDescription>,
|
||||
#[strategy(resources_sharing_n_sites(1))] resources_unknown: Vec<ResourceDescription>,
|
||||
#[strategy(resources_sharing_n_sites(1))] resources_online: Vec<Resource>,
|
||||
#[strategy(resources_sharing_n_sites(1))] resources_unknown: Vec<Resource>,
|
||||
#[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<ResourceDescription>,
|
||||
#[strategy(resources_sharing_n_sites(1))] resources: Vec<Resource>,
|
||||
#[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<ResourceDescription>,
|
||||
#[strategy(resource())] single_site_resource: ResourceDescription,
|
||||
#[strategy(resources_sharing_n_sites(2))] multi_site_resources: Vec<Resource>,
|
||||
#[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<Value = ResourceDescription> {
|
||||
fn resource() -> impl Strategy<Value = Resource> {
|
||||
crate::proptest::resource(site().prop_map(|s| vec![s]))
|
||||
}
|
||||
|
||||
fn cidr_resource() -> impl Strategy<Value = ResourceDescriptionCidr> {
|
||||
fn cidr_resource() -> impl Strategy<Value = CidrResource> {
|
||||
crate::proptest::cidr_resource(any_ip_network(8), site().prop_map(|s| vec![s]))
|
||||
}
|
||||
|
||||
fn dns_resource() -> impl Strategy<Value = ResourceDescriptionDns> {
|
||||
fn dns_resource() -> impl Strategy<Value = DnsResource> {
|
||||
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<Value = Vec<ResourceDescription>> {
|
||||
fn resources_sharing_n_sites(num_sites: usize) -> impl Strategy<Value = Vec<Resource>> {
|
||||
collection::vec(site(), num_sites)
|
||||
.prop_flat_map(|sites| collection::vec(crate::proptest::resource(Just(sites)), 1..=100))
|
||||
}
|
||||
|
||||
206
rust/connlib/tunnel/src/client/resource.rs
Normal file
206
rust/connlib/tunnel/src/client/resource.rs
Normal file
@@ -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<String>,
|
||||
pub sites: Vec<Site>,
|
||||
}
|
||||
|
||||
/// 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<String>,
|
||||
pub sites: Vec<Site>,
|
||||
}
|
||||
|
||||
/// 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<Site>,
|
||||
}
|
||||
|
||||
impl Resource {
|
||||
pub fn from_description(resource: ResourceDescription) -> Option<Self> {
|
||||
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<DnsResource> {
|
||||
match self {
|
||||
Resource::Dns(d) => Some(d),
|
||||
Resource::Cidr(_) | Resource::Internet(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address_string(&self) -> Option<String> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Site>,
|
||||
}
|
||||
|
||||
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<Site>,
|
||||
}
|
||||
|
||||
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<Site>,
|
||||
}
|
||||
|
||||
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<String> {
|
||||
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<Site> {
|
||||
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<ResourceDescriptionDns> {
|
||||
match self {
|
||||
ResourceDescription::Dns(d) => Some(d),
|
||||
ResourceDescription::Cidr(_) | ResourceDescription::Internet(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_cidr(self) -> Option<ResourceDescriptionCidr> {
|
||||
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<Relay>,
|
||||
}
|
||||
|
||||
#[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<GatewayId>,
|
||||
@@ -252,7 +129,7 @@ pub struct GatewaysIceCandidates {
|
||||
pub candidates: BTreeSet<String>,
|
||||
}
|
||||
|
||||
#[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::<PhoenixMessage<_, ()>>(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::<IngressMessages>(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<IngressMessages, ReplyMessages> =
|
||||
serde_json::from_str(message).unwrap();
|
||||
]"#;
|
||||
|
||||
serde_json::from_str::<Vec<ResourceDescription>>(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::<IngressMessages>(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::<IngressMessages>(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::<ReplyMessages>(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::<ReplyMessages>(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<IngressMessages, ReplyMessages> =
|
||||
serde_json::from_str(message).unwrap();
|
||||
assert_eq!(m, ingress_message);
|
||||
|
||||
let message = serde_json::from_str::<IngressMessages>(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<IngressMessages, ReplyMessages> =
|
||||
serde_json::from_str(message).unwrap();
|
||||
assert_eq!(m, ingress_message);
|
||||
|
||||
let message = serde_json::from_str::<IngressMessages>(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<IngressMessages, ReplyMessages> =
|
||||
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<IngressMessages, ReplyMessages> =
|
||||
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<IngressMessages, ReplyMessages> =
|
||||
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<IngressMessages, ReplyMessages> =
|
||||
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<IngressMessages, ReplyMessages> =
|
||||
serde_json::from_str(message).unwrap();
|
||||
assert_eq!(m, ingress_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_relays_message() {
|
||||
let m = PhoenixMessage::<EgressMessages, ()>::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::<EgressMessages, ReplyMessages>::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::<IngressMessages>(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::<IngressMessages>(message).unwrap();
|
||||
|
||||
assert_eq!(ingress_message, expected);
|
||||
assert_eq!(actual_json, expected_json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use std::{
|
||||
pub type Filters = Vec<Filter>;
|
||||
|
||||
/// 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<ResolveRequest>,
|
||||
}
|
||||
|
||||
// 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<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[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<ClientId>,
|
||||
@@ -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<IngressMessages, ()> = serde_json::from_str(message).unwrap();
|
||||
|
||||
let message = serde_json::from_str::<IngressMessages>(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<IngressMessages, ()> = serde_json::from_str(message).unwrap();
|
||||
|
||||
let message = serde_json::from_str::<IngressMessages>(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::<IngressMessages>(msg).unwrap();
|
||||
let message = serde_json::from_str::<IngressMessages>(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::<PhoenixMessage<IngressMessages, ()>>(message).unwrap();
|
||||
assert_eq!(m, ingress_message);
|
||||
let message = serde_json::from_str::<IngressMessages>(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::<PhoenixMessage<IngressMessages, ()>>(message).unwrap();
|
||||
assert_eq!(m, ingress_message);
|
||||
let message = serde_json::from_str::<IngressMessages>(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::<PhoenixMessage<IngressMessages, ()>>(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::<PhoenixMessage<IngressMessages, ()>>(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::<PhoenixMessage<IngressMessages, ()>>(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::<PhoenixMessage<IngressMessages, ()>>(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::<PhoenixMessage<IngressMessages, ()>>(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::<IngressMessages>(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::<IngressMessages>(message).unwrap();
|
||||
let message = serde_json::from_str::<IngressMessages>(json).unwrap();
|
||||
|
||||
assert_eq!(ingress_message, expected);
|
||||
assert!(matches!(message, IngressMessages::RelaysPresence(_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Value = Vec<Site>> + Clone + 'static,
|
||||
) -> impl Strategy<Value = ResourceDescription> {
|
||||
) -> impl Strategy<Value = Resource> {
|
||||
any::<bool>().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<Value = Vec<Site>>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionDns> {
|
||||
pub fn dns_resource(sites: impl Strategy<Value = Vec<Site>>) -> impl Strategy<Value = DnsResource> {
|
||||
(
|
||||
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<Value = IpNetwork>,
|
||||
sites: impl Strategy<Value = Vec<Site>>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionCidr> {
|
||||
) -> impl Strategy<Value = CidrResource> {
|
||||
(
|
||||
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<Value = Vec<Site>>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionInternet> {
|
||||
(resource_id(), sites).prop_map(move |(id, sites)| ResourceDescriptionInternet {
|
||||
) -> impl Strategy<Value = InternetResource> {
|
||||
(resource_id(), sites).prop_map(move |(id, sites)| InternetResource {
|
||||
name: "Internet Resource".to_string(),
|
||||
id,
|
||||
sites,
|
||||
|
||||
@@ -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<ResourceDescription> {
|
||||
fn all_resources_not_known_to_client(&self) -> Vec<client::Resource> {
|
||||
let mut all_resources = self.portal.all_resources();
|
||||
all_resources.retain(|r| !self.client.inner().has_resource(r.id()));
|
||||
|
||||
|
||||
@@ -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<ResourceDescription>,
|
||||
resources: Vec<Resource>,
|
||||
|
||||
#[derivative(Debug = "ignore")]
|
||||
internet_resource: Option<ResourceId>,
|
||||
@@ -374,7 +369,7 @@ impl RefClient {
|
||||
fn recalculate_cidr_routes(&mut self) -> IpNetworkTable<ResourceId> {
|
||||
let mut table = IpNetworkTable::<ResourceId>::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<ResourceDescription> {
|
||||
pub(crate) fn all_resources(&self) -> Vec<Resource> {
|
||||
self.resources.clone()
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Site>) -> impl Strategy<Value = Site> {
|
||||
|
||||
fn cidr_resource_outside_reserved_ranges(
|
||||
sites: impl Strategy<Value = Site>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionCidr> {
|
||||
) -> impl Strategy<Value = CidrResource> {
|
||||
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<Value = Site>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionInternet> {
|
||||
fn internet_resource(site: impl Strategy<Value = Site>) -> impl Strategy<Value = InternetResource> {
|
||||
crate::proptest::internet_resource(site.prop_map(|s| vec![s]))
|
||||
}
|
||||
|
||||
fn non_wildcard_dns_resource(
|
||||
site: impl Strategy<Value = Site>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionDns> {
|
||||
) -> impl Strategy<Value = DnsResource> {
|
||||
dns_resource(site.prop_map(|s| vec![s]))
|
||||
}
|
||||
|
||||
fn star_wildcard_dns_resource(
|
||||
site: impl Strategy<Value = Site>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionDns> {
|
||||
dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| ResourceDescriptionDns {
|
||||
) -> impl Strategy<Value = DnsResource> {
|
||||
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<Value = Site>,
|
||||
) -> impl Strategy<Value = ResourceDescriptionDns> {
|
||||
dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| ResourceDescriptionDns {
|
||||
) -> impl Strategy<Value = DnsResource> {
|
||||
dns_resource(site.prop_map(|s| vec![s])).prop_map(|r| DnsResource {
|
||||
address: format!("**.{}", r.address),
|
||||
..r
|
||||
})
|
||||
|
||||
@@ -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<ResourceId, SiteId>,
|
||||
cidr_resources: BTreeMap<ResourceId, client::ResourceDescriptionCidr>,
|
||||
dns_resources: BTreeMap<ResourceId, client::ResourceDescriptionDns>,
|
||||
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<ResourceId, client::CidrResource>,
|
||||
dns_resources: BTreeMap<ResourceId, client::DnsResource>,
|
||||
internet_resource: client::InternetResource,
|
||||
|
||||
#[derivative(Debug = "ignore")]
|
||||
gateway_selector: Selector,
|
||||
@@ -43,9 +45,9 @@ impl StubPortal {
|
||||
pub(crate) fn new(
|
||||
gateways_by_site: BTreeMap<SiteId, BTreeSet<GatewayId>>,
|
||||
gateway_selector: Selector,
|
||||
cidr_resources: BTreeSet<client::ResourceDescriptionCidr>,
|
||||
dns_resources: BTreeSet<client::ResourceDescriptionDns>,
|
||||
internet_resource: client::ResourceDescriptionInternet,
|
||||
cidr_resources: BTreeSet<client::CidrResource>,
|
||||
dns_resources: BTreeSet<client::DnsResource>,
|
||||
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<client::ResourceDescription> {
|
||||
pub(crate) fn all_resources(&self) -> Vec<client::Resource> {
|
||||
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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user