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
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Binding {
31 Public,
33 Internal,
35 Kubernetes,
37}
38
39impl Binding {
40 pub fn as_str(&self) -> &'static str {
42 match self {
43 Binding::Public => "public",
44 Binding::Internal => "internal",
45 Binding::Kubernetes => "kubernetes",
46 }
47 }
48
49 pub(crate) fn validate(s: &str) -> Result<(), String> {
51 match s.to_lowercase().as_str() {
52 "public" | "internal" | "kubernetes" => Ok(()),
53 _ => Err(format!(
54 "Invalid binding value '{}'. Expected 'public', 'internal', or 'kubernetes'",
55 s
56 )),
57 }
58 }
59}
60
61impl From<Binding> for String {
62 fn from(binding: Binding) -> String {
63 binding.as_str().to_string()
64 }
65}
66
67impl std::str::FromStr for Binding {
68 type Err = String;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 match s.to_lowercase().as_str() {
72 "public" => Ok(Binding::Public),
73 "internal" => Ok(Binding::Internal),
74 "kubernetes" => Ok(Binding::Kubernetes),
75 _ => Err(format!(
76 "Invalid binding value '{}'. Expected 'public', 'internal', or 'kubernetes'",
77 s
78 )),
79 }
80 }
81}
82
83impl std::fmt::Display for Binding {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(f, "{}", self.as_str())
86 }
87}
88
89pub(crate) fn default_forwards_to() -> &'static str {
90 static FORWARDS_TO: OnceCell<String> = OnceCell::new();
91
92 FORWARDS_TO
93 .get_or_init(|| {
94 let hostname = hostname::get()
95 .unwrap_or("<unknown>".into())
96 .to_string_lossy()
97 .into_owned();
98 let exe = env::current_exe()
99 .unwrap_or("<unknown>".into())
100 .to_string_lossy()
101 .into_owned();
102 let pid = process::id();
103 format!("app://{hostname}/{exe}?pid={pid}")
104 })
105 .as_str()
106}
107
108#[async_trait]
110pub trait TunnelBuilder: From<Session> {
111 type Tunnel: Tunnel;
113
114 async fn listen(&self) -> Result<Self::Tunnel, RpcError>;
116}
117
118#[async_trait]
121pub trait ForwarderBuilder: TunnelBuilder {
122 async fn listen_and_forward(&self, to_url: Url) -> Result<Forwarder<Self::Tunnel>, RpcError>;
127}
128
129macro_rules! impl_builder {
130 ($(#[$m:meta])* $name:ident, $opts:ty, $tun:ident, $edgepoint:tt) => {
131 $(#[$m])*
132 #[derive(Clone)]
133 pub struct $name {
134 options: $opts,
135 session: Option<Session>,
137 }
138
139 mod __builder_impl {
140 use $crate::forwarder::Forwarder;
141 use $crate::config::common::ForwarderBuilder;
142 use $crate::config::common::TunnelBuilder;
143 use $crate::session::RpcError;
144 use async_trait::async_trait;
145 use url::Url;
146
147 use super::*;
148
149 impl From<Session> for $name {
150 fn from(session: Session) -> Self {
151 $name {
152 options: Default::default(),
153 session: session.into(),
154 }
155 }
156 }
157
158 #[async_trait]
159 impl TunnelBuilder for $name {
160 type Tunnel = $tun;
161
162 async fn listen(&self) -> Result<$tun, RpcError> {
163 Ok($tun {
164 inner: self
165 .session
166 .as_ref()
167 .unwrap()
168 .start_tunnel(&self.options)
169 .await?,
170 })
171 }
172 }
173
174 #[async_trait]
175 impl ForwarderBuilder for $name {
176 async fn listen_and_forward(&self, to_url: Url) -> Result<Forwarder<$tun>, RpcError> {
177 let mut cfg = self.clone();
178 cfg.for_forwarding_to(&to_url).await;
179 let tunnel = cfg.listen().await?;
180 let info = tunnel.make_info();
181 $crate::forwarder::forward(tunnel, info, to_url)
182 }
183 }
184 }
185 };
186}
187
188pub(crate) trait TunnelConfig {
190 fn forwards_to(&self) -> String;
194 fn forwards_proto(&self) -> String;
196 fn verify_upstream_tls(&self) -> bool;
198 fn extra(&self) -> BindExtra;
200 fn proto(&self) -> String;
202 fn opts(&self) -> Option<BindOpts>;
204 fn labels(&self) -> HashMap<String, String>;
206}
207
208impl<T> TunnelConfig for &T
210where
211 T: TunnelConfig,
212{
213 fn forwards_to(&self) -> String {
214 (**self).forwards_to()
215 }
216
217 fn forwards_proto(&self) -> String {
218 (**self).forwards_proto()
219 }
220 fn verify_upstream_tls(&self) -> bool {
221 (**self).verify_upstream_tls()
222 }
223 fn extra(&self) -> BindExtra {
224 (**self).extra()
225 }
226 fn proto(&self) -> String {
227 (**self).proto()
228 }
229 fn opts(&self) -> Option<BindOpts> {
230 (**self).opts()
231 }
232 fn labels(&self) -> HashMap<String, String> {
233 (**self).labels()
234 }
235}
236
237#[derive(Clone, Default)]
239pub(crate) struct CidrRestrictions {
240 pub(crate) allowed: Vec<String>,
242 pub(crate) denied: Vec<String>,
244}
245
246impl CidrRestrictions {
247 pub(crate) fn allow(&mut self, cidr: impl Into<String>) {
248 self.allowed.push(cidr.into());
249 }
250 pub(crate) fn deny(&mut self, cidr: impl Into<String>) {
251 self.denied.push(cidr.into());
252 }
253}
254
255#[derive(Default, Clone)]
257pub(crate) struct CommonOpts {
258 pub(crate) cidr_restrictions: CidrRestrictions,
260 pub(crate) proxy_proto: ProxyProto,
263 pub(crate) metadata: Option<String>,
265 pub(crate) forwards_to: Option<String>,
268 pub(crate) forwards_proto: Option<String>,
270 verify_upstream_tls: Option<bool>,
272 pub(crate) policy: Option<Policy>,
274 pub(crate) traffic_policy: Option<String>,
277 pub(crate) pooling_enabled: Option<bool>,
279}
280
281impl CommonOpts {
282 pub(crate) fn ip_restriction(&self) -> Option<IpRestriction> {
284 (!self.cidr_restrictions.allowed.is_empty() || !self.cidr_restrictions.denied.is_empty())
285 .then_some(self.cidr_restrictions.clone().into())
286 }
287
288 pub(crate) fn for_forwarding_to(&mut self, to_url: &Url) -> &mut Self {
289 self.forwards_to = Some(to_url.as_str().into());
290 self
291 }
292
293 pub(crate) fn set_verify_upstream_tls(&mut self, verify_upstream_tls: bool) {
294 self.verify_upstream_tls = Some(verify_upstream_tls)
295 }
296
297 pub(crate) fn verify_upstream_tls(&self) -> bool {
298 self.verify_upstream_tls.unwrap_or(true)
299 }
300}
301
302impl From<CidrRestrictions> for IpRestriction {
304 fn from(cr: CidrRestrictions) -> Self {
305 IpRestriction {
306 allow_cidrs: cr.allowed,
307 deny_cidrs: cr.denied,
308 }
309 }
310}
311
312impl From<&[bytes::Bytes]> for MutualTls {
313 fn from(b: &[bytes::Bytes]) -> Self {
314 let mut aggregated = Vec::new();
315 b.iter().for_each(|c| aggregated.extend(c));
316 MutualTls {
317 mutual_tls_ca: aggregated,
318 }
319 }
320}