Skip to content

Commit e89bedb

Browse files
committed
tproxy: The ending s in the url must be on the port part
1 parent 3f378ac commit e89bedb

File tree

2 files changed

+98
-27
lines changed

2 files changed

+98
-27
lines changed

tproxy/src/proxy.rs

Lines changed: 97 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn is_subdomain(sni: &str, base_domain: &str) -> bool {
5050

5151
struct DstInfo {
5252
app_id: String,
53-
port: Option<u16>,
53+
port: u16,
5454
is_tls: bool,
5555
}
5656

@@ -62,23 +62,38 @@ fn parse_destination(sni: &str, dotted_base_domain: &str) -> Result<DstInfo> {
6262
if subdomain.contains('.') {
6363
bail!("only one level of subdomain is supported");
6464
}
65+
let mut parts = subdomain.split('-');
66+
let app_id = parts.next().context("no app id found")?.to_owned();
67+
if app_id.is_empty() {
68+
bail!("app id is empty");
69+
}
70+
let last_part = parts.next();
6571
let is_tls;
66-
let subdomain = match subdomain.strip_suffix("s") {
72+
let port;
73+
match last_part {
6774
None => {
6875
is_tls = false;
69-
subdomain
76+
port = None;
7077
}
71-
Some(subdomain) => {
72-
is_tls = true;
73-
subdomain
78+
Some(last_part) => {
79+
let port_str = match last_part.strip_suffix('s') {
80+
None => {
81+
is_tls = false;
82+
last_part
83+
}
84+
Some(last_part) => {
85+
is_tls = true;
86+
last_part
87+
}
88+
};
89+
port = if port_str.is_empty() {
90+
None
91+
} else {
92+
Some(port_str.parse::<u16>().context("invalid port")?)
93+
};
7494
}
7595
};
76-
let mut parts = subdomain.split('-');
77-
let app_id = parts.next().context("no app id found")?.to_owned();
78-
let port = parts
79-
.next()
80-
.map(|p| p.parse().context("invalid port"))
81-
.transpose()?;
96+
let port = port.unwrap_or(if is_tls { 443 } else { 80 });
8297
if parts.next().is_some() {
8398
bail!("invalid sni format");
8499
}
@@ -106,15 +121,9 @@ async fn handle_connection(
106121
if is_subdomain(&sni, dotted_base_domain) {
107122
let dst = parse_destination(&sni, dotted_base_domain)?;
108123
if dst.is_tls {
109-
tls_passthough::proxy_to_app(
110-
state,
111-
inbound,
112-
buffer,
113-
&dst.app_id,
114-
dst.port.unwrap_or(443),
115-
)
116-
.await
117-
.with_context(|| format!("error on connection {sni}"))
124+
tls_passthough::proxy_to_app(state, inbound, buffer, &dst.app_id, dst.port)
125+
.await
126+
.with_context(|| format!("error on connection {sni}"))
118127
} else {
119128
tls_terminate_proxy
120129
.proxy(inbound, buffer, &dst.app_id, dst.port)
@@ -200,7 +209,70 @@ pub fn start(config: ProxyConfig, app_state: Proxy) {
200209
});
201210
}
202211

203-
// async fn connect_to_app(state: &AppState, app_id: &str, port: u16) -> Result<TcpStream> {
204-
// let host = state.lock().select_a_host(app_id).context(format!("tapp {app_id} not found"))?;
205-
// TcpStream::connect((host.ip, port))
206-
// }
212+
#[cfg(test)]
213+
mod tests {
214+
use super::*;
215+
216+
#[test]
217+
fn test_parse_destination() {
218+
let base_domain = ".example.com";
219+
220+
// Test basic app_id only
221+
let result = parse_destination("myapp.example.com", base_domain).unwrap();
222+
assert_eq!(result.app_id, "myapp");
223+
assert_eq!(result.port, 80);
224+
assert!(!result.is_tls);
225+
226+
// Test app_id with custom port
227+
let result = parse_destination("myapp-8080.example.com", base_domain).unwrap();
228+
assert_eq!(result.app_id, "myapp");
229+
assert_eq!(result.port, 8080);
230+
assert!(!result.is_tls);
231+
232+
// Test app_id with TLS
233+
let result = parse_destination("myapp-443s.example.com", base_domain).unwrap();
234+
assert_eq!(result.app_id, "myapp");
235+
assert_eq!(result.port, 443);
236+
assert!(result.is_tls);
237+
238+
// Test app_id with custom port and TLS
239+
let result = parse_destination("myapp-8443s.example.com", base_domain).unwrap();
240+
assert_eq!(result.app_id, "myapp");
241+
assert_eq!(result.port, 8443);
242+
assert!(result.is_tls);
243+
244+
// Test default port but ends with s
245+
let result = parse_destination("myapps.example.com", base_domain).unwrap();
246+
assert_eq!(result.app_id, "myapps");
247+
assert_eq!(result.port, 80);
248+
assert!(!result.is_tls);
249+
250+
// Test default port but ends with s in port part
251+
let result = parse_destination("myapp-s.example.com", base_domain).unwrap();
252+
assert_eq!(result.app_id, "myapp");
253+
assert_eq!(result.port, 443);
254+
assert!(result.is_tls);
255+
}
256+
257+
#[test]
258+
fn test_parse_destination_errors() {
259+
let base_domain = ".example.com";
260+
261+
// Test invalid domain suffix
262+
assert!(parse_destination("myapp.wrong.com", base_domain).is_err());
263+
264+
// Test multiple subdomains
265+
assert!(parse_destination("invalid.myapp.example.com", base_domain).is_err());
266+
267+
// Test invalid port format
268+
assert!(parse_destination("myapp-65536.example.com", base_domain).is_err());
269+
assert!(parse_destination("myapp-abc.example.com", base_domain).is_err());
270+
271+
// Test too many parts
272+
assert!(parse_destination("myapp-8080-extra.example.com", base_domain).is_err());
273+
274+
// Test empty app_id
275+
assert!(parse_destination("-8080.example.com", base_domain).is_err());
276+
assert!(parse_destination("myapp-8080ss.example.com", base_domain).is_err());
277+
}
278+
}

tproxy/src/proxy/tls_terminate.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,8 @@ impl TlsTerminateProxy {
118118
inbound: TcpStream,
119119
buffer: Vec<u8>,
120120
app_id: &str,
121-
port: Option<u16>,
121+
port: u16,
122122
) -> Result<()> {
123-
let port = port.unwrap_or(80);
124123
let addresses = self
125124
.app_state
126125
.lock()

0 commit comments

Comments
 (0)