Remove username and password when parsing proxies (#686)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
use http::uri::Scheme;
|
use http::uri::{Scheme, Authority};
|
||||||
use hyper::client::connect::{Connect, Connected, Destination};
|
use hyper::client::connect::{Connect, Connected, Destination};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
@@ -255,22 +255,13 @@ impl Connector {
|
|||||||
) -> Result<(Conn, Connected), io::Error> {
|
) -> Result<(Conn, Connected), io::Error> {
|
||||||
log::trace!("proxy({:?}) intercepts {:?}", proxy_scheme, dst);
|
log::trace!("proxy({:?}) intercepts {:?}", proxy_scheme, dst);
|
||||||
|
|
||||||
let (puri, _auth) = match proxy_scheme {
|
let (proxy_dst, _auth) = match proxy_scheme {
|
||||||
ProxyScheme::Http { uri, auth, .. } => (uri, auth),
|
ProxyScheme::Http { host, auth } => (into_dst(Scheme::HTTP, host), auth),
|
||||||
|
ProxyScheme::Https { host, auth } => (into_dst(Scheme::HTTPS, host), auth),
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
ProxyScheme::Socks5 { .. } => return this.connect_socks(dst, proxy_scheme),
|
ProxyScheme::Socks5 { .. } => return this.connect_socks(dst, proxy_scheme),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ndst = dst.clone();
|
|
||||||
|
|
||||||
let new_scheme = puri.scheme_part().map(Scheme::as_str).unwrap_or("http");
|
|
||||||
ndst.set_scheme(new_scheme)
|
|
||||||
.expect("proxy target scheme should be valid");
|
|
||||||
|
|
||||||
ndst.set_host(puri.host().expect("proxy target should have host"))
|
|
||||||
.expect("proxy target host should be valid");
|
|
||||||
|
|
||||||
ndst.set_port(puri.port_part().map(|port| port.as_u16()));
|
|
||||||
|
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
let auth = _auth;
|
let auth = _auth;
|
||||||
@@ -285,7 +276,7 @@ impl Connector {
|
|||||||
http.set_nodelay(self.nodelay);
|
http.set_nodelay(self.nodelay);
|
||||||
let tls_connector = tokio_tls::TlsConnector::from(tls.clone());
|
let tls_connector = tokio_tls::TlsConnector::from(tls.clone());
|
||||||
let http = hyper_tls::HttpsConnector::from((http, tls_connector));
|
let http = hyper_tls::HttpsConnector::from((http, tls_connector));
|
||||||
let (conn, connected) = http.connect(ndst).await?;
|
let (conn, connected) = http.connect(proxy_dst).await?;
|
||||||
log::trace!("tunneling HTTPS over proxy");
|
log::trace!("tunneling HTTPS over proxy");
|
||||||
let tunneled = tunnel(conn, host.clone(), port, self.user_agent.clone(), auth).await?;
|
let tunneled = tunnel(conn, host.clone(), port, self.user_agent.clone(), auth).await?;
|
||||||
let tls_connector = tokio_tls::TlsConnector::from(tls.clone());
|
let tls_connector = tokio_tls::TlsConnector::from(tls.clone());
|
||||||
@@ -313,7 +304,7 @@ impl Connector {
|
|||||||
http.set_nodelay(no_delay);
|
http.set_nodelay(no_delay);
|
||||||
let http = hyper_rustls::HttpsConnector::from((http, tls_proxy.clone()));
|
let http = hyper_rustls::HttpsConnector::from((http, tls_proxy.clone()));
|
||||||
let tls = tls.clone();
|
let tls = tls.clone();
|
||||||
let (conn, connected) = http.connect(ndst).await?;
|
let (conn, connected) = http.connect(proxy_dst).await?;
|
||||||
log::trace!("tunneling HTTPS over proxy");
|
log::trace!("tunneling HTTPS over proxy");
|
||||||
let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host)
|
let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host)
|
||||||
.map(|dnsname| dnsname.to_owned())
|
.map(|dnsname| dnsname.to_owned())
|
||||||
@@ -336,10 +327,24 @@ impl Connector {
|
|||||||
Inner::Http(_) => (),
|
Inner::Http(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.connect_with_maybe_proxy(ndst, true).await
|
self.connect_with_maybe_proxy(proxy_dst, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_dst(scheme: Scheme, host: Authority) -> Destination {
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
// TODO: Should the `http` crate get `From<(Scheme, Authority)> for Uri`?
|
||||||
|
http::Uri::builder()
|
||||||
|
.scheme(scheme)
|
||||||
|
.authority(host)
|
||||||
|
.path_and_query(http::uri::PathAndQuery::from_static("/"))
|
||||||
|
.build()
|
||||||
|
.expect("scheme and authority is valid Uri")
|
||||||
|
.try_into()
|
||||||
|
.expect("scheme and authority is valid Destination")
|
||||||
|
}
|
||||||
|
|
||||||
//#[cfg(feature = "trust-dns")]
|
//#[cfg(feature = "trust-dns")]
|
||||||
//fn http_connector() -> crate::Result<HttpConnector> {
|
//fn http_connector() -> crate::Result<HttpConnector> {
|
||||||
// TrustDnsResolver::new()
|
// TrustDnsResolver::new()
|
||||||
|
|||||||
115
src/proxy.rs
115
src/proxy.rs
@@ -60,7 +60,11 @@ pub struct Proxy {
|
|||||||
pub enum ProxyScheme {
|
pub enum ProxyScheme {
|
||||||
Http {
|
Http {
|
||||||
auth: Option<HeaderValue>,
|
auth: Option<HeaderValue>,
|
||||||
uri: hyper::Uri,
|
host: http::uri::Authority,
|
||||||
|
},
|
||||||
|
Https {
|
||||||
|
auth: Option<HeaderValue>,
|
||||||
|
host: http::uri::Authority,
|
||||||
},
|
},
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
Socks5 {
|
Socks5 {
|
||||||
@@ -226,6 +230,7 @@ impl Proxy {
|
|||||||
| Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(),
|
| Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(),
|
||||||
Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme {
|
Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme {
|
||||||
ProxyScheme::Http { auth, .. } => auth,
|
ProxyScheme::Http { auth, .. } => auth,
|
||||||
|
ProxyScheme::Https { auth, .. } => auth,
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
@@ -278,10 +283,18 @@ impl ProxyScheme {
|
|||||||
// To start conservative, keep builders private for now.
|
// To start conservative, keep builders private for now.
|
||||||
|
|
||||||
/// Proxy traffic via the specified URL over HTTP
|
/// Proxy traffic via the specified URL over HTTP
|
||||||
fn http<T: IntoUrl>(url: T) -> crate::Result<Self> {
|
fn http(host: &str) -> crate::Result<Self> {
|
||||||
Ok(ProxyScheme::Http {
|
Ok(ProxyScheme::Http {
|
||||||
auth: None,
|
auth: None,
|
||||||
uri: crate::into_url::expect_uri(&url.into_url()?),
|
host: host.parse().map_err(crate::error::builder)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Proxy traffic via the specified URL over HTTPS
|
||||||
|
fn https(host: &str) -> crate::Result<Self> {
|
||||||
|
Ok(ProxyScheme::Https {
|
||||||
|
auth: None,
|
||||||
|
host: host.parse().map_err(crate::error::builder)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +343,11 @@ impl ProxyScheme {
|
|||||||
ProxyScheme::Http { ref mut auth, .. } => {
|
ProxyScheme::Http { ref mut auth, .. } => {
|
||||||
let header = encode_basic_auth(&username.into(), &password.into());
|
let header = encode_basic_auth(&username.into(), &password.into());
|
||||||
*auth = Some(header);
|
*auth = Some(header);
|
||||||
}
|
},
|
||||||
|
ProxyScheme::Https { ref mut auth, .. } => {
|
||||||
|
let header = encode_basic_auth(&username.into(), &password.into());
|
||||||
|
*auth = Some(header);
|
||||||
|
},
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
ProxyScheme::Socks5 { ref mut auth, .. } => {
|
ProxyScheme::Socks5 { ref mut auth, .. } => {
|
||||||
*auth = Some((username.into(), password.into()));
|
*auth = Some((username.into(), password.into()));
|
||||||
@@ -338,11 +355,32 @@ impl ProxyScheme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn if_no_auth(mut self, update: &Option<HeaderValue>) -> Self {
|
||||||
|
match self {
|
||||||
|
ProxyScheme::Http { ref mut auth, .. } => {
|
||||||
|
if auth.is_none() {
|
||||||
|
*auth = update.clone();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ProxyScheme::Https { ref mut auth, .. } => {
|
||||||
|
if auth.is_none() {
|
||||||
|
*auth = update.clone();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#[cfg(feature = "socks")]
|
||||||
|
ProxyScheme::Socks5 { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a URL into a proxy scheme
|
/// Convert a URL into a proxy scheme
|
||||||
///
|
///
|
||||||
/// Supported schemes: HTTP, HTTPS, (SOCKS5, SOCKS5H if `socks` feature is enabled).
|
/// Supported schemes: HTTP, HTTPS, (SOCKS5, SOCKS5H if `socks` feature is enabled).
|
||||||
// Private for now...
|
// Private for now...
|
||||||
fn parse(url: Url) -> crate::Result<Self> {
|
fn parse(url: Url) -> crate::Result<Self> {
|
||||||
|
use url::Position;
|
||||||
|
|
||||||
// Resolve URL to a host and port
|
// Resolve URL to a host and port
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
let to_addr = || {
|
let to_addr = || {
|
||||||
@@ -357,7 +395,8 @@ impl ProxyScheme {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut scheme = match url.scheme() {
|
let mut scheme = match url.scheme() {
|
||||||
"http" | "https" => Self::http(url.clone())?,
|
"http" => Self::http(&url[Position::BeforeHost..Position::AfterPort])?,
|
||||||
|
"https" => Self::https(&url[Position::BeforeHost..Position::AfterPort])?,
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
"socks5" => Self::socks5(to_addr()?)?,
|
"socks5" => Self::socks5(to_addr()?)?,
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
@@ -375,13 +414,22 @@ impl ProxyScheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn http_uri(&self) -> &http::Uri {
|
fn scheme(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
ProxyScheme::Http { ref uri, .. } => {
|
ProxyScheme::Http { .. } => "http",
|
||||||
uri
|
ProxyScheme::Https { .. } => "https",
|
||||||
},
|
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
_ => panic!("socks not expected"),
|
ProxyScheme::Socks5 => "socks5",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn host(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
ProxyScheme::Http { host, .. } => host.as_str(),
|
||||||
|
ProxyScheme::Https { host, .. } => host.as_str(),
|
||||||
|
#[cfg(feature = "socks")]
|
||||||
|
ProxyScheme::Socks5 => panic!("socks5"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,20 +481,7 @@ impl Custom {
|
|||||||
|
|
||||||
(self.func)(&url)
|
(self.func)(&url)
|
||||||
.and_then(|result| result.ok())
|
.and_then(|result| result.ok())
|
||||||
.map(|scheme| match scheme {
|
.map(|scheme| scheme.if_no_auth(&self.auth))
|
||||||
ProxyScheme::Http { auth, uri } => {
|
|
||||||
if auth.is_some() {
|
|
||||||
ProxyScheme::Http { auth, uri }
|
|
||||||
} else {
|
|
||||||
ProxyScheme::Http {
|
|
||||||
auth: self.auth.clone(),
|
|
||||||
uri,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "socks")]
|
|
||||||
socks => socks,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -659,11 +694,18 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn intercepted_uri(p: &Proxy, s: &str) -> Uri {
|
fn intercepted_uri(p: &Proxy, s: &str) -> Uri {
|
||||||
match p.intercept(&url(s)).unwrap() {
|
let (scheme, host) = match p.intercept(&url(s)).unwrap() {
|
||||||
ProxyScheme::Http { uri, .. } => uri,
|
ProxyScheme::Http { host, .. } => ("http", host),
|
||||||
|
ProxyScheme::Https { host, .. } => ("https", host),
|
||||||
#[cfg(feature = "socks")]
|
#[cfg(feature = "socks")]
|
||||||
_ => panic!("intercepted as socks"),
|
_ => panic!("intercepted as socks"),
|
||||||
}
|
};
|
||||||
|
http::Uri::builder()
|
||||||
|
.scheme(scheme)
|
||||||
|
.authority(host)
|
||||||
|
.path_and_query("/")
|
||||||
|
.build()
|
||||||
|
.expect("intercepted_uri")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -727,6 +769,19 @@ mod tests {
|
|||||||
assert!(p.intercept(&url(other)).is_none());
|
assert!(p.intercept(&url(other)).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proxy_scheme_parse() {
|
||||||
|
let ps = "http://foo:bar@localhost:1239".into_proxy_scheme().unwrap();
|
||||||
|
|
||||||
|
match ps {
|
||||||
|
ProxyScheme::Http { auth, host } => {
|
||||||
|
assert_eq!(auth.unwrap(), encode_basic_auth("foo", "bar"));
|
||||||
|
assert_eq!(host, "localhost:1239");
|
||||||
|
},
|
||||||
|
other => panic!("unexpected: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_sys_proxies_parsing() {
|
fn test_get_sys_proxies_parsing() {
|
||||||
// save system setting first.
|
// save system setting first.
|
||||||
@@ -744,7 +799,9 @@ mod tests {
|
|||||||
env::set_var("http_proxy", "http://127.0.0.1/");
|
env::set_var("http_proxy", "http://127.0.0.1/");
|
||||||
let proxies = get_sys_proxies();
|
let proxies = get_sys_proxies();
|
||||||
|
|
||||||
assert_eq!(proxies["http"].http_uri(), "http://127.0.0.1/");
|
let p = &proxies["http"];
|
||||||
|
assert_eq!(p.scheme(), "http");
|
||||||
|
assert_eq!(p.host(), "127.0.0.1");
|
||||||
|
|
||||||
// reset user setting when guards drop
|
// reset user setting when guards drop
|
||||||
}
|
}
|
||||||
@@ -759,7 +816,7 @@ mod tests {
|
|||||||
env::set_var("HTTP_PROXY", "http://evil/");
|
env::set_var("HTTP_PROXY", "http://evil/");
|
||||||
|
|
||||||
// not in CGI yet
|
// not in CGI yet
|
||||||
assert_eq!(get_sys_proxies()["http"].http_uri(), "http://evil/");
|
assert_eq!(get_sys_proxies()["http"].host(), "evil");
|
||||||
|
|
||||||
// set like we're in CGI
|
// set like we're in CGI
|
||||||
env::set_var("REQUEST_METHOD", "GET");
|
env::set_var("REQUEST_METHOD", "GET");
|
||||||
|
|||||||
Reference in New Issue
Block a user