mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-10-08 19:06:14 -08:00
tcp_stun_servers explicitly controls TCP STUN servers. If tcp_stun_servers is not configured, TCP STUN falls back to configured stun_servers. If neither is configured, TCP STUN uses the built-in default TCP STUN list. Empty lists explicitly disable the corresponding STUN server list. Empty CLI/env overrides now clear existing configured STUN servers instead of appending nothing.
690 lines
25 KiB
Rust
690 lines
25 KiB
Rust
//! Portable normalization from the shared TOML model into one core instance.
|
|
|
|
use std::collections::BTreeSet;
|
|
|
|
use crate::{
|
|
config::{
|
|
EncryptionAlgorithm, IpPrefix, NodeConfig, ProxyNetworkConfig, RouteConfig,
|
|
gateway::{GatewayRuntimeConfig, ProxyRuntimeConfig},
|
|
peers::{AclRuleConfig, HostRoutingPolicy, PublicIpv6ProviderConfig},
|
|
runtime::CoreRuntimeConfig,
|
|
toml::{ConfigLoader as _, Flags, TomlConfig},
|
|
},
|
|
connectivity::{
|
|
direct::DirectConnectorOptions,
|
|
manual::{ManualConnectorOptions, discovery::ManualEndpointDiscoveryConfig},
|
|
stun::StunServerConfig,
|
|
},
|
|
listener::plan::ListenerRuntimeConfig,
|
|
packet::CompressorAlgo,
|
|
peers::{
|
|
context::PeerRuntimeSnapshotInput,
|
|
peer_manager::{PortablePeerManagerConfig, RouteAlgoType},
|
|
},
|
|
socket::{NetNamespace, SocketContext, tcp::TcpBindOptions, udp::UdpBindOptions},
|
|
tunnel::encrypt::algorithm_is_available,
|
|
};
|
|
|
|
use easytier_proto::common::CompressionAlgoPb;
|
|
|
|
use super::{CoreConnectivityConfig, CoreInstanceConfig};
|
|
|
|
const OSPF_UPDATE_MY_FOREIGN_NETWORK_INTERVAL_SEC: u64 = 10;
|
|
const MAX_DIRECT_CONNS_PER_PEER_IN_FOREIGN_NETWORK: usize = 3;
|
|
|
|
/// Host facts and policy that cannot be derived from the shared TOML model.
|
|
///
|
|
/// This input deliberately contains no routes, ACL, peer, gateway, listener,
|
|
/// or other portable configuration. Core combines it with TOML through one
|
|
/// normalization path for both initial construction and runtime patching.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct CoreInstanceHostConfig {
|
|
pub hostname_fallback: Option<String>,
|
|
pub host_routing: HostRoutingPolicy,
|
|
pub force_exit_node: bool,
|
|
pub allow_interface_bind: bool,
|
|
pub smoltcp_available: bool,
|
|
pub requires_smoltcp: bool,
|
|
pub icmp_failure_is_fatal: bool,
|
|
pub public_ipv6_provider_supported: bool,
|
|
pub gateway_enabled: bool,
|
|
pub proxy_enabled: bool,
|
|
pub vpn_portal_enabled: bool,
|
|
pub magic_dns_enabled: bool,
|
|
pub kcp_enabled: bool,
|
|
pub quic_enabled: bool,
|
|
pub udp_broadcast_enabled: bool,
|
|
pub upnp_enabled: bool,
|
|
pub tcp_hole_punching_enabled: bool,
|
|
pub ignore_unsupported_config: bool,
|
|
pub easytier_version: String,
|
|
pub endpoint_protocols: Vec<String>,
|
|
}
|
|
|
|
impl Default for CoreInstanceHostConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
hostname_fallback: None,
|
|
host_routing: HostRoutingPolicy::default(),
|
|
force_exit_node: false,
|
|
allow_interface_bind: true,
|
|
smoltcp_available: false,
|
|
requires_smoltcp: false,
|
|
icmp_failure_is_fatal: false,
|
|
public_ipv6_provider_supported: false,
|
|
gateway_enabled: true,
|
|
proxy_enabled: true,
|
|
vpn_portal_enabled: true,
|
|
magic_dns_enabled: true,
|
|
kcp_enabled: true,
|
|
quic_enabled: true,
|
|
udp_broadcast_enabled: true,
|
|
upnp_enabled: true,
|
|
tcp_hole_punching_enabled: true,
|
|
ignore_unsupported_config: false,
|
|
easytier_version: env!("CARGO_PKG_VERSION").to_owned(),
|
|
endpoint_protocols: ManualEndpointDiscoveryConfig::default().srv_protocols,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CoreInstanceHostConfig {
|
|
pub(crate) fn accepts_runtime_url(&self, url: &url::Url) -> bool {
|
|
!self.ignore_unsupported_config
|
|
|| self
|
|
.endpoint_protocols
|
|
.iter()
|
|
.any(|scheme| scheme.eq_ignore_ascii_case(url.scheme()))
|
|
}
|
|
|
|
fn runtime_flags(&self, mut flags: Flags) -> Flags {
|
|
if !self.ignore_unsupported_config {
|
|
return flags;
|
|
}
|
|
|
|
if !self.smoltcp_available {
|
|
flags.no_tun = false;
|
|
flags.use_smoltcp = false;
|
|
}
|
|
if !self.proxy_enabled {
|
|
flags.enable_exit_node = false;
|
|
}
|
|
if !self.magic_dns_enabled {
|
|
flags.accept_dns = false;
|
|
}
|
|
if !self.kcp_enabled {
|
|
flags.enable_kcp_proxy = false;
|
|
flags.disable_kcp_input = true;
|
|
flags.disable_relay_kcp = true;
|
|
flags.enable_relay_foreign_network_kcp = false;
|
|
}
|
|
if !self.quic_enabled {
|
|
flags.enable_quic_proxy = false;
|
|
flags.disable_quic_input = true;
|
|
flags.disable_relay_quic = true;
|
|
flags.enable_relay_foreign_network_quic = false;
|
|
}
|
|
if !self.udp_broadcast_enabled {
|
|
flags.enable_udp_broadcast_relay = false;
|
|
}
|
|
if !self.upnp_enabled {
|
|
flags.disable_upnp = true;
|
|
}
|
|
if !self.tcp_hole_punching_enabled {
|
|
flags.disable_tcp_hole_punching = true;
|
|
}
|
|
if CompressionAlgoPb::try_from(flags.data_compress_algo)
|
|
.ok()
|
|
.and_then(|algorithm| CompressorAlgo::try_from(algorithm).ok())
|
|
.is_some_and(|algorithm| !algorithm.is_available())
|
|
{
|
|
flags.data_compress_algo = CompressionAlgoPb::None as i32;
|
|
}
|
|
|
|
if flags
|
|
.encryption_algorithm
|
|
.parse::<EncryptionAlgorithm>()
|
|
.is_ok_and(|algorithm| !algorithm_is_available(algorithm))
|
|
&& algorithm_is_available(EncryptionAlgorithm::AesGcm)
|
|
{
|
|
flags.encryption_algorithm = EncryptionAlgorithm::AesGcm.to_string();
|
|
}
|
|
|
|
flags
|
|
}
|
|
}
|
|
|
|
impl CoreInstanceConfig {
|
|
/// Normalizes the complete shared TOML model using OS-independent defaults.
|
|
///
|
|
/// A Host may still project runtime facts such as a fallback hostname or
|
|
/// platform capability after parsing, but it does not need another network
|
|
/// configuration schema.
|
|
pub fn from_toml(config: &TomlConfig) -> anyhow::Result<Self> {
|
|
Self::from_toml_with_host(config, &CoreInstanceHostConfig::default())
|
|
}
|
|
|
|
/// Normalizes TOML with explicit Host facts and policy.
|
|
pub fn from_toml_with_host(
|
|
config: &TomlConfig,
|
|
host: &CoreInstanceHostConfig,
|
|
) -> anyhow::Result<Self> {
|
|
let flags = host.runtime_flags(config.get_flags());
|
|
let instance_id = config.get_id();
|
|
let identity: crate::config::NetworkIdentity = config.get_network_identity().into();
|
|
let network_name = identity.network_name.clone();
|
|
let socket_context = SocketContext::default()
|
|
.with_socket_mark(flags.socket_mark)
|
|
.with_netns(config.get_netns().map(NetNamespace::new));
|
|
let hostname = match config.get_hostname() {
|
|
hostname if !hostname.is_empty() => hostname,
|
|
_ => host.hostname_fallback.clone().unwrap_or_default(),
|
|
};
|
|
let acl = config.get_acl();
|
|
let peers = config
|
|
.get_peers()
|
|
.into_iter()
|
|
.filter(|peer| host.accepts_runtime_url(&peer.uri))
|
|
.collect::<Vec<_>>();
|
|
let proxy_networks = if host.ignore_unsupported_config && !host.proxy_enabled {
|
|
Vec::new()
|
|
} else {
|
|
config.get_proxy_cidrs()
|
|
};
|
|
|
|
let peer_snapshot =
|
|
crate::config::peers::PeerRuntimeSnapshot::from_host_input(PeerRuntimeSnapshotInput {
|
|
node: NodeConfig {
|
|
peer_id: None,
|
|
instance_id: Some(*instance_id.as_bytes()),
|
|
hostname: (!hostname.is_empty()).then_some(hostname),
|
|
network_name: network_name.clone(),
|
|
},
|
|
routes: RouteConfig {
|
|
ipv4: config.get_ipv4().map(|value| IpPrefix {
|
|
address: value.address().into(),
|
|
prefix_len: value.network_length(),
|
|
}),
|
|
ipv6: config.get_ipv6().map(|value| IpPrefix {
|
|
address: value.address().into(),
|
|
prefix_len: value.network_length(),
|
|
}),
|
|
proxy_networks: proxy_networks
|
|
.into_iter()
|
|
.map(|proxy| ProxyNetworkConfig {
|
|
real: IpPrefix {
|
|
address: proxy.cidr.first_address().into(),
|
|
prefix_len: proxy.cidr.network_length(),
|
|
},
|
|
mapped: proxy.mapped_cidr.map(|mapped| IpPrefix {
|
|
address: mapped.first_address().into(),
|
|
prefix_len: mapped.network_length(),
|
|
}),
|
|
})
|
|
.collect(),
|
|
..Default::default()
|
|
},
|
|
network_identity: identity,
|
|
stun_info: Default::default(),
|
|
flags: flags.clone(),
|
|
secure_mode: config.get_secure_mode(),
|
|
host_routing: host.host_routing,
|
|
acl: acl.clone(),
|
|
easytier_version: host.easytier_version.clone(),
|
|
vpn_portal_cidr: (!host.ignore_unsupported_config || host.vpn_portal_enabled)
|
|
.then(|| config.get_vpn_portal_config())
|
|
.flatten()
|
|
.map(|portal| portal.client_cidr),
|
|
pinned_peers: peers
|
|
.iter()
|
|
.cloned()
|
|
.map(|peer| (peer.uri, peer.peer_public_key))
|
|
.collect(),
|
|
ospf_update_my_foreign_network_interval_sec:
|
|
OSPF_UPDATE_MY_FOREIGN_NETWORK_INTERVAL_SEC,
|
|
max_direct_conns_per_peer_in_foreign_network:
|
|
MAX_DIRECT_CONNS_PER_PEER_IN_FOREIGN_NETWORK,
|
|
hmac_secret_digest: false,
|
|
});
|
|
let peer = PortablePeerManagerConfig {
|
|
snapshot: peer_snapshot,
|
|
route_algo: RouteAlgoType::Ospf,
|
|
exit_nodes: if host.ignore_unsupported_config && !host.proxy_enabled {
|
|
Vec::new()
|
|
} else {
|
|
config.get_exit_nodes()
|
|
},
|
|
foreign_context_default_flags: host.runtime_flags(TomlConfig::default().get_flags()),
|
|
};
|
|
|
|
let tcp_bind = TcpBindOptions::default().with_context(socket_context.clone());
|
|
let udp_bind = UdpBindOptions::direct_connect().with_context(socket_context.clone());
|
|
let listeners = Some(ListenerRuntimeConfig::new(
|
|
config
|
|
.get_listener_uris()
|
|
.into_iter()
|
|
.filter(|url| host.accepts_runtime_url(url))
|
|
.collect(),
|
|
flags.enable_ipv6,
|
|
socket_context.clone(),
|
|
));
|
|
let socks5_bind = (!host.ignore_unsupported_config || host.gateway_enabled)
|
|
.then(|| config.get_socks5_portal())
|
|
.flatten()
|
|
.map(|url| {
|
|
let host = url
|
|
.host_str()
|
|
.ok_or_else(|| anyhow::anyhow!("SOCKS5 portal host is missing"))?;
|
|
let port = url
|
|
.port()
|
|
.ok_or_else(|| anyhow::anyhow!("SOCKS5 portal port is missing"))?;
|
|
format!("{host}:{port}")
|
|
.parse()
|
|
.map_err(|error| anyhow::anyhow!("invalid SOCKS5 portal address: {error}"))
|
|
})
|
|
.transpose()?;
|
|
let runtime = CoreRuntimeConfig {
|
|
acl: AclRuleConfig {
|
|
acl,
|
|
tcp_whitelist: config.get_tcp_whitelist(),
|
|
udp_whitelist: config.get_udp_whitelist(),
|
|
whitelist_priority: None,
|
|
},
|
|
dhcp_ipv4: config.get_dhcp(),
|
|
gateway: GatewayRuntimeConfig {
|
|
socks5_bind,
|
|
port_forwards: if host.ignore_unsupported_config && !host.gateway_enabled {
|
|
Vec::new()
|
|
} else {
|
|
config.get_port_forwards()
|
|
},
|
|
},
|
|
manual_routes: config
|
|
.get_routes()
|
|
.map(|routes| routes.into_iter().collect::<BTreeSet<_>>()),
|
|
proxy: ProxyRuntimeConfig {
|
|
enable_exit_node: flags.enable_exit_node || host.force_exit_node,
|
|
no_tun: flags.no_tun,
|
|
forward_by_system: flags.proxy_forward_by_system,
|
|
force_smoltcp: host.smoltcp_available
|
|
&& (flags.use_smoltcp || flags.no_tun || host.requires_smoltcp),
|
|
icmp_failure_is_fatal: host.icmp_failure_is_fatal,
|
|
udp_response_ipv4_mtu: 1280,
|
|
},
|
|
public_ipv6_auto: config.get_ipv6_public_addr_auto()
|
|
&& (!host.ignore_unsupported_config || host.public_ipv6_provider_supported),
|
|
public_ipv6_provider: PublicIpv6ProviderConfig {
|
|
provider_enabled: config.get_ipv6_public_addr_provider()
|
|
&& (!host.ignore_unsupported_config || host.public_ipv6_provider_supported),
|
|
configured_prefix: (!host.ignore_unsupported_config
|
|
|| host.public_ipv6_provider_supported)
|
|
.then(|| config.get_ipv6_public_addr_prefix())
|
|
.flatten(),
|
|
provider_supported: host.public_ipv6_provider_supported,
|
|
},
|
|
};
|
|
let stun_servers = config.get_stun_servers();
|
|
|
|
Ok(Self {
|
|
instance_name: config.get_inst_name(),
|
|
peer,
|
|
connectivity: CoreConnectivityConfig {
|
|
initial_peers: peers.into_iter().map(|peer| peer.uri).collect(),
|
|
listeners,
|
|
runtime,
|
|
startup_plan: super::CoreInstanceStartupPlan {
|
|
gateway: host.gateway_enabled,
|
|
},
|
|
stun: StunServerConfig {
|
|
udp_servers: stun_servers
|
|
.clone()
|
|
.unwrap_or_else(|| StunServerConfig::default().udp_servers),
|
|
tcp_servers: config
|
|
.get_tcp_stun_servers()
|
|
.or_else(|| stun_servers.clone())
|
|
.unwrap_or_else(|| StunServerConfig::default().tcp_servers),
|
|
udp_v6_servers: config
|
|
.get_stun_servers_v6()
|
|
.or_else(|| stun_servers.as_ref().map(|_| Vec::new()))
|
|
.unwrap_or_else(|| StunServerConfig::default().udp_v6_servers),
|
|
},
|
|
endpoint_discovery: ManualEndpointDiscoveryConfig {
|
|
user_agent: format!("easytier/{}", host.easytier_version),
|
|
network_name: network_name.clone(),
|
|
http_tcp_bind: tcp_bind.clone(),
|
|
dns_record_context: socket_context,
|
|
srv_protocols: host.endpoint_protocols.clone(),
|
|
..Default::default()
|
|
},
|
|
manual: ManualConnectorOptions {
|
|
bind_device: flags.bind_device,
|
|
allow_interface_bind: host.allow_interface_bind,
|
|
tcp_bind: tcp_bind.clone(),
|
|
udp_bind: udp_bind.clone(),
|
|
..Default::default()
|
|
},
|
|
direct: DirectConnectorOptions {
|
|
default_protocol: flags.default_protocol,
|
|
enable_ipv6: flags.enable_ipv6,
|
|
allow_public_server: true,
|
|
bind_device: flags.bind_device,
|
|
allow_interface_bind: host.allow_interface_bind,
|
|
tcp_bind,
|
|
udp_bind,
|
|
testing: false,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn tcp_stun_servers_follow_toml_override_rules() {
|
|
let fallback = TomlConfig::new_from_str(
|
|
r#"
|
|
stun_servers = ["fallback.example.com:3478"]
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let normalized = CoreInstanceConfig::from_toml(&fallback).unwrap();
|
|
assert_eq!(
|
|
normalized.connectivity.stun.tcp_servers,
|
|
["fallback.example.com:3478"]
|
|
);
|
|
|
|
let overridden = TomlConfig::new_from_str(
|
|
r#"
|
|
stun_servers = ["fallback.example.com:3478"]
|
|
tcp_stun_servers = ["tcp.example.com:3478"]
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let normalized = CoreInstanceConfig::from_toml(&overridden).unwrap();
|
|
assert_eq!(
|
|
normalized.connectivity.stun.tcp_servers,
|
|
["tcp.example.com:3478"]
|
|
);
|
|
|
|
let disabled = TomlConfig::new_from_str(
|
|
r#"
|
|
stun_servers = ["fallback.example.com:3478"]
|
|
tcp_stun_servers = []
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let normalized = CoreInstanceConfig::from_toml(&disabled).unwrap();
|
|
assert!(normalized.connectivity.stun.tcp_servers.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn custom_udp_stun_servers_disable_default_ipv6_servers() {
|
|
let config = TomlConfig::new_from_str(
|
|
r#"
|
|
stun_servers = ["custom.example.com:3478"]
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let normalized = CoreInstanceConfig::from_toml(&config).unwrap();
|
|
assert!(normalized.connectivity.stun.udp_v6_servers.is_empty());
|
|
|
|
let config = TomlConfig::new_from_str(
|
|
r#"
|
|
stun_servers = ["custom.example.com:3478"]
|
|
stun_servers_v6 = ["custom-v6.example.com:3478"]
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let normalized = CoreInstanceConfig::from_toml(&config).unwrap();
|
|
assert_eq!(
|
|
normalized.connectivity.stun.udp_v6_servers,
|
|
["custom-v6.example.com:3478"]
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "config-write")]
|
|
#[test]
|
|
fn explicit_stun_servers_survive_dump_reload() {
|
|
let assert_roundtrip = |config: TomlConfig| {
|
|
let before = CoreInstanceConfig::from_toml(&config)
|
|
.unwrap()
|
|
.connectivity
|
|
.stun;
|
|
let reloaded = TomlConfig::new_from_str(&config.dump()).unwrap();
|
|
let after = CoreInstanceConfig::from_toml(&reloaded)
|
|
.unwrap()
|
|
.connectivity
|
|
.stun;
|
|
|
|
assert_eq!(after, before);
|
|
};
|
|
|
|
let defaults = StunServerConfig::default();
|
|
let config = TomlConfig::default();
|
|
config.set_stun_servers(Some(defaults.udp_servers.clone()));
|
|
assert_roundtrip(config);
|
|
|
|
let config = TomlConfig::default();
|
|
config.set_stun_servers(Some(vec!["custom.example.com:3478".to_string()]));
|
|
config.set_tcp_stun_servers(Some(defaults.tcp_servers));
|
|
config.set_stun_servers_v6(Some(defaults.udp_v6_servers));
|
|
assert_roundtrip(config);
|
|
}
|
|
|
|
#[test]
|
|
fn shared_toml_normalizes_instance_identity_and_connectivity() {
|
|
let config = TomlConfig::new_from_str(
|
|
r#"
|
|
instance_id = "018f4fb1-7a2c-7d1f-9d89-935b0ad7e135"
|
|
instance_name = "wasi-test"
|
|
hostname = "portable-host"
|
|
ipv4 = "10.144.0.2/24"
|
|
listeners = ["tcp://0.0.0.0:11010"]
|
|
|
|
[[peer]]
|
|
uri = "tcp://127.0.0.1:11010"
|
|
|
|
[network_identity]
|
|
network_name = "portable"
|
|
network_secret = "secret"
|
|
|
|
[flags]
|
|
disable_p2p = true
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
|
|
let normalized = CoreInstanceConfig::from_toml(&config).unwrap();
|
|
assert_eq!(normalized.instance_name, "wasi-test");
|
|
assert_eq!(
|
|
normalized.peer.snapshot.runtime.core.node.instance_id,
|
|
Some(*config.get_id().as_bytes())
|
|
);
|
|
assert_eq!(normalized.peer.snapshot.runtime.core.node.peer_id, None);
|
|
assert_eq!(
|
|
normalized
|
|
.peer
|
|
.snapshot
|
|
.runtime
|
|
.core
|
|
.node
|
|
.hostname
|
|
.as_deref(),
|
|
Some("portable-host")
|
|
);
|
|
assert_eq!(
|
|
normalized.peer.snapshot.runtime.core.node.network_name,
|
|
"portable"
|
|
);
|
|
assert_eq!(normalized.connectivity.initial_peers.len(), 1);
|
|
assert_eq!(
|
|
normalized
|
|
.connectivity
|
|
.listeners
|
|
.as_ref()
|
|
.unwrap()
|
|
.urls
|
|
.len(),
|
|
1
|
|
);
|
|
assert!(normalized.peer.snapshot.flags.disable_p2p);
|
|
}
|
|
|
|
#[test]
|
|
fn host_config_supplies_only_platform_policy() {
|
|
let config = TomlConfig::default();
|
|
let host = CoreInstanceHostConfig {
|
|
hostname_fallback: Some("host-fallback".to_owned()),
|
|
host_routing: HostRoutingPolicy {
|
|
local_exit_node_fallback: true,
|
|
},
|
|
force_exit_node: true,
|
|
allow_interface_bind: false,
|
|
smoltcp_available: true,
|
|
requires_smoltcp: true,
|
|
icmp_failure_is_fatal: true,
|
|
public_ipv6_provider_supported: true,
|
|
gateway_enabled: false,
|
|
easytier_version: "host-version".to_owned(),
|
|
endpoint_protocols: vec!["host-protocol".to_owned()],
|
|
..Default::default()
|
|
};
|
|
|
|
let normalized = CoreInstanceConfig::from_toml_with_host(&config, &host).unwrap();
|
|
|
|
assert_eq!(
|
|
normalized
|
|
.peer
|
|
.snapshot
|
|
.runtime
|
|
.core
|
|
.node
|
|
.hostname
|
|
.as_deref(),
|
|
Some("host-fallback")
|
|
);
|
|
assert!(
|
|
normalized
|
|
.peer
|
|
.snapshot
|
|
.runtime
|
|
.host_routing
|
|
.local_exit_node_fallback
|
|
);
|
|
assert_eq!(normalized.peer.snapshot.easytier_version, "host-version");
|
|
assert!(normalized.connectivity.runtime.proxy.enable_exit_node);
|
|
assert!(normalized.connectivity.runtime.proxy.force_smoltcp);
|
|
assert!(normalized.connectivity.runtime.proxy.icmp_failure_is_fatal);
|
|
assert!(
|
|
normalized
|
|
.connectivity
|
|
.runtime
|
|
.public_ipv6_provider
|
|
.provider_supported
|
|
);
|
|
assert!(!normalized.connectivity.startup_plan.gateway);
|
|
assert!(!normalized.connectivity.manual.allow_interface_bind);
|
|
assert!(!normalized.connectivity.direct.allow_interface_bind);
|
|
assert_eq!(
|
|
normalized.connectivity.endpoint_discovery.srv_protocols,
|
|
["host-protocol"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ignored_capabilities_stay_in_toml_but_not_runtime_config() {
|
|
let config = TomlConfig::new_from_str(
|
|
r#"
|
|
listeners = ["tcp://127.0.0.1:11010", "quic://127.0.0.1:11011"]
|
|
proxy_network = [{ cidr = "10.20.0.0/16" }]
|
|
|
|
[[peer]]
|
|
uri = "tcp://127.0.0.1:11010"
|
|
|
|
[[peer]]
|
|
uri = "quic://127.0.0.1:11011"
|
|
|
|
[flags]
|
|
enable_exit_node = true
|
|
enable_kcp_proxy = true
|
|
accept_dns = true
|
|
encryption_algorithm = "chacha20"
|
|
data_compress_algo = "Zstd"
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
config.set_exit_nodes(vec!["10.144.144.2".parse().unwrap()]);
|
|
config.set_ipv6_public_addr_provider(true);
|
|
config.get_id();
|
|
let before = config.dump();
|
|
|
|
let host = CoreInstanceHostConfig {
|
|
ignore_unsupported_config: true,
|
|
smoltcp_available: true,
|
|
proxy_enabled: false,
|
|
gateway_enabled: false,
|
|
public_ipv6_provider_supported: false,
|
|
magic_dns_enabled: false,
|
|
kcp_enabled: false,
|
|
quic_enabled: false,
|
|
endpoint_protocols: vec!["tcp".to_owned(), "udp".to_owned()],
|
|
..Default::default()
|
|
};
|
|
|
|
let normalized = CoreInstanceConfig::from_toml_with_host(&config, &host).unwrap();
|
|
|
|
assert_eq!(config.dump(), before);
|
|
assert_eq!(normalized.connectivity.initial_peers.len(), 1);
|
|
assert_eq!(
|
|
normalized
|
|
.connectivity
|
|
.listeners
|
|
.as_ref()
|
|
.unwrap()
|
|
.urls
|
|
.len(),
|
|
1
|
|
);
|
|
assert!(
|
|
normalized
|
|
.peer
|
|
.snapshot
|
|
.runtime
|
|
.core
|
|
.routes
|
|
.proxy_networks
|
|
.is_empty()
|
|
);
|
|
assert!(normalized.peer.exit_nodes.is_empty());
|
|
assert!(!normalized.connectivity.runtime.proxy.enable_exit_node);
|
|
assert!(
|
|
!normalized
|
|
.connectivity
|
|
.runtime
|
|
.public_ipv6_provider
|
|
.provider_enabled
|
|
);
|
|
let flags = &normalized.peer.snapshot.flags;
|
|
assert!(!flags.enable_kcp_proxy);
|
|
assert!(flags.disable_kcp_input);
|
|
assert!(!flags.accept_dns);
|
|
let expected_encryption = if algorithm_is_available(EncryptionAlgorithm::ChaCha20) {
|
|
EncryptionAlgorithm::ChaCha20
|
|
} else {
|
|
EncryptionAlgorithm::AesGcm
|
|
};
|
|
assert_eq!(flags.encryption_algorithm, expected_encryption.to_string());
|
|
assert_eq!(
|
|
flags.data_compress_algo,
|
|
if CompressorAlgo::ZstdDefault.is_available() {
|
|
CompressionAlgoPb::Zstd as i32
|
|
} else {
|
|
CompressionAlgoPb::None as i32
|
|
}
|
|
);
|
|
}
|
|
}
|