1use std::{
2 collections::HashMap,
3 convert::From,
4};
5
6use url::Url;
7
8use super::{
9 common::ProxyProto,
10 Policy,
11};
12#[allow(unused_imports)]
14use crate::config::{
15 ForwarderBuilder,
16 TunnelBuilder,
17};
18use crate::{
19 config::common::{
20 default_forwards_to,
21 CommonOpts,
22 TunnelConfig,
23 },
24 internals::proto::{
25 self,
26 BindExtra,
27 BindOpts,
28 },
29 tunnel::TcpTunnel,
30 Session,
31};
32
33#[derive(Default, Clone)]
35struct TcpOptions {
36 pub(crate) common_opts: CommonOpts,
37 pub(crate) remote_addr: Option<String>,
38 pub(crate) bindings: Vec<String>,
39}
40
41impl TunnelConfig for TcpOptions {
42 fn forwards_to(&self) -> String {
43 self.common_opts
44 .forwards_to
45 .clone()
46 .unwrap_or(default_forwards_to().into())
47 }
48 fn extra(&self) -> BindExtra {
49 BindExtra {
50 token: Default::default(),
51 ip_policy_ref: Default::default(),
52 metadata: self.common_opts.metadata.clone().unwrap_or_default(),
53 bindings: self.bindings.clone(),
54 pooling_enabled: self.common_opts.pooling_enabled.unwrap_or(false),
55 }
56 }
57 fn proto(&self) -> String {
58 "tcp".into()
59 }
60
61 fn forwards_proto(&self) -> String {
62 String::new()
64 }
65
66 fn verify_upstream_tls(&self) -> bool {
67 self.common_opts.verify_upstream_tls()
68 }
69
70 fn opts(&self) -> Option<BindOpts> {
71 let mut tcp_endpoint = proto::TcpEndpoint::default();
73
74 if let Some(remote_addr) = self.remote_addr.as_ref() {
75 tcp_endpoint.addr = remote_addr.clone();
76 }
77 tcp_endpoint.proxy_proto = self.common_opts.proxy_proto;
78
79 tcp_endpoint.ip_restriction = self.common_opts.ip_restriction();
80
81 tcp_endpoint.traffic_policy = if self.common_opts.traffic_policy.is_some() {
82 self.common_opts.traffic_policy.clone().map(From::from)
83 } else if self.common_opts.policy.is_some() {
84 self.common_opts.policy.clone().map(From::from)
85 } else {
86 None
87 };
88 Some(BindOpts::Tcp(tcp_endpoint))
89 }
90 fn labels(&self) -> HashMap<String, String> {
91 HashMap::new()
92 }
93}
94
95impl_builder! {
96 TcpTunnelBuilder, TcpOptions, TcpTunnel, endpoint
100}
101
102impl TcpTunnelBuilder {
104 pub fn allow_cidr(&mut self, cidr: impl Into<String>) -> &mut Self {
108 self.options.common_opts.cidr_restrictions.allow(cidr);
109 self
110 }
111 pub fn deny_cidr(&mut self, cidr: impl Into<String>) -> &mut Self {
115 self.options.common_opts.cidr_restrictions.deny(cidr);
116 self
117 }
118 pub fn proxy_proto(&mut self, proxy_proto: ProxyProto) -> &mut Self {
120 self.options.common_opts.proxy_proto = proxy_proto;
121 self
122 }
123 pub fn metadata(&mut self, metadata: impl Into<String>) -> &mut Self {
127 self.options.common_opts.metadata = Some(metadata.into());
128 self
129 }
130 pub fn binding(&mut self, binding: impl Into<String>) -> &mut Self {
132 self.options.bindings.push(binding.into());
133 self
134 }
135 pub fn forwards_to(&mut self, forwards_to: impl Into<String>) -> &mut Self {
144 self.options.common_opts.forwards_to = Some(forwards_to.into());
145 self
146 }
147
148 pub fn verify_upstream_tls(&mut self, verify_upstream_tls: bool) -> &mut Self {
150 self.options
151 .common_opts
152 .set_verify_upstream_tls(verify_upstream_tls);
153 self
154 }
155
156 pub fn remote_addr(&mut self, remote_addr: impl Into<String>) -> &mut Self {
160 self.options.remote_addr = Some(remote_addr.into());
161 self
162 }
163
164 pub fn policy<S>(&mut self, s: S) -> Result<&mut Self, S::Error>
166 where
167 S: TryInto<Policy>,
168 {
169 self.options.common_opts.policy = Some(s.try_into()?);
170 Ok(self)
171 }
172
173 pub fn traffic_policy(&mut self, policy_str: impl Into<String>) -> &mut Self {
175 self.options.common_opts.traffic_policy = Some(policy_str.into());
176 self
177 }
178
179 pub(crate) async fn for_forwarding_to(&mut self, to_url: &Url) -> &mut Self {
180 self.options.common_opts.for_forwarding_to(to_url);
181 self
182 }
183
184 pub fn pooling_enabled(&mut self, pooling_enabled: impl Into<bool>) -> &mut Self {
186 self.options.common_opts.pooling_enabled = Some(pooling_enabled.into());
187 self
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use super::*;
194 use crate::config::policies::test::POLICY_JSON;
195 const BINDING: &str = "public";
196 const METADATA: &str = "testmeta";
197 const TEST_FORWARD: &str = "testforward";
198 const REMOTE_ADDR: &str = "4.tcp.ngrok.io:1337";
199 const ALLOW_CIDR: &str = "0.0.0.0/0";
200 const DENY_CIDR: &str = "10.1.1.1/32";
201
202 #[test]
203 fn test_interface_to_proto() {
204 tunnel_test(
207 &TcpTunnelBuilder {
208 session: None,
209 options: Default::default(),
210 }
211 .allow_cidr(ALLOW_CIDR)
212 .deny_cidr(DENY_CIDR)
213 .proxy_proto(ProxyProto::V2)
214 .metadata(METADATA)
215 .binding(BINDING)
216 .remote_addr(REMOTE_ADDR)
217 .forwards_to(TEST_FORWARD)
218 .policy(POLICY_JSON)
219 .unwrap()
220 .options,
221 );
222 }
223
224 fn tunnel_test<C>(tunnel_cfg: &C)
225 where
226 C: TunnelConfig,
227 {
228 assert_eq!(TEST_FORWARD, tunnel_cfg.forwards_to());
229
230 let extra = tunnel_cfg.extra();
231 assert_eq!(String::default(), *extra.token);
232 assert_eq!(METADATA, extra.metadata);
233 assert_eq!(Vec::from([BINDING]), extra.bindings);
234 assert_eq!(String::default(), extra.ip_policy_ref);
235
236 assert_eq!("tcp", tunnel_cfg.proto());
237
238 let opts = tunnel_cfg.opts().unwrap();
239 assert!(matches!(opts, BindOpts::Tcp { .. }));
240 if let BindOpts::Tcp(endpoint) = opts {
241 assert_eq!(REMOTE_ADDR, endpoint.addr);
242 assert!(matches!(endpoint.proxy_proto, ProxyProto::V2));
243
244 let ip_restriction = endpoint.ip_restriction.unwrap();
245 assert_eq!(Vec::from([ALLOW_CIDR]), ip_restriction.allow_cidrs);
246 assert_eq!(Vec::from([DENY_CIDR]), ip_restriction.deny_cidrs);
247 }
248
249 assert_eq!(HashMap::new(), tunnel_cfg.labels());
250 }
251}