1use std::{
2 collections::HashMap,
3 env,
4 process,
5};
6
7use async_trait::async_trait;
8use once_cell::sync::OnceCell;
9use url::Url;
10
11pub use crate::internals::proto::ProxyProto;
12use crate::{
13 config::policies::Policy,
14 forwarder::Forwarder,
15 internals::proto::{
16 BindExtra,
17 BindOpts,
18 IpRestriction,
19 MutualTls,
20 },
21 session::RpcError,
22 Session,
23 Tunnel,
24};
25
26pub(crate) fn default_forwards_to() -> &'static str {
27 static FORWARDS_TO: OnceCell<String> = OnceCell::new();
28
29 FORWARDS_TO
30 .get_or_init(|| {
31 let hostname = hostname::get()
32 .unwrap_or("<unknown>".into())
33 .to_string_lossy()
34 .into_owned();
35 let exe = env::current_exe()
36 .unwrap_or("<unknown>".into())
37 .to_string_lossy()
38 .into_owned();
39 let pid = process::id();
40 format!("app://{hostname}/{exe}?pid={pid}")
41 })
42 .as_str()
43}
44
45#[async_trait]
47pub trait TunnelBuilder: From<Session> {
48 type Tunnel: Tunnel;
50
51 async fn listen(&self) -> Result<Self::Tunnel, RpcError>;
53}
54
55#[async_trait]
58pub trait ForwarderBuilder: TunnelBuilder {
59 async fn listen_and_forward(&self, to_url: Url) -> Result<Forwarder<Self::Tunnel>, RpcError>;
64}
65
66macro_rules! impl_builder {
67 ($(#[$m:meta])* $name:ident, $opts:ty, $tun:ident, $edgepoint:tt) => {
68 $(#[$m])*
69 #[derive(Clone)]
70 pub struct $name {
71 options: $opts,
72 session: Option<Session>,
74 }
75
76 mod __builder_impl {
77 use $crate::forwarder::Forwarder;
78 use $crate::config::common::ForwarderBuilder;
79 use $crate::config::common::TunnelBuilder;
80 use $crate::session::RpcError;
81 use async_trait::async_trait;
82 use url::Url;
83
84 use super::*;
85
86 impl From<Session> for $name {
87 fn from(session: Session) -> Self {
88 $name {
89 options: Default::default(),
90 session: session.into(),
91 }
92 }
93 }
94
95 #[async_trait]
96 impl TunnelBuilder for $name {
97 type Tunnel = $tun;
98
99 async fn listen(&self) -> Result<$tun, RpcError> {
100 Ok($tun {
101 inner: self
102 .session
103 .as_ref()
104 .unwrap()
105 .start_tunnel(&self.options)
106 .await?,
107 })
108 }
109 }
110
111 #[async_trait]
112 impl ForwarderBuilder for $name {
113 async fn listen_and_forward(&self, to_url: Url) -> Result<Forwarder<$tun>, RpcError> {
114 let mut cfg = self.clone();
115 cfg.for_forwarding_to(&to_url).await;
116 let tunnel = cfg.listen().await?;
117 let info = tunnel.make_info();
118 $crate::forwarder::forward(tunnel, info, to_url)
119 }
120 }
121 }
122 };
123}
124
125pub(crate) trait TunnelConfig {
127 fn forwards_to(&self) -> String;
131 fn forwards_proto(&self) -> String;
133 fn verify_upstream_tls(&self) -> bool;
135 fn extra(&self) -> BindExtra;
137 fn proto(&self) -> String;
139 fn opts(&self) -> Option<BindOpts>;
141 fn labels(&self) -> HashMap<String, String>;
143}
144
145impl<T> TunnelConfig for &T
147where
148 T: TunnelConfig,
149{
150 fn forwards_to(&self) -> String {
151 (**self).forwards_to()
152 }
153
154 fn forwards_proto(&self) -> String {
155 (**self).forwards_proto()
156 }
157 fn verify_upstream_tls(&self) -> bool {
158 (**self).verify_upstream_tls()
159 }
160 fn extra(&self) -> BindExtra {
161 (**self).extra()
162 }
163 fn proto(&self) -> String {
164 (**self).proto()
165 }
166 fn opts(&self) -> Option<BindOpts> {
167 (**self).opts()
168 }
169 fn labels(&self) -> HashMap<String, String> {
170 (**self).labels()
171 }
172}
173
174#[derive(Clone, Default)]
176pub(crate) struct CidrRestrictions {
177 pub(crate) allowed: Vec<String>,
179 pub(crate) denied: Vec<String>,
181}
182
183impl CidrRestrictions {
184 pub(crate) fn allow(&mut self, cidr: impl Into<String>) {
185 self.allowed.push(cidr.into());
186 }
187 pub(crate) fn deny(&mut self, cidr: impl Into<String>) {
188 self.denied.push(cidr.into());
189 }
190}
191
192#[derive(Default, Clone)]
194pub(crate) struct CommonOpts {
195 pub(crate) cidr_restrictions: CidrRestrictions,
197 pub(crate) proxy_proto: ProxyProto,
200 pub(crate) metadata: Option<String>,
202 pub(crate) forwards_to: Option<String>,
205 pub(crate) forwards_proto: Option<String>,
207 verify_upstream_tls: Option<bool>,
209 pub(crate) policy: Option<Policy>,
211 pub(crate) traffic_policy: Option<String>,
214 pub(crate) pooling_enabled: Option<bool>,
216}
217
218impl CommonOpts {
219 pub(crate) fn ip_restriction(&self) -> Option<IpRestriction> {
221 (!self.cidr_restrictions.allowed.is_empty() || !self.cidr_restrictions.denied.is_empty())
222 .then_some(self.cidr_restrictions.clone().into())
223 }
224
225 pub(crate) fn for_forwarding_to(&mut self, to_url: &Url) -> &mut Self {
226 self.forwards_to = Some(to_url.as_str().into());
227 self
228 }
229
230 pub(crate) fn set_verify_upstream_tls(&mut self, verify_upstream_tls: bool) {
231 self.verify_upstream_tls = Some(verify_upstream_tls)
232 }
233
234 pub(crate) fn verify_upstream_tls(&self) -> bool {
235 self.verify_upstream_tls.unwrap_or(true)
236 }
237}
238
239impl From<CidrRestrictions> for IpRestriction {
241 fn from(cr: CidrRestrictions) -> Self {
242 IpRestriction {
243 allow_cidrs: cr.allowed,
244 deny_cidrs: cr.denied,
245 }
246 }
247}
248
249impl From<&[bytes::Bytes]> for MutualTls {
250 fn from(b: &[bytes::Bytes]) -> Self {
251 let mut aggregated = Vec::new();
252 b.iter().for_each(|c| aggregated.extend(c));
253 MutualTls {
254 mutual_tls_ca: aggregated,
255 }
256 }
257}