summaryrefslogtreecommitdiffstats
path: root/vendor/gix-transport/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /vendor/gix-transport/src
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-transport/src')
-rw-r--r--vendor/gix-transport/src/client/async_io/connect.rs2
-rw-r--r--vendor/gix-transport/src/client/async_io/mod.rs2
-rw-r--r--vendor/gix-transport/src/client/blocking_io/file.rs6
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs2
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/mod.rs2
-rw-r--r--vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs103
-rw-r--r--vendor/gix-transport/src/client/blocking_io/ssh/mod.rs23
-rw-r--r--vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs19
-rw-r--r--vendor/gix-transport/src/client/blocking_io/ssh/tests.rs23
-rw-r--r--vendor/gix-transport/src/client/capabilities.rs4
-rw-r--r--vendor/gix-transport/src/client/git/mod.rs15
-rw-r--r--vendor/gix-transport/src/client/non_io_types.rs2
-rw-r--r--vendor/gix-transport/src/lib.rs6
13 files changed, 163 insertions, 46 deletions
diff --git a/vendor/gix-transport/src/client/async_io/connect.rs b/vendor/gix-transport/src/client/async_io/connect.rs
index fe2a5808e..67b20db03 100644
--- a/vendor/gix-transport/src/client/async_io/connect.rs
+++ b/vendor/gix-transport/src/client/async_io/connect.rs
@@ -1,6 +1,6 @@
pub use crate::client::non_io_types::connect::{Error, Options};
-#[cfg(any(feature = "async-std"))]
+#[cfg(feature = "async-std")]
pub(crate) mod function {
use std::convert::TryInto;
diff --git a/vendor/gix-transport/src/client/async_io/mod.rs b/vendor/gix-transport/src/client/async_io/mod.rs
index 6cb1a500e..1ea85cdcf 100644
--- a/vendor/gix-transport/src/client/async_io/mod.rs
+++ b/vendor/gix-transport/src/client/async_io/mod.rs
@@ -9,5 +9,5 @@ pub use traits::{SetServiceResponse, Transport, TransportV2Ext};
///
pub mod connect;
-#[cfg(any(feature = "async-std"))]
+#[cfg(feature = "async-std")]
pub use connect::function::connect;
diff --git a/vendor/gix-transport/src/client/blocking_io/file.rs b/vendor/gix-transport/src/client/blocking_io/file.rs
index 599f56c23..613fd2357 100644
--- a/vendor/gix-transport/src/client/blocking_io/file.rs
+++ b/vendor/gix-transport/src/client/blocking_io/file.rs
@@ -211,6 +211,11 @@ impl client::Transport for SpawnProcessOnDemand {
};
cmd.stdin = Stdio::piped();
cmd.stdout = Stdio::piped();
+ if self.path.first() == Some(&b'-') {
+ return Err(client::Error::AmbiguousPath {
+ path: self.path.clone(),
+ });
+ }
let repo_path = if self.ssh_cmd.is_some() {
cmd.args.push(service.as_str().into());
gix_quote::single(self.path.as_ref()).to_os_str_lossy().into_owned()
@@ -225,6 +230,7 @@ impl client::Transport for SpawnProcessOnDemand {
}
cmd.envs(std::mem::take(&mut self.envs));
+ gix_features::trace::debug!(command = ?cmd, "gix_transport::SpawnProcessOnDemand");
let mut child = cmd.spawn().map_err(|err| client::Error::InvokeProgram {
source: err,
command: cmd_name.into_owned(),
diff --git a/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs b/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs
index 3b435423d..e6b0bee03 100644
--- a/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs
+++ b/vendor/gix-transport/src/client/blocking_io/http/curl/remote.rs
@@ -269,7 +269,7 @@ pub fn new() -> (
handler.send_data = Some(send);
let (send, receive_headers) = pipe::unidirectional(1);
handler.send_header = Some(send);
- let (send_body, receive_body) = pipe::unidirectional(None);
+ let (send_body, receive_body) = pipe::unidirectional(0);
(receive_data, receive_headers, send_body, receive_body)
};
diff --git a/vendor/gix-transport/src/client/blocking_io/http/mod.rs b/vendor/gix-transport/src/client/blocking_io/http/mod.rs
index 64cc892d8..fb62b8f9a 100644
--- a/vendor/gix-transport/src/client/blocking_io/http/mod.rs
+++ b/vendor/gix-transport/src/client/blocking_io/http/mod.rs
@@ -517,5 +517,5 @@ pub fn connect(url: gix_url::Url, desired_version: Protocol) -> Transport<Impl>
}
///
-#[cfg(feature = "http-client-curl")]
+#[cfg(any(feature = "http-client-curl", feature = "http-client-reqwest"))]
pub mod redirect;
diff --git a/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs b/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs
index 724528ab9..7f8e82846 100644
--- a/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs
+++ b/vendor/gix-transport/src/client/blocking_io/http/reqwest/remote.rs
@@ -3,14 +3,12 @@ use std::{
convert::TryFrom,
io::{Read, Write},
str::FromStr,
+ sync::{atomic, Arc},
};
use gix_features::io::pipe;
-use crate::client::{
- http,
- http::{reqwest::Remote, traits::PostBodyDataKind},
-};
+use crate::client::http::{self, options::FollowRedirects, redirect, reqwest::Remote, traits::PostBodyDataKind};
/// The error returned by the 'remote' helper, a purely internal construct to perform http requests.
#[derive(Debug, thiserror::Error)]
@@ -22,6 +20,8 @@ pub enum Error {
ReadPostBody(#[from] std::io::Error),
#[error("Request configuration failed")]
ConfigureRequest(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
+ #[error(transparent)]
+ Redirect(#[from] redirect::Error),
}
impl crate::IsSpuriousError for Error {
@@ -40,23 +40,57 @@ impl Default for Remote {
let (req_send, req_recv) = std::sync::mpsc::sync_channel(0);
let (res_send, res_recv) = std::sync::mpsc::sync_channel(0);
let handle = std::thread::spawn(move || -> Result<(), Error> {
+ let mut follow = None;
+ let mut redirected_base_url = None::<String>;
+ let allow_redirects = Arc::new(atomic::AtomicBool::new(false));
+
// We may error while configuring, which is expected as part of the internal protocol. The error will be
// received and the sender of the request might restart us.
let client = reqwest::blocking::ClientBuilder::new()
.connect_timeout(std::time::Duration::from_secs(20))
.http1_title_case_headers()
+ .redirect(reqwest::redirect::Policy::custom({
+ let allow_redirects = allow_redirects.clone();
+ move |attempt| {
+ if allow_redirects.load(atomic::Ordering::Relaxed) {
+ let curr_url = attempt.url();
+ let prev_urls = attempt.previous();
+
+ match prev_urls.first() {
+ Some(prev_url) if prev_url.host_str() != curr_url.host_str() => {
+ // git does not want to be redirected to a different host.
+ attempt.stop()
+ }
+ _ => {
+ // emulate default git behaviour which relies on curl default behaviour apparently.
+ const CURL_DEFAULT_REDIRS: usize = 50;
+ if prev_urls.len() >= CURL_DEFAULT_REDIRS {
+ attempt.error("too many redirects")
+ } else {
+ attempt.follow()
+ }
+ }
+ }
+ } else {
+ attempt.stop()
+ }
+ }
+ }))
.build()?;
+
for Request {
url,
+ base_url,
headers,
upload_body_kind,
config,
} in req_recv
{
+ let effective_url = redirect::swap_tails(redirected_base_url.as_deref(), &base_url, url.clone());
let mut req_builder = if upload_body_kind.is_some() {
- client.post(url)
+ client.post(&effective_url)
} else {
- client.get(url)
+ client.get(&effective_url)
}
.headers(headers);
let (post_body_tx, mut post_body_rx) = pipe::unidirectional(0);
@@ -91,7 +125,21 @@ impl Default for Remote {
}
}
}
- let mut res = match client.execute(req).and_then(|res| res.error_for_status()) {
+
+ let follow = follow.get_or_insert(config.follow_redirects);
+ allow_redirects.store(
+ matches!(follow, FollowRedirects::Initial | FollowRedirects::All),
+ atomic::Ordering::Relaxed,
+ );
+
+ if *follow == FollowRedirects::Initial {
+ *follow = FollowRedirects::None;
+ }
+
+ let mut res = match client
+ .execute(req)
+ .and_then(reqwest::blocking::Response::error_for_status)
+ {
Ok(res) => res,
Err(err) => {
let (kind, err) = match err.status() {
@@ -113,6 +161,11 @@ impl Default for Remote {
}
};
+ let actual_url = res.url().as_str();
+ if actual_url != effective_url.as_str() {
+ redirected_base_url = redirect::base_url(actual_url, &base_url, url)?.into();
+ }
+
let send_headers = {
let headers = res.headers();
move || -> std::io::Result<()> {
@@ -152,10 +205,24 @@ impl Default for Remote {
/// utilities
impl Remote {
+ fn restore_thread_after_failure(&mut self) -> http::Error {
+ let err_that_brought_thread_down = self
+ .handle
+ .take()
+ .expect("thread handle present")
+ .join()
+ .expect("handler thread should never panic")
+ .expect_err("something should have gone wrong with curl (we join on error only)");
+ *self = Remote::default();
+ http::Error::InitHttpClient {
+ source: Box::new(err_that_brought_thread_down),
+ }
+ }
+
fn make_request(
&mut self,
url: &str,
- _base_url: &str,
+ base_url: &str,
headers: impl IntoIterator<Item = impl AsRef<str>>,
upload_body_kind: Option<PostBodyDataKind>,
) -> Result<http::PostResponse<pipe::Reader, pipe::Reader, pipe::Writer>, http::Error> {
@@ -176,14 +243,19 @@ impl Remote {
None => continue,
};
}
- self.request
+ if self
+ .request
.send(Request {
url: url.to_owned(),
+ base_url: base_url.to_owned(),
headers: header_map,
upload_body_kind,
config: self.config.clone(),
})
- .expect("the remote cannot be down at this point");
+ .is_err()
+ {
+ return Err(self.restore_thread_after_failure());
+ }
let Response {
headers,
@@ -192,15 +264,7 @@ impl Remote {
} = match self.response.recv() {
Ok(res) => res,
Err(_) => {
- let err = self
- .handle
- .take()
- .expect("always present")
- .join()
- .expect("no panic")
- .expect_err("no receiver means thread is down with init error");
- *self = Self::default();
- return Err(http::Error::InitHttpClient { source: Box::new(err) });
+ return Err(self.restore_thread_after_failure());
}
};
@@ -246,6 +310,7 @@ impl http::Http for Remote {
pub(crate) struct Request {
pub url: String,
+ pub base_url: String,
pub headers: reqwest::header::HeaderMap,
pub upload_body_kind: Option<PostBodyDataKind>,
pub config: http::Options,
diff --git a/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs b/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs
index 7c042dc28..642aab9fd 100644
--- a/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs
+++ b/vendor/gix-transport/src/client/blocking_io/ssh/mod.rs
@@ -8,6 +8,8 @@ use crate::{client::blocking_io, Protocol};
pub enum Error {
#[error("The scheme in \"{}\" is not usable for an ssh connection", .0.to_bstring())]
UnsupportedScheme(gix_url::Url),
+ #[error("Host name '{host}' could be mistaken for a command-line argument")]
+ AmbiguousHostName { host: String },
}
impl crate::IsSpuriousError for Error {}
@@ -37,12 +39,17 @@ pub mod invocation {
/// The error returned when producing ssh invocation arguments based on a selected invocation kind.
#[derive(Debug, thiserror::Error)]
- #[error("The 'Simple' ssh variant doesn't support {function}")]
- pub struct Error {
- /// The simple command that should have been invoked.
- pub command: OsString,
- /// The function that was unsupported
- pub function: &'static str,
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Host name '{host}' could be mistaken for a command-line argument")]
+ AmbiguousHostName { host: String },
+ #[error("The 'Simple' ssh variant doesn't support {function}")]
+ Unsupported {
+ /// The simple command that should have been invoked.
+ command: OsString,
+ /// The function that was unsupported
+ function: &'static str,
+ },
}
}
@@ -105,7 +112,9 @@ pub fn connect(
.stdin(Stdio::null())
.with_shell()
.arg("-G")
- .arg(url.host().expect("always set for ssh urls")),
+ .arg(url.host_argument_safe().ok_or_else(|| Error::AmbiguousHostName {
+ host: url.host().expect("set in ssh urls").into(),
+ })?),
)
.status()
.ok()
diff --git a/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs b/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs
index 5e9d14a82..70905829f 100644
--- a/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs
+++ b/vendor/gix-transport/src/client/blocking_io/ssh/program_kind.rs
@@ -31,7 +31,6 @@ impl ProgramKind {
if disallow_shell {
prepare.use_shell = false;
}
- let host = url.host().expect("present in ssh urls");
match self {
ProgramKind::Ssh => {
if desired_version != Protocol::V1 {
@@ -54,7 +53,7 @@ impl ProgramKind {
}
ProgramKind::Simple => {
if url.port.is_some() {
- return Err(ssh::invocation::Error {
+ return Err(ssh::invocation::Error::Unsupported {
command: ssh_cmd.into(),
function: "setting the port",
});
@@ -62,8 +61,18 @@ impl ProgramKind {
}
};
let host_as_ssh_arg = match url.user() {
- Some(user) => format!("{user}@{host}"),
- None => host.into(),
+ Some(user) => {
+ let host = url.host().expect("present in ssh urls");
+ format!("{user}@{host}")
+ }
+ None => {
+ let host = url
+ .host_argument_safe()
+ .ok_or_else(|| ssh::invocation::Error::AmbiguousHostName {
+ host: url.host().expect("ssh host always set").into(),
+ })?;
+ host.into()
+ }
};
// Try to force ssh to yield english messages (for parsing later)
@@ -107,7 +116,7 @@ impl ProgramKind {
impl<'a> From<&'a OsStr> for ProgramKind {
fn from(v: &'a OsStr) -> Self {
let p = std::path::Path::new(v);
- match p.file_stem().and_then(|s| s.to_str()) {
+ match p.file_stem().and_then(OsStr::to_str) {
None => ProgramKind::Simple,
Some(stem) => {
if stem.eq_ignore_ascii_case("ssh") {
diff --git a/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs b/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs
index f0820d14e..4e4da7807 100644
--- a/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs
+++ b/vendor/gix-transport/src/client/blocking_io/ssh/tests.rs
@@ -144,13 +144,28 @@ mod program_kind {
assert!(call_args(kind, "ssh://user@host:43/p", Protocol::V2).ends_with("-P 43 user@host"));
}
}
+ #[test]
+ fn ambiguous_host_is_allowed_with_user() {
+ assert_eq!(
+ call_args(ProgramKind::Ssh, "ssh://user@-arg/p", Protocol::V2),
+ joined(&["ssh", "-o", "SendEnv=GIT_PROTOCOL", "user@-arg"])
+ );
+ }
+
+ #[test]
+ fn ambiguous_host_is_disallowed() {
+ assert!(matches!(
+ try_call(ProgramKind::Ssh, "ssh://-arg/p", Protocol::V2),
+ Err(ssh::invocation::Error::AmbiguousHostName { host }) if host == "-arg"
+ ));
+ }
#[test]
fn simple_cannot_handle_any_arguments() {
- match try_call(ProgramKind::Simple, "ssh://user@host:42/p", Protocol::V2) {
- Err(ssh::invocation::Error { .. }) => {}
- _ => panic!("BUG: unexpected outcome"),
- }
+ assert!(matches!(
+ try_call(ProgramKind::Simple, "ssh://user@host:42/p", Protocol::V2),
+ Err(ssh::invocation::Error::Unsupported { .. })
+ ));
assert_eq!(
call_args(ProgramKind::Simple, "ssh://user@host/p", Protocol::V2),
joined(&["simple", "user@host"]),
diff --git a/vendor/gix-transport/src/client/capabilities.rs b/vendor/gix-transport/src/client/capabilities.rs
index 29b5504ba..21513ace6 100644
--- a/vendor/gix-transport/src/client/capabilities.rs
+++ b/vendor/gix-transport/src/client/capabilities.rs
@@ -65,11 +65,11 @@ impl<'a> Capability<'a> {
/// Note that the caller must know whether a single or multiple values are expected, in which
/// case [`values()`][Capability::values()] should be called.
pub fn value(&self) -> Option<&'a BStr> {
- self.0.splitn(2, |b| *b == b'=').nth(1).map(|s| s.as_bstr())
+ self.0.splitn(2, |b| *b == b'=').nth(1).map(ByteSlice::as_bstr)
}
/// Returns the values of a capability if its [`value()`][Capability::value()] is space separated.
pub fn values(&self) -> Option<impl Iterator<Item = &'a BStr>> {
- self.value().map(|v| v.split(|b| *b == b' ').map(|s| s.as_bstr()))
+ self.value().map(|v| v.split(|b| *b == b' ').map(ByteSlice::as_bstr))
}
/// Returns true if its space-separated [`value()`][Capability::value()] contains the given `want`ed capability.
pub fn supports(&self, want: impl Into<&'a BStr>) -> Option<bool> {
diff --git a/vendor/gix-transport/src/client/git/mod.rs b/vendor/gix-transport/src/client/git/mod.rs
index 2b950b44a..d27f468ff 100644
--- a/vendor/gix-transport/src/client/git/mod.rs
+++ b/vendor/gix-transport/src/client/git/mod.rs
@@ -165,6 +165,21 @@ mod message {
"git-upload-pack hello\\world\0host=host:404\0"
)
}
+
+ #[test]
+ fn with_strange_host_and_port() {
+ assert_eq!(
+ git::message::connect(
+ Service::UploadPack,
+ Protocol::V1,
+ b"--upload-pack=attack",
+ Some(&("--proxy=other-attack".into(), Some(404))),
+ &[]
+ ),
+ "git-upload-pack --upload-pack=attack\0host=--proxy=other-attack:404\0",
+ "we explicitly allow possible `-arg` arguments to be passed to the git daemon - the remote must protect against exploitation, we don't want to prevent legitimate cases"
+ )
+ }
}
}
diff --git a/vendor/gix-transport/src/client/non_io_types.rs b/vendor/gix-transport/src/client/non_io_types.rs
index 807b22a8f..a1dbb247c 100644
--- a/vendor/gix-transport/src/client/non_io_types.rs
+++ b/vendor/gix-transport/src/client/non_io_types.rs
@@ -138,6 +138,8 @@ mod error {
Http(#[from] HttpError),
#[error(transparent)]
SshInvocation(SshInvocationError),
+ #[error("The repository path '{path}' could be mistaken for a command-line argument")]
+ AmbiguousPath { path: BString },
}
impl crate::IsSpuriousError for Error {
diff --git a/vendor/gix-transport/src/lib.rs b/vendor/gix-transport/src/lib.rs
index a098e635a..4ec2ea615 100644
--- a/vendor/gix-transport/src/lib.rs
+++ b/vendor/gix-transport/src/lib.rs
@@ -21,7 +21,6 @@ pub use gix_packetline as packetline;
/// The version of the way client and server communicate.
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[allow(missing_docs)]
pub enum Protocol {
/// Version 0 is like V1, but doesn't show capabilities at all, at least when hosted without `git-daemon`.
V0 = 0,
@@ -86,10 +85,7 @@ pub use traits::IsSpuriousError;
pub mod client;
#[doc(inline)]
-#[cfg(any(
- feature = "blocking-client",
- all(feature = "async-client", any(feature = "async-std"))
-))]
+#[cfg(any(feature = "blocking-client", all(feature = "async-client", feature = "async-std")))]
pub use client::connect;
#[cfg(all(feature = "async-client", feature = "blocking-client"))]