Skip to content

Commit f192fd6

Browse files
committed
Add IPv6 address support for parsing https host and port
1 parent e78a96c commit f192fd6

File tree

2 files changed

+64
-21
lines changed

2 files changed

+64
-21
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,4 @@ rhoxy --port 8080
5757
```
5858

5959
### TODO
60-
- Handle IPv6 properly
6160
- MITM proxy mode

src/protocol/https.rs

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,38 @@ where
8080
}
8181

8282
fn parse_host_port(target: &str) -> Result<(String, u16)> {
83-
let parts: Vec<&str> = target.split(':').collect();
84-
match parts.len() {
85-
1 => Ok((parts[0].to_string(), 443)),
86-
2 => {
87-
let port = parts[1]
83+
// IPv6
84+
if target.starts_with('[') {
85+
if let Some(bracket_end) = target.find("]:") {
86+
let host = target[1..bracket_end].to_string();
87+
let port_str = &target[bracket_end + 2..];
88+
let port = port_str
8889
.parse::<u16>()
89-
.map_err(|_| anyhow::anyhow!("Invalid port: {}", parts[1]))?;
90-
Ok((parts[0].to_string(), port))
90+
.map_err(|_| anyhow::anyhow!("Invalid port: {}", port_str))?;
91+
return Ok((host, port));
92+
} else if target.ends_with(']') {
93+
let host = target[1..target.len() - 1].to_string();
94+
return Ok((host, 443));
95+
} else {
96+
return Err(anyhow::anyhow!("Invalid IPv6 format: {}", target));
9197
}
92-
_ => Err(anyhow::anyhow!("Invalid target format: {}", target)),
98+
}
99+
100+
// IPv6 without port or IPv4 with port
101+
if let Some(colon_pos) = target.rfind(':') {
102+
let colon_count = target.matches(':').count();
103+
if colon_count > 1 {
104+
return Ok((target.to_string(), 443));
105+
}
106+
107+
let host = target[..colon_pos].to_string();
108+
let port_str = &target[colon_pos + 1..];
109+
let port = port_str
110+
.parse::<u16>()
111+
.map_err(|_| anyhow::anyhow!("Invalid port: {}", port_str))?;
112+
Ok((host, port))
113+
} else {
114+
Ok((target.to_string(), 443))
93115
}
94116
}
95117

@@ -127,15 +149,9 @@ mod tests {
127149

128150
#[test]
129151
fn test_parse_host_port_ipv6() {
130-
// TODO: Fix this to support IPv6 addresses
131-
let result = parse_host_port("[::1]:8080");
132-
assert!(result.is_err());
133-
assert!(
134-
result
135-
.unwrap_err()
136-
.to_string()
137-
.contains("Invalid target format")
138-
);
152+
let result = parse_host_port("[::1]:8080").unwrap();
153+
assert_eq!(result.0, "::1");
154+
assert_eq!(result.1, 8080);
139155
}
140156

141157
#[test]
@@ -153,14 +169,42 @@ mod tests {
153169
}
154170

155171
#[test]
156-
fn test_parse_host_port_too_many_colons() {
157-
let result = parse_host_port("example.com:8080:extra");
172+
fn test_parse_host_port_ipv6_with_brackets_and_port() {
173+
let result = parse_host_port("[2001:db8::1]:8080").unwrap();
174+
assert_eq!(result.0, "2001:db8::1");
175+
assert_eq!(result.1, 8080);
176+
}
177+
178+
#[test]
179+
fn test_parse_host_port_ipv6_with_brackets_no_port() {
180+
let result = parse_host_port("[2001:db8::1]").unwrap();
181+
assert_eq!(result.0, "2001:db8::1");
182+
assert_eq!(result.1, 443);
183+
}
184+
185+
#[test]
186+
fn test_parse_host_port_ipv6_without_brackets() {
187+
let result = parse_host_port("2001:db8::1").unwrap();
188+
assert_eq!(result.0, "2001:db8::1");
189+
assert_eq!(result.1, 443);
190+
}
191+
192+
#[test]
193+
fn test_parse_host_port_ipv6_localhost() {
194+
let result = parse_host_port("[::1]:3000").unwrap();
195+
assert_eq!(result.0, "::1");
196+
assert_eq!(result.1, 3000);
197+
}
198+
199+
#[test]
200+
fn test_parse_host_port_invalid_ipv6_brackets() {
201+
let result = parse_host_port("[2001:db8::1:invalid");
158202
assert!(result.is_err());
159203
assert!(
160204
result
161205
.unwrap_err()
162206
.to_string()
163-
.contains("Invalid target format")
207+
.contains("Invalid IPv6 format")
164208
);
165209
}
166210

0 commit comments

Comments
 (0)