use std::{
collections::HashMap,
env,
process,
};
use async_trait::async_trait;
use once_cell::sync::OnceCell;
use url::Url;
pub use crate::internals::proto::ProxyProto;
use crate::{
config::policies::Policy,
forwarder::Forwarder,
internals::proto::{
BindExtra,
BindOpts,
IpRestriction,
MutualTls,
},
session::RpcError,
Session,
Tunnel,
};
pub(crate) fn default_forwards_to() -> &'static str {
static FORWARDS_TO: OnceCell<String> = OnceCell::new();
FORWARDS_TO
.get_or_init(|| {
let hostname = hostname::get()
.unwrap_or("<unknown>".into())
.to_string_lossy()
.into_owned();
let exe = env::current_exe()
.unwrap_or("<unknown>".into())
.to_string_lossy()
.into_owned();
let pid = process::id();
format!("app://{hostname}/{exe}?pid={pid}")
})
.as_str()
}
#[async_trait]
pub trait TunnelBuilder: From<Session> {
type Tunnel: Tunnel;
async fn listen(&self) -> Result<Self::Tunnel, RpcError>;
}
#[async_trait]
pub trait ForwarderBuilder: TunnelBuilder {
async fn listen_and_forward(&self, to_url: Url) -> Result<Forwarder<Self::Tunnel>, RpcError>;
}
macro_rules! impl_builder {
($(#[$m:meta])* $name:ident, $opts:ty, $tun:ident, $edgepoint:tt) => {
$(#[$m])*
#[derive(Clone)]
pub struct $name {
options: $opts,
session: Option<Session>,
}
mod __builder_impl {
use $crate::forwarder::Forwarder;
use $crate::config::common::ForwarderBuilder;
use $crate::config::common::TunnelBuilder;
use $crate::session::RpcError;
use async_trait::async_trait;
use url::Url;
use super::*;
impl From<Session> for $name {
fn from(session: Session) -> Self {
$name {
options: Default::default(),
session: session.into(),
}
}
}
#[async_trait]
impl TunnelBuilder for $name {
type Tunnel = $tun;
async fn listen(&self) -> Result<$tun, RpcError> {
Ok($tun {
inner: self
.session
.as_ref()
.unwrap()
.start_tunnel(&self.options)
.await?,
})
}
}
#[async_trait]
impl ForwarderBuilder for $name {
async fn listen_and_forward(&self, to_url: Url) -> Result<Forwarder<$tun>, RpcError> {
let mut cfg = self.clone();
cfg.for_forwarding_to(&to_url).await;
let tunnel = cfg.listen().await?;
let info = tunnel.make_info();
$crate::forwarder::forward(tunnel, info, to_url)
}
}
}
};
}
pub(crate) trait TunnelConfig {
fn forwards_to(&self) -> String;
fn forwards_proto(&self) -> String;
fn verify_upstream_tls(&self) -> bool;
fn extra(&self) -> BindExtra;
fn proto(&self) -> String;
fn opts(&self) -> Option<BindOpts>;
fn labels(&self) -> HashMap<String, String>;
}
impl<'a, T> TunnelConfig for &'a T
where
T: TunnelConfig,
{
fn forwards_to(&self) -> String {
(**self).forwards_to()
}
fn forwards_proto(&self) -> String {
(**self).forwards_proto()
}
fn verify_upstream_tls(&self) -> bool {
(**self).verify_upstream_tls()
}
fn extra(&self) -> BindExtra {
(**self).extra()
}
fn proto(&self) -> String {
(**self).proto()
}
fn opts(&self) -> Option<BindOpts> {
(**self).opts()
}
fn labels(&self) -> HashMap<String, String> {
(**self).labels()
}
}
#[derive(Clone, Default)]
pub(crate) struct CidrRestrictions {
pub(crate) allowed: Vec<String>,
pub(crate) denied: Vec<String>,
}
impl CidrRestrictions {
pub(crate) fn allow(&mut self, cidr: impl Into<String>) {
self.allowed.push(cidr.into());
}
pub(crate) fn deny(&mut self, cidr: impl Into<String>) {
self.denied.push(cidr.into());
}
}
#[derive(Default, Clone)]
pub(crate) struct CommonOpts {
pub(crate) cidr_restrictions: CidrRestrictions,
pub(crate) proxy_proto: ProxyProto,
pub(crate) metadata: Option<String>,
pub(crate) forwards_to: Option<String>,
pub(crate) forwards_proto: Option<String>,
verify_upstream_tls: Option<bool>,
pub(crate) policy: Option<Policy>,
pub(crate) traffic_policy: Option<String>,
}
impl CommonOpts {
pub(crate) fn ip_restriction(&self) -> Option<IpRestriction> {
(!self.cidr_restrictions.allowed.is_empty() || !self.cidr_restrictions.denied.is_empty())
.then_some(self.cidr_restrictions.clone().into())
}
pub(crate) fn for_forwarding_to(&mut self, to_url: &Url) -> &mut Self {
self.forwards_to = Some(to_url.as_str().into());
self
}
pub(crate) fn set_verify_upstream_tls(&mut self, verify_upstream_tls: bool) {
self.verify_upstream_tls = Some(verify_upstream_tls)
}
pub(crate) fn verify_upstream_tls(&self) -> bool {
self.verify_upstream_tls.unwrap_or(true)
}
}
impl From<CidrRestrictions> for IpRestriction {
fn from(cr: CidrRestrictions) -> Self {
IpRestriction {
allow_cidrs: cr.allowed,
deny_cidrs: cr.denied,
}
}
}
impl From<&[bytes::Bytes]> for MutualTls {
fn from(b: &[bytes::Bytes]) -> Self {
let mut aggregated = Vec::new();
b.iter().for_each(|c| aggregated.extend(c));
MutualTls {
mutual_tls_ca: aggregated,
}
}
}