Skip to content

Commit d9f17ce

Browse files
committed
fix(client): allow calling Destination::set_host with IPv6 addresses
Closes #1661
1 parent 5bfc110 commit d9f17ce

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

src/client/connect/mod.rs

+45-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use std::error::Error as StdError;
99
use std::mem;
1010

11-
use bytes::{BufMut, BytesMut};
11+
use bytes::{BufMut, Bytes, BytesMut};
1212
use futures::Future;
1313
use http::{uri, Uri};
1414
use tokio_io::{AsyncRead, AsyncWrite};
@@ -131,13 +131,20 @@ impl Destination {
131131
///
132132
/// Returns an error if the string is not a valid hostname.
133133
pub fn set_host(&mut self, host: &str) -> ::Result<()> {
134-
if host.contains(&['@',':'][..]) {
134+
// Prevent any userinfo setting, it's bad!
135+
if host.contains('@') {
135136
return Err(::error::Parse::Uri.into());
136137
}
137138
let auth = if let Some(port) = self.port() {
138-
format!("{}:{}", host, port).parse().map_err(::error::Parse::from)?
139+
let bytes = Bytes::from(format!("{}:{}", host, port));
140+
uri::Authority::from_shared(bytes)
141+
.map_err(::error::Parse::from)?
139142
} else {
140-
host.parse().map_err(::error::Parse::from)?
143+
let auth = host.parse::<uri::Authority>().map_err(::error::Parse::from)?;
144+
if auth.port().is_some() {
145+
return Err(::error::Parse::Uri.into());
146+
}
147+
auth
141148
};
142149
self.update_uri(move |parts| {
143150
parts.authority = Some(auth);
@@ -305,6 +312,30 @@ mod tests {
305312
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
306313
assert_eq!(dst.port(), None, "error doesn't modify dst");
307314

315+
// Check port isn't snuck into `set_host`.
316+
dst.set_host("seanmonstar.com:3030").expect_err("set_host sneaky port");
317+
assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
318+
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
319+
assert_eq!(dst.port(), None, "error doesn't modify dst");
320+
321+
// Check userinfo isn't snuck into `set_host`.
322+
dst.set_host("sean@nope").expect_err("set_host sneaky userinfo");
323+
assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
324+
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
325+
assert_eq!(dst.port(), None, "error doesn't modify dst");
326+
327+
// Allow IPv6 hosts
328+
dst.set_host("[::1]").expect("set_host with IPv6");
329+
assert_eq!(dst.host(), "::1");
330+
assert_eq!(dst.port(), None, "IPv6 didn't affect port");
331+
332+
// However, IPv6 with a port is rejected.
333+
dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port");
334+
assert_eq!(dst.host(), "::1");
335+
assert_eq!(dst.port(), None);
336+
337+
// -----------------
338+
308339
// Also test that an exist port is set correctly.
309340
let mut dst = Destination {
310341
uri: "http://hyper.rs:8080".parse().expect("initial parse 2"),
@@ -335,6 +366,16 @@ mod tests {
335366
assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
336367
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
337368
assert_eq!(dst.port(), Some(8080), "error doesn't modify dst");
369+
370+
// Allow IPv6 hosts
371+
dst.set_host("[::1]").expect("set_host with IPv6");
372+
assert_eq!(dst.host(), "::1");
373+
assert_eq!(dst.port(), Some(8080), "IPv6 didn't affect port");
374+
375+
// However, IPv6 with a port is rejected.
376+
dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port");
377+
assert_eq!(dst.host(), "::1");
378+
assert_eq!(dst.port(), Some(8080));
338379
}
339380

340381
#[test]

src/error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,12 @@ impl From<http::uri::InvalidUri> for Parse {
371371
}
372372
}
373373

374+
impl From<http::uri::InvalidUriBytes> for Parse {
375+
fn from(_: http::uri::InvalidUriBytes) -> Parse {
376+
Parse::Uri
377+
}
378+
}
379+
374380
impl From<http::uri::InvalidUriParts> for Parse {
375381
fn from(_: http::uri::InvalidUriParts) -> Parse {
376382
Parse::Uri

0 commit comments

Comments
 (0)