use crate::llm::actions::Server;
use crate::privilege::SystemCapabilities;
use crate::protocol::metadata::PortTransport;
pub const DEFAULT_HOST: &str = "127.0.0.1";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortFallback {
NeedsPrivilege,
InUse,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DefaultPort {
pub port: u16,
pub well_known: Option<u16>,
pub transport: PortTransport,
pub fallback: Option<PortFallback>,
}
impl DefaultPort {
pub fn describe(&self) -> String {
match (self.well_known, self.fallback) {
(Some(p), None) => format!("well-known port {p}"),
(Some(p), Some(PortFallback::NeedsPrivilege)) => {
format!("well-known port {p} needs root; starting on an OS-assigned port")
}
(Some(p), Some(PortFallback::InUse)) => {
format!("well-known port {p} is in use; starting on an OS-assigned port")
}
(None, _) if self.port == 0 => {
"no well-known port; starting on an OS-assigned port".to_string()
}
(None, _) => format!("port {}", self.port),
}
}
}
pub fn resolve_default_port(
well_known: Option<u16>,
transport: PortTransport,
host: &str,
caps: &SystemCapabilities,
) -> DefaultPort {
let Some(port) = well_known.filter(|p| *p != 0) else {
return DefaultPort {
port: 0,
well_known: None,
transport,
fallback: None,
};
};
let fallback = if port < 1024 && !caps.can_bind_privileged_ports {
Some(PortFallback::NeedsPrivilege)
} else if port_is_in_use(host, port, transport) {
Some(PortFallback::InUse)
} else {
None
};
DefaultPort {
port: if fallback.is_some() { 0 } else { port },
well_known: Some(port),
transport,
fallback,
}
}
pub fn port_is_in_use(host: &str, port: u16, transport: PortTransport) -> bool {
let result = match transport {
PortTransport::Tcp => std::net::TcpListener::bind((host, port)).map(drop),
PortTransport::Udp => std::net::UdpSocket::bind((host, port)).map(drop),
PortTransport::Sctp => return false,
};
matches!(result, Err(e) if e.kind() == std::io::ErrorKind::AddrInUse)
}
pub fn default_port_for_server(
protocol: &dyn Server,
host: Option<&str>,
caps: &SystemCapabilities,
) -> Option<DefaultPort> {
let metadata = protocol.metadata();
let binding = protocol.default_binding();
if let Some(well_known) = metadata.well_known_port {
let binding_host = binding.as_ref().and_then(|b| b.host.clone());
let host = host
.map(str::to_string)
.or(binding_host)
.unwrap_or_else(|| DEFAULT_HOST.to_string());
return Some(resolve_default_port(
Some(well_known),
metadata.well_known_transport,
&host,
caps,
));
}
let port = match binding {
Some(b) => b.port?,
None => 0,
};
Some(DefaultPort {
port,
well_known: None,
transport: metadata.well_known_transport,
fallback: None,
})
}