mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-02 05:23:14 +00:00
feat(core): refactor hostname to ServerHostnameInfo with name/hostname pair
- Rename Hostname to ServerHostnameInfo, add name + hostname fields - Add set_hostname_rpc for changing hostname at runtime - Migrate alpha_20: generate serverInfo.name from hostname, delete ui.name - Extract gateway.rs helpers to fix rustfmt nesting depth issue - Add i18n key for hostname validation error - Update SDK bindings
This commit is contained in:
@@ -279,9 +279,7 @@ impl Resolver {
|
||||
let Some((ref config, ref opts)) = last_config else {
|
||||
continue;
|
||||
};
|
||||
let static_servers: Option<
|
||||
std::collections::VecDeque<SocketAddr>,
|
||||
> = db
|
||||
let static_servers: Option<std::collections::VecDeque<SocketAddr>> = db
|
||||
.peek()
|
||||
.await
|
||||
.as_public()
|
||||
@@ -290,12 +288,9 @@ impl Resolver {
|
||||
.as_dns()
|
||||
.as_static_servers()
|
||||
.de()?;
|
||||
let hash =
|
||||
crate::util::serde::hash_serializable::<sha2::Sha256, _>(&(
|
||||
config,
|
||||
opts,
|
||||
&static_servers,
|
||||
))?;
|
||||
let hash = crate::util::serde::hash_serializable::<sha2::Sha256, _>(
|
||||
&(config, opts, &static_servers),
|
||||
)?;
|
||||
if hash == prev {
|
||||
prev = hash;
|
||||
continue;
|
||||
@@ -320,26 +315,25 @@ impl Resolver {
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
let forward_servers =
|
||||
if let Some(servers) = &static_servers {
|
||||
servers
|
||||
.iter()
|
||||
.flat_map(|addr| {
|
||||
[
|
||||
NameServerConfig::new(*addr, Protocol::Udp),
|
||||
NameServerConfig::new(*addr, Protocol::Tcp),
|
||||
]
|
||||
})
|
||||
.map(|n| to_value(&n))
|
||||
.collect::<Result<_, Error>>()?
|
||||
} else {
|
||||
config
|
||||
.name_servers()
|
||||
.into_iter()
|
||||
.skip(4)
|
||||
.map(to_value)
|
||||
.collect::<Result<_, Error>>()?
|
||||
};
|
||||
let forward_servers = if let Some(servers) = &static_servers {
|
||||
servers
|
||||
.iter()
|
||||
.flat_map(|addr| {
|
||||
[
|
||||
NameServerConfig::new(*addr, Protocol::Udp),
|
||||
NameServerConfig::new(*addr, Protocol::Tcp),
|
||||
]
|
||||
})
|
||||
.map(|n| to_value(&n))
|
||||
.collect::<Result<_, Error>>()?
|
||||
} else {
|
||||
config
|
||||
.name_servers()
|
||||
.into_iter()
|
||||
.skip(4)
|
||||
.map(to_value)
|
||||
.collect::<Result<_, Error>>()?
|
||||
};
|
||||
let auth: Vec<Arc<dyn AuthorityObject>> = vec![Arc::new(
|
||||
ForwardAuthority::builder_tokio(ForwardConfig {
|
||||
name_servers: from_value(Value::Array(forward_servers))?,
|
||||
@@ -349,17 +343,15 @@ impl Resolver {
|
||||
.map_err(|e| Error::new(eyre!("{e}"), ErrorKind::Network))?,
|
||||
)];
|
||||
{
|
||||
let mut guard = tokio::time::timeout(
|
||||
Duration::from_secs(10),
|
||||
catalog.write(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("{}", t!("net.dns.timeout-updating-catalog")),
|
||||
ErrorKind::Timeout,
|
||||
)
|
||||
})?;
|
||||
let mut guard =
|
||||
tokio::time::timeout(Duration::from_secs(10), catalog.write())
|
||||
.await
|
||||
.map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("{}", t!("net.dns.timeout-updating-catalog")),
|
||||
ErrorKind::Timeout,
|
||||
)
|
||||
})?;
|
||||
guard.upsert(Name::root().into(), auth);
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::future::Future;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use clap::Parser;
|
||||
use futures::{FutureExt, Stream, StreamExt, TryStreamExt};
|
||||
@@ -732,6 +732,57 @@ async fn get_wan_ipv4(iface: &str, base_url: &Url) -> Result<Option<Ipv4Addr>, E
|
||||
Ok(Some(trimmed.parse()?))
|
||||
}
|
||||
|
||||
struct PolicyRoutingCleanup {
|
||||
table_id: u32,
|
||||
iface: String,
|
||||
}
|
||||
impl Drop for PolicyRoutingCleanup {
|
||||
fn drop(&mut self) {
|
||||
let table_str = self.table_id.to_string();
|
||||
let iface = std::mem::take(&mut self.iface);
|
||||
tokio::spawn(async move {
|
||||
Command::new("ip")
|
||||
.arg("rule")
|
||||
.arg("del")
|
||||
.arg("fwmark")
|
||||
.arg(&table_str)
|
||||
.arg("lookup")
|
||||
.arg(&table_str)
|
||||
.arg("priority")
|
||||
.arg("50")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
Command::new("ip")
|
||||
.arg("route")
|
||||
.arg("flush")
|
||||
.arg("table")
|
||||
.arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
Command::new("iptables")
|
||||
.arg("-t")
|
||||
.arg("mangle")
|
||||
.arg("-D")
|
||||
.arg("PREROUTING")
|
||||
.arg("-i")
|
||||
.arg(&iface)
|
||||
.arg("-m")
|
||||
.arg("conntrack")
|
||||
.arg("--ctstate")
|
||||
.arg("NEW")
|
||||
.arg("-j")
|
||||
.arg("CONNMARK")
|
||||
.arg("--set-mark")
|
||||
.arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(connection, device_proxy, write_to, db))]
|
||||
async fn watch_ip(
|
||||
connection: &Connection,
|
||||
@@ -758,12 +809,14 @@ async fn watch_ip(
|
||||
.with_stream(device_proxy.receive_ip6_config_changed().await.stub())
|
||||
.with_async_fn(|| {
|
||||
async {
|
||||
tokio::time::sleep(Duration::from_secs(300)).await;
|
||||
tokio::time::sleep(Duration::from_secs(600)).await;
|
||||
Ok(())
|
||||
}
|
||||
.fuse()
|
||||
});
|
||||
|
||||
let mut prev_attempt: Option<Instant> = None;
|
||||
|
||||
loop {
|
||||
until
|
||||
.run(async {
|
||||
@@ -850,10 +903,7 @@ async fn watch_ip(
|
||||
// Policy routing: track per-interface table for cleanup on scope exit
|
||||
let policy_table_id = if !matches!(
|
||||
device_type,
|
||||
Some(
|
||||
NetworkInterfaceType::Bridge
|
||||
| NetworkInterfaceType::Loopback
|
||||
)
|
||||
Some(NetworkInterfaceType::Bridge | NetworkInterfaceType::Loopback)
|
||||
) {
|
||||
if_nametoindex(iface.as_str())
|
||||
.map(|idx| 1000 + idx)
|
||||
@@ -861,44 +911,7 @@ async fn watch_ip(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
struct PolicyRoutingCleanup {
|
||||
table_id: u32,
|
||||
iface: String,
|
||||
}
|
||||
impl Drop for PolicyRoutingCleanup {
|
||||
fn drop(&mut self) {
|
||||
let table_str = self.table_id.to_string();
|
||||
let iface = std::mem::take(&mut self.iface);
|
||||
tokio::spawn(async move {
|
||||
Command::new("ip")
|
||||
.arg("rule").arg("del")
|
||||
.arg("fwmark").arg(&table_str)
|
||||
.arg("lookup").arg(&table_str)
|
||||
.arg("priority").arg("50")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
Command::new("ip")
|
||||
.arg("route").arg("flush")
|
||||
.arg("table").arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
Command::new("iptables")
|
||||
.arg("-t").arg("mangle")
|
||||
.arg("-D").arg("PREROUTING")
|
||||
.arg("-i").arg(&iface)
|
||||
.arg("-m").arg("conntrack")
|
||||
.arg("--ctstate").arg("NEW")
|
||||
.arg("-j").arg("CONNMARK")
|
||||
.arg("--set-mark").arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
let _policy_guard: Option<PolicyRoutingCleanup> =
|
||||
let policy_guard: Option<PolicyRoutingCleanup> =
|
||||
policy_table_id.map(|t| PolicyRoutingCleanup {
|
||||
table_id: t,
|
||||
iface: iface.as_str().to_owned(),
|
||||
@@ -906,271 +919,18 @@ async fn watch_ip(
|
||||
|
||||
loop {
|
||||
until
|
||||
.run(async {
|
||||
let addresses = ip4_proxy
|
||||
.address_data()
|
||||
.await?
|
||||
.into_iter()
|
||||
.chain(ip6_proxy.address_data().await?)
|
||||
.collect_vec();
|
||||
let lan_ip: OrdSet<IpAddr> = [
|
||||
Some(ip4_proxy.gateway().await?)
|
||||
.filter(|g| !g.is_empty())
|
||||
.and_then(|g| g.parse::<IpAddr>().log_err()),
|
||||
Some(ip6_proxy.gateway().await?)
|
||||
.filter(|g| !g.is_empty())
|
||||
.and_then(|g| g.parse::<IpAddr>().log_err()),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|a| a)
|
||||
.collect();
|
||||
let mut ntp_servers = OrdSet::new();
|
||||
let mut dns_servers = OrdSet::new();
|
||||
if let Some(dhcp4_proxy) = &dhcp4_proxy {
|
||||
let dhcp = dhcp4_proxy.options().await?;
|
||||
if let Some(ntp) = dhcp.ntp_servers {
|
||||
ntp_servers.extend(
|
||||
ntp.split_whitespace()
|
||||
.map(InternedString::intern),
|
||||
);
|
||||
}
|
||||
if let Some(dns) = dhcp.domain_name_servers {
|
||||
dns_servers.extend(
|
||||
dns.split_ascii_whitespace()
|
||||
.filter_map(|s| {
|
||||
s.parse::<IpAddr>().log_err()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
let scope_id = if_nametoindex(iface.as_str())
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
let subnets: OrdSet<IpNet> = addresses
|
||||
.into_iter()
|
||||
.map(IpNet::try_from)
|
||||
.try_collect()?;
|
||||
// Policy routing: ensure replies exit the same interface
|
||||
// they arrived on, eliminating the need for MASQUERADE.
|
||||
if let Some(guard) = &_policy_guard {
|
||||
let table_id = guard.table_id;
|
||||
let table_str = table_id.to_string();
|
||||
|
||||
let ipv4_gateway: Option<Ipv4Addr> =
|
||||
lan_ip.iter().find_map(|ip| match ip {
|
||||
IpAddr::V4(v4) => Some(v4),
|
||||
_ => None,
|
||||
}).copied();
|
||||
|
||||
// Flush and rebuild per-interface routing table.
|
||||
// Clone all non-default routes from the main table so
|
||||
// that LAN IPs on other subnets remain reachable when
|
||||
// the priority-75 catch-all overrides default routing,
|
||||
// then replace the default route with this interface's.
|
||||
Command::new("ip")
|
||||
.arg("route").arg("flush")
|
||||
.arg("table").arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
if let Ok(main_routes) = Command::new("ip")
|
||||
.arg("route").arg("show")
|
||||
.arg("table").arg("main")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.and_then(|b| String::from_utf8(b).with_kind(ErrorKind::Utf8))
|
||||
{
|
||||
for line in main_routes.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with("default") {
|
||||
continue;
|
||||
}
|
||||
let mut cmd = Command::new("ip");
|
||||
cmd.arg("route").arg("add");
|
||||
for part in line.split_whitespace() {
|
||||
// Skip status flags that appear in
|
||||
// route output but are not valid for
|
||||
// `ip route add`.
|
||||
if part == "linkdown" || part == "dead" {
|
||||
continue;
|
||||
}
|
||||
cmd.arg(part);
|
||||
}
|
||||
cmd.arg("table").arg(&table_str);
|
||||
cmd.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
// Add default route via this interface's gateway
|
||||
{
|
||||
let mut cmd = Command::new("ip");
|
||||
cmd.arg("route").arg("add").arg("default");
|
||||
if let Some(gw) = ipv4_gateway {
|
||||
cmd.arg("via").arg(gw.to_string());
|
||||
}
|
||||
cmd.arg("dev").arg(iface.as_str())
|
||||
.arg("table").arg(&table_str);
|
||||
if ipv4_gateway.is_none() {
|
||||
cmd.arg("scope").arg("link");
|
||||
}
|
||||
cmd.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
// Ensure global CONNMARK restore rules in mangle
|
||||
// PREROUTING (forwarded packets) and OUTPUT (locally-generated replies).
|
||||
// Both are needed: PREROUTING handles DNAT-forwarded traffic,
|
||||
// OUTPUT handles replies from locally-bound listeners (e.g. vhost).
|
||||
// The `-m mark --mark 0` condition ensures we only restore
|
||||
// when the packet has no existing fwmark, preserving marks
|
||||
// set by WireGuard on encapsulation packets.
|
||||
for chain in ["PREROUTING", "OUTPUT"] {
|
||||
if Command::new("iptables")
|
||||
.arg("-t").arg("mangle")
|
||||
.arg("-C").arg(chain)
|
||||
.arg("-m").arg("mark").arg("--mark").arg("0")
|
||||
.arg("-j").arg("CONNMARK")
|
||||
.arg("--restore-mark")
|
||||
.invoke(ErrorKind::Network).await
|
||||
.is_err()
|
||||
{
|
||||
Command::new("iptables")
|
||||
.arg("-t").arg("mangle")
|
||||
.arg("-I").arg(chain).arg("1")
|
||||
.arg("-m").arg("mark").arg("--mark").arg("0")
|
||||
.arg("-j").arg("CONNMARK")
|
||||
.arg("--restore-mark")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
// Mark NEW connections arriving on this interface
|
||||
// with its routing table ID via conntrack mark
|
||||
if Command::new("iptables")
|
||||
.arg("-t").arg("mangle")
|
||||
.arg("-C").arg("PREROUTING")
|
||||
.arg("-i").arg(iface.as_str())
|
||||
.arg("-m").arg("conntrack")
|
||||
.arg("--ctstate").arg("NEW")
|
||||
.arg("-j").arg("CONNMARK")
|
||||
.arg("--set-mark").arg(&table_str)
|
||||
.invoke(ErrorKind::Network).await
|
||||
.is_err()
|
||||
{
|
||||
Command::new("iptables")
|
||||
.arg("-t").arg("mangle")
|
||||
.arg("-A").arg("PREROUTING")
|
||||
.arg("-i").arg(iface.as_str())
|
||||
.arg("-m").arg("conntrack")
|
||||
.arg("--ctstate").arg("NEW")
|
||||
.arg("-j").arg("CONNMARK")
|
||||
.arg("--set-mark").arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
// Ensure fwmark-based ip rule for this interface's table
|
||||
let rules_output = String::from_utf8(
|
||||
Command::new("ip")
|
||||
.arg("rule").arg("list")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await?,
|
||||
)?;
|
||||
if !rules_output.lines().any(|l| {
|
||||
l.contains("fwmark")
|
||||
&& l.contains(&format!("lookup {table_id}"))
|
||||
}) {
|
||||
Command::new("ip")
|
||||
.arg("rule").arg("add")
|
||||
.arg("fwmark").arg(&table_str)
|
||||
.arg("lookup").arg(&table_str)
|
||||
.arg("priority").arg("50")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
let ifconfig_url = if let Some(db) = db {
|
||||
db.peek()
|
||||
.await
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_ifconfig_url()
|
||||
.de()
|
||||
.unwrap_or_else(|_| crate::db::model::public::default_ifconfig_url())
|
||||
} else {
|
||||
crate::db::model::public::default_ifconfig_url()
|
||||
};
|
||||
let wan_ip = if !subnets.is_empty()
|
||||
&& !matches!(
|
||||
device_type,
|
||||
Some(
|
||||
NetworkInterfaceType::Bridge
|
||||
| NetworkInterfaceType::Loopback
|
||||
)
|
||||
) {
|
||||
match get_wan_ipv4(iface.as_str(), &ifconfig_url).await {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"{}",
|
||||
t!("net.gateway.failed-to-determine-wan-ip", iface = iface.to_string(), error = e.to_string())
|
||||
);
|
||||
tracing::debug!("{e:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut ip_info = IpInfo {
|
||||
name: name.clone(),
|
||||
scope_id,
|
||||
device_type,
|
||||
subnets,
|
||||
lan_ip,
|
||||
wan_ip,
|
||||
ntp_servers,
|
||||
dns_servers,
|
||||
};
|
||||
|
||||
write_to.send_if_modified(
|
||||
|m: &mut OrdMap<GatewayId, NetworkInterfaceInfo>| {
|
||||
let (name, secure, gateway_type, prev_wan_ip) = m
|
||||
.get(&iface)
|
||||
.map_or((None, None, None, None), |i| {
|
||||
(
|
||||
i.name.clone(),
|
||||
i.secure,
|
||||
i.gateway_type,
|
||||
i.ip_info
|
||||
.as_ref()
|
||||
.and_then(|i| i.wan_ip),
|
||||
)
|
||||
});
|
||||
ip_info.wan_ip = ip_info.wan_ip.or(prev_wan_ip);
|
||||
let ip_info = Arc::new(ip_info);
|
||||
m.insert(
|
||||
iface.clone(),
|
||||
NetworkInterfaceInfo {
|
||||
name,
|
||||
secure,
|
||||
ip_info: Some(ip_info.clone()),
|
||||
gateway_type,
|
||||
},
|
||||
)
|
||||
.filter(|old| &old.ip_info == &Some(ip_info))
|
||||
.is_none()
|
||||
},
|
||||
);
|
||||
|
||||
Ok::<_, Error>(())
|
||||
})
|
||||
.run(poll_ip_info(
|
||||
&ip4_proxy,
|
||||
&ip6_proxy,
|
||||
&dhcp4_proxy,
|
||||
&policy_guard,
|
||||
&iface,
|
||||
&mut prev_attempt,
|
||||
db,
|
||||
write_to,
|
||||
device_type,
|
||||
&name,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
})
|
||||
@@ -1181,6 +941,319 @@ async fn watch_ip(
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_policy_routing(
|
||||
guard: &PolicyRoutingCleanup,
|
||||
iface: &GatewayId,
|
||||
lan_ip: &OrdSet<IpAddr>,
|
||||
) -> Result<(), Error> {
|
||||
let table_id = guard.table_id;
|
||||
let table_str = table_id.to_string();
|
||||
|
||||
let ipv4_gateway: Option<Ipv4Addr> = lan_ip
|
||||
.iter()
|
||||
.find_map(|ip| match ip {
|
||||
IpAddr::V4(v4) => Some(v4),
|
||||
_ => None,
|
||||
})
|
||||
.copied();
|
||||
|
||||
// Flush and rebuild per-interface routing table.
|
||||
// Clone all non-default routes from the main table so that LAN IPs on
|
||||
// other subnets remain reachable when the priority-75 catch-all overrides
|
||||
// default routing, then replace the default route with this interface's.
|
||||
Command::new("ip")
|
||||
.arg("route")
|
||||
.arg("flush")
|
||||
.arg("table")
|
||||
.arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
if let Ok(main_routes) = Command::new("ip")
|
||||
.arg("route")
|
||||
.arg("show")
|
||||
.arg("table")
|
||||
.arg("main")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.and_then(|b| String::from_utf8(b).with_kind(ErrorKind::Utf8))
|
||||
{
|
||||
for line in main_routes.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with("default") {
|
||||
continue;
|
||||
}
|
||||
let mut cmd = Command::new("ip");
|
||||
cmd.arg("route").arg("add");
|
||||
for part in line.split_whitespace() {
|
||||
// Skip status flags that appear in route output but
|
||||
// are not valid for `ip route add`.
|
||||
if part == "linkdown" || part == "dead" {
|
||||
continue;
|
||||
}
|
||||
cmd.arg(part);
|
||||
}
|
||||
cmd.arg("table").arg(&table_str);
|
||||
cmd.invoke(ErrorKind::Network).await.log_err();
|
||||
}
|
||||
}
|
||||
// Add default route via this interface's gateway
|
||||
{
|
||||
let mut cmd = Command::new("ip");
|
||||
cmd.arg("route").arg("add").arg("default");
|
||||
if let Some(gw) = ipv4_gateway {
|
||||
cmd.arg("via").arg(gw.to_string());
|
||||
}
|
||||
cmd.arg("dev")
|
||||
.arg(iface.as_str())
|
||||
.arg("table")
|
||||
.arg(&table_str);
|
||||
if ipv4_gateway.is_none() {
|
||||
cmd.arg("scope").arg("link");
|
||||
}
|
||||
cmd.invoke(ErrorKind::Network).await.log_err();
|
||||
}
|
||||
|
||||
// Ensure global CONNMARK restore rules in mangle PREROUTING (forwarded
|
||||
// packets) and OUTPUT (locally-generated replies). Both are needed:
|
||||
// PREROUTING handles DNAT-forwarded traffic, OUTPUT handles replies from
|
||||
// locally-bound listeners (e.g. vhost). The `-m mark --mark 0` condition
|
||||
// ensures we only restore when the packet has no existing fwmark,
|
||||
// preserving marks set by WireGuard on encapsulation packets.
|
||||
for chain in ["PREROUTING", "OUTPUT"] {
|
||||
if Command::new("iptables")
|
||||
.arg("-t")
|
||||
.arg("mangle")
|
||||
.arg("-C")
|
||||
.arg(chain)
|
||||
.arg("-m")
|
||||
.arg("mark")
|
||||
.arg("--mark")
|
||||
.arg("0")
|
||||
.arg("-j")
|
||||
.arg("CONNMARK")
|
||||
.arg("--restore-mark")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
Command::new("iptables")
|
||||
.arg("-t")
|
||||
.arg("mangle")
|
||||
.arg("-I")
|
||||
.arg(chain)
|
||||
.arg("1")
|
||||
.arg("-m")
|
||||
.arg("mark")
|
||||
.arg("--mark")
|
||||
.arg("0")
|
||||
.arg("-j")
|
||||
.arg("CONNMARK")
|
||||
.arg("--restore-mark")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
// Mark NEW connections arriving on this interface with its routing
|
||||
// table ID via conntrack mark
|
||||
if Command::new("iptables")
|
||||
.arg("-t")
|
||||
.arg("mangle")
|
||||
.arg("-C")
|
||||
.arg("PREROUTING")
|
||||
.arg("-i")
|
||||
.arg(iface.as_str())
|
||||
.arg("-m")
|
||||
.arg("conntrack")
|
||||
.arg("--ctstate")
|
||||
.arg("NEW")
|
||||
.arg("-j")
|
||||
.arg("CONNMARK")
|
||||
.arg("--set-mark")
|
||||
.arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
Command::new("iptables")
|
||||
.arg("-t")
|
||||
.arg("mangle")
|
||||
.arg("-A")
|
||||
.arg("PREROUTING")
|
||||
.arg("-i")
|
||||
.arg(iface.as_str())
|
||||
.arg("-m")
|
||||
.arg("conntrack")
|
||||
.arg("--ctstate")
|
||||
.arg("NEW")
|
||||
.arg("-j")
|
||||
.arg("CONNMARK")
|
||||
.arg("--set-mark")
|
||||
.arg(&table_str)
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
// Ensure fwmark-based ip rule for this interface's table
|
||||
let rules_output = String::from_utf8(
|
||||
Command::new("ip")
|
||||
.arg("rule")
|
||||
.arg("list")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await?,
|
||||
)?;
|
||||
if !rules_output
|
||||
.lines()
|
||||
.any(|l| l.contains("fwmark") && l.contains(&format!("lookup {table_id}")))
|
||||
{
|
||||
Command::new("ip")
|
||||
.arg("rule")
|
||||
.arg("add")
|
||||
.arg("fwmark")
|
||||
.arg(&table_str)
|
||||
.arg("lookup")
|
||||
.arg(&table_str)
|
||||
.arg("priority")
|
||||
.arg("50")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn poll_ip_info(
|
||||
ip4_proxy: &Ip4ConfigProxy<'_>,
|
||||
ip6_proxy: &Ip6ConfigProxy<'_>,
|
||||
dhcp4_proxy: &Option<Dhcp4ConfigProxy<'_>>,
|
||||
policy_guard: &Option<PolicyRoutingCleanup>,
|
||||
iface: &GatewayId,
|
||||
prev_attempt: &mut Option<Instant>,
|
||||
db: Option<&TypedPatchDb<Database>>,
|
||||
write_to: &Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>,
|
||||
device_type: Option<NetworkInterfaceType>,
|
||||
name: &InternedString,
|
||||
) -> Result<(), Error> {
|
||||
let addresses = ip4_proxy
|
||||
.address_data()
|
||||
.await?
|
||||
.into_iter()
|
||||
.chain(ip6_proxy.address_data().await?)
|
||||
.collect_vec();
|
||||
let lan_ip: OrdSet<IpAddr> = [
|
||||
Some(ip4_proxy.gateway().await?)
|
||||
.filter(|g| !g.is_empty())
|
||||
.and_then(|g| g.parse::<IpAddr>().log_err()),
|
||||
Some(ip6_proxy.gateway().await?)
|
||||
.filter(|g| !g.is_empty())
|
||||
.and_then(|g| g.parse::<IpAddr>().log_err()),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|a| a)
|
||||
.collect();
|
||||
let mut ntp_servers = OrdSet::new();
|
||||
let mut dns_servers = OrdSet::new();
|
||||
if let Some(dhcp4_proxy) = dhcp4_proxy {
|
||||
let dhcp = dhcp4_proxy.options().await?;
|
||||
if let Some(ntp) = dhcp.ntp_servers {
|
||||
ntp_servers.extend(ntp.split_whitespace().map(InternedString::intern));
|
||||
}
|
||||
if let Some(dns) = dhcp.domain_name_servers {
|
||||
dns_servers.extend(
|
||||
dns.split_ascii_whitespace()
|
||||
.filter_map(|s| s.parse::<IpAddr>().log_err())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
let scope_id = if_nametoindex(iface.as_str()).with_kind(ErrorKind::Network)?;
|
||||
let subnets: OrdSet<IpNet> = addresses.into_iter().map(IpNet::try_from).try_collect()?;
|
||||
|
||||
// Policy routing: ensure replies exit the same interface they arrived on,
|
||||
// eliminating the need for MASQUERADE.
|
||||
if let Some(guard) = policy_guard {
|
||||
apply_policy_routing(guard, iface, &lan_ip).await?;
|
||||
}
|
||||
|
||||
let ifconfig_url = if let Some(db) = db {
|
||||
db.peek()
|
||||
.await
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_ifconfig_url()
|
||||
.de()
|
||||
.unwrap_or_else(|_| crate::db::model::public::default_ifconfig_url())
|
||||
} else {
|
||||
crate::db::model::public::default_ifconfig_url()
|
||||
};
|
||||
let wan_ip = if prev_attempt.map_or(true, |i| i.elapsed() > Duration::from_secs(300))
|
||||
&& !subnets.is_empty()
|
||||
&& !matches!(
|
||||
device_type,
|
||||
Some(NetworkInterfaceType::Bridge | NetworkInterfaceType::Loopback)
|
||||
) {
|
||||
*prev_attempt = Some(Instant::now());
|
||||
match get_wan_ipv4(iface.as_str(), &ifconfig_url).await {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"{}",
|
||||
t!(
|
||||
"net.gateway.failed-to-determine-wan-ip",
|
||||
iface = iface.to_string(),
|
||||
error = e.to_string()
|
||||
)
|
||||
);
|
||||
tracing::debug!("{e:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut ip_info = IpInfo {
|
||||
name: name.clone(),
|
||||
scope_id,
|
||||
device_type,
|
||||
subnets,
|
||||
lan_ip,
|
||||
wan_ip,
|
||||
ntp_servers,
|
||||
dns_servers,
|
||||
};
|
||||
|
||||
write_to.send_if_modified(|m: &mut OrdMap<GatewayId, NetworkInterfaceInfo>| {
|
||||
let (name, secure, gateway_type, prev_wan_ip) =
|
||||
m.get(iface).map_or((None, None, None, None), |i| {
|
||||
(
|
||||
i.name.clone(),
|
||||
i.secure,
|
||||
i.gateway_type,
|
||||
i.ip_info.as_ref().and_then(|i| i.wan_ip),
|
||||
)
|
||||
});
|
||||
ip_info.wan_ip = ip_info.wan_ip.or(prev_wan_ip);
|
||||
let ip_info = Arc::new(ip_info);
|
||||
m.insert(
|
||||
iface.clone(),
|
||||
NetworkInterfaceInfo {
|
||||
name,
|
||||
secure,
|
||||
ip_info: Some(ip_info.clone()),
|
||||
gateway_type,
|
||||
},
|
||||
)
|
||||
.filter(|old| &old.ip_info == &Some(ip_info))
|
||||
.is_none()
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(_connection, device_proxy, watch_activation))]
|
||||
async fn watch_activated(
|
||||
_connection: &Connection,
|
||||
@@ -1287,8 +1360,7 @@ impl NetworkInterfaceController {
|
||||
.as_network_mut()
|
||||
.as_gateways_mut()
|
||||
.ser(info)?;
|
||||
let hostname =
|
||||
crate::hostname::Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let hostname = crate::hostname::ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
for host in all_hosts(db) {
|
||||
host?.update_addresses(&hostname, info, &ports)?;
|
||||
|
||||
@@ -10,7 +10,7 @@ use ts_rs::TS;
|
||||
use crate::GatewayId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::hostname::ServerHostname;
|
||||
use crate::net::acme::AcmeProvider;
|
||||
use crate::net::host::{HostApiKind, all_hosts};
|
||||
use crate::prelude::*;
|
||||
@@ -197,7 +197,7 @@ pub async fn add_public_domain<Kind: HostApiKind>(
|
||||
.as_public_domains_mut()
|
||||
.insert(&fqdn, &PublicDomainConfig { acme, gateway })?;
|
||||
handle_duplicates(db)?;
|
||||
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let hostname = ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let gateways = db.as_public().as_server_info().as_network().as_gateways().de()?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
Kind::host_for(&inheritance, db)?.update_addresses(&hostname, &gateways, &ports)
|
||||
@@ -230,8 +230,13 @@ pub async fn remove_public_domain<Kind: HostApiKind>(
|
||||
Kind::host_for(&inheritance, db)?
|
||||
.as_public_domains_mut()
|
||||
.remove(&fqdn)?;
|
||||
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let gateways = db.as_public().as_server_info().as_network().as_gateways().de()?;
|
||||
let hostname = ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let gateways = db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
Kind::host_for(&inheritance, db)?.update_addresses(&hostname, &gateways, &ports)
|
||||
})
|
||||
@@ -262,8 +267,13 @@ pub async fn add_private_domain<Kind: HostApiKind>(
|
||||
.upsert(&fqdn, || Ok(BTreeSet::new()))?
|
||||
.mutate(|d| Ok(d.insert(gateway)))?;
|
||||
handle_duplicates(db)?;
|
||||
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let gateways = db.as_public().as_server_info().as_network().as_gateways().de()?;
|
||||
let hostname = ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let gateways = db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
Kind::host_for(&inheritance, db)?.update_addresses(&hostname, &gateways, &ports)
|
||||
})
|
||||
@@ -284,8 +294,13 @@ pub async fn remove_private_domain<Kind: HostApiKind>(
|
||||
Kind::host_for(&inheritance, db)?
|
||||
.as_private_domains_mut()
|
||||
.mutate(|d| Ok(d.remove(&domain)))?;
|
||||
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let gateways = db.as_public().as_server_info().as_network().as_gateways().de()?;
|
||||
let hostname = ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let gateways = db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
Kind::host_for(&inheritance, db)?.update_addresses(&hostname, &gateways, &ports)
|
||||
})
|
||||
|
||||
@@ -15,7 +15,7 @@ use ts_rs::TS;
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::db::model::public::{NetworkInterfaceInfo, NetworkInterfaceType};
|
||||
use crate::hostname::Hostname;
|
||||
use crate::hostname::ServerHostname;
|
||||
use crate::net::forward::AvailablePorts;
|
||||
use crate::net::host::address::{HostAddress, PublicDomainConfig, address_api};
|
||||
use crate::net::host::binding::{BindInfo, BindOptions, Bindings, binding};
|
||||
@@ -82,7 +82,7 @@ impl Host {
|
||||
impl Model<Host> {
|
||||
pub fn update_addresses(
|
||||
&mut self,
|
||||
mdns: &Hostname,
|
||||
mdns: &ServerHostname,
|
||||
gateways: &OrdMap<GatewayId, NetworkInterfaceInfo>,
|
||||
available_ports: &AvailablePorts,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
@@ -13,7 +13,7 @@ use tokio_rustls::rustls::ClientConfig as TlsClientConfig;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::db::model::Database;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::hostname::ServerHostname;
|
||||
use crate::net::dns::DnsController;
|
||||
use crate::net::forward::{
|
||||
ForwardRequirements, InterfacePortForwardController, START9_BRIDGE_IFACE, add_iptables_rule,
|
||||
@@ -651,7 +651,7 @@ impl NetService {
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let hostname = ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let mut ports = db.as_private().as_available_ports().de()?;
|
||||
let host = host_for(db, pkg_id.as_ref(), &id)?;
|
||||
host.add_binding(&mut ports, internal_port, options)?;
|
||||
@@ -676,7 +676,7 @@ impl NetService {
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let hostname = ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
if let Some(ref pkg_id) = pkg_id {
|
||||
for (host_id, host) in db
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::SOURCE_DATE;
|
||||
use crate::account::AccountInfo;
|
||||
use crate::db::model::Database;
|
||||
use crate::db::{DbAccess, DbAccessMut};
|
||||
use crate::hostname::Hostname;
|
||||
use crate::hostname::ServerHostname;
|
||||
use crate::init::check_time_is_synchronized;
|
||||
use crate::net::gateway::GatewayInfo;
|
||||
use crate::net::tls::TlsHandler;
|
||||
@@ -283,7 +283,7 @@ pub fn gen_nistp256() -> Result<PKey<Private>, Error> {
|
||||
#[instrument(skip_all)]
|
||||
pub fn make_root_cert(
|
||||
root_key: &PKey<Private>,
|
||||
hostname: &Hostname,
|
||||
hostname: &ServerHostname,
|
||||
start_time: SystemTime,
|
||||
) -> Result<X509, Error> {
|
||||
let mut builder = X509Builder::new()?;
|
||||
@@ -300,7 +300,8 @@ pub fn make_root_cert(
|
||||
builder.set_serial_number(&*rand_serial()?)?;
|
||||
|
||||
let mut subject_name_builder = X509NameBuilder::new()?;
|
||||
subject_name_builder.append_entry_by_text("CN", &format!("{} Local Root CA", &*hostname.0))?;
|
||||
subject_name_builder
|
||||
.append_entry_by_text("CN", &format!("{} Local Root CA", hostname.as_ref()))?;
|
||||
subject_name_builder.append_entry_by_text("O", "Start9")?;
|
||||
subject_name_builder.append_entry_by_text("OU", "StartOS")?;
|
||||
let subject_name = subject_name_builder.build();
|
||||
|
||||
@@ -31,7 +31,7 @@ use tokio_util::io::ReaderStream;
|
||||
use url::Url;
|
||||
|
||||
use crate::context::{DiagnosticContext, InitContext, RpcContext, SetupContext};
|
||||
use crate::hostname::Hostname;
|
||||
use crate::hostname::ServerHostname;
|
||||
use crate::middleware::auth::Auth;
|
||||
use crate::middleware::auth::session::ValidSessionToken;
|
||||
use crate::middleware::cors::Cors;
|
||||
@@ -105,8 +105,9 @@ impl UiContext for RpcContext {
|
||||
get(move || {
|
||||
let ctx = self.clone();
|
||||
async move {
|
||||
ctx.account
|
||||
.peek(|account| cert_send(&account.root_ca_cert, &account.hostname))
|
||||
ctx.account.peek(|account| {
|
||||
cert_send(&account.root_ca_cert, &account.hostname.hostname)
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
@@ -419,7 +420,7 @@ pub fn bad_request() -> Response {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn cert_send(cert: &X509, hostname: &Hostname) -> Result<Response, Error> {
|
||||
fn cert_send(cert: &X509, hostname: &ServerHostname) -> Result<Response, Error> {
|
||||
let pem = cert.to_pem()?;
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
@@ -435,7 +436,7 @@ fn cert_send(cert: &X509, hostname: &Hostname) -> Result<Response, Error> {
|
||||
.header(http::header::CONTENT_LENGTH, pem.len())
|
||||
.header(
|
||||
http::header::CONTENT_DISPOSITION,
|
||||
format!("attachment; filename={}.crt", &hostname.0),
|
||||
format!("attachment; filename={}.crt", hostname.as_ref()),
|
||||
)
|
||||
.body(Body::from(pem))
|
||||
.with_kind(ErrorKind::Network)
|
||||
|
||||
@@ -171,20 +171,14 @@ where
|
||||
let (metadata, stream) = ready!(self.accept.poll_accept(cx)?);
|
||||
let mut tls_handler = self.tls_handler.clone();
|
||||
let mut fut = async move {
|
||||
let res = match tokio::time::timeout(
|
||||
Duration::from_secs(15),
|
||||
async {
|
||||
let mut acceptor = LazyConfigAcceptor::new(
|
||||
Acceptor::default(),
|
||||
BackTrackingIO::new(stream),
|
||||
);
|
||||
let mut mid: tokio_rustls::StartHandshake<
|
||||
BackTrackingIO<AcceptStream>,
|
||||
> = match (&mut acceptor).await {
|
||||
let res = match tokio::time::timeout(Duration::from_secs(15), async {
|
||||
let mut acceptor =
|
||||
LazyConfigAcceptor::new(Acceptor::default(), BackTrackingIO::new(stream));
|
||||
let mut mid: tokio_rustls::StartHandshake<BackTrackingIO<AcceptStream>> =
|
||||
match (&mut acceptor).await {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
let mut stream =
|
||||
acceptor.take_io().or_not_found("acceptor io")?;
|
||||
let mut stream = acceptor.take_io().or_not_found("acceptor io")?;
|
||||
let (_, buf) = stream.rewind();
|
||||
if std::str::from_utf8(buf)
|
||||
.ok()
|
||||
@@ -208,42 +202,39 @@ where
|
||||
}
|
||||
}
|
||||
};
|
||||
let hello = mid.client_hello();
|
||||
if let Some(cfg) = tls_handler.get_config(&hello, &metadata).await {
|
||||
let buffered = mid.io.stop_buffering();
|
||||
mid.io
|
||||
.write_all(&buffered)
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
return Ok(match mid.into_stream(Arc::new(cfg)).await {
|
||||
Ok(stream) => {
|
||||
let s = stream.get_ref().1;
|
||||
Some((
|
||||
TlsMetadata {
|
||||
inner: metadata,
|
||||
tls_info: TlsHandshakeInfo {
|
||||
sni: s
|
||||
.server_name()
|
||||
.map(InternedString::intern),
|
||||
alpn: s
|
||||
.alpn_protocol()
|
||||
.map(|a| MaybeUtf8String(a.to_vec())),
|
||||
},
|
||||
let hello = mid.client_hello();
|
||||
if let Some(cfg) = tls_handler.get_config(&hello, &metadata).await {
|
||||
let buffered = mid.io.stop_buffering();
|
||||
mid.io
|
||||
.write_all(&buffered)
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
return Ok(match mid.into_stream(Arc::new(cfg)).await {
|
||||
Ok(stream) => {
|
||||
let s = stream.get_ref().1;
|
||||
Some((
|
||||
TlsMetadata {
|
||||
inner: metadata,
|
||||
tls_info: TlsHandshakeInfo {
|
||||
sni: s.server_name().map(InternedString::intern),
|
||||
alpn: s
|
||||
.alpn_protocol()
|
||||
.map(|a| MaybeUtf8String(a.to_vec())),
|
||||
},
|
||||
Box::pin(stream) as AcceptStream,
|
||||
))
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::trace!("Error completing TLS handshake: {e}");
|
||||
tracing::trace!("{e:?}");
|
||||
None
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
Box::pin(stream) as AcceptStream,
|
||||
))
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::trace!("Error completing TLS handshake: {e}");
|
||||
tracing::trace!("{e:?}");
|
||||
None
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
},
|
||||
)
|
||||
Ok(None)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(res) => res,
|
||||
|
||||
@@ -175,8 +175,13 @@ pub async fn remove_tunnel(
|
||||
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
let hostname = crate::hostname::Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let gateways = db.as_public().as_server_info().as_network().as_gateways().de()?;
|
||||
let hostname = crate::hostname::ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let gateways = db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
for host in all_hosts(db) {
|
||||
let host = host?;
|
||||
@@ -194,8 +199,13 @@ pub async fn remove_tunnel(
|
||||
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
let hostname = crate::hostname::Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
||||
let gateways = db.as_public().as_server_info().as_network().as_gateways().de()?;
|
||||
let hostname = crate::hostname::ServerHostname::load(db.as_public().as_server_info())?;
|
||||
let gateways = db
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_network()
|
||||
.as_gateways()
|
||||
.de()?;
|
||||
let ports = db.as_private().as_available_ports().de()?;
|
||||
for host in all_hosts(db) {
|
||||
let host = host?;
|
||||
|
||||
@@ -161,7 +161,10 @@ pub struct WifiAddParams {
|
||||
password: String,
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub async fn add(ctx: RpcContext, WifiAddParams { ssid, password }: WifiAddParams) -> Result<(), Error> {
|
||||
pub async fn add(
|
||||
ctx: RpcContext,
|
||||
WifiAddParams { ssid, password }: WifiAddParams,
|
||||
) -> Result<(), Error> {
|
||||
let wifi_manager = ctx.wifi_manager.clone();
|
||||
if !ssid.is_ascii() {
|
||||
return Err(Error::new(
|
||||
@@ -240,7 +243,10 @@ pub struct WifiSsidParams {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn connect(ctx: RpcContext, WifiSsidParams { ssid }: WifiSsidParams) -> Result<(), Error> {
|
||||
pub async fn connect(
|
||||
ctx: RpcContext,
|
||||
WifiSsidParams { ssid }: WifiSsidParams,
|
||||
) -> Result<(), Error> {
|
||||
let wifi_manager = ctx.wifi_manager.clone();
|
||||
if !ssid.is_ascii() {
|
||||
return Err(Error::new(
|
||||
|
||||
Reference in New Issue
Block a user