From d5164c67bc82b17912db03d94127048996f03f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 17 Sep 2024 17:18:34 +0400 Subject: [PATCH 01/21] =?UTF-8?q?=F0=9F=9A=9A=20zb:=20move=20address=20to?= =?UTF-8?q?=20legacy=5Faddress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following commits introduce a different implementation. --- zbus/src/blocking/connection/builder.rs | 4 ++-- zbus/src/connection/builder.rs | 8 +++---- zbus/src/connection/mod.rs | 2 +- zbus/src/{address => legacy_address}/mod.rs | 24 +++++++++++-------- .../transport/autolaunch.rs | 0 .../transport/launchd.rs | 0 .../transport/mod.rs | 0 .../transport/tcp.rs | 0 .../transport/unix.rs | 0 .../transport/vsock.rs | 0 zbus/src/lib.rs | 4 ++-- 11 files changed, 23 insertions(+), 19 deletions(-) rename zbus/src/{address => legacy_address}/mod.rs (96%) rename zbus/src/{address => legacy_address}/transport/autolaunch.rs (100%) rename zbus/src/{address => legacy_address}/transport/launchd.rs (100%) rename zbus/src/{address => legacy_address}/transport/mod.rs (100%) rename zbus/src/{address => legacy_address}/transport/tcp.rs (100%) rename zbus/src/{address => legacy_address}/transport/unix.rs (100%) rename zbus/src/{address => legacy_address}/transport/vsock.rs (100%) diff --git a/zbus/src/blocking/connection/builder.rs b/zbus/src/blocking/connection/builder.rs index 17beb196b..d0e5f9c33 100644 --- a/zbus/src/blocking/connection/builder.rs +++ b/zbus/src/blocking/connection/builder.rs @@ -15,8 +15,8 @@ use zvariant::{ObjectPath, Str}; #[cfg(feature = "p2p")] use crate::Guid; use crate::{ - address::Address, blocking::Connection, connection::socket::BoxedSplit, names::WellKnownName, - object_server::Interface, utils::block_on, AuthMechanism, Error, Result, + blocking::Connection, connection::socket::BoxedSplit, legacy_address::Address, + names::WellKnownName, object_server::Interface, utils::block_on, AuthMechanism, Error, Result, }; /// A builder for [`zbus::blocking::Connection`]. diff --git a/zbus/src/connection/builder.rs b/zbus/src/connection/builder.rs index fcf56257b..5b8fff940 100644 --- a/zbus/src/connection/builder.rs +++ b/zbus/src/connection/builder.rs @@ -24,7 +24,7 @@ use vsock::VsockStream; use zvariant::{ObjectPath, Str}; use crate::{ - address::{self, Address}, + legacy_address::{self, Address}, names::{InterfaceName, WellKnownName}, object_server::{ArcInterface, Interface}, Connection, Error, Executor, Guid, OwnedGuid, Result, @@ -536,13 +536,13 @@ impl<'a> Builder<'a> { guid = address.guid().map(|g| g.to_owned().into()); match address.connect().await? { #[cfg(any(unix, not(feature = "tokio")))] - address::transport::Stream::Unix(stream) => stream.into(), - address::transport::Stream::Tcp(stream) => stream.into(), + legacy_address::transport::Stream::Unix(stream) => stream.into(), + legacy_address::transport::Stream::Tcp(stream) => stream.into(), #[cfg(any( all(feature = "vsock", not(feature = "tokio")), feature = "tokio-vsock" ))] - address::transport::Stream::Vsock(stream) => stream.into(), + legacy_address::transport::Stream::Vsock(stream) => stream.into(), } } Target::Socket(stream) => stream, diff --git a/zbus/src/connection/mod.rs b/zbus/src/connection/mod.rs index b1e45ca58..e48083142 100644 --- a/zbus/src/connection/mod.rs +++ b/zbus/src/connection/mod.rs @@ -1386,7 +1386,7 @@ mod tests { #[cfg(target_os = "macos")] #[test] fn connect_launchd_session_bus() { - use crate::address::{transport::Launchd, Address, Transport}; + use crate::legacy_address::{transport::Launchd, Address, Transport}; crate::block_on(async { let addr = Address::from(Transport::Launchd(Launchd::new( "DBUS_LAUNCHD_SESSION_BUS_SOCKET", diff --git a/zbus/src/address/mod.rs b/zbus/src/legacy_address/mod.rs similarity index 96% rename from zbus/src/address/mod.rs rename to zbus/src/legacy_address/mod.rs index 40707f48e..e0627e820 100644 --- a/zbus/src/address/mod.rs +++ b/zbus/src/legacy_address/mod.rs @@ -181,11 +181,11 @@ mod tests { Address, }; #[cfg(target_os = "macos")] - use crate::address::transport::Launchd; + use crate::legacy_address::transport::Launchd; #[cfg(windows)] - use crate::address::transport::{Autolaunch, AutolaunchScope}; + use crate::legacy_address::transport::{Autolaunch, AutolaunchScope}; use crate::{ - address::transport::{Unix, UnixSocket}, + legacy_address::transport::{Unix, UnixSocket}, Error, }; use std::str::FromStr; @@ -308,9 +308,11 @@ mod tests { #[cfg(all(feature = "vsock", not(feature = "tokio")))] assert_eq!( Address::from_str(&format!("vsock:cid=98,port=2934,guid={guid}")).unwrap(), - Address::from(Transport::Vsock(super::transport::Vsock::new(98, 2934))) - .set_guid(guid) - .unwrap(), + Address::from(Transport::Vsock(legacy_address::transport::Vsock::new( + 98, 2934 + ))) + .set_guid(guid) + .unwrap(), ); assert_eq!( Address::from_str("unix:dir=/some/dir").unwrap(), @@ -403,10 +405,12 @@ mod tests { { let guid = crate::Guid::generate(); assert_eq!( - Address::from(Transport::Vsock(super::transport::Vsock::new(98, 2934))) - .set_guid(guid.clone()) - .unwrap() - .to_string(), + Address::from(Transport::Vsock(legacy_address::transport::Vsock::new( + 98, 2934 + ))) + .set_guid(guid.clone()) + .unwrap() + .to_string(), format!("vsock:cid=98,port=2934,guid={guid}"), ); } diff --git a/zbus/src/address/transport/autolaunch.rs b/zbus/src/legacy_address/transport/autolaunch.rs similarity index 100% rename from zbus/src/address/transport/autolaunch.rs rename to zbus/src/legacy_address/transport/autolaunch.rs diff --git a/zbus/src/address/transport/launchd.rs b/zbus/src/legacy_address/transport/launchd.rs similarity index 100% rename from zbus/src/address/transport/launchd.rs rename to zbus/src/legacy_address/transport/launchd.rs diff --git a/zbus/src/address/transport/mod.rs b/zbus/src/legacy_address/transport/mod.rs similarity index 100% rename from zbus/src/address/transport/mod.rs rename to zbus/src/legacy_address/transport/mod.rs diff --git a/zbus/src/address/transport/tcp.rs b/zbus/src/legacy_address/transport/tcp.rs similarity index 100% rename from zbus/src/address/transport/tcp.rs rename to zbus/src/legacy_address/transport/tcp.rs diff --git a/zbus/src/address/transport/unix.rs b/zbus/src/legacy_address/transport/unix.rs similarity index 100% rename from zbus/src/address/transport/unix.rs rename to zbus/src/legacy_address/transport/unix.rs diff --git a/zbus/src/address/transport/vsock.rs b/zbus/src/legacy_address/transport/vsock.rs similarity index 100% rename from zbus/src/address/transport/vsock.rs rename to zbus/src/legacy_address/transport/vsock.rs diff --git a/zbus/src/lib.rs b/zbus/src/lib.rs index b4abf950f..310a8c5ee 100644 --- a/zbus/src/lib.rs +++ b/zbus/src/lib.rs @@ -46,8 +46,8 @@ pub use dbus_error::*; mod error; pub use error::*; -pub mod address; -pub use address::Address; +pub mod legacy_address; +pub use legacy_address::Address; mod guid; pub use guid::*; From b5293c71bc48e6b4272b61e7ca87f95b51331f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 13 Sep 2024 15:11:48 +0400 Subject: [PATCH 02/21] =?UTF-8?q?=E2=9C=A8=20zb:=20add=20new=20address=20m?= =?UTF-8?q?odule=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on https://github.com/elmarco/dbus-addr --- zbus/src/address/address_list.rs | 81 ++++++ zbus/src/address/mod.rs | 349 +++++++++++++++++++++++ zbus/src/address/percent.rs | 209 ++++++++++++++ zbus/src/address/tests.rs | 181 ++++++++++++ zbus/src/address/transport/autolaunch.rs | 77 +++++ zbus/src/address/transport/launchd.rs | 46 +++ zbus/src/address/transport/mod.rs | 100 +++++++ zbus/src/address/transport/nonce_tcp.rs | 100 +++++++ zbus/src/address/transport/systemd.rs | 28 ++ zbus/src/address/transport/tcp.rs | 115 ++++++++ zbus/src/address/transport/unix.rs | 118 ++++++++ zbus/src/address/transport/unixexec.rs | 86 ++++++ zbus/src/address/transport/vsock.rs | 67 +++++ zbus/src/lib.rs | 3 + 14 files changed, 1560 insertions(+) create mode 100644 zbus/src/address/address_list.rs create mode 100644 zbus/src/address/mod.rs create mode 100644 zbus/src/address/percent.rs create mode 100644 zbus/src/address/tests.rs create mode 100644 zbus/src/address/transport/autolaunch.rs create mode 100644 zbus/src/address/transport/launchd.rs create mode 100644 zbus/src/address/transport/mod.rs create mode 100644 zbus/src/address/transport/nonce_tcp.rs create mode 100644 zbus/src/address/transport/systemd.rs create mode 100644 zbus/src/address/transport/tcp.rs create mode 100644 zbus/src/address/transport/unix.rs create mode 100644 zbus/src/address/transport/unixexec.rs create mode 100644 zbus/src/address/transport/vsock.rs diff --git a/zbus/src/address/address_list.rs b/zbus/src/address/address_list.rs new file mode 100644 index 000000000..a246ae5ca --- /dev/null +++ b/zbus/src/address/address_list.rs @@ -0,0 +1,81 @@ +use std::{borrow::Cow, fmt}; + +use super::{DBusAddr, Error, Result, ToDBusAddrs}; + +/// A bus address list. +/// +/// D-Bus addresses are `;`-separated. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct DBusAddrList<'a> { + addr: Cow<'a, str>, +} + +impl<'a> ToDBusAddrs<'a> for DBusAddrList<'a> { + type Iter = DBusAddrListIter<'a>; + + /// Get an iterator over the D-Bus addresses. + fn to_dbus_addrs(&'a self) -> Self::Iter { + DBusAddrListIter::new(self) + } +} + +impl<'a> Iterator for DBusAddrListIter<'a> { + type Item = Result>; + + fn next(&mut self) -> Option { + if self.next_index >= self.data.len() { + return None; + } + + let mut addr = &self.data[self.next_index..]; + if let Some(end) = addr.find(';') { + addr = &addr[..end]; + self.next_index += end + 1; + } else { + self.next_index = self.data.len(); + } + + Some(DBusAddr::try_from(addr)) + } +} + +/// An iterator of D-Bus addresses. +pub struct DBusAddrListIter<'a> { + data: &'a str, + next_index: usize, +} + +impl<'a> DBusAddrListIter<'a> { + fn new(list: &'a DBusAddrList<'_>) -> Self { + Self { + data: list.addr.as_ref(), + next_index: 0, + } + } +} + +impl<'a> TryFrom for DBusAddrList<'a> { + type Error = Error; + + fn try_from(value: String) -> Result { + Ok(Self { + addr: Cow::Owned(value), + }) + } +} + +impl<'a> TryFrom<&'a str> for DBusAddrList<'a> { + type Error = Error; + + fn try_from(value: &'a str) -> Result { + Ok(Self { + addr: Cow::Borrowed(value), + }) + } +} + +impl fmt::Display for DBusAddrList<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.addr) + } +} diff --git a/zbus/src/address/mod.rs b/zbus/src/address/mod.rs new file mode 100644 index 000000000..528dd223f --- /dev/null +++ b/zbus/src/address/mod.rs @@ -0,0 +1,349 @@ +//! D-Bus address handling. +//! +//! Server addresses consist of a transport name followed by a colon, and then an optional, +//! comma-separated list of keys and values in the form key=value. +//! +//! # Miscellaneous and caveats on D-Bus addresses +//! +//! * Assumes values are UTF-8 encoded. +//! +//! * Duplicated keys are accepted, last pair wins. +//! +//! * Assumes that empty `key=val` is accepted, so `transport:,,guid=...` is valid. +//! +//! * Allows key only, so `transport:foo,bar` is ok. +//! +//! * Accept unknown keys and transports. +//! +//! See also: +//! +//! * [Server addresses] in the D-Bus specification. +//! +//! [Server addresses]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses + +use std::{borrow::Cow, env, fmt}; + +#[cfg(all(unix, not(target_os = "macos")))] +use nix::unistd::Uid; + +pub mod transport; + +mod address_list; +pub use address_list::{DBusAddrList, DBusAddrListIter}; + +mod percent; +pub use percent::*; + +#[cfg(test)] +mod tests; + +/// Error returned when an address is invalid. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Error { + UnknownTransport, + MissingTransport, + Encoding(String), + DuplicateKey(String), + MissingKey(String), + MissingValue(String), + InvalidValue(String), + UnknownTcpFamily(String), + Other(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::UnknownTransport => write!(f, "Unsupported transport in address"), + Error::MissingTransport => write!(f, "Missing transport in address"), + Error::Encoding(e) => write!(f, "Encoding error: {e}"), + Error::DuplicateKey(e) => write!(f, "Duplicate key: `{e}`"), + Error::MissingKey(e) => write!(f, "Missing key: `{e}`"), + Error::MissingValue(e) => write!(f, "Missing value for key: `{e}`"), + Error::InvalidValue(e) => write!(f, "Invalid value for key: `{e}`"), + Error::UnknownTcpFamily(e) => write!(f, "Unknown TCP address family: `{e}`"), + Error::Other(e) => write!(f, "Other error: {e}"), + } + } +} + +impl std::error::Error for Error {} + +pub type Result = std::result::Result; + +/// Get the address for session socket respecting the DBUS_SESSION_BUS_ADDRESS environment +/// variable. If we don't recognize the value (or it's not set) we fall back to +/// $XDG_RUNTIME_DIR/bus +pub fn session() -> Result> { + match env::var("DBUS_SESSION_BUS_ADDRESS") { + Ok(val) => DBusAddrList::try_from(val), + _ => { + #[cfg(windows)] + { + DBusAddrList::try_from("autolaunch:scope=*user;autolaunch:") + } + + #[cfg(all(unix, not(target_os = "macos")))] + { + let runtime_dir = env::var("XDG_RUNTIME_DIR") + .unwrap_or_else(|_| format!("/run/user/{}", Uid::effective())); + let path = format!("unix:path={runtime_dir}/bus"); + + DBusAddrList::try_from(path) + } + + #[cfg(target_os = "macos")] + { + DBusAddrList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET") + } + } + } +} + +/// Get the address for system bus respecting the DBUS_SYSTEM_BUS_ADDRESS environment +/// variable. If we don't recognize the value (or it's not set) we fall back to +/// /var/run/dbus/system_bus_socket +pub fn system() -> Result> { + match env::var("DBUS_SYSTEM_BUS_ADDRESS") { + Ok(val) => DBusAddrList::try_from(val), + _ => { + #[cfg(all(unix, not(target_os = "macos")))] + return DBusAddrList::try_from("unix:path=/var/run/dbus/system_bus_socket"); + + #[cfg(windows)] + return DBusAddrList::try_from("autolaunch:"); + + #[cfg(target_os = "macos")] + return DBusAddrList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET"); + } + } +} + +/// A bus address. +/// +/// Example: +/// ``` +/// use zbus::DBusAddr; +/// +/// let _: DBusAddr = "unix:path=/tmp/dbus.sock".try_into().unwrap(); +/// ``` +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct DBusAddr<'a> { + pub(super) addr: Cow<'a, str>, +} + +impl<'a> DBusAddr<'a> { + /// The connection GUID if any. + pub fn guid(&self) -> Option> { + self.get_string("guid").and_then(|res| res.ok()) + } + + /// Transport connection details + pub fn transport(&self) -> Result> { + self.try_into() + } + + pub(super) fn key_val_iter(&'a self) -> KeyValIter<'a> { + let mut split = self.addr.splitn(2, ':'); + // skip transport:.. + split.next(); + let kv = split.next().unwrap_or(""); + KeyValIter::new(kv) + } + + fn new>>(addr: A) -> Result { + let addr = addr.into(); + let addr = Self { addr }; + + addr.validate()?; + + Ok(addr) + } + + fn validate(&self) -> Result<()> { + self.transport()?; + for (k, v) in self.key_val_iter() { + let v = match v { + Some(v) => decode_percents(v)?, + _ => Cow::from(b"" as &[_]), + }; + if k == "guid" { + validate_guid(v.as_ref())?; + } + } + + Ok(()) + } + + // the last key=val wins + fn get_string(&'a self, key: &str) -> Option>> { + let mut val = None; + for (k, v) in self.key_val_iter() { + if key == k { + val = v; + } + } + val.map(decode_percents_str) + } +} + +fn validate_guid(value: &[u8]) -> Result<()> { + if value.len() != 32 || value.iter().any(|&c| !c.is_ascii_hexdigit()) { + return Err(Error::InvalidValue("guid".into())); + } + + Ok(()) +} + +impl DBusAddr<'_> { + pub fn to_owned(&self) -> DBusAddr<'static> { + let addr = self.addr.to_string(); + DBusAddr { addr: addr.into() } + } +} + +impl<'a> TryFrom for DBusAddr<'a> { + type Error = Error; + + fn try_from(addr: String) -> Result { + Self::new(addr) + } +} + +impl<'a> TryFrom<&'a str> for DBusAddr<'a> { + type Error = Error; + + fn try_from(addr: &'a str) -> Result { + Self::new(addr) + } +} + +impl fmt::Display for DBusAddr<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let kv = KeyValFmt::new().add("guid", self.guid()); + let t = self.transport().map_err(|_| fmt::Error)?; + let kv = t.key_val_fmt_add(kv); + write!(f, "{t}:{kv}")?; + Ok(()) + } +} + +pub(super) struct KeyValIter<'a> { + data: &'a str, + next_index: usize, +} + +impl<'a> KeyValIter<'a> { + fn new(data: &'a str) -> Self { + KeyValIter { + data, + next_index: 0, + } + } +} + +impl<'a> Iterator for KeyValIter<'a> { + type Item = (&'a str, Option<&'a str>); + + fn next(&mut self) -> Option { + if self.next_index >= self.data.len() { + return None; + } + + let mut pair = &self.data[self.next_index..]; + if let Some(end) = pair.find(',') { + pair = &pair[..end]; + self.next_index += end + 1; + } else { + self.next_index = self.data.len(); + } + let mut split = pair.split('='); + // SAFETY: first split always returns something + let key = split.next().unwrap(); + + Some((key, split.next())) + } +} + +pub(crate) trait KeyValFmtAdd { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b>; +} + +pub(crate) struct KeyValFmt<'a> { + fields: Vec<(Box, Box)>, +} + +impl<'a> KeyValFmt<'a> { + fn new() -> Self { + Self { fields: vec![] } + } + + pub(crate) fn add(mut self, key: K, val: Option) -> Self + where + K: fmt::Display + 'a, + V: Encodable + 'a, + { + if let Some(val) = val { + self.fields.push((Box::new(key), Box::new(val))); + } + + self + } +} + +impl fmt::Display for KeyValFmt<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut first = true; + for (k, v) in self.fields.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{k}=")?; + v.encode(f)?; + first = false; + } + + Ok(()) + } +} + +/// A trait for objects which can be converted or resolved to one or more [`DBusAddr`] values. +pub trait ToDBusAddrs<'a> { + type Iter: Iterator>>; + + fn to_dbus_addrs(&'a self) -> Self::Iter; +} + +impl<'a> ToDBusAddrs<'a> for DBusAddr<'a> { + type Iter = std::iter::Once>>; + + /// Get an iterator over the D-Bus addresses. + fn to_dbus_addrs(&'a self) -> Self::Iter { + std::iter::once(Ok(self.clone())) + } +} + +impl<'a> ToDBusAddrs<'a> for str { + type Iter = std::iter::Once>>; + + fn to_dbus_addrs(&'a self) -> Self::Iter { + std::iter::once(self.try_into()) + } +} + +impl<'a> ToDBusAddrs<'a> for String { + type Iter = std::iter::Once>>; + + fn to_dbus_addrs(&'a self) -> Self::Iter { + std::iter::once(self.as_str().try_into()) + } +} + +impl<'a> ToDBusAddrs<'a> for Vec>> { + type Iter = std::iter::Cloned>>>; + + /// Get an iterator over the D-Bus addresses. + fn to_dbus_addrs(&'a self) -> Self::Iter { + self.iter().cloned() + } +} diff --git a/zbus/src/address/percent.rs b/zbus/src/address/percent.rs new file mode 100644 index 000000000..ccd2c6310 --- /dev/null +++ b/zbus/src/address/percent.rs @@ -0,0 +1,209 @@ +use std::{ + borrow::Cow, + ffi::{OsStr, OsString}, + fmt, +}; + +use super::{Error, Result}; + +// A trait for types that can be percent-encoded and written to a [`fmt::Formatter`]. +pub(crate) trait Encodable { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result; +} + +impl Encodable for T { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.to_string().as_bytes()) + } +} + +pub(crate) struct EncData(pub T); + +impl> Encodable for EncData { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.0.as_ref()) + } +} + +pub(crate) struct EncOsStr(pub T); + +impl Encodable for EncOsStr<&Cow<'_, OsStr>> { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.0.to_string_lossy().as_bytes()) + } +} + +impl Encodable for EncOsStr<&OsStr> { + fn encode(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + encode_percents(f, self.0.to_string_lossy().as_bytes()) + } +} + +/// Percent-encode the value. +pub fn encode_percents(f: &mut dyn fmt::Write, value: &[u8]) -> std::fmt::Result { + for &byte in value { + if matches!(byte, b'-' | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'_' | b'/' | b'.' | b'\\' | b'*') + { + // Write the byte directly if it's in the allowed set + f.write_char(byte as char)?; + } else { + // Otherwise, write its percent-encoded form + write!(f, "%{:02X}", byte)?; + } + } + + Ok(()) +} + +/// Percent-decode the string. +pub fn decode_percents(value: &str) -> Result> { + // Check if decoding is necessary + let needs_decoding = value.chars().any(|c| c == '%' || !is_allowed_char(c)); + + if !needs_decoding { + return Ok(Cow::Borrowed(value.as_bytes())); + } + + let mut decoded = Vec::with_capacity(value.len()); + let mut chars = value.chars(); + + while let Some(c) = chars.next() { + match c { + '%' => { + let high = chars + .next() + .ok_or_else(|| Error::Encoding("Incomplete percent-encoded sequence".into()))?; + let low = chars + .next() + .ok_or_else(|| Error::Encoding("Incomplete percent-encoded sequence".into()))?; + decoded.push(decode_hex_pair(high, low)?); + } + _ if is_allowed_char(c) => decoded.push(c as u8), + _ => return Err(Error::Encoding("Invalid character in address".into())), + } + } + + Ok(Cow::Owned(decoded)) +} + +fn is_allowed_char(c: char) -> bool { + matches!(c, '-' | '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '/' | '.' | '\\' | '*') +} + +fn decode_hex_pair(high: char, low: char) -> Result { + let high_digit = decode_hex(high)?; + let low_digit = decode_hex(low)?; + + Ok(high_digit << 4 | low_digit) +} + +fn decode_hex(c: char) -> Result { + match c { + '0'..='9' => Ok(c as u8 - b'0'), + 'a'..='f' => Ok(c as u8 - b'a' + 10), + 'A'..='F' => Ok(c as u8 - b'A' + 10), + + _ => Err(Error::Encoding( + "Invalid hexadecimal character in percent-encoded sequence".into(), + )), + } +} + +pub(super) fn decode_percents_str(value: &str) -> Result> { + cow_bytes_to_str(decode_percents(value)?) +} + +fn cow_bytes_to_str(cow: Cow<'_, [u8]>) -> Result> { + match cow { + Cow::Borrowed(bytes) => Ok(Cow::Borrowed( + std::str::from_utf8(bytes).map_err(|e| Error::Encoding(format!("{e}")))?, + )), + Cow::Owned(bytes) => Ok(Cow::Owned( + String::from_utf8(bytes).map_err(|e| Error::Encoding(format!("{e}")))?, + )), + } +} + +pub(super) fn decode_percents_os_str(value: &str) -> Result> { + cow_bytes_to_os_str(decode_percents(value)?) +} + +fn cow_bytes_to_os_str(cow: Cow<'_, [u8]>) -> Result> { + match cow { + Cow::Borrowed(bytes) => Ok(Cow::Borrowed(OsStr::new( + std::str::from_utf8(bytes).map_err(|e| Error::Encoding(format!("{e}")))?, + ))), + Cow::Owned(bytes) => Ok(Cow::Owned(OsString::from( + String::from_utf8(bytes).map_err(|e| Error::Encoding(format!("{e}")))?, + ))), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_ascii() { + const INPUT: &[u8] = "hello".as_bytes(); + + let mut output = String::new(); + encode_percents(&mut output, INPUT).unwrap(); + assert_eq!(output, "hello"); + + let result = decode_percents(&output).unwrap(); + assert!(matches!(result, Cow::Borrowed(_))); + assert_eq!(result, Cow::Borrowed(INPUT)); + } + + #[test] + fn special_characters() { + const INPUT: &[u8] = "hello world!".as_bytes(); + + let mut output = String::new(); + encode_percents(&mut output, INPUT).unwrap(); + assert_eq!(output, "hello%20world%21"); + + let result = decode_percents(&output).unwrap(); + assert!(matches!(result, Cow::Owned(_))); + assert_eq!(result, Cow::Borrowed(INPUT)); + } + + #[test] + fn empty_input() { + const INPUT: &[u8] = "".as_bytes(); + + let mut output = String::new(); + encode_percents(&mut output, INPUT).unwrap(); + assert_eq!(output, ""); + + let result = decode_percents(&output).unwrap(); + assert!(matches!(result, Cow::Borrowed(_))); + assert_eq!(result, Cow::Borrowed(INPUT)); + } + + #[test] + fn non_ascii_characters() { + const INPUT: &[u8] = "😊".as_bytes(); + + let mut output = String::new(); + encode_percents(&mut output, INPUT).unwrap(); + assert_eq!(output, "%F0%9F%98%8A"); + + let result = decode_percents(&output).unwrap(); + assert!(matches!(result, Cow::Owned(_))); + assert_eq!(result, Cow::Borrowed(INPUT)); + } + + #[test] + fn incomplete_encoding() { + let result = decode_percents("incomplete%"); + assert!(result.is_err()); + } + + #[test] + fn invalid_characters() { + let result = decode_percents("invalid%2Gchar"); + assert!(result.is_err()); + } +} diff --git a/zbus/src/address/tests.rs b/zbus/src/address/tests.rs new file mode 100644 index 000000000..3230cc7fb --- /dev/null +++ b/zbus/src/address/tests.rs @@ -0,0 +1,181 @@ +use std::{borrow::Cow, ffi::OsStr}; + +use super::{ + transport::{AutolaunchScope, TcpFamily, Transport, UnixAddrKind}, + DBusAddr, +}; + +#[test] +fn parse_err() { + assert_eq!( + DBusAddr::try_from("").unwrap_err().to_string(), + "Missing transport in address" + ); + assert_eq!( + DBusAddr::try_from("foo").unwrap_err().to_string(), + "Missing transport in address" + ); + assert_eq!( + DBusAddr::try_from("foo:").unwrap_err().to_string(), + "Unsupported transport in address" + ); + assert_eq!( + DBusAddr::try_from("tcp:opt=%1").unwrap_err().to_string(), + "Encoding error: Incomplete percent-encoded sequence" + ); + assert_eq!( + DBusAddr::try_from("tcp:opt=%1z").unwrap_err().to_string(), + "Encoding error: Invalid hexadecimal character in percent-encoded sequence" + ); + assert_eq!( + DBusAddr::try_from("tcp:opt=1\rz").unwrap_err().to_string(), + "Encoding error: Invalid character in address" + ); + assert_eq!( + DBusAddr::try_from("tcp:guid=9406e28972c595c590766c9564ce623") + .unwrap_err() + .to_string(), + "Invalid value for key: `guid`" + ); + assert_eq!( + DBusAddr::try_from("tcp:guid=9406e28972c595c590766c9564ce623g") + .unwrap_err() + .to_string(), + "Invalid value for key: `guid`" + ); + + let addr = DBusAddr::try_from("tcp:guid=9406e28972c595c590766c9564ce623f").unwrap(); + addr.guid().unwrap(); +} + +#[test] +fn parse_unix() { + let addr = DBusAddr::try_from("unix:path=/tmp/dbus-foo,guid=9406e28972c595c590766c9564ce623f") + .unwrap(); + let Transport::Unix(u) = addr.transport().unwrap() else { + panic!(); + }; + assert_eq!( + u.kind(), + &UnixAddrKind::Path(Cow::Borrowed(OsStr::new("/tmp/dbus-foo"))) + ); + + assert_eq!( + DBusAddr::try_from("unix:foo=blah").unwrap_err().to_string(), + "Other error: invalid `unix:` address, missing required key" + ); + assert_eq!( + DBusAddr::try_from("unix:path=/blah,abstract=foo") + .unwrap_err() + .to_string(), + "Other error: invalid address, only one of `path` `dir` `tmpdir` `abstract` or `runtime` expected" + ); + assert_eq!( + DBusAddr::try_from("unix:runtime=no") + .unwrap_err() + .to_string(), + "Invalid value for key: `runtime`" + ); + DBusAddr::try_from(String::from("unix:path=/tmp/foo")).unwrap(); +} + +#[test] +fn parse_launchd() { + let addr = DBusAddr::try_from("launchd:env=FOOBAR").unwrap(); + let Transport::Launchd(t) = addr.transport().unwrap() else { + panic!(); + }; + assert_eq!(t.env(), "FOOBAR"); + + assert_eq!( + DBusAddr::try_from("launchd:weof").unwrap_err().to_string(), + "Missing key: `env`" + ); +} + +#[test] +fn parse_systemd() { + let addr = DBusAddr::try_from("systemd:").unwrap(); + let Transport::Systemd(_) = addr.transport().unwrap() else { + panic!(); + }; +} + +#[test] +fn parse_tcp() { + let addr = DBusAddr::try_from("tcp:host=localhost,bind=*,port=0,family=ipv4").unwrap(); + let Transport::Tcp(t) = addr.transport().unwrap() else { + panic!(); + }; + assert_eq!(t.host().unwrap(), "localhost"); + assert_eq!(t.bind().unwrap(), "*"); + assert_eq!(t.port().unwrap(), 0); + assert_eq!(t.family().unwrap(), TcpFamily::IPv4); + + let addr = DBusAddr::try_from("tcp:").unwrap(); + let Transport::Tcp(t) = addr.transport().unwrap() else { + panic!(); + }; + assert!(t.host().is_none()); + assert!(t.bind().is_none()); + assert!(t.port().is_none()); + assert!(t.family().is_none()); +} + +#[test] +fn parse_nonce_tcp() { + let addr = + DBusAddr::try_from("nonce-tcp:host=localhost,bind=*,port=0,family=ipv6,noncefile=foo") + .unwrap(); + let Transport::NonceTcp(t) = addr.transport().unwrap() else { + panic!(); + }; + assert_eq!(t.host().unwrap(), "localhost"); + assert_eq!(t.bind().unwrap(), "*"); + assert_eq!(t.port().unwrap(), 0); + assert_eq!(t.family().unwrap(), TcpFamily::IPv6); + assert_eq!(t.noncefile().unwrap(), "foo"); +} + +#[test] +fn parse_unixexec() { + let addr = DBusAddr::try_from("unixexec:path=/bin/test,argv2=foo").unwrap(); + let Transport::Unixexec(t) = addr.transport().unwrap() else { + panic!(); + }; + + assert_eq!(t.path(), "/bin/test"); + assert_eq!(t.argv(), &[(2, Cow::from("foo"))]); + + assert_eq!( + DBusAddr::try_from("unixexec:weof").unwrap_err().to_string(), + "Missing key: `path`" + ); +} + +#[test] +fn parse_autolaunch() { + let addr = DBusAddr::try_from("autolaunch:scope=*user").unwrap(); + let Transport::Autolaunch(t) = addr.transport().unwrap() else { + panic!(); + }; + assert_eq!(t.scope().unwrap(), &AutolaunchScope::User); +} + +#[test] +#[cfg(feature = "vsock")] +fn parse_vsock() { + let addr = DBusAddr::try_from("vsock:cid=12,port=32").unwrap(); + let Transport::Vsock(t) = addr.transport().unwrap() else { + panic!(); + }; + assert_eq!(t.port(), Some(32)); + assert_eq!(t.cid(), Some(12)); + + assert_eq!( + DBusAddr::try_from("vsock:port=abc") + .unwrap_err() + .to_string(), + "Invalid value for key: `port`" + ); +} diff --git a/zbus/src/address/transport/autolaunch.rs b/zbus/src/address/transport/autolaunch.rs new file mode 100644 index 000000000..c69191336 --- /dev/null +++ b/zbus/src/address/transport/autolaunch.rs @@ -0,0 +1,77 @@ +use std::{borrow::Cow, fmt}; + +use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; + +/// Scope of autolaunch (Windows only) +#[derive(Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum AutolaunchScope<'a> { + /// Limit session bus to dbus installation path. + InstallPath, + /// Limit session bus to the recent user. + User, + /// other values - specify dedicated session bus like "release", "debug" or other. + Other(Cow<'a, str>), +} + +impl fmt::Display for AutolaunchScope<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InstallPath => write!(f, "*install-path"), + Self::User => write!(f, "*user"), + Self::Other(o) => write!(f, "{o}"), + } + } +} + +impl<'a> TryFrom> for AutolaunchScope<'a> { + type Error = Error; + + fn try_from(s: Cow<'a, str>) -> Result { + match s.as_ref() { + "*install-path" => Ok(Self::InstallPath), + "*user" => Ok(Self::User), + _ => Ok(Self::Other(s)), + } + } +} + +/// `autolaunch:` D-Bus transport. +/// +/// +#[derive(Debug, PartialEq, Eq, Default)] +pub struct Autolaunch<'a> { + scope: Option>, +} + +impl<'a> Autolaunch<'a> { + /// Scope of autolaunch (Windows only) + pub fn scope(&self) -> Option<&AutolaunchScope<'a>> { + self.scope.as_ref() + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Autolaunch<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let mut res = Autolaunch::default(); + + for (k, v) in s.key_val_iter() { + match (k, v) { + ("scope", Some(v)) => { + res.scope = Some(decode_percents_str(v)?.try_into()?); + } + _ => continue, + } + } + + Ok(res) + } +} + +impl KeyValFmtAdd for Autolaunch<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("scope", self.scope()) + } +} diff --git a/zbus/src/address/transport/launchd.rs b/zbus/src/address/transport/launchd.rs new file mode 100644 index 000000000..ffcb5c5cf --- /dev/null +++ b/zbus/src/address/transport/launchd.rs @@ -0,0 +1,46 @@ +use std::borrow::Cow; + +use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; + +/// `launchd:` D-Bus transport. +/// +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Launchd<'a> { + env: Cow<'a, str>, +} + +impl<'a> Launchd<'a> { + /// Environment variable. + /// + /// Environment variable used to get the path of the unix domain socket for the launchd created + /// dbus-daemon. + pub fn env(&self) -> &str { + self.env.as_ref() + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Launchd<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + for (k, v) in s.key_val_iter() { + match (k, v) { + ("env", Some(v)) => { + return Ok(Launchd { + env: decode_percents_str(v)?, + }); + } + _ => continue, + } + } + + Err(Error::MissingKey("env".into())) + } +} + +impl KeyValFmtAdd for Launchd<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("env", Some(self.env())) + } +} diff --git a/zbus/src/address/transport/mod.rs b/zbus/src/address/transport/mod.rs new file mode 100644 index 000000000..9ba0cb2b6 --- /dev/null +++ b/zbus/src/address/transport/mod.rs @@ -0,0 +1,100 @@ +//! D-Bus supported transports. + +use std::fmt; + +use super::{percent, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; + +mod autolaunch; +pub use autolaunch::{Autolaunch, AutolaunchScope}; + +mod launchd; +pub use launchd::Launchd; + +mod nonce_tcp; +pub use nonce_tcp::NonceTcp; + +mod systemd; +pub use systemd::Systemd; + +mod tcp; +pub use tcp::{Tcp, TcpFamily}; + +mod unix; +pub use unix::{Unix, UnixAddrKind}; + +mod unixexec; +pub use unixexec::Unixexec; + +mod vsock; +pub use vsock::Vsock; + +/// A D-Bus transport. +#[derive(Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Transport<'a> { + /// Unix Domain Sockets transport. + Unix(unix::Unix<'a>), + /// launchd transport. + Launchd(launchd::Launchd<'a>), + /// systemd transport. + Systemd(systemd::Systemd<'a>), + /// TCP Sockets transport. + Tcp(tcp::Tcp<'a>), + /// Nonce-authenticated TCP Sockets transport. + NonceTcp(nonce_tcp::NonceTcp<'a>), + /// Executed Subprocesses on Unix transport. + Unixexec(unixexec::Unixexec<'a>), + /// Autolaunch transport. + Autolaunch(autolaunch::Autolaunch<'a>), + /// VSOCK Sockets transport. + Vsock(vsock::Vsock<'a>), +} + +impl fmt::Display for Transport<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Unix(_) => write!(f, "unix"), + Self::Launchd(_) => write!(f, "launchd"), + Self::Systemd(_) => write!(f, "systemd"), + Self::Tcp(_) => write!(f, "tcp"), + Self::NonceTcp(_) => write!(f, "nonce-tcp"), + Self::Unixexec(_) => write!(f, "unixexec"), + Self::Autolaunch(_) => write!(f, "autolaunch"), + Self::Vsock(_) => write!(f, "vsock"), + } + } +} + +impl KeyValFmtAdd for Transport<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + match self { + Self::Unix(t) => t.key_val_fmt_add(kv), + Self::Launchd(t) => t.key_val_fmt_add(kv), + Self::Systemd(t) => t.key_val_fmt_add(kv), + Self::Tcp(t) => t.key_val_fmt_add(kv), + Self::NonceTcp(t) => t.key_val_fmt_add(kv), + Self::Unixexec(t) => t.key_val_fmt_add(kv), + Self::Autolaunch(t) => t.key_val_fmt_add(kv), + Self::Vsock(t) => t.key_val_fmt_add(kv), + } + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Transport<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let col = s.addr.find(':').ok_or(Error::MissingTransport)?; + match &s.addr[..col] { + "unix" => Ok(Self::Unix(s.try_into()?)), + "launchd" => Ok(Self::Launchd(s.try_into()?)), + "systemd" => Ok(Self::Systemd(s.try_into()?)), + "tcp" => Ok(Self::Tcp(s.try_into()?)), + "nonce-tcp" => Ok(Self::NonceTcp(s.try_into()?)), + "unixexec" => Ok(Self::Unixexec(s.try_into()?)), + "autolaunch" => Ok(Self::Autolaunch(s.try_into()?)), + "vsock" => Ok(Self::Vsock(s.try_into()?)), + _ => Err(Error::UnknownTransport), + } + } +} diff --git a/zbus/src/address/transport/nonce_tcp.rs b/zbus/src/address/transport/nonce_tcp.rs new file mode 100644 index 000000000..26cf077b2 --- /dev/null +++ b/zbus/src/address/transport/nonce_tcp.rs @@ -0,0 +1,100 @@ +use std::{borrow::Cow, ffi::OsStr}; + +use super::{ + percent::{decode_percents_os_str, decode_percents_str, EncOsStr}, + tcp::TcpFamily, + DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result, +}; + +/// `nonce-tcp:` D-Bus transport. +/// +/// +#[derive(Debug, Default, PartialEq, Eq)] +pub struct NonceTcp<'a> { + host: Option>, + bind: Option>, + port: Option, + family: Option, + noncefile: Option>, +} + +impl<'a> NonceTcp<'a> { + /// If set, the DNS name or IP address. + pub fn host(&self) -> Option<&str> { + self.host.as_ref().map(|v| v.as_ref()) + } + + /// If set, the listenable address. + /// + /// Used in a listenable address to configure the interface on which the server will listen: + /// either the IP address of one of the local machine's interfaces (most commonly `127.0.0.1`), + /// or a DNS name that resolves to one of those IP addresses, or `*` to listen on all interfaces + /// simultaneously. + pub fn bind(&self) -> Option<&str> { + self.bind.as_ref().map(|v| v.as_ref()) + } + + /// If set, the TCP port. + /// + /// The TCP port the server will open. A zero value let the server choose a free port provided + /// from the underlying operating system. + pub fn port(&self) -> Option { + self.port + } + + /// If set, the type of socket family. + pub fn family(&self) -> Option { + self.family + } + + /// If set, the nonce file location. + /// + /// File location containing the secret. This is only meaningful in connectable addresses. + pub fn noncefile(&self) -> Option<&OsStr> { + self.noncefile.as_ref().map(|v| v.as_ref()) + } +} + +impl KeyValFmtAdd for NonceTcp<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("host", self.host()) + .add("bind", self.bind()) + .add("port", self.port()) + .add("family", self.family()) + .add("noncefile", self.noncefile().map(EncOsStr)) + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for NonceTcp<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let mut res = NonceTcp::default(); + for (k, v) in s.key_val_iter() { + match (k, v) { + ("host", Some(v)) => { + res.host = Some(decode_percents_str(v)?); + } + ("bind", Some(v)) => { + res.bind = Some(decode_percents_str(v)?); + } + ("port", Some(v)) => { + res.port = Some( + decode_percents_str(v)? + .parse() + .map_err(|_| Error::InvalidValue("port".into()))?, + ); + } + ("family", Some(v)) => { + res.family = Some(decode_percents_str(v)?.as_ref().try_into()?); + } + ("noncefile", Some(v)) => { + res.noncefile = Some(decode_percents_os_str(v)?); + } + _ => continue, + } + } + + Ok(res) + } +} diff --git a/zbus/src/address/transport/systemd.rs b/zbus/src/address/transport/systemd.rs new file mode 100644 index 000000000..7e9f1162e --- /dev/null +++ b/zbus/src/address/transport/systemd.rs @@ -0,0 +1,28 @@ +use std::marker::PhantomData; + +use super::{DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; + +/// `systemd:` D-Bus transport. +/// +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Systemd<'a> { + // use a phantom lifetime for eventually future fields and consistency + phantom: PhantomData<&'a ()>, +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Systemd<'a> { + type Error = Error; + + fn try_from(_s: &'a DBusAddr<'a>) -> Result { + Ok(Systemd { + phantom: PhantomData, + }) + } +} + +impl KeyValFmtAdd for Systemd<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv + } +} diff --git a/zbus/src/address/transport/tcp.rs b/zbus/src/address/transport/tcp.rs new file mode 100644 index 000000000..57aea6781 --- /dev/null +++ b/zbus/src/address/transport/tcp.rs @@ -0,0 +1,115 @@ +use std::{borrow::Cow, fmt}; + +use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; + +/// TCP IP address family +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum TcpFamily { + /// IPv4 + IPv4, + /// IPv6 + IPv6, +} + +impl fmt::Display for TcpFamily { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::IPv4 => write!(f, "ipv4"), + Self::IPv6 => write!(f, "ipv6"), + } + } +} + +impl TryFrom<&str> for TcpFamily { + type Error = Error; + + fn try_from(s: &str) -> Result { + match s { + "ipv4" => Ok(Self::IPv4), + "ipv6" => Ok(Self::IPv6), + _ => Err(Error::UnknownTcpFamily(s.into())), + } + } +} + +/// `tcp:` D-Bus transport. +/// +/// +#[derive(Debug, Default, PartialEq, Eq)] +pub struct Tcp<'a> { + host: Option>, + bind: Option>, + port: Option, + family: Option, +} + +impl<'a> Tcp<'a> { + /// If set, DNS name or IP address. + pub fn host(&self) -> Option<&str> { + self.host.as_ref().map(|v| v.as_ref()) + } + + /// If set, the listenable address. + /// + /// Used in a listenable address to configure the interface on which the server will listen: + /// either the IP address of one of the local machine's interfaces (most commonly `127.0.0.1`), + /// or a DNS name that resolves to one of those IP addresses, or `*` to listen on all interfaces + /// simultaneously. + pub fn bind(&self) -> Option<&str> { + self.bind.as_ref().map(|v| v.as_ref()) + } + + /// If set, the TCP port. + /// + /// The TCP port the server will open. A zero value let the server choose a free port provided + /// from the underlying operating system. + pub fn port(&self) -> Option { + self.port + } + + /// If set, the type of socket family. + pub fn family(&self) -> Option { + self.family + } +} + +impl KeyValFmtAdd for Tcp<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("host", self.host()) + .add("bind", self.bind()) + .add("port", self.port()) + .add("family", self.family()) + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Tcp<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let mut res = Tcp::default(); + for (k, v) in s.key_val_iter() { + match (k, v) { + ("host", Some(v)) => { + res.host = Some(decode_percents_str(v)?); + } + ("bind", Some(v)) => { + res.bind = Some(decode_percents_str(v)?); + } + ("port", Some(v)) => { + res.port = Some( + decode_percents_str(v)? + .parse() + .map_err(|_| Error::InvalidValue("port".into()))?, + ); + } + ("family", Some(v)) => { + res.family = Some(decode_percents_str(v)?.as_ref().try_into()?); + } + _ => continue, + } + } + + Ok(res) + } +} diff --git a/zbus/src/address/transport/unix.rs b/zbus/src/address/transport/unix.rs new file mode 100644 index 000000000..8dfcefc7d --- /dev/null +++ b/zbus/src/address/transport/unix.rs @@ -0,0 +1,118 @@ +use std::{borrow::Cow, ffi::OsStr}; + +use super::{ + percent::{decode_percents, decode_percents_os_str, decode_percents_str, EncData, EncOsStr}, + DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result, +}; + +/// A sub-type of `unix:` transport. +#[derive(Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum UnixAddrKind<'a> { + /// Path of the unix domain socket. + Path(Cow<'a, OsStr>), + /// Directory in which a socket file with a random file name starting with 'dbus-' should be + /// created by a server. + Dir(Cow<'a, OsStr>), + /// The same as "dir", except that on platforms with abstract sockets, a server may attempt to + /// create an abstract socket whose name starts with this directory instead of a path-based + /// socket. + Tmpdir(Cow<'a, OsStr>), + /// Unique string in the abstract namespace, often syntactically resembling a path but + /// unconnected to the filesystem namespace + Abstract(Cow<'a, [u8]>), + /// Listen on $XDG_RUNTIME_DIR/bus. + Runtime, +} + +impl KeyValFmtAdd for UnixAddrKind<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + match self { + UnixAddrKind::Path(p) => kv.add("path", Some(EncOsStr(p))), + UnixAddrKind::Dir(p) => kv.add("dir", Some(EncOsStr(p))), + UnixAddrKind::Tmpdir(p) => kv.add("tmpdir", Some(EncOsStr(p))), + UnixAddrKind::Abstract(p) => kv.add("abstract", Some(EncData(p))), + UnixAddrKind::Runtime => kv.add("runtime", Some("yes")), + } + } +} + +/// `unix:` D-Bus transport. +/// +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Unix<'a> { + kind: UnixAddrKind<'a>, +} + +impl<'a> Unix<'a> { + /// One of the various `unix:` addresses. + pub fn kind(&self) -> &UnixAddrKind<'a> { + &self.kind + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Unix<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let mut kind = None; + let mut iter = s.key_val_iter(); + for (k, v) in &mut iter { + match k { + "path" | "dir" | "tmpdir" => { + let v = v.ok_or_else(|| Error::MissingValue(k.into()))?; + let v = decode_percents_os_str(v)?; + kind = Some(match k { + "path" => UnixAddrKind::Path(v), + "dir" => UnixAddrKind::Dir(v), + "tmpdir" => UnixAddrKind::Tmpdir(v), + // can't happen, we matched those earlier + _ => panic!(), + }); + + break; + } + "abstract" => { + let v = v.ok_or_else(|| Error::MissingValue(k.into()))?; + let v = decode_percents(v)?; + kind = Some(UnixAddrKind::Abstract(v)); + + break; + } + "runtime" => { + let v = v.ok_or_else(|| Error::MissingValue(k.into()))?; + let v = decode_percents_str(v)?; + if v != "yes" { + return Err(Error::InvalidValue(k.into())); + } + kind = Some(UnixAddrKind::Runtime); + + break; + } + _ => continue, + } + } + let Some(kind) = kind else { + return Err(Error::Other( + "invalid `unix:` address, missing required key".into(), + )); + }; + for (k, _) in iter { + match k { + "path" | "dir" | "tmpdir" | "abstract" | "runtime" => { + return Err(Error::Other("invalid address, only one of `path` `dir` `tmpdir` `abstract` or `runtime` expected".into())); + } + _ => (), + } + } + + Ok(Unix { kind }) + } +} + +impl KeyValFmtAdd for Unix<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + self.kind().key_val_fmt_add(kv) + } +} diff --git a/zbus/src/address/transport/unixexec.rs b/zbus/src/address/transport/unixexec.rs new file mode 100644 index 000000000..740e65b1c --- /dev/null +++ b/zbus/src/address/transport/unixexec.rs @@ -0,0 +1,86 @@ +use std::{borrow::Cow, ffi::OsStr, fmt}; + +use super::{ + percent::{decode_percents_os_str, decode_percents_str, EncOsStr}, + DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result, +}; + +#[derive(Debug, PartialEq, Eq)] +struct Argv(usize); + +impl fmt::Display for Argv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let n = self.0; + + write!(f, "argv{n}") + } +} + +/// `unixexec:` D-Bus transport. +/// +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Unixexec<'a> { + path: Cow<'a, OsStr>, + argv: Vec<(usize, Cow<'a, str>)>, +} + +impl<'a> Unixexec<'a> { + /// Binary to execute. + /// + /// Path of the binary to execute, either an absolute path or a binary name that is searched for + /// in the default search path of the OS. This corresponds to the first argument of execlp(). + /// This key is mandatory. + pub fn path(&self) -> &OsStr { + self.path.as_ref() + } + + /// Arguments. + /// + /// Arguments to pass to the binary as `[(nth, arg),...]`. + pub fn argv(&self) -> &[(usize, Cow<'a, str>)] { + self.argv.as_ref() + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Unixexec<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let mut path = None; + let mut argv = Vec::new(); + + for (k, v) in s.key_val_iter() { + match (k, v) { + ("path", Some(v)) => { + path = Some(decode_percents_os_str(v)?); + } + (k, Some(v)) if k.starts_with("argv") => { + let n: usize = k[4..].parse().map_err(|_| Error::InvalidValue(k.into()))?; + let arg = decode_percents_str(v)?; + argv.push((n, arg)); + } + _ => continue, + } + } + + let Some(path) = path else { + return Err(Error::MissingKey("path".into())); + }; + + argv.sort_by_key(|(num, _)| *num); + + Ok(Self { path, argv }) + } +} + +impl KeyValFmtAdd for Unixexec<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, mut kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv = kv.add("path", Some(EncOsStr(self.path()))); + for (n, arg) in self.argv() { + kv = kv.add(Argv(*n), Some(arg)); + } + + kv + } +} diff --git a/zbus/src/address/transport/vsock.rs b/zbus/src/address/transport/vsock.rs new file mode 100644 index 000000000..953d99b24 --- /dev/null +++ b/zbus/src/address/transport/vsock.rs @@ -0,0 +1,67 @@ +use std::marker::PhantomData; + +use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; + +/// `vsock:` D-Bus transport. +#[derive(Debug, PartialEq, Eq)] +pub struct Vsock<'a> { + // no cid means ANY + cid: Option, + // no port means ANY + port: Option, + // use a phantom lifetime for eventually future fields and consistency + phantom: PhantomData<&'a ()>, +} + +impl<'a> Vsock<'a> { + /// The VSOCK port. + pub fn port(&self) -> Option { + self.port + } + + /// The VSOCK CID. + pub fn cid(&self) -> Option { + self.cid + } +} + +impl<'a> TryFrom<&'a DBusAddr<'a>> for Vsock<'a> { + type Error = Error; + + fn try_from(s: &'a DBusAddr<'a>) -> Result { + let mut port = None; + let mut cid = None; + + for (k, v) in s.key_val_iter() { + match (k, v) { + ("port", Some(v)) => { + port = Some( + decode_percents_str(v)? + .parse() + .map_err(|_| Error::InvalidValue(k.into()))?, + ); + } + ("cid", Some(v)) => { + cid = Some( + decode_percents_str(v)? + .parse() + .map_err(|_| Error::InvalidValue(k.into()))?, + ) + } + _ => continue, + } + } + + Ok(Vsock { + port, + cid, + phantom: PhantomData, + }) + } +} + +impl KeyValFmtAdd for Vsock<'_> { + fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { + kv.add("cid", self.cid()).add("port", self.port()) + } +} diff --git a/zbus/src/lib.rs b/zbus/src/lib.rs index 310a8c5ee..2c2ced25f 100644 --- a/zbus/src/lib.rs +++ b/zbus/src/lib.rs @@ -46,6 +46,9 @@ pub use dbus_error::*; mod error; pub use error::*; +pub mod address; +pub use address::DBusAddr; + pub mod legacy_address; pub use legacy_address::Address; From 803a7d5cd6a25d8109e6d4905e5a7ad9e2660f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 13 Sep 2024 15:27:11 +0400 Subject: [PATCH 03/21] =?UTF-8?q?=E2=9C=A8=20zb:=20add=20From?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert to legacy Error::Address string error. --- zbus/src/error.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zbus/src/error.rs b/zbus/src/error.rs index 2980a492b..4adb4ed30 100644 --- a/zbus/src/error.rs +++ b/zbus/src/error.rs @@ -199,6 +199,12 @@ impl From for Error { } } +impl From for Error { + fn from(val: crate::address::Error) -> Self { + Error::Address(val.to_string()) + } +} + impl From for Error { fn from(val: VariantError) -> Self { Error::Variant(val) From db090f540fee156d5c01aecea842e134e109add9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 30 Jan 2024 12:27:47 +0400 Subject: [PATCH 04/21] =?UTF-8?q?=F0=9F=92=A5=20zb:=20change=20connection,?= =?UTF-8?q?=20handle=20;-separated=20address?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move connection logic to connect.rs. We start moving parsing to the new ::address step by step, here with top-level list. legacy_address is going to be gradually replaced. --- zbus/src/blocking/connection/builder.rs | 7 ++-- zbus/src/connection/builder.rs | 38 +++++++++----------- zbus/src/connection/connect.rs | 47 +++++++++++++++++++++++++ zbus/src/connection/mod.rs | 2 ++ 4 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 zbus/src/connection/connect.rs diff --git a/zbus/src/blocking/connection/builder.rs b/zbus/src/blocking/connection/builder.rs index d0e5f9c33..b9fe7dac4 100644 --- a/zbus/src/blocking/connection/builder.rs +++ b/zbus/src/blocking/connection/builder.rs @@ -15,7 +15,7 @@ use zvariant::{ObjectPath, Str}; #[cfg(feature = "p2p")] use crate::Guid; use crate::{ - blocking::Connection, connection::socket::BoxedSplit, legacy_address::Address, + address::ToDBusAddrs, blocking::Connection, connection::socket::BoxedSplit, names::WellKnownName, object_server::Interface, utils::block_on, AuthMechanism, Error, Result, }; @@ -40,10 +40,9 @@ impl<'a> Builder<'a> { /// Create a builder for a connection that will use the given [D-Bus bus address]. /// /// [D-Bus bus address]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses - pub fn address(address: A) -> Result + pub fn address<'t, A>(address: &'t A) -> Result where - A: TryInto
, - A::Error: Into, + A: ToDBusAddrs<'t> + ?Sized, { crate::connection::Builder::address(address).map(Self) } diff --git a/zbus/src/connection/builder.rs b/zbus/src/connection/builder.rs index 5b8fff940..daaf94807 100644 --- a/zbus/src/connection/builder.rs +++ b/zbus/src/connection/builder.rs @@ -24,13 +24,14 @@ use vsock::VsockStream; use zvariant::{ObjectPath, Str}; use crate::{ - legacy_address::{self, Address}, + address::{DBusAddr, ToDBusAddrs}, names::{InterfaceName, WellKnownName}, object_server::{ArcInterface, Interface}, Connection, Error, Executor, Guid, OwnedGuid, Result, }; use super::{ + connect::connect_address, handshake::{AuthMechanism, Authenticated}, socket::{BoxedSplit, ReadHalf, Split, WriteHalf}, }; @@ -47,7 +48,7 @@ enum Target { feature = "tokio-vsock" ))] VsockStream(VsockStream), - Address(Address), + Address(Vec>), Socket(Split, Box>), AuthenticatedSocket(Split, Box>), } @@ -79,12 +80,12 @@ assert_impl_all!(Builder<'_>: Send, Sync, Unpin); impl<'a> Builder<'a> { /// Create a builder for the session/user message bus connection. pub fn session() -> Result { - Ok(Self::new(Target::Address(Address::session()?))) + Self::address(&crate::address::session()?) } /// Create a builder for the system-wide message bus connection. pub fn system() -> Result { - Ok(Self::new(Target::Address(Address::system()?))) + Self::address(&crate::address::system()?) } /// Create a builder for a connection that will use the given [D-Bus bus address]. @@ -118,14 +119,17 @@ impl<'a> Builder<'a> { /// current session using `ibus address` command. /// /// [D-Bus bus address]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses - pub fn address(address: A) -> Result + pub fn address<'t, A>(address: &'t A) -> Result where - A: TryInto
, - A::Error: Into, + A: ToDBusAddrs<'t> + ?Sized, { - Ok(Self::new(Target::Address( - address.try_into().map_err(Into::into)?, - ))) + let addr = address + .to_dbus_addrs() + .filter_map(std::result::Result::ok) + .map(|a| a.to_owned()) + .collect(); + + Ok(Builder::new(Target::Address(addr))) } /// Create a builder for a connection that will use the given unix stream. @@ -533,17 +537,9 @@ impl<'a> Builder<'a> { #[cfg(feature = "tokio-vsock")] Target::VsockStream(stream) => stream.into(), Target::Address(address) => { - guid = address.guid().map(|g| g.to_owned().into()); - match address.connect().await? { - #[cfg(any(unix, not(feature = "tokio")))] - legacy_address::transport::Stream::Unix(stream) => stream.into(), - legacy_address::transport::Stream::Tcp(stream) => stream.into(), - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - legacy_address::transport::Stream::Vsock(stream) => stream.into(), - } + return connect_address(&address) + .await + .map(|(split, guid)| (split, guid, false)); } Target::Socket(stream) => stream, Target::AuthenticatedSocket(stream) => { diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs new file mode 100644 index 000000000..37c893424 --- /dev/null +++ b/zbus/src/connection/connect.rs @@ -0,0 +1,47 @@ +use tracing::debug; + +use crate::{address::DBusAddr, legacy_address, Error, Guid, OwnedGuid, Result}; + +use super::socket::BoxedSplit; + +async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> { + let guid = match addr.guid() { + Some(g) => Some(Guid::try_from(g.as_ref())?.into()), + _ => None, + }; + let split = match addr.transport()? { + _ => { + // safety: unwrap() for code transition => addr is valid already + let legacy: crate::Address = addr.to_string().parse().unwrap(); + match legacy.connect().await { + #[cfg(any(unix, not(feature = "tokio")))] + Ok(legacy_address::transport::Stream::Unix(stream)) => stream.into(), + Ok(legacy_address::transport::Stream::Tcp(stream)) => stream.into(), + #[cfg(any( + all(feature = "vsock", not(feature = "tokio")), + feature = "tokio-vsock" + ))] + Ok(legacy_address::transport::Stream::Vsock(stream)) => stream.into(), + _ => return Err(Error::Address("unhandled address".into())), + } + } + }; + Ok((split, guid)) +} + +pub(crate) async fn connect_address( + address: &[DBusAddr<'_>], +) -> Result<(BoxedSplit, Option)> { + for addr in address { + match connect(addr).await { + Ok(res) => { + return Ok(res); + } + Err(e) => { + debug!("Failed to connect to: {}", e); + continue; + } + } + } + Err(Error::Address("No connectable address".into())) +} diff --git a/zbus/src/connection/mod.rs b/zbus/src/connection/mod.rs index e48083142..9a8f0b615 100644 --- a/zbus/src/connection/mod.rs +++ b/zbus/src/connection/mod.rs @@ -43,6 +43,8 @@ use socket_reader::SocketReader; pub(crate) mod handshake; use handshake::Authenticated; +mod connect; + const DEFAULT_MAX_QUEUED: usize = 64; const DEFAULT_MAX_METHOD_RETURN_QUEUED: usize = 8; From 175cb5b7f88350bdc972668cd43d6b1fe8a766a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 14:39:19 +0400 Subject: [PATCH 05/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20handle=20TC?= =?UTF-8?q?P=20transport=20connection=20(without=20legacy=5Faddress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compared to the existing implementation, this will take the family into account (except with tokio), as well as multiple host resolution. --- zbus/src/connection/connect.rs | 8 +++- zbus/src/connection/socket/mod.rs | 2 +- zbus/src/connection/socket/tcp.rs | 66 +++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index 37c893424..16ae421d6 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -1,8 +1,11 @@ use tracing::debug; -use crate::{address::DBusAddr, legacy_address, Error, Guid, OwnedGuid, Result}; +use crate::{ + address::{transport::Transport, DBusAddr}, + legacy_address, Error, Guid, OwnedGuid, Result, +}; -use super::socket::BoxedSplit; +use super::socket::{self, BoxedSplit}; async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> { let guid = match addr.guid() { @@ -10,6 +13,7 @@ async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> _ => None, }; let split = match addr.transport()? { + Transport::Tcp(t) => socket::tcp::connect(&t).await?.into(), _ => { // safety: unwrap() for code transition => addr is valid already let legacy: crate::Address = addr.to_string().parse().unwrap(); diff --git a/zbus/src/connection/socket/mod.rs b/zbus/src/connection/socket/mod.rs index d0a17eac6..ece7a5531 100644 --- a/zbus/src/connection/socket/mod.rs +++ b/zbus/src/connection/socket/mod.rs @@ -6,7 +6,7 @@ pub use channel::Channel; mod split; pub use split::{BoxedSplit, Split}; -mod tcp; +pub(crate) mod tcp; mod unix; mod vsock; diff --git a/zbus/src/connection/socket/tcp.rs b/zbus/src/connection/socket/tcp.rs index 144bd2eae..f4c38c296 100644 --- a/zbus/src/connection/socket/tcp.rs +++ b/zbus/src/connection/socket/tcp.rs @@ -6,6 +6,8 @@ use std::os::fd::BorrowedFd; #[cfg(not(feature = "tokio"))] use std::{net::TcpStream, sync::Arc}; +use crate::{address::transport::TcpFamily, Error, Result}; + use super::{ReadHalf, RecvmsgResult, WriteHalf}; #[cfg(feature = "tokio")] use super::{Socket, Split}; @@ -170,3 +172,67 @@ fn win32_credentials_from_addr( .set_process_id(pid) .set_windows_sid(sid)) } + +#[cfg(not(feature = "tokio"))] +type Stream = Async; +#[cfg(feature = "tokio")] +type Stream = tokio::net::TcpStream; + +async fn connect_with(host: &str, port: u16, family: Option) -> Result { + #[cfg(not(feature = "tokio"))] + { + use std::net::ToSocketAddrs; + + let host = host.to_string(); + let addrs = crate::Task::spawn_blocking( + move || -> Result> { + let addrs = (host, port).to_socket_addrs()?.filter(|a| { + if let Some(family) = family { + if family == TcpFamily::IPv4 { + a.is_ipv4() + } else { + a.is_ipv6() + } + } else { + true + } + }); + Ok(addrs.collect()) + }, + "connect tcp", + ) + .await + .map_err(|e| Error::Address(format!("Failed to receive TCP addresses: {e}")))?; + + // we could attempt connections in parallel? + let mut last_err = Error::Address("Failed to connect".into()); + for addr in addrs { + match Stream::connect(addr).await { + Ok(stream) => return Ok(stream), + Err(e) => last_err = e.into(), + } + } + + Err(last_err) + } + + #[cfg(feature = "tokio")] + { + // FIXME: doesn't handle family + let _ = family; + Stream::connect((host, port)) + .await + .map_err(|e| Error::InputOutput(e.into())) + } +} + +pub(crate) async fn connect(addr: &crate::address::transport::Tcp<'_>) -> Result { + let Some(host) = addr.host() else { + return Err(Error::Address("No host in address".into())); + }; + let Some(port) = addr.port() else { + return Err(Error::Address("No port in address".into())); + }; + + connect_with(host, port, addr.family()).await +} From b31033ddb4e1e97e230f73dbba85c0c470ceb024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 14:39:19 +0400 Subject: [PATCH 06/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20move=20TCP?= =?UTF-8?q?=20connection=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/socket/tcp.rs | 19 +++++++++++++++++++ zbus/src/legacy_address/mod.rs | 8 -------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/zbus/src/connection/socket/tcp.rs b/zbus/src/connection/socket/tcp.rs index f4c38c296..f53812f8c 100644 --- a/zbus/src/connection/socket/tcp.rs +++ b/zbus/src/connection/socket/tcp.rs @@ -236,3 +236,22 @@ pub(crate) async fn connect(addr: &crate::address::transport::Tcp<'_>) -> Result connect_with(host, port, addr.family()).await } + +#[cfg(test)] +mod tests { + use crate::address::{transport::Transport, DBusAddr}; + + #[test] + fn connect() { + let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + let addr: DBusAddr<'_> = format!("tcp:host=localhost,port={port}") + .try_into() + .unwrap(); + let tcp = match addr.transport().unwrap() { + Transport::Tcp(tcp) => tcp, + _ => unreachable!(), + }; + crate::utils::block_on(super::connect(&tcp)).unwrap(); + } +} diff --git a/zbus/src/legacy_address/mod.rs b/zbus/src/legacy_address/mod.rs index e0627e820..555404b65 100644 --- a/zbus/src/legacy_address/mod.rs +++ b/zbus/src/legacy_address/mod.rs @@ -416,14 +416,6 @@ mod tests { } } - #[test] - fn connect_tcp() { - let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - let addr = Address::from_str(&format!("tcp:host=localhost,port={port}")).unwrap(); - crate::utils::block_on(async { addr.connect().await }).unwrap(); - } - #[test] fn connect_nonce_tcp() { struct PercentEncoded<'a>(&'a [u8]); From beb6d81c6e973c8c3055eb4dafc516e7fd7b881b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 15:31:16 +0400 Subject: [PATCH 07/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20handle=20No?= =?UTF-8?q?nceTCP=20transport=20(without=20legacy=5Faddress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect.rs | 1 + zbus/src/connection/socket/tcp.rs | 38 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index 16ae421d6..422080386 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -14,6 +14,7 @@ async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> }; let split = match addr.transport()? { Transport::Tcp(t) => socket::tcp::connect(&t).await?.into(), + Transport::NonceTcp(t) => socket::tcp::connect_nonce(&t).await?.into(), _ => { // safety: unwrap() for code transition => addr is valid already let legacy: crate::Address = addr.to_string().parse().unwrap(); diff --git a/zbus/src/connection/socket/tcp.rs b/zbus/src/connection/socket/tcp.rs index f53812f8c..ede7ab6ef 100644 --- a/zbus/src/connection/socket/tcp.rs +++ b/zbus/src/connection/socket/tcp.rs @@ -237,6 +237,44 @@ pub(crate) async fn connect(addr: &crate::address::transport::Tcp<'_>) -> Result connect_with(host, port, addr.family()).await } +pub(crate) async fn connect_nonce( + addr: &crate::address::transport::NonceTcp<'_>, +) -> Result { + let Some(host) = addr.host() else { + return Err(Error::Address("No host in address".into())); + }; + let Some(port) = addr.port() else { + return Err(Error::Address("No port in address".into())); + }; + let Some(noncefile) = addr.noncefile() else { + return Err(Error::Address("No noncefile in address".into())); + }; + + #[allow(unused_mut)] + let mut stream = connect_with(host, port, addr.family()).await?; + + #[cfg(not(feature = "tokio"))] + { + use std::io::prelude::*; + + let nonce = std::fs::read(noncefile)?; + let mut nonce = &nonce[..]; + + while !nonce.is_empty() { + let len = stream.write_with(|mut s| s.write(nonce)).await?; + nonce = &nonce[len..]; + } + } + + #[cfg(feature = "tokio")] + { + let nonce = tokio::fs::read(noncefile).await?; + tokio::io::AsyncWriteExt::write_all(&mut stream, &nonce).await?; + } + + Ok(stream) +} + #[cfg(test)] mod tests { use crate::address::{transport::Transport, DBusAddr}; From bb85a98944cf4b7c061c58dabddc0347894b0367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 15:31:16 +0400 Subject: [PATCH 08/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20move=20Nonc?= =?UTF-8?q?eTCP=20connection=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/socket/tcp.rs | 59 +++++++++++++++++++++++++++++++ zbus/src/legacy_address/mod.rs | 55 ---------------------------- 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/zbus/src/connection/socket/tcp.rs b/zbus/src/connection/socket/tcp.rs index ede7ab6ef..05ccebb90 100644 --- a/zbus/src/connection/socket/tcp.rs +++ b/zbus/src/connection/socket/tcp.rs @@ -292,4 +292,63 @@ mod tests { }; crate::utils::block_on(super::connect(&tcp)).unwrap(); } + + #[test] + fn connect_nonce_tcp() { + struct PercentEncoded<'a>(&'a [u8]); + + impl std::fmt::Display for PercentEncoded<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + crate::address::encode_percents(f, self.0) + } + } + + use std::io::Write; + + const TEST_COOKIE: &[u8] = b"VERILY SECRETIVE"; + + let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let mut cookie = tempfile::NamedTempFile::new().unwrap(); + cookie.as_file_mut().write_all(TEST_COOKIE).unwrap(); + + let encoded_path = format!( + "{}", + PercentEncoded(cookie.path().to_str().unwrap().as_ref()) + ); + + let addr: DBusAddr<'_> = + format!("nonce-tcp:host=localhost,port={port},noncefile={encoded_path}") + .try_into() + .unwrap(); + let tcp = match addr.transport().unwrap() { + Transport::NonceTcp(tcp) => tcp, + _ => unreachable!(), + }; + + let (sender, receiver) = std::sync::mpsc::sync_channel(1); + + std::thread::spawn(move || { + use std::io::Read; + + let mut client = listener.incoming().next().unwrap().unwrap(); + + let mut buf = [0u8; 16]; + client.read_exact(&mut buf).unwrap(); + + sender.send(buf == TEST_COOKIE).unwrap(); + }); + + crate::utils::block_on(super::connect_nonce(&tcp)).unwrap(); + + let saw_cookie = receiver + .recv_timeout(std::time::Duration::from_millis(100)) + .expect("nonce file content hasn't been received by server thread in time"); + + assert!( + saw_cookie, + "nonce file content has been received, but was invalid" + ); + } } diff --git a/zbus/src/legacy_address/mod.rs b/zbus/src/legacy_address/mod.rs index 555404b65..46119aefd 100644 --- a/zbus/src/legacy_address/mod.rs +++ b/zbus/src/legacy_address/mod.rs @@ -415,59 +415,4 @@ mod tests { ); } } - - #[test] - fn connect_nonce_tcp() { - struct PercentEncoded<'a>(&'a [u8]); - - impl std::fmt::Display for PercentEncoded<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - super::transport::encode_percents(f, self.0) - } - } - - use std::io::Write; - - const TEST_COOKIE: &[u8] = b"VERILY SECRETIVE"; - - let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); - let port = listener.local_addr().unwrap().port(); - - let mut cookie = tempfile::NamedTempFile::new().unwrap(); - cookie.as_file_mut().write_all(TEST_COOKIE).unwrap(); - - let encoded_path = format!( - "{}", - PercentEncoded(cookie.path().to_str().unwrap().as_ref()) - ); - - let addr = Address::from_str(&format!( - "nonce-tcp:host=localhost,port={port},noncefile={encoded_path}" - )) - .unwrap(); - - let (sender, receiver) = std::sync::mpsc::sync_channel(1); - - std::thread::spawn(move || { - use std::io::Read; - - let mut client = listener.incoming().next().unwrap().unwrap(); - - let mut buf = [0u8; 16]; - client.read_exact(&mut buf).unwrap(); - - sender.send(buf == TEST_COOKIE).unwrap(); - }); - - crate::utils::block_on(addr.connect()).unwrap(); - - let saw_cookie = receiver - .recv_timeout(std::time::Duration::from_millis(100)) - .expect("nonce file content hasn't been received by server thread in time"); - - assert!( - saw_cookie, - "nonce file content has been received, but was invalid" - ); - } } From fbed5014176ca3c256395708ab80eff700d4b04e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 15:31:16 +0400 Subject: [PATCH 09/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20handle=20Un?= =?UTF-8?q?ix=20transport=20(without=20legacy=5Faddress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect.rs | 2 + zbus/src/connection/socket/mod.rs | 2 +- zbus/src/connection/socket/unix.rs | 65 +++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index 422080386..3a4ad5bc2 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -15,6 +15,8 @@ async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> let split = match addr.transport()? { Transport::Tcp(t) => socket::tcp::connect(&t).await?.into(), Transport::NonceTcp(t) => socket::tcp::connect_nonce(&t).await?.into(), + #[cfg(any(unix, not(feature = "tokio")))] + Transport::Unix(u) => socket::unix::connect(&u).await?.into(), _ => { // safety: unwrap() for code transition => addr is valid already let legacy: crate::Address = addr.to_string().parse().unwrap(); diff --git a/zbus/src/connection/socket/mod.rs b/zbus/src/connection/socket/mod.rs index ece7a5531..0077c3124 100644 --- a/zbus/src/connection/socket/mod.rs +++ b/zbus/src/connection/socket/mod.rs @@ -7,7 +7,7 @@ mod split; pub use split::{BoxedSplit, Split}; pub(crate) mod tcp; -mod unix; +pub(crate) mod unix; mod vsock; #[cfg(not(feature = "tokio"))] diff --git a/zbus/src/connection/socket/unix.rs b/zbus/src/connection/socket/unix.rs index f93329767..c65b1df0a 100644 --- a/zbus/src/connection/socket/unix.rs +++ b/zbus/src/connection/socket/unix.rs @@ -2,7 +2,7 @@ use async_io::Async; #[cfg(unix)] use std::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, RawFd}; -#[cfg(all(unix, not(feature = "tokio")))] +#[cfg(unix)] use std::os::unix::net::UnixStream; #[cfg(not(feature = "tokio"))] use std::sync::Arc; @@ -24,6 +24,8 @@ use nix::{ #[cfg(unix)] use crate::utils::FDS_MAX; +#[cfg(any(unix, not(feature = "tokio")))] +use crate::{Error, Result}; #[cfg(all(unix, not(feature = "tokio")))] #[async_trait::async_trait] @@ -378,3 +380,64 @@ fn send_zero_byte_blocking(fd: RawFd) -> io::Result { ) .map_err(|e| e.into()) } + +#[cfg(not(feature = "tokio"))] +type Stream = Async; +#[cfg(all(unix, feature = "tokio"))] +type Stream = tokio::net::UnixStream; + +#[cfg(any(unix, not(feature = "tokio")))] +pub(crate) async fn connect(addr: &crate::address::transport::Unix<'_>) -> Result { + use crate::address::transport::UnixAddrKind; + #[cfg(target_os = "linux")] + use std::os::linux::net::SocketAddrExt; + #[cfg(unix)] + use std::os::unix::net::SocketAddr; + + let kind = addr.kind(); + + // This is a `path` in case of Windows until uds_windows provides the needed API: + // https://github.com/haraldh/rust_uds_windows/issues/14 + let addr = match kind { + #[cfg(unix)] + UnixAddrKind::Path(p) => SocketAddr::from_pathname(std::path::Path::new(p))?, + #[cfg(windows)] + UnixAddrKind::Path(p) => p.clone().into_owned(), + #[cfg(target_os = "linux")] + UnixAddrKind::Abstract(name) => SocketAddr::from_abstract_name(name)?, + _ => return Err(Error::Address("Address is not connectable".into())), + }; + + let stream = crate::Task::spawn_blocking( + move || -> Result<_> { + #[cfg(unix)] + let stream = UnixStream::connect_addr(&addr)?; + #[cfg(windows)] + let stream = UnixStream::connect(addr)?; + stream.set_nonblocking(true)?; + + Ok(stream) + }, + "unix stream connection", + ) + .await?; + + #[cfg(not(feature = "tokio"))] + { + Async::new(stream).map_err(|e| Error::InputOutput(e.into())) + } + + #[cfg(feature = "tokio")] + { + #[cfg(unix)] + { + tokio::net::UnixStream::from_std(stream).map_err(|e| Error::InputOutput(e.into())) + } + + #[cfg(not(unix))] + { + let _ = stream; + Err(Error::Unsupported) + } + } +} From 1a93abd069a6016348fad07c0c850ab3a8a6bdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 15:31:16 +0400 Subject: [PATCH 10/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20handle=20Vs?= =?UTF-8?q?ock=20transport=20connection=20(without=20legacy=5Faddress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect.rs | 5 ++++ zbus/src/connection/socket/mod.rs | 2 +- zbus/src/connection/socket/vsock.rs | 44 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index 3a4ad5bc2..1e4091af6 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -17,6 +17,11 @@ async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> Transport::NonceTcp(t) => socket::tcp::connect_nonce(&t).await?.into(), #[cfg(any(unix, not(feature = "tokio")))] Transport::Unix(u) => socket::unix::connect(&u).await?.into(), + #[cfg(any( + all(feature = "vsock", not(feature = "tokio")), + feature = "tokio-vsock" + ))] + Transport::Vsock(v) => socket::vsock::connect(&v).await?.into(), _ => { // safety: unwrap() for code transition => addr is valid already let legacy: crate::Address = addr.to_string().parse().unwrap(); diff --git a/zbus/src/connection/socket/mod.rs b/zbus/src/connection/socket/mod.rs index 0077c3124..9d9062c0a 100644 --- a/zbus/src/connection/socket/mod.rs +++ b/zbus/src/connection/socket/mod.rs @@ -8,7 +8,7 @@ pub use split::{BoxedSplit, Split}; pub(crate) mod tcp; pub(crate) mod unix; -mod vsock; +pub(crate) mod vsock; #[cfg(not(feature = "tokio"))] use async_io::Async; diff --git a/zbus/src/connection/socket/vsock.rs b/zbus/src/connection/socket/vsock.rs index ec26e487b..4889167c2 100644 --- a/zbus/src/connection/socket/vsock.rs +++ b/zbus/src/connection/socket/vsock.rs @@ -1,3 +1,13 @@ +#[cfg(all(feature = "vsock", not(feature = "tokio")))] +#[cfg(not(feature = "tokio"))] +use async_io::Async; + +#[cfg(any( + all(feature = "vsock", not(feature = "tokio")), + feature = "tokio-vsock" +))] +use crate::{Error, Result}; + #[cfg(feature = "tokio-vsock")] use super::{Socket, Split}; @@ -104,3 +114,37 @@ impl super::WriteHalf for tokio_vsock::WriteHalf { tokio::io::AsyncWriteExt::shutdown(self).await } } + +#[cfg(all(feature = "vsock", not(feature = "tokio")))] +type Stream = Async; +#[cfg(feature = "tokio-vsock")] +type Stream = tokio_vsock::VsockStream; + +#[cfg(any( + all(feature = "vsock", not(feature = "tokio")), + feature = "tokio-vsock" +))] +pub(crate) async fn connect(addr: &crate::address::transport::Vsock<'_>) -> Result { + let Some(cid) = addr.cid() else { + return Err(Error::Address("No cid in address".into())); + }; + let Some(port) = addr.port() else { + return Err(Error::Address("No port in address".into())); + }; + + #[cfg(all(feature = "vsock", not(feature = "tokio")))] + { + let stream = crate::Task::spawn_blocking( + move || vsock::VsockStream::connect_with_cid_port(cid, port), + "connect vsock", + ) + .await + .map_err(|e| Error::Address(format!("Failed to connect: {e}")))?; + Ok(Async::new(stream).map_err(|e| Error::InputOutput(e.into()))?) + } + + #[cfg(feature = "tokio-vsock")] + Stream::connect(cid, port) + .await + .map_err(|e| Error::InputOutput(e.into())) +} From 36020eb0a0b8ce50801c885035c9e45ceda9399b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 1 Feb 2024 10:55:11 +0400 Subject: [PATCH 11/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20handle=20La?= =?UTF-8?q?unchd=20transport=20connection=20(without=20legacy=5Faddress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect.rs | 4 +++ zbus/src/connection/connect/macos.rs | 37 ++++++++++++++++++++++++++++ zbus/src/connection/socket/unix.rs | 4 +-- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 zbus/src/connection/connect/macos.rs diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index 1e4091af6..2780d1d13 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -7,6 +7,8 @@ use crate::{ use super::socket::{self, BoxedSplit}; +mod macos; + async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> { let guid = match addr.guid() { Some(g) => Some(Guid::try_from(g.as_ref())?.into()), @@ -22,6 +24,8 @@ async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> feature = "tokio-vsock" ))] Transport::Vsock(v) => socket::vsock::connect(&v).await?.into(), + #[cfg(target_os = "macos")] + Transport::Launchd(l) => macos::connect(&l).await?.into(), _ => { // safety: unwrap() for code transition => addr is valid already let legacy: crate::Address = addr.to_string().parse().unwrap(); diff --git a/zbus/src/connection/connect/macos.rs b/zbus/src/connection/connect/macos.rs new file mode 100644 index 000000000..d2c411eb4 --- /dev/null +++ b/zbus/src/connection/connect/macos.rs @@ -0,0 +1,37 @@ +#![cfg(target_os = "macos")] + +use super::socket; +use crate::{ + address::{transport::Transport, DBusAddr}, + process::run, + Error, Result, +}; + +async fn launchd_bus_address(env_key: &str) -> Result> { + let output = run("launchctl", ["getenv", env_key]) + .await + .expect("failed to wait on launchctl output"); + + if !output.status.success() { + return Err(Error::Address(format!( + "launchctl terminated with code: {}", + output.status + ))); + } + + let addr = String::from_utf8(output.stdout) + .map_err(|e| Error::Address(format!("Unable to parse launchctl output as UTF-8: {}", e)))?; + + Ok(format!("unix:path={}", addr.trim()).try_into()?) +} + +pub(crate) async fn connect( + l: &crate::address::transport::Launchd<'_>, +) -> Result { + let addr = launchd_bus_address(l.env()).await?; + + match addr.transport()? { + Transport::Unix(t) => socket::unix::connect(&t).await, + _ => Err(Error::Address(format!("Address is unsupported: {}", addr))), + } +} diff --git a/zbus/src/connection/socket/unix.rs b/zbus/src/connection/socket/unix.rs index c65b1df0a..72edff430 100644 --- a/zbus/src/connection/socket/unix.rs +++ b/zbus/src/connection/socket/unix.rs @@ -382,9 +382,9 @@ fn send_zero_byte_blocking(fd: RawFd) -> io::Result { } #[cfg(not(feature = "tokio"))] -type Stream = Async; +pub(crate) type Stream = Async; #[cfg(all(unix, feature = "tokio"))] -type Stream = tokio::net::UnixStream; +pub(crate) type Stream = tokio::net::UnixStream; #[cfg(any(unix, not(feature = "tokio")))] pub(crate) async fn connect(addr: &crate::address::transport::Unix<'_>) -> Result { From c62289da86883394259ffd8a068a5294ccc663b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 15:31:16 +0400 Subject: [PATCH 12/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20move=20Laun?= =?UTF-8?q?chd=20connection=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect/macos.rs | 17 +++++++++++++++++ zbus/src/connection/mod.rs | 13 ------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/zbus/src/connection/connect/macos.rs b/zbus/src/connection/connect/macos.rs index d2c411eb4..4125285bb 100644 --- a/zbus/src/connection/connect/macos.rs +++ b/zbus/src/connection/connect/macos.rs @@ -35,3 +35,20 @@ pub(crate) async fn connect( _ => Err(Error::Address(format!("Address is unsupported: {}", addr))), } } + +#[cfg(test)] +mod tests { + use crate::address::{transport::Transport, DBusAddr}; + + #[test] + fn connect_launchd_session_bus() { + let addr: DBusAddr<'_> = "launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET" + .try_into() + .unwrap(); + let launchd = match addr.transport().unwrap() { + Transport::Launchd(l) => l, + _ => unreachable!(), + }; + crate::utils::block_on(super::connect(&launchd)).unwrap(); + } +} diff --git a/zbus/src/connection/mod.rs b/zbus/src/connection/mod.rs index 9a8f0b615..cf016a2b6 100644 --- a/zbus/src/connection/mod.rs +++ b/zbus/src/connection/mod.rs @@ -1385,19 +1385,6 @@ mod tests { crate::block_on(async { addr.connect().await }).expect("Unable to connect to session bus"); } - #[cfg(target_os = "macos")] - #[test] - fn connect_launchd_session_bus() { - use crate::legacy_address::{transport::Launchd, Address, Transport}; - crate::block_on(async { - let addr = Address::from(Transport::Launchd(Launchd::new( - "DBUS_LAUNCHD_SESSION_BUS_SOCKET", - ))); - addr.connect().await - }) - .expect("Unable to connect to session bus"); - } - #[test] #[timeout(15000)] fn disconnect_on_drop() { From bcaad6e7370870f1b06998da62e0d896c88c61c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 1 Feb 2024 12:12:00 +0400 Subject: [PATCH 13/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20make=20wind?= =?UTF-8?q?ows=5Fautolaunch=5Fbus=5Faddress()=20return=20String?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parsing can be done with legacy Address and DBusAddr --- zbus/src/legacy_address/transport/mod.rs | 2 +- zbus/src/win32.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/zbus/src/legacy_address/transport/mod.rs b/zbus/src/legacy_address/transport/mod.rs index 47fd200d0..437c17791 100644 --- a/zbus/src/legacy_address/transport/mod.rs +++ b/zbus/src/legacy_address/transport/mod.rs @@ -194,7 +194,7 @@ impl Transport { "Autolaunch scopes are currently unsupported".to_owned(), )), None => { - let addr = autolaunch_bus_address()?; + let addr: crate::Address = autolaunch_bus_address()?.parse()?; addr.connect().await } }, diff --git a/zbus/src/win32.rs b/zbus/src/win32.rs index 664fe1071..2d4d47653 100644 --- a/zbus/src/win32.rs +++ b/zbus/src/win32.rs @@ -30,7 +30,6 @@ use windows_sys::Win32::{ }, }; -use crate::Address; #[cfg(not(feature = "tokio"))] use uds_windows::UnixStream; @@ -305,7 +304,7 @@ fn read_shm(name: &str) -> Result, crate::Error> { Ok(data.to_bytes().to_owned()) } -pub fn autolaunch_bus_address() -> Result { +pub fn autolaunch_bus_address() -> Result { let mutex = Mutex::new("DBusAutolaunchMutex")?; let _guard = mutex.lock(); @@ -313,7 +312,7 @@ pub fn autolaunch_bus_address() -> Result { let addr = String::from_utf8(addr) .map_err(|e| crate::Error::Address(format!("Unable to parse address as UTF-8: {}", e)))?; - addr.parse() + Ok(addr) } #[cfg(test)] From 4d2cdfd6b7f6fd7941a4b0a44627c2333c8a4d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 1 Feb 2024 12:15:27 +0400 Subject: [PATCH 14/21] =?UTF-8?q?=E2=9C=A8=20zb:=20make=20connect()=20recu?= =?UTF-8?q?rsively=20callable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Connecting to an address can recursively resolve to a different address to connect to. Because it's an async function, box it. --- zbus/src/connection/connect.rs | 72 ++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index 2780d1d13..fbf034f64 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -1,3 +1,4 @@ +use std::{future::Future, pin::Pin}; use tracing::debug; use crate::{ @@ -9,40 +10,45 @@ use super::socket::{self, BoxedSplit}; mod macos; -async fn connect(addr: &DBusAddr<'_>) -> Result<(BoxedSplit, Option)> { - let guid = match addr.guid() { - Some(g) => Some(Guid::try_from(g.as_ref())?.into()), - _ => None, - }; - let split = match addr.transport()? { - Transport::Tcp(t) => socket::tcp::connect(&t).await?.into(), - Transport::NonceTcp(t) => socket::tcp::connect_nonce(&t).await?.into(), - #[cfg(any(unix, not(feature = "tokio")))] - Transport::Unix(u) => socket::unix::connect(&u).await?.into(), - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - Transport::Vsock(v) => socket::vsock::connect(&v).await?.into(), - #[cfg(target_os = "macos")] - Transport::Launchd(l) => macos::connect(&l).await?.into(), - _ => { - // safety: unwrap() for code transition => addr is valid already - let legacy: crate::Address = addr.to_string().parse().unwrap(); - match legacy.connect().await { - #[cfg(any(unix, not(feature = "tokio")))] - Ok(legacy_address::transport::Stream::Unix(stream)) => stream.into(), - Ok(legacy_address::transport::Stream::Tcp(stream)) => stream.into(), - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - Ok(legacy_address::transport::Stream::Vsock(stream)) => stream.into(), - _ => return Err(Error::Address("unhandled address".into())), +type ConnectResult = Result<(BoxedSplit, Option)>; + +fn connect(addr: &DBusAddr<'_>) -> Pin>> { + let addr = addr.to_owned(); + Box::pin(async move { + let guid = match addr.guid() { + Some(g) => Some(Guid::try_from(g.as_ref())?.into()), + _ => None, + }; + let split = match addr.transport()? { + Transport::Tcp(t) => socket::tcp::connect(&t).await?.into(), + Transport::NonceTcp(t) => socket::tcp::connect_nonce(&t).await?.into(), + #[cfg(any(unix, not(feature = "tokio")))] + Transport::Unix(u) => socket::unix::connect(&u).await?.into(), + #[cfg(any( + all(feature = "vsock", not(feature = "tokio")), + feature = "tokio-vsock" + ))] + Transport::Vsock(v) => socket::vsock::connect(&v).await?.into(), + #[cfg(target_os = "macos")] + Transport::Launchd(l) => macos::connect(&l).await?.into(), + _ => { + // safety: unwrap() for code transition => addr is valid already + let legacy: crate::Address = addr.to_string().parse().unwrap(); + match legacy.connect().await { + #[cfg(any(unix, not(feature = "tokio")))] + Ok(legacy_address::transport::Stream::Unix(stream)) => stream.into(), + Ok(legacy_address::transport::Stream::Tcp(stream)) => stream.into(), + #[cfg(any( + all(feature = "vsock", not(feature = "tokio")), + feature = "tokio-vsock" + ))] + Ok(legacy_address::transport::Stream::Vsock(stream)) => stream.into(), + _ => return Err(Error::Address("unhandled address".into())), + } } - } - }; - Ok((split, guid)) + }; + Ok((split, guid)) + }) } pub(crate) async fn connect_address( From a9dbca6fa365b67c9e9ede1873abb14f4bafec48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 1 Feb 2024 12:17:30 +0400 Subject: [PATCH 15/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20handle=20wi?= =?UTF-8?q?n32=20Autolaunch=20connection=20(without=20legacy=5Faddress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect.rs | 5 +++++ zbus/src/connection/connect/win32.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 zbus/src/connection/connect/win32.rs diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index fbf034f64..b3096eb9f 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -9,6 +9,7 @@ use crate::{ use super::socket::{self, BoxedSplit}; mod macos; +mod win32; type ConnectResult = Result<(BoxedSplit, Option)>; @@ -31,6 +32,10 @@ fn connect(addr: &DBusAddr<'_>) -> Pin>> Transport::Vsock(v) => socket::vsock::connect(&v).await?.into(), #[cfg(target_os = "macos")] Transport::Launchd(l) => macos::connect(&l).await?.into(), + #[cfg(target_os = "windows")] + Transport::Autolaunch(l) => { + return win32::connect(&l).await; + } _ => { // safety: unwrap() for code transition => addr is valid already let legacy: crate::Address = addr.to_string().parse().unwrap(); diff --git a/zbus/src/connection/connect/win32.rs b/zbus/src/connection/connect/win32.rs new file mode 100644 index 000000000..49ea990fb --- /dev/null +++ b/zbus/src/connection/connect/win32.rs @@ -0,0 +1,26 @@ +#![cfg(target_os = "windows")] + +use super::BoxedSplit; +use crate::{ + address::{transport::Transport, DBusAddr}, + win32::autolaunch_bus_address, + Error, OwnedGuid, Result, +}; + +pub(crate) async fn connect( + l: &crate::address::transport::Autolaunch<'_>, +) -> Result<(BoxedSplit, Option)> { + if l.scope().is_some() { + return Err(Error::Address( + "autolaunch with scope isn't supported yet".into(), + )); + } + + let addr: DBusAddr<'_> = autolaunch_bus_address()?.try_into()?; + + if let Transport::Autolaunch(_) = addr.transport()? { + return Err(Error::Address("Recursive autolaunch: address".into())); + } + + super::connect(&addr).await +} From 00b2c35ce53899cf1e2d383956ab1e372e594c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 15:31:16 +0400 Subject: [PATCH 16/21] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20zb:=20move=20win3?= =?UTF-8?q?2=20Autolaunch=20connection=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect/win32.rs | 15 +++++++++++++++ zbus/src/connection/mod.rs | 9 --------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/zbus/src/connection/connect/win32.rs b/zbus/src/connection/connect/win32.rs index 49ea990fb..8da717002 100644 --- a/zbus/src/connection/connect/win32.rs +++ b/zbus/src/connection/connect/win32.rs @@ -24,3 +24,18 @@ pub(crate) async fn connect( super::connect(&addr).await } + +#[cfg(test)] +mod tests { + #[test] + fn connect_autolaunch_session_bus() { + use crate::address::{transport::Transport, DBusAddr}; + + let addr: DBusAddr<'_> = "autolaunch:".try_into().unwrap(); + let autolaunch = match addr.transport().unwrap() { + Transport::Autolaunch(l) => l, + _ => unreachable!(), + }; + crate::utils::block_on(super::connect(&autolaunch)).unwrap(); + } +} diff --git a/zbus/src/connection/mod.rs b/zbus/src/connection/mod.rs index cf016a2b6..69fff600c 100644 --- a/zbus/src/connection/mod.rs +++ b/zbus/src/connection/mod.rs @@ -1376,15 +1376,6 @@ mod tests { use std::{pin::pin, time::Duration}; use test_log::test; - #[cfg(windows)] - #[test] - fn connect_autolaunch_session_bus() { - let addr = - crate::win32::autolaunch_bus_address().expect("Unable to get session bus address"); - - crate::block_on(async { addr.connect().await }).expect("Unable to connect to session bus"); - } - #[test] #[timeout(15000)] fn disconnect_on_drop() { From c2c6d42bca86f2216975b2363a0b535f41b2f736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 31 Jan 2024 18:05:40 +0400 Subject: [PATCH 17/21] =?UTF-8?q?=F0=9F=94=A5=20zb:=20drop=20legacy=5Faddr?= =?UTF-8?q?ess=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/connection/connect.rs | 16 +- zbus/src/legacy_address/mod.rs | 418 ------------------ .../legacy_address/transport/autolaunch.rs | 83 ---- zbus/src/legacy_address/transport/launchd.rs | 60 --- zbus/src/legacy_address/transport/mod.rs | 350 --------------- zbus/src/legacy_address/transport/tcp.rs | 229 ---------- zbus/src/legacy_address/transport/unix.rs | 129 ------ zbus/src/legacy_address/transport/vsock.rs | 49 -- zbus/src/lib.rs | 3 - 9 files changed, 2 insertions(+), 1335 deletions(-) delete mode 100644 zbus/src/legacy_address/mod.rs delete mode 100644 zbus/src/legacy_address/transport/autolaunch.rs delete mode 100644 zbus/src/legacy_address/transport/launchd.rs delete mode 100644 zbus/src/legacy_address/transport/mod.rs delete mode 100644 zbus/src/legacy_address/transport/tcp.rs delete mode 100644 zbus/src/legacy_address/transport/unix.rs delete mode 100644 zbus/src/legacy_address/transport/vsock.rs diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index b3096eb9f..adf17fb43 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -3,7 +3,7 @@ use tracing::debug; use crate::{ address::{transport::Transport, DBusAddr}, - legacy_address, Error, Guid, OwnedGuid, Result, + Error, Guid, OwnedGuid, Result, }; use super::socket::{self, BoxedSplit}; @@ -37,19 +37,7 @@ fn connect(addr: &DBusAddr<'_>) -> Pin>> return win32::connect(&l).await; } _ => { - // safety: unwrap() for code transition => addr is valid already - let legacy: crate::Address = addr.to_string().parse().unwrap(); - match legacy.connect().await { - #[cfg(any(unix, not(feature = "tokio")))] - Ok(legacy_address::transport::Stream::Unix(stream)) => stream.into(), - Ok(legacy_address::transport::Stream::Tcp(stream)) => stream.into(), - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - Ok(legacy_address::transport::Stream::Vsock(stream)) => stream.into(), - _ => return Err(Error::Address("unhandled address".into())), - } + return Err(Error::Address(format!("Unhandled address: {}", addr))); } }; Ok((split, guid)) diff --git a/zbus/src/legacy_address/mod.rs b/zbus/src/legacy_address/mod.rs deleted file mode 100644 index 46119aefd..000000000 --- a/zbus/src/legacy_address/mod.rs +++ /dev/null @@ -1,418 +0,0 @@ -//! D-Bus address handling. -//! -//! Server addresses consist of a transport name followed by a colon, and then an optional, -//! comma-separated list of keys and values in the form key=value. -//! -//! See also: -//! -//! * [Server addresses] in the D-Bus specification. -//! -//! [Server addresses]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses - -pub mod transport; - -use crate::{Error, Guid, OwnedGuid, Result}; -#[cfg(all(unix, not(target_os = "macos")))] -use nix::unistd::Uid; -use std::{collections::HashMap, env, str::FromStr}; - -use std::fmt::{Display, Formatter}; - -use self::transport::Stream; -pub use self::transport::Transport; - -/// A bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub struct Address { - guid: Option, - transport: Transport, -} - -impl Address { - /// Create a new `Address` from a `Transport`. - pub fn new(transport: Transport) -> Self { - Self { - transport, - guid: None, - } - } - - /// Set the GUID for this address. - pub fn set_guid(mut self, guid: G) -> Result - where - G: TryInto, - G::Error: Into, - { - self.guid = Some(guid.try_into().map_err(Into::into)?); - - Ok(self) - } - - /// The transport details for this address. - pub fn transport(&self) -> &Transport { - &self.transport - } - - #[cfg_attr(any(target_os = "macos", windows), async_recursion::async_recursion)] - pub(crate) async fn connect(self) -> Result { - self.transport.connect().await - } - - /// Get the address for the session socket respecting the `DBUS_SESSION_BUS_ADDRESS` environment - /// variable. If we don't recognize the value (or it's not set) we fall back to - /// `$XDG_RUNTIME_DIR/bus`. - pub fn session() -> Result { - match env::var("DBUS_SESSION_BUS_ADDRESS") { - Ok(val) => Self::from_str(&val), - _ => { - #[cfg(windows)] - return Self::from_str("autolaunch:"); - - #[cfg(all(unix, not(target_os = "macos")))] - { - let runtime_dir = env::var("XDG_RUNTIME_DIR") - .unwrap_or_else(|_| format!("/run/user/{}", Uid::effective())); - let path = format!("unix:path={runtime_dir}/bus"); - - Self::from_str(&path) - } - - #[cfg(target_os = "macos")] - return Self::from_str("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET"); - } - } - } - - /// Get the address for the system bus respecting the `DBUS_SYSTEM_BUS_ADDRESS` environment - /// variable. If we don't recognize the value (or it's not set) we fall back to - /// `/var/run/dbus/system_bus_socket`. - pub fn system() -> Result { - match env::var("DBUS_SYSTEM_BUS_ADDRESS") { - Ok(val) => Self::from_str(&val), - _ => { - #[cfg(all(unix, not(target_os = "macos")))] - return Self::from_str("unix:path=/var/run/dbus/system_bus_socket"); - - #[cfg(windows)] - return Self::from_str("autolaunch:"); - - #[cfg(target_os = "macos")] - return Self::from_str("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET"); - } - } - } - - /// The GUID for this address, if known. - pub fn guid(&self) -> Option<&Guid<'_>> { - self.guid.as_ref().map(|guid| guid.inner()) - } -} - -impl Display for Address { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.transport.fmt(f)?; - - if let Some(guid) = &self.guid { - write!(f, ",guid={}", guid)?; - } - - Ok(()) - } -} - -impl FromStr for Address { - type Err = Error; - - /// Parse the transport part of a D-Bus address into a `Transport`. - fn from_str(address: &str) -> Result { - let col = address - .find(':') - .ok_or_else(|| Error::Address("address has no colon".to_owned()))?; - let transport = &address[..col]; - let mut options = HashMap::new(); - - if address.len() > col + 1 { - for kv in address[col + 1..].split(',') { - let (k, v) = match kv.find('=') { - Some(eq) => (&kv[..eq], &kv[eq + 1..]), - None => { - return Err(Error::Address( - "missing = when parsing key/value".to_owned(), - )) - } - }; - if options.insert(k, v).is_some() { - return Err(Error::Address(format!( - "Key `{k}` specified multiple times" - ))); - } - } - } - - Ok(Self { - guid: options - .remove("guid") - .map(|s| Guid::from_str(s).map(|guid| OwnedGuid::from(guid).to_owned())) - .transpose()?, - transport: Transport::from_options(transport, options)?, - }) - } -} - -impl TryFrom<&str> for Address { - type Error = Error; - - fn try_from(value: &str) -> Result { - Self::from_str(value) - } -} - -impl From for Address { - fn from(transport: Transport) -> Self { - Self::new(transport) - } -} - -#[cfg(test)] -mod tests { - use super::{ - transport::{Tcp, TcpTransportFamily, Transport}, - Address, - }; - #[cfg(target_os = "macos")] - use crate::legacy_address::transport::Launchd; - #[cfg(windows)] - use crate::legacy_address::transport::{Autolaunch, AutolaunchScope}; - use crate::{ - legacy_address::transport::{Unix, UnixSocket}, - Error, - }; - use std::str::FromStr; - use test_log::test; - - #[test] - fn parse_dbus_addresses() { - match Address::from_str("").unwrap_err() { - Error::Address(e) => assert_eq!(e, "address has no colon"), - _ => panic!(), - } - match Address::from_str("foo").unwrap_err() { - Error::Address(e) => assert_eq!(e, "address has no colon"), - _ => panic!(), - } - match Address::from_str("foo:opt").unwrap_err() { - Error::Address(e) => assert_eq!(e, "missing = when parsing key/value"), - _ => panic!(), - } - match Address::from_str("foo:opt=1,opt=2").unwrap_err() { - Error::Address(e) => assert_eq!(e, "Key `opt` specified multiple times"), - _ => panic!(), - } - match Address::from_str("tcp:host=localhost").unwrap_err() { - Error::Address(e) => assert_eq!(e, "tcp address is missing `port`"), - _ => panic!(), - } - match Address::from_str("tcp:host=localhost,port=32f").unwrap_err() { - Error::Address(e) => assert_eq!(e, "invalid tcp `port`"), - _ => panic!(), - } - match Address::from_str("tcp:host=localhost,port=123,family=ipv7").unwrap_err() { - Error::Address(e) => assert_eq!(e, "invalid tcp address `family`: ipv7"), - _ => panic!(), - } - match Address::from_str("unix:foo=blah").unwrap_err() { - Error::Address(e) => assert_eq!(e, "unix: address is invalid"), - _ => panic!(), - } - #[cfg(target_os = "linux")] - match Address::from_str("unix:path=/tmp,abstract=foo").unwrap_err() { - Error::Address(e) => { - assert_eq!(e, "unix: address is invalid") - } - _ => panic!(), - } - assert_eq!( - Address::from_str("unix:path=/tmp/dbus-foo").unwrap(), - Transport::Unix(Unix::new(UnixSocket::File("/tmp/dbus-foo".into()))).into(), - ); - #[cfg(target_os = "linux")] - assert_eq!( - Address::from_str("unix:abstract=/tmp/dbus-foo").unwrap(), - Transport::Unix(Unix::new(UnixSocket::Abstract("/tmp/dbus-foo".into()))).into(), - ); - let guid = crate::Guid::generate(); - assert_eq!( - Address::from_str(&format!("unix:path=/tmp/dbus-foo,guid={guid}")).unwrap(), - Address::from(Transport::Unix(Unix::new(UnixSocket::File( - "/tmp/dbus-foo".into() - )))) - .set_guid(guid.clone()) - .unwrap(), - ); - assert_eq!( - Address::from_str("tcp:host=localhost,port=4142").unwrap(), - Transport::Tcp(Tcp::new("localhost", 4142)).into(), - ); - assert_eq!( - Address::from_str("tcp:host=localhost,port=4142,family=ipv4").unwrap(), - Transport::Tcp(Tcp::new("localhost", 4142).set_family(Some(TcpTransportFamily::Ipv4))) - .into(), - ); - assert_eq!( - Address::from_str("tcp:host=localhost,port=4142,family=ipv6").unwrap(), - Transport::Tcp(Tcp::new("localhost", 4142).set_family(Some(TcpTransportFamily::Ipv6))) - .into(), - ); - assert_eq!( - Address::from_str("tcp:host=localhost,port=4142,family=ipv6,noncefile=/a/file/path") - .unwrap(), - Transport::Tcp( - Tcp::new("localhost", 4142) - .set_family(Some(TcpTransportFamily::Ipv6)) - .set_nonce_file(Some(b"/a/file/path".to_vec())) - ) - .into(), - ); - assert_eq!( - Address::from_str( - "nonce-tcp:host=localhost,port=4142,family=ipv6,noncefile=/a/file/path%20to%20file%201234" - ) - .unwrap(), - Transport::Tcp( - Tcp::new("localhost", 4142) - .set_family(Some(TcpTransportFamily::Ipv6)) - .set_nonce_file(Some(b"/a/file/path to file 1234".to_vec())) - ).into() - ); - #[cfg(windows)] - assert_eq!( - Address::from_str("autolaunch:").unwrap(), - Transport::Autolaunch(Autolaunch::new()).into(), - ); - #[cfg(windows)] - assert_eq!( - Address::from_str("autolaunch:scope=*my_cool_scope*").unwrap(), - Transport::Autolaunch( - Autolaunch::new() - .set_scope(Some(AutolaunchScope::Other("*my_cool_scope*".to_string()))) - ) - .into(), - ); - #[cfg(target_os = "macos")] - assert_eq!( - Address::from_str("launchd:env=my_cool_env_key").unwrap(), - Transport::Launchd(Launchd::new("my_cool_env_key")).into(), - ); - - #[cfg(all(feature = "vsock", not(feature = "tokio")))] - assert_eq!( - Address::from_str(&format!("vsock:cid=98,port=2934,guid={guid}")).unwrap(), - Address::from(Transport::Vsock(legacy_address::transport::Vsock::new( - 98, 2934 - ))) - .set_guid(guid) - .unwrap(), - ); - assert_eq!( - Address::from_str("unix:dir=/some/dir").unwrap(), - Transport::Unix(Unix::new(UnixSocket::Dir("/some/dir".into()))).into(), - ); - assert_eq!( - Address::from_str("unix:tmpdir=/some/dir").unwrap(), - Transport::Unix(Unix::new(UnixSocket::TmpDir("/some/dir".into()))).into(), - ); - } - - #[test] - fn stringify_dbus_addresses() { - assert_eq!( - Address::from(Transport::Unix(Unix::new(UnixSocket::File( - "/tmp/dbus-foo".into() - )))) - .to_string(), - "unix:path=/tmp/dbus-foo", - ); - assert_eq!( - Address::from(Transport::Unix(Unix::new(UnixSocket::Dir( - "/tmp/dbus-foo".into() - )))) - .to_string(), - "unix:dir=/tmp/dbus-foo", - ); - assert_eq!( - Address::from(Transport::Unix(Unix::new(UnixSocket::TmpDir( - "/tmp/dbus-foo".into() - )))) - .to_string(), - "unix:tmpdir=/tmp/dbus-foo" - ); - // FIXME: figure out how to handle abstract on Windows - #[cfg(target_os = "linux")] - assert_eq!( - Address::from(Transport::Unix(Unix::new(UnixSocket::Abstract( - "/tmp/dbus-foo".into() - )))) - .to_string(), - "unix:abstract=/tmp/dbus-foo" - ); - assert_eq!( - Address::from(Transport::Tcp(Tcp::new("localhost", 4142))).to_string(), - "tcp:host=localhost,port=4142" - ); - assert_eq!( - Address::from(Transport::Tcp( - Tcp::new("localhost", 4142).set_family(Some(TcpTransportFamily::Ipv4)) - )) - .to_string(), - "tcp:host=localhost,port=4142,family=ipv4" - ); - assert_eq!( - Address::from(Transport::Tcp( - Tcp::new("localhost", 4142).set_family(Some(TcpTransportFamily::Ipv6)) - )) - .to_string(), - "tcp:host=localhost,port=4142,family=ipv6" - ); - assert_eq!( - Address::from(Transport::Tcp(Tcp::new("localhost", 4142) - .set_family(Some(TcpTransportFamily::Ipv6)) - .set_nonce_file(Some(b"/a/file/path to file 1234".to_vec()) - ))) - .to_string(), - "nonce-tcp:noncefile=/a/file/path%20to%20file%201234,host=localhost,port=4142,family=ipv6" - ); - #[cfg(windows)] - assert_eq!( - Address::from(Transport::Autolaunch(Autolaunch::new())).to_string(), - "autolaunch:" - ); - #[cfg(windows)] - assert_eq!( - Address::from(Transport::Autolaunch(Autolaunch::new().set_scope(Some( - AutolaunchScope::Other("*my_cool_scope*".to_string()) - )))) - .to_string(), - "autolaunch:scope=*my_cool_scope*" - ); - #[cfg(target_os = "macos")] - assert_eq!( - Address::from(Transport::Launchd(Launchd::new("my_cool_key"))).to_string(), - "launchd:env=my_cool_key" - ); - - #[cfg(all(feature = "vsock", not(feature = "tokio")))] - { - let guid = crate::Guid::generate(); - assert_eq!( - Address::from(Transport::Vsock(legacy_address::transport::Vsock::new( - 98, 2934 - ))) - .set_guid(guid.clone()) - .unwrap() - .to_string(), - format!("vsock:cid=98,port=2934,guid={guid}"), - ); - } - } -} diff --git a/zbus/src/legacy_address/transport/autolaunch.rs b/zbus/src/legacy_address/transport/autolaunch.rs deleted file mode 100644 index 5233672b8..000000000 --- a/zbus/src/legacy_address/transport/autolaunch.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::{Error, Result}; -use std::collections::HashMap; - -/// Transport properties of an autolaunch D-Bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Autolaunch { - pub(super) scope: Option, -} - -impl std::fmt::Display for Autolaunch { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "autolaunch:")?; - if let Some(scope) = &self.scope { - write!(f, "scope={}", scope)?; - } - - Ok(()) - } -} - -impl Default for Autolaunch { - fn default() -> Self { - Self::new() - } -} - -impl Autolaunch { - /// Create a new autolaunch transport. - pub fn new() -> Self { - Self { scope: None } - } - - /// Set the `autolaunch:` address `scope` value. - pub fn set_scope(mut self, scope: Option) -> Self { - self.scope = scope; - - self - } - - /// The optional scope. - pub fn scope(&self) -> Option<&AutolaunchScope> { - self.scope.as_ref() - } - - pub(super) fn from_options(opts: HashMap<&str, &str>) -> Result { - opts.get("scope") - .map(|scope| -> Result<_> { - let decoded = super::decode_percents(scope)?; - match decoded.as_slice() { - b"install-path" => Ok(AutolaunchScope::InstallPath), - b"user" => Ok(AutolaunchScope::User), - _ => String::from_utf8(decoded) - .map(AutolaunchScope::Other) - .map_err(|_| { - Error::Address("autolaunch scope is not valid UTF-8".to_owned()) - }), - } - }) - .transpose() - .map(|scope| Self { scope }) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum AutolaunchScope { - /// Limit session bus to dbus installation path. - InstallPath, - /// Limit session bus to the recent user. - User, - /// Other values - specify dedicated session bus like "release", "debug" or other. - Other(String), -} - -impl std::fmt::Display for AutolaunchScope { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::InstallPath => write!(f, "*install-path"), - Self::User => write!(f, "*user"), - Self::Other(o) => write!(f, "{o}"), - } - } -} diff --git a/zbus/src/legacy_address/transport/launchd.rs b/zbus/src/legacy_address/transport/launchd.rs deleted file mode 100644 index 482fcc32f..000000000 --- a/zbus/src/legacy_address/transport/launchd.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::{Transport, Unix, UnixSocket}; -use crate::{process::run, Result}; -use std::collections::HashMap; - -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -/// The transport properties of a launchd D-Bus address. -pub struct Launchd { - pub(super) env: String, -} - -impl Launchd { - /// Create a new launchd D-Bus address. - pub fn new(env: &str) -> Self { - Self { - env: env.to_string(), - } - } - - /// The path of the unix domain socket for the launchd created dbus-daemon. - pub fn env(&self) -> &str { - &self.env - } - - /// Determine the actual transport details behind a launchd address. - pub(super) async fn bus_address(&self) -> Result { - let output = run("launchctl", ["getenv", self.env()]) - .await - .expect("failed to wait on launchctl output"); - - if !output.status.success() { - return Err(crate::Error::Address(format!( - "launchctl terminated with code: {}", - output.status - ))); - } - - let addr = String::from_utf8(output.stdout).map_err(|e| { - crate::Error::Address(format!("Unable to parse launchctl output as UTF-8: {}", e)) - })?; - - Ok(Transport::Unix(Unix::new(UnixSocket::File( - addr.trim().into(), - )))) - } - - pub(super) fn from_options(opts: HashMap<&str, &str>) -> Result { - opts.get("env") - .ok_or_else(|| crate::Error::Address("missing env key".into())) - .map(|env| Self { - env: env.to_string(), - }) - } -} - -impl std::fmt::Display for Launchd { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "launchd:env={}", self.env) - } -} diff --git a/zbus/src/legacy_address/transport/mod.rs b/zbus/src/legacy_address/transport/mod.rs deleted file mode 100644 index 437c17791..000000000 --- a/zbus/src/legacy_address/transport/mod.rs +++ /dev/null @@ -1,350 +0,0 @@ -//! D-Bus transport Information module. -//! -//! This module provides the trasport information for D-Bus addresses. - -#[cfg(windows)] -use crate::win32::autolaunch_bus_address; -use crate::{Error, Result}; -#[cfg(not(feature = "tokio"))] -use async_io::Async; -use std::collections::HashMap; -#[cfg(not(feature = "tokio"))] -use std::net::TcpStream; -#[cfg(unix)] -use std::os::unix::net::{SocketAddr, UnixStream}; -#[cfg(feature = "tokio")] -use tokio::net::TcpStream; -#[cfg(feature = "tokio-vsock")] -use tokio_vsock::VsockStream; -#[cfg(windows)] -use uds_windows::UnixStream; -#[cfg(all(feature = "vsock", not(feature = "tokio")))] -use vsock::VsockStream; - -use std::{ - fmt::{Display, Formatter}, - str::from_utf8_unchecked, -}; - -mod unix; -pub use unix::{Unix, UnixSocket}; -mod tcp; -pub use tcp::{Tcp, TcpTransportFamily}; -#[cfg(windows)] -mod autolaunch; -#[cfg(windows)] -pub use autolaunch::{Autolaunch, AutolaunchScope}; -#[cfg(target_os = "macos")] -mod launchd; -#[cfg(target_os = "macos")] -pub use launchd::Launchd; -#[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" -))] -#[path = "vsock.rs"] -// Gotta rename to avoid name conflict with the `vsock` crate. -mod vsock_transport; -#[cfg(target_os = "linux")] -use std::os::linux::net::SocketAddrExt; -#[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" -))] -pub use vsock_transport::Vsock; - -/// The transport properties of a D-Bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum Transport { - /// A Unix Domain Socket address. - Unix(Unix), - /// A TCP address. - Tcp(Tcp), - /// An autolaunch D-Bus address. - #[cfg(windows)] - Autolaunch(Autolaunch), - /// A launchd D-Bus address. - #[cfg(target_os = "macos")] - Launchd(Launchd), - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - /// A VSOCK address. - /// - /// This variant is only available when either the `vsock` or `tokio-vsock` feature is enabled. - /// The type of `stream` is `vsock::VsockStream` with the `vsock` feature and - /// `tokio_vsock::VsockStream` with the `tokio-vsock` feature. - Vsock(Vsock), -} - -impl Transport { - #[cfg_attr(any(target_os = "macos", windows), async_recursion::async_recursion)] - pub(super) async fn connect(self) -> Result { - match self { - Transport::Unix(unix) => { - // This is a `path` in case of Windows until uds_windows provides the needed API: - // https://github.com/haraldh/rust_uds_windows/issues/14 - let addr = match unix.take_path() { - #[cfg(unix)] - UnixSocket::File(path) => SocketAddr::from_pathname(path)?, - #[cfg(windows)] - UnixSocket::File(path) => path, - #[cfg(target_os = "linux")] - UnixSocket::Abstract(name) => { - SocketAddr::from_abstract_name(name.as_encoded_bytes())? - } - UnixSocket::Dir(_) | UnixSocket::TmpDir(_) => { - // you can't connect to a unix:dir - return Err(Error::Unsupported); - } - }; - let stream = crate::Task::spawn_blocking( - move || -> Result<_> { - #[cfg(unix)] - let stream = UnixStream::connect_addr(&addr)?; - #[cfg(windows)] - let stream = UnixStream::connect(addr)?; - stream.set_nonblocking(true)?; - - Ok(stream) - }, - "unix stream connection", - ) - .await?; - #[cfg(not(feature = "tokio"))] - { - Async::new(stream) - .map(Stream::Unix) - .map_err(|e| Error::InputOutput(e.into())) - } - - #[cfg(feature = "tokio")] - { - #[cfg(unix)] - { - tokio::net::UnixStream::from_std(stream) - .map(Stream::Unix) - .map_err(|e| Error::InputOutput(e.into())) - } - - #[cfg(not(unix))] - { - let _ = stream; - Err(Error::Unsupported) - } - } - } - #[cfg(all(feature = "vsock", not(feature = "tokio")))] - Transport::Vsock(addr) => { - let stream = VsockStream::connect_with_cid_port(addr.cid(), addr.port())?; - Async::new(stream).map(Stream::Vsock).map_err(Into::into) - } - - #[cfg(feature = "tokio-vsock")] - Transport::Vsock(addr) => VsockStream::connect(addr.cid(), addr.port()) - .await - .map(Stream::Vsock) - .map_err(Into::into), - - Transport::Tcp(mut addr) => match addr.take_nonce_file() { - Some(nonce_file) => { - #[allow(unused_mut)] - let mut stream = addr.connect().await?; - - #[cfg(unix)] - let nonce_file = { - use std::os::unix::ffi::OsStrExt; - std::ffi::OsStr::from_bytes(&nonce_file) - }; - - #[cfg(windows)] - let nonce_file = std::str::from_utf8(&nonce_file).map_err(|_| { - Error::Address("nonce file path is invalid UTF-8".to_owned()) - })?; - - #[cfg(not(feature = "tokio"))] - { - let nonce = std::fs::read(nonce_file)?; - let mut nonce = &nonce[..]; - - while !nonce.is_empty() { - let len = stream - .write_with(|mut s| std::io::Write::write(&mut s, nonce)) - .await?; - nonce = &nonce[len..]; - } - } - - #[cfg(feature = "tokio")] - { - let nonce = tokio::fs::read(nonce_file).await?; - tokio::io::AsyncWriteExt::write_all(&mut stream, &nonce).await?; - } - - Ok(Stream::Tcp(stream)) - } - None => addr.connect().await.map(Stream::Tcp), - }, - - #[cfg(windows)] - Transport::Autolaunch(Autolaunch { scope }) => match scope { - Some(_) => Err(Error::Address( - "Autolaunch scopes are currently unsupported".to_owned(), - )), - None => { - let addr: crate::Address = autolaunch_bus_address()?.parse()?; - addr.connect().await - } - }, - - #[cfg(target_os = "macos")] - Transport::Launchd(launchd) => { - let addr = launchd.bus_address().await?; - addr.connect().await - } - } - } - - // Helper for `FromStr` impl of `Address`. - pub(super) fn from_options(transport: &str, options: HashMap<&str, &str>) -> Result { - match transport { - "unix" => Unix::from_options(options).map(Self::Unix), - "tcp" => Tcp::from_options(options, false).map(Self::Tcp), - "nonce-tcp" => Tcp::from_options(options, true).map(Self::Tcp), - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - "vsock" => Vsock::from_options(options).map(Self::Vsock), - #[cfg(windows)] - "autolaunch" => Autolaunch::from_options(options).map(Self::Autolaunch), - #[cfg(target_os = "macos")] - "launchd" => Launchd::from_options(options).map(Self::Launchd), - - _ => Err(Error::Address(format!( - "unsupported transport '{transport}'" - ))), - } - } -} - -#[cfg(not(feature = "tokio"))] -#[derive(Debug)] -pub(crate) enum Stream { - Unix(Async), - Tcp(Async), - #[cfg(feature = "vsock")] - Vsock(Async), -} - -#[cfg(feature = "tokio")] -#[derive(Debug)] -pub(crate) enum Stream { - #[cfg(unix)] - Unix(tokio::net::UnixStream), - Tcp(TcpStream), - #[cfg(feature = "tokio-vsock")] - Vsock(VsockStream), -} - -fn decode_hex(c: char) -> Result { - match c { - '0'..='9' => Ok(c as u8 - b'0'), - 'a'..='f' => Ok(c as u8 - b'a' + 10), - 'A'..='F' => Ok(c as u8 - b'A' + 10), - - _ => Err(Error::Address( - "invalid hexadecimal character in percent-encoded sequence".to_owned(), - )), - } -} - -pub(crate) fn decode_percents(value: &str) -> Result> { - let mut iter = value.chars(); - let mut decoded = Vec::new(); - - while let Some(c) = iter.next() { - if matches!(c, '-' | '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '/' | '.' | '\\' | '*') { - decoded.push(c as u8) - } else if c == '%' { - decoded.push( - decode_hex(iter.next().ok_or_else(|| { - Error::Address("incomplete percent-encoded sequence".to_owned()) - })?)? - << 4 - | decode_hex(iter.next().ok_or_else(|| { - Error::Address("incomplete percent-encoded sequence".to_owned()) - })?)?, - ); - } else { - return Err(Error::Address("Invalid character in address".to_owned())); - } - } - - Ok(decoded) -} - -pub(super) fn encode_percents(f: &mut Formatter<'_>, mut value: &[u8]) -> std::fmt::Result { - const LOOKUP: &str = "\ -%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f\ -%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f\ -%20%21%22%23%24%25%26%27%28%29%2a%2b%2c%2d%2e%2f\ -%30%31%32%33%34%35%36%37%38%39%3a%3b%3c%3d%3e%3f\ -%40%41%42%43%44%45%46%47%48%49%4a%4b%4c%4d%4e%4f\ -%50%51%52%53%54%55%56%57%58%59%5a%5b%5c%5d%5e%5f\ -%60%61%62%63%64%65%66%67%68%69%6a%6b%6c%6d%6e%6f\ -%70%71%72%73%74%75%76%77%78%79%7a%7b%7c%7d%7e%7f\ -%80%81%82%83%84%85%86%87%88%89%8a%8b%8c%8d%8e%8f\ -%90%91%92%93%94%95%96%97%98%99%9a%9b%9c%9d%9e%9f\ -%a0%a1%a2%a3%a4%a5%a6%a7%a8%a9%aa%ab%ac%ad%ae%af\ -%b0%b1%b2%b3%b4%b5%b6%b7%b8%b9%ba%bb%bc%bd%be%bf\ -%c0%c1%c2%c3%c4%c5%c6%c7%c8%c9%ca%cb%cc%cd%ce%cf\ -%d0%d1%d2%d3%d4%d5%d6%d7%d8%d9%da%db%dc%dd%de%df\ -%e0%e1%e2%e3%e4%e5%e6%e7%e8%e9%ea%eb%ec%ed%ee%ef\ -%f0%f1%f2%f3%f4%f5%f6%f7%f8%f9%fa%fb%fc%fd%fe%ff"; - - loop { - let pos = value.iter().position( - |c| !matches!(c, b'-' | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'_' | b'/' | b'.' | b'\\' | b'*'), - ); - - if let Some(pos) = pos { - // SAFETY: The above `position()` call made sure that only ASCII chars are in the string - // up to `pos` - f.write_str(unsafe { from_utf8_unchecked(&value[..pos]) })?; - - let c = value[pos]; - value = &value[pos + 1..]; - - let pos = c as usize * 3; - f.write_str(&LOOKUP[pos..pos + 3])?; - } else { - // SAFETY: The above `position()` call made sure that only ASCII chars are in the rest - // of the string - f.write_str(unsafe { from_utf8_unchecked(value) })?; - return Ok(()); - } - } -} - -impl Display for Transport { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Tcp(tcp) => write!(f, "{}", tcp)?, - Self::Unix(unix) => write!(f, "{}", unix)?, - #[cfg(any( - all(feature = "vsock", not(feature = "tokio")), - feature = "tokio-vsock" - ))] - Self::Vsock(vsock) => write!(f, "{}", vsock)?, - #[cfg(windows)] - Self::Autolaunch(autolaunch) => write!(f, "{}", autolaunch)?, - #[cfg(target_os = "macos")] - Self::Launchd(launchd) => write!(f, "{}", launchd)?, - } - - Ok(()) - } -} diff --git a/zbus/src/legacy_address/transport/tcp.rs b/zbus/src/legacy_address/transport/tcp.rs deleted file mode 100644 index c289fa666..000000000 --- a/zbus/src/legacy_address/transport/tcp.rs +++ /dev/null @@ -1,229 +0,0 @@ -use super::encode_percents; -use crate::{Error, Result}; -#[cfg(not(feature = "tokio"))] -use async_io::Async; -#[cfg(not(feature = "tokio"))] -use std::net::{SocketAddr, TcpStream, ToSocketAddrs}; -use std::{ - collections::HashMap, - fmt::{Display, Formatter}, - str::FromStr, -}; -#[cfg(feature = "tokio")] -use tokio::net::TcpStream; - -/// A TCP transport in a D-Bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Tcp { - pub(super) host: String, - pub(super) bind: Option, - pub(super) port: u16, - pub(super) family: Option, - pub(super) nonce_file: Option>, -} - -impl Tcp { - /// Create a new TCP transport with the given host and port. - pub fn new(host: &str, port: u16) -> Self { - Self { - host: host.to_owned(), - port, - bind: None, - family: None, - nonce_file: None, - } - } - - /// Set the `tcp:` address `bind` value. - pub fn set_bind(mut self, bind: Option) -> Self { - self.bind = bind; - - self - } - - /// Set the `tcp:` address `family` value. - pub fn set_family(mut self, family: Option) -> Self { - self.family = family; - - self - } - - /// Set the `tcp:` address `noncefile` value. - pub fn set_nonce_file(mut self, nonce_file: Option>) -> Self { - self.nonce_file = nonce_file; - - self - } - - /// The `tcp:` address `host` value. - pub fn host(&self) -> &str { - &self.host - } - - /// The `tcp:` address `bind` value. - pub fn bind(&self) -> Option<&str> { - self.bind.as_deref() - } - - /// The `tcp:` address `port` value. - pub fn port(&self) -> u16 { - self.port - } - - /// The `tcp:` address `family` value. - pub fn family(&self) -> Option { - self.family - } - - /// The nonce file path, if any. - pub fn nonce_file(&self) -> Option<&[u8]> { - self.nonce_file.as_deref() - } - - /// Take ownership of the nonce file path, if any. - pub fn take_nonce_file(&mut self) -> Option> { - self.nonce_file.take() - } - - pub(super) fn from_options( - opts: HashMap<&str, &str>, - nonce_tcp_required: bool, - ) -> Result { - let bind = None; - if opts.contains_key("bind") { - return Err(Error::Address("`bind` isn't yet supported".into())); - } - - let host = opts - .get("host") - .ok_or_else(|| Error::Address("tcp address is missing `host`".into()))? - .to_string(); - let port = opts - .get("port") - .ok_or_else(|| Error::Address("tcp address is missing `port`".into()))?; - let port = port - .parse::() - .map_err(|_| Error::Address("invalid tcp `port`".into()))?; - let family = opts - .get("family") - .map(|f| TcpTransportFamily::from_str(f)) - .transpose()?; - let nonce_file = opts - .get("noncefile") - .map(|f| super::decode_percents(f)) - .transpose()?; - if nonce_tcp_required && nonce_file.is_none() { - return Err(Error::Address( - "nonce-tcp address is missing `noncefile`".into(), - )); - } - - Ok(Self { - host, - bind, - port, - family, - nonce_file, - }) - } - - #[cfg(not(feature = "tokio"))] - pub(super) async fn connect(self) -> Result> { - let addrs = crate::Task::spawn_blocking( - move || -> Result> { - let addrs = (self.host(), self.port()).to_socket_addrs()?.filter(|a| { - if let Some(family) = self.family() { - if family == TcpTransportFamily::Ipv4 { - a.is_ipv4() - } else { - a.is_ipv6() - } - } else { - true - } - }); - Ok(addrs.collect()) - }, - "connect tcp", - ) - .await - .map_err(|e| Error::Address(format!("Failed to receive TCP addresses: {e}")))?; - - // we could attempt connections in parallel? - let mut last_err = Error::Address("Failed to connect".into()); - for addr in addrs { - match Async::::connect(addr).await { - Ok(stream) => return Ok(stream), - Err(e) => last_err = e.into(), - } - } - - Err(last_err) - } - - #[cfg(feature = "tokio")] - pub(super) async fn connect(self) -> Result { - TcpStream::connect((self.host(), self.port())) - .await - .map_err(|e| Error::InputOutput(e.into())) - } -} - -impl Display for Tcp { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self.nonce_file() { - Some(nonce_file) => { - f.write_str("nonce-tcp:noncefile=")?; - encode_percents(f, nonce_file)?; - f.write_str(",")?; - } - None => f.write_str("tcp:")?, - } - f.write_str("host=")?; - - encode_percents(f, self.host().as_bytes())?; - - write!(f, ",port={}", self.port())?; - - if let Some(bind) = self.bind() { - f.write_str(",bind=")?; - encode_percents(f, bind.as_bytes())?; - } - - if let Some(family) = self.family() { - write!(f, ",family={family}")?; - } - - Ok(()) - } -} - -/// A `tcp:` address family. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum TcpTransportFamily { - Ipv4, - Ipv6, -} - -impl FromStr for TcpTransportFamily { - type Err = Error; - - fn from_str(family: &str) -> Result { - match family { - "ipv4" => Ok(Self::Ipv4), - "ipv6" => Ok(Self::Ipv6), - _ => Err(Error::Address(format!( - "invalid tcp address `family`: {family}" - ))), - } - } -} - -impl Display for TcpTransportFamily { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Ipv4 => write!(f, "ipv4"), - Self::Ipv6 => write!(f, "ipv6"), - } - } -} diff --git a/zbus/src/legacy_address/transport/unix.rs b/zbus/src/legacy_address/transport/unix.rs deleted file mode 100644 index 9d6f2acf7..000000000 --- a/zbus/src/legacy_address/transport/unix.rs +++ /dev/null @@ -1,129 +0,0 @@ -#[cfg(target_os = "linux")] -use std::ffi::OsString; -use std::{ - ffi::OsStr, - fmt::{Display, Formatter}, - path::PathBuf, -}; - -#[cfg(unix)] -use super::encode_percents; - -/// A Unix domain socket transport in a D-Bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Unix { - path: UnixSocket, -} - -impl Unix { - /// Create a new Unix transport with the given path. - pub fn new(path: UnixSocket) -> Self { - Self { path } - } - - /// The path. - pub fn path(&self) -> &UnixSocket { - &self.path - } - - /// Take the path, consuming `self`. - pub fn take_path(self) -> UnixSocket { - self.path - } - - pub(super) fn from_options(opts: std::collections::HashMap<&str, &str>) -> crate::Result { - let path = opts.get("path"); - let abs = opts.get("abstract"); - let dir = opts.get("dir"); - let tmpdir = opts.get("tmpdir"); - let path = match (path, abs, dir, tmpdir) { - (Some(p), None, None, None) => UnixSocket::File(PathBuf::from(p)), - #[cfg(target_os = "linux")] - (None, Some(p), None, None) => UnixSocket::Abstract(OsString::from(p)), - #[cfg(not(target_os = "linux"))] - (None, Some(_), None, None) => { - return Err(crate::Error::Address( - "abstract sockets currently Linux-only".to_owned(), - )); - } - (None, None, Some(p), None) => UnixSocket::Dir(PathBuf::from(p)), - (None, None, None, Some(p)) => UnixSocket::TmpDir(PathBuf::from(p)), - _ => { - return Err(crate::Error::Address("unix: address is invalid".to_owned())); - } - }; - - Ok(Self::new(path)) - } -} - -impl Display for Unix { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "unix:{}", self.path) - } -} - -/// A Unix domain socket path in a D-Bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum UnixSocket { - /// A path to a unix domain socket on the filesystem. - File(PathBuf), - /// An abstract unix domain socket name. - #[cfg(target_os = "linux")] - Abstract(OsString), - /// A listenable address using the specified path, in which a socket file with a random file - /// name starting with 'dbus-' will be created by the server. See [UNIX domain socket address] - /// reference documentation. - /// - /// This address is mostly relevant to server (typically bus broker) implementations. - /// - /// [UNIX domain socket address]: https://dbus.freedesktop.org/doc/dbus-specification.html#transports-unix-domain-sockets-addresses - Dir(PathBuf), - /// The same as UnixDir, except that on platforms with abstract sockets, the server may attempt - /// to create an abstract socket whose name starts with this directory instead of a path-based - /// socket. - /// - /// This address is mostly relevant to server (typically bus broker) implementations. - TmpDir(PathBuf), -} - -impl Display for UnixSocket { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - fn fmt_unix_path(f: &mut Formatter<'_>, path: &OsStr) -> std::fmt::Result { - #[cfg(unix)] - { - use std::os::unix::ffi::OsStrExt; - - encode_percents(f, path.as_bytes())?; - } - - #[cfg(windows)] - write!(f, "{}", path.to_str().ok_or(std::fmt::Error)?)?; - - Ok(()) - } - - match self { - UnixSocket::File(path) => { - f.write_str("path=")?; - fmt_unix_path(f, path.as_os_str())?; - } - #[cfg(target_os = "linux")] - UnixSocket::Abstract(name) => { - f.write_str("abstract=")?; - fmt_unix_path(f, name)?; - } - UnixSocket::Dir(path) => { - f.write_str("dir=")?; - fmt_unix_path(f, path.as_os_str())?; - } - UnixSocket::TmpDir(path) => { - f.write_str("tmpdir=")?; - fmt_unix_path(f, path.as_os_str())?; - } - } - - Ok(()) - } -} diff --git a/zbus/src/legacy_address/transport/vsock.rs b/zbus/src/legacy_address/transport/vsock.rs deleted file mode 100644 index f18c60df6..000000000 --- a/zbus/src/legacy_address/transport/vsock.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::{Error, Result}; -use std::collections::HashMap; - -/// A VSOCK D-Bus address. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Vsock { - pub(super) cid: u32, - pub(super) port: u32, -} - -impl Vsock { - /// Create a new VSOCK address. - pub fn new(cid: u32, port: u32) -> Self { - Self { cid, port } - } - - /// The Client ID. - pub fn cid(&self) -> u32 { - self.cid - } - - /// The port. - pub fn port(&self) -> u32 { - self.port - } - - pub(super) fn from_options(opts: HashMap<&str, &str>) -> Result { - let cid = opts - .get("cid") - .ok_or_else(|| Error::Address("VSOCK address is missing cid=".into()))?; - let cid = cid - .parse::() - .map_err(|e| Error::Address(format!("Failed to parse VSOCK cid `{}`: {}", cid, e)))?; - let port = opts - .get("port") - .ok_or_else(|| Error::Address("VSOCK address is missing port=".into()))?; - let port = port - .parse::() - .map_err(|e| Error::Address(format!("Failed to parse VSOCK port `{}`: {}", port, e)))?; - - Ok(Self { cid, port }) - } -} - -impl std::fmt::Display for Vsock { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "vsock:cid={},port={}", self.cid, self.port) - } -} diff --git a/zbus/src/lib.rs b/zbus/src/lib.rs index 2c2ced25f..e9d37877a 100644 --- a/zbus/src/lib.rs +++ b/zbus/src/lib.rs @@ -49,9 +49,6 @@ pub use error::*; pub mod address; pub use address::DBusAddr; -pub mod legacy_address; -pub use legacy_address::Address; - mod guid; pub use guid::*; From dec224c20637765df9e8a45ba80eb31f9b6dc621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 18 Sep 2024 10:59:26 +0400 Subject: [PATCH 18/21] =?UTF-8?q?=E2=9E=96=20zb:=20drop=20async-recursion?= =?UTF-8?q?=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 12 ------------ zbus/Cargo.toml | 3 --- 2 files changed, 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eaaf391e9..99bfc69a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,17 +187,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - [[package]] name = "async-signal" version = "0.2.8" @@ -2309,7 +2298,6 @@ dependencies = [ "async-io", "async-lock", "async-process", - "async-recursion", "async-task", "async-trait", "blocking", diff --git a/zbus/Cargo.toml b/zbus/Cargo.toml index f6760d275..01fee9ca7 100644 --- a/zbus/Cargo.toml +++ b/zbus/Cargo.toml @@ -113,9 +113,6 @@ nix = { version = "0.29", default-features = false, features = [ # Cargo doesn't provide a way to do that for only specific target OS: https://github.com/rust-lang/cargo/issues/1197. async-process = "2.2.2" -[target.'cfg(any(target_os = "macos", windows))'.dependencies] -async-recursion = "1.1.1" - [dev-dependencies] zbus_xml = { path = "../zbus_xml", version = "4.0.0" } doc-comment = "0.3.3" From 979019c4f50f738269c3589552497cb1908877e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 18 Sep 2024 10:42:07 +0400 Subject: [PATCH 19/21] =?UTF-8?q?=F0=9F=9A=9A=20zb:=20rename=20DBusAddr=20?= =?UTF-8?q?to=20Address?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As requested by Zeeshan. --- zbus/src/address/address_list.rs | 30 +++++----- zbus/src/address/mod.rs | 74 ++++++++++++------------ zbus/src/address/tests.rs | 56 +++++++++--------- zbus/src/address/transport/autolaunch.rs | 6 +- zbus/src/address/transport/launchd.rs | 6 +- zbus/src/address/transport/mod.rs | 6 +- zbus/src/address/transport/nonce_tcp.rs | 6 +- zbus/src/address/transport/systemd.rs | 6 +- zbus/src/address/transport/tcp.rs | 6 +- zbus/src/address/transport/unix.rs | 6 +- zbus/src/address/transport/unixexec.rs | 6 +- zbus/src/address/transport/vsock.rs | 6 +- zbus/src/blocking/connection/builder.rs | 4 +- zbus/src/connection/builder.rs | 8 +-- zbus/src/connection/connect.rs | 6 +- zbus/src/connection/connect/macos.rs | 8 +-- zbus/src/connection/connect/win32.rs | 8 +-- zbus/src/connection/socket/tcp.rs | 6 +- zbus/src/lib.rs | 2 +- 19 files changed, 127 insertions(+), 129 deletions(-) diff --git a/zbus/src/address/address_list.rs b/zbus/src/address/address_list.rs index a246ae5ca..8d7d0f8f0 100644 --- a/zbus/src/address/address_list.rs +++ b/zbus/src/address/address_list.rs @@ -1,26 +1,26 @@ use std::{borrow::Cow, fmt}; -use super::{DBusAddr, Error, Result, ToDBusAddrs}; +use super::{Address, Error, Result, ToAddresses}; /// A bus address list. /// /// D-Bus addresses are `;`-separated. #[derive(Debug, PartialEq, Eq, Clone)] -pub struct DBusAddrList<'a> { +pub struct AddressList<'a> { addr: Cow<'a, str>, } -impl<'a> ToDBusAddrs<'a> for DBusAddrList<'a> { - type Iter = DBusAddrListIter<'a>; +impl<'a> ToAddresses<'a> for AddressList<'a> { + type Iter = AddressListIter<'a>; /// Get an iterator over the D-Bus addresses. - fn to_dbus_addrs(&'a self) -> Self::Iter { - DBusAddrListIter::new(self) + fn to_addresses(&'a self) -> Self::Iter { + AddressListIter::new(self) } } -impl<'a> Iterator for DBusAddrListIter<'a> { - type Item = Result>; +impl<'a> Iterator for AddressListIter<'a> { + type Item = Result>; fn next(&mut self) -> Option { if self.next_index >= self.data.len() { @@ -35,18 +35,18 @@ impl<'a> Iterator for DBusAddrListIter<'a> { self.next_index = self.data.len(); } - Some(DBusAddr::try_from(addr)) + Some(Address::try_from(addr)) } } /// An iterator of D-Bus addresses. -pub struct DBusAddrListIter<'a> { +pub struct AddressListIter<'a> { data: &'a str, next_index: usize, } -impl<'a> DBusAddrListIter<'a> { - fn new(list: &'a DBusAddrList<'_>) -> Self { +impl<'a> AddressListIter<'a> { + fn new(list: &'a AddressList<'_>) -> Self { Self { data: list.addr.as_ref(), next_index: 0, @@ -54,7 +54,7 @@ impl<'a> DBusAddrListIter<'a> { } } -impl<'a> TryFrom for DBusAddrList<'a> { +impl<'a> TryFrom for AddressList<'a> { type Error = Error; fn try_from(value: String) -> Result { @@ -64,7 +64,7 @@ impl<'a> TryFrom for DBusAddrList<'a> { } } -impl<'a> TryFrom<&'a str> for DBusAddrList<'a> { +impl<'a> TryFrom<&'a str> for AddressList<'a> { type Error = Error; fn try_from(value: &'a str) -> Result { @@ -74,7 +74,7 @@ impl<'a> TryFrom<&'a str> for DBusAddrList<'a> { } } -impl fmt::Display for DBusAddrList<'_> { +impl fmt::Display for AddressList<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.addr) } diff --git a/zbus/src/address/mod.rs b/zbus/src/address/mod.rs index 528dd223f..c50eb056b 100644 --- a/zbus/src/address/mod.rs +++ b/zbus/src/address/mod.rs @@ -29,7 +29,7 @@ use nix::unistd::Uid; pub mod transport; mod address_list; -pub use address_list::{DBusAddrList, DBusAddrListIter}; +pub use address_list::{AddressList, AddressListIter}; mod percent; pub use percent::*; @@ -74,13 +74,13 @@ pub type Result = std::result::Result; /// Get the address for session socket respecting the DBUS_SESSION_BUS_ADDRESS environment /// variable. If we don't recognize the value (or it's not set) we fall back to /// $XDG_RUNTIME_DIR/bus -pub fn session() -> Result> { +pub fn session() -> Result> { match env::var("DBUS_SESSION_BUS_ADDRESS") { - Ok(val) => DBusAddrList::try_from(val), + Ok(val) => AddressList::try_from(val), _ => { #[cfg(windows)] { - DBusAddrList::try_from("autolaunch:scope=*user;autolaunch:") + AddressList::try_from("autolaunch:scope=*user;autolaunch:") } #[cfg(all(unix, not(target_os = "macos")))] @@ -89,12 +89,12 @@ pub fn session() -> Result> { .unwrap_or_else(|_| format!("/run/user/{}", Uid::effective())); let path = format!("unix:path={runtime_dir}/bus"); - DBusAddrList::try_from(path) + AddressList::try_from(path) } #[cfg(target_os = "macos")] { - DBusAddrList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET") + AddressList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET") } } } @@ -103,18 +103,18 @@ pub fn session() -> Result> { /// Get the address for system bus respecting the DBUS_SYSTEM_BUS_ADDRESS environment /// variable. If we don't recognize the value (or it's not set) we fall back to /// /var/run/dbus/system_bus_socket -pub fn system() -> Result> { +pub fn system() -> Result> { match env::var("DBUS_SYSTEM_BUS_ADDRESS") { - Ok(val) => DBusAddrList::try_from(val), + Ok(val) => AddressList::try_from(val), _ => { #[cfg(all(unix, not(target_os = "macos")))] - return DBusAddrList::try_from("unix:path=/var/run/dbus/system_bus_socket"); + return AddressList::try_from("unix:path=/var/run/dbus/system_bus_socket"); #[cfg(windows)] - return DBusAddrList::try_from("autolaunch:"); + return AddressList::try_from("autolaunch:"); #[cfg(target_os = "macos")] - return DBusAddrList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET"); + return AddressList::try_from("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET"); } } } @@ -123,16 +123,16 @@ pub fn system() -> Result> { /// /// Example: /// ``` -/// use zbus::DBusAddr; +/// use zbus::Address; /// -/// let _: DBusAddr = "unix:path=/tmp/dbus.sock".try_into().unwrap(); +/// let _: Address = "unix:path=/tmp/dbus.sock".try_into().unwrap(); /// ``` #[derive(Debug, PartialEq, Eq, Clone)] -pub struct DBusAddr<'a> { +pub struct Address<'a> { pub(super) addr: Cow<'a, str>, } -impl<'a> DBusAddr<'a> { +impl<'a> Address<'a> { /// The connection GUID if any. pub fn guid(&self) -> Option> { self.get_string("guid").and_then(|res| res.ok()) @@ -195,14 +195,14 @@ fn validate_guid(value: &[u8]) -> Result<()> { Ok(()) } -impl DBusAddr<'_> { - pub fn to_owned(&self) -> DBusAddr<'static> { +impl Address<'_> { + pub fn to_owned(&self) -> Address<'static> { let addr = self.addr.to_string(); - DBusAddr { addr: addr.into() } + Address { addr: addr.into() } } } -impl<'a> TryFrom for DBusAddr<'a> { +impl<'a> TryFrom for Address<'a> { type Error = Error; fn try_from(addr: String) -> Result { @@ -210,7 +210,7 @@ impl<'a> TryFrom for DBusAddr<'a> { } } -impl<'a> TryFrom<&'a str> for DBusAddr<'a> { +impl<'a> TryFrom<&'a str> for Address<'a> { type Error = Error; fn try_from(addr: &'a str) -> Result { @@ -218,7 +218,7 @@ impl<'a> TryFrom<&'a str> for DBusAddr<'a> { } } -impl fmt::Display for DBusAddr<'_> { +impl fmt::Display for Address<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let kv = KeyValFmt::new().add("guid", self.guid()); let t = self.transport().map_err(|_| fmt::Error)?; @@ -307,43 +307,43 @@ impl fmt::Display for KeyValFmt<'_> { } } -/// A trait for objects which can be converted or resolved to one or more [`DBusAddr`] values. -pub trait ToDBusAddrs<'a> { - type Iter: Iterator>>; +/// A trait for objects which can be converted or resolved to one or more [`Address`] values. +pub trait ToAddresses<'a> { + type Iter: Iterator>>; - fn to_dbus_addrs(&'a self) -> Self::Iter; + fn to_addresses(&'a self) -> Self::Iter; } -impl<'a> ToDBusAddrs<'a> for DBusAddr<'a> { - type Iter = std::iter::Once>>; +impl<'a> ToAddresses<'a> for Address<'a> { + type Iter = std::iter::Once>>; /// Get an iterator over the D-Bus addresses. - fn to_dbus_addrs(&'a self) -> Self::Iter { + fn to_addresses(&'a self) -> Self::Iter { std::iter::once(Ok(self.clone())) } } -impl<'a> ToDBusAddrs<'a> for str { - type Iter = std::iter::Once>>; +impl<'a> ToAddresses<'a> for str { + type Iter = std::iter::Once>>; - fn to_dbus_addrs(&'a self) -> Self::Iter { + fn to_addresses(&'a self) -> Self::Iter { std::iter::once(self.try_into()) } } -impl<'a> ToDBusAddrs<'a> for String { - type Iter = std::iter::Once>>; +impl<'a> ToAddresses<'a> for String { + type Iter = std::iter::Once>>; - fn to_dbus_addrs(&'a self) -> Self::Iter { + fn to_addresses(&'a self) -> Self::Iter { std::iter::once(self.as_str().try_into()) } } -impl<'a> ToDBusAddrs<'a> for Vec>> { - type Iter = std::iter::Cloned>>>; +impl<'a> ToAddresses<'a> for Vec>> { + type Iter = std::iter::Cloned>>>; /// Get an iterator over the D-Bus addresses. - fn to_dbus_addrs(&'a self) -> Self::Iter { + fn to_addresses(&'a self) -> Self::Iter { self.iter().cloned() } } diff --git a/zbus/src/address/tests.rs b/zbus/src/address/tests.rs index 3230cc7fb..9d11597ce 100644 --- a/zbus/src/address/tests.rs +++ b/zbus/src/address/tests.rs @@ -2,56 +2,56 @@ use std::{borrow::Cow, ffi::OsStr}; use super::{ transport::{AutolaunchScope, TcpFamily, Transport, UnixAddrKind}, - DBusAddr, + Address, }; #[test] fn parse_err() { assert_eq!( - DBusAddr::try_from("").unwrap_err().to_string(), + Address::try_from("").unwrap_err().to_string(), "Missing transport in address" ); assert_eq!( - DBusAddr::try_from("foo").unwrap_err().to_string(), + Address::try_from("foo").unwrap_err().to_string(), "Missing transport in address" ); assert_eq!( - DBusAddr::try_from("foo:").unwrap_err().to_string(), + Address::try_from("foo:").unwrap_err().to_string(), "Unsupported transport in address" ); assert_eq!( - DBusAddr::try_from("tcp:opt=%1").unwrap_err().to_string(), + Address::try_from("tcp:opt=%1").unwrap_err().to_string(), "Encoding error: Incomplete percent-encoded sequence" ); assert_eq!( - DBusAddr::try_from("tcp:opt=%1z").unwrap_err().to_string(), + Address::try_from("tcp:opt=%1z").unwrap_err().to_string(), "Encoding error: Invalid hexadecimal character in percent-encoded sequence" ); assert_eq!( - DBusAddr::try_from("tcp:opt=1\rz").unwrap_err().to_string(), + Address::try_from("tcp:opt=1\rz").unwrap_err().to_string(), "Encoding error: Invalid character in address" ); assert_eq!( - DBusAddr::try_from("tcp:guid=9406e28972c595c590766c9564ce623") + Address::try_from("tcp:guid=9406e28972c595c590766c9564ce623") .unwrap_err() .to_string(), "Invalid value for key: `guid`" ); assert_eq!( - DBusAddr::try_from("tcp:guid=9406e28972c595c590766c9564ce623g") + Address::try_from("tcp:guid=9406e28972c595c590766c9564ce623g") .unwrap_err() .to_string(), "Invalid value for key: `guid`" ); - let addr = DBusAddr::try_from("tcp:guid=9406e28972c595c590766c9564ce623f").unwrap(); + let addr = Address::try_from("tcp:guid=9406e28972c595c590766c9564ce623f").unwrap(); addr.guid().unwrap(); } #[test] fn parse_unix() { - let addr = DBusAddr::try_from("unix:path=/tmp/dbus-foo,guid=9406e28972c595c590766c9564ce623f") - .unwrap(); + let addr = + Address::try_from("unix:path=/tmp/dbus-foo,guid=9406e28972c595c590766c9564ce623f").unwrap(); let Transport::Unix(u) = addr.transport().unwrap() else { panic!(); }; @@ -61,41 +61,41 @@ fn parse_unix() { ); assert_eq!( - DBusAddr::try_from("unix:foo=blah").unwrap_err().to_string(), + Address::try_from("unix:foo=blah").unwrap_err().to_string(), "Other error: invalid `unix:` address, missing required key" ); assert_eq!( - DBusAddr::try_from("unix:path=/blah,abstract=foo") + Address::try_from("unix:path=/blah,abstract=foo") .unwrap_err() .to_string(), "Other error: invalid address, only one of `path` `dir` `tmpdir` `abstract` or `runtime` expected" ); assert_eq!( - DBusAddr::try_from("unix:runtime=no") + Address::try_from("unix:runtime=no") .unwrap_err() .to_string(), "Invalid value for key: `runtime`" ); - DBusAddr::try_from(String::from("unix:path=/tmp/foo")).unwrap(); + Address::try_from(String::from("unix:path=/tmp/foo")).unwrap(); } #[test] fn parse_launchd() { - let addr = DBusAddr::try_from("launchd:env=FOOBAR").unwrap(); + let addr = Address::try_from("launchd:env=FOOBAR").unwrap(); let Transport::Launchd(t) = addr.transport().unwrap() else { panic!(); }; assert_eq!(t.env(), "FOOBAR"); assert_eq!( - DBusAddr::try_from("launchd:weof").unwrap_err().to_string(), + Address::try_from("launchd:weof").unwrap_err().to_string(), "Missing key: `env`" ); } #[test] fn parse_systemd() { - let addr = DBusAddr::try_from("systemd:").unwrap(); + let addr = Address::try_from("systemd:").unwrap(); let Transport::Systemd(_) = addr.transport().unwrap() else { panic!(); }; @@ -103,7 +103,7 @@ fn parse_systemd() { #[test] fn parse_tcp() { - let addr = DBusAddr::try_from("tcp:host=localhost,bind=*,port=0,family=ipv4").unwrap(); + let addr = Address::try_from("tcp:host=localhost,bind=*,port=0,family=ipv4").unwrap(); let Transport::Tcp(t) = addr.transport().unwrap() else { panic!(); }; @@ -112,7 +112,7 @@ fn parse_tcp() { assert_eq!(t.port().unwrap(), 0); assert_eq!(t.family().unwrap(), TcpFamily::IPv4); - let addr = DBusAddr::try_from("tcp:").unwrap(); + let addr = Address::try_from("tcp:").unwrap(); let Transport::Tcp(t) = addr.transport().unwrap() else { panic!(); }; @@ -125,7 +125,7 @@ fn parse_tcp() { #[test] fn parse_nonce_tcp() { let addr = - DBusAddr::try_from("nonce-tcp:host=localhost,bind=*,port=0,family=ipv6,noncefile=foo") + Address::try_from("nonce-tcp:host=localhost,bind=*,port=0,family=ipv6,noncefile=foo") .unwrap(); let Transport::NonceTcp(t) = addr.transport().unwrap() else { panic!(); @@ -139,7 +139,7 @@ fn parse_nonce_tcp() { #[test] fn parse_unixexec() { - let addr = DBusAddr::try_from("unixexec:path=/bin/test,argv2=foo").unwrap(); + let addr = Address::try_from("unixexec:path=/bin/test,argv2=foo").unwrap(); let Transport::Unixexec(t) = addr.transport().unwrap() else { panic!(); }; @@ -148,14 +148,14 @@ fn parse_unixexec() { assert_eq!(t.argv(), &[(2, Cow::from("foo"))]); assert_eq!( - DBusAddr::try_from("unixexec:weof").unwrap_err().to_string(), + Address::try_from("unixexec:weof").unwrap_err().to_string(), "Missing key: `path`" ); } #[test] fn parse_autolaunch() { - let addr = DBusAddr::try_from("autolaunch:scope=*user").unwrap(); + let addr = Address::try_from("autolaunch:scope=*user").unwrap(); let Transport::Autolaunch(t) = addr.transport().unwrap() else { panic!(); }; @@ -165,7 +165,7 @@ fn parse_autolaunch() { #[test] #[cfg(feature = "vsock")] fn parse_vsock() { - let addr = DBusAddr::try_from("vsock:cid=12,port=32").unwrap(); + let addr = Address::try_from("vsock:cid=12,port=32").unwrap(); let Transport::Vsock(t) = addr.transport().unwrap() else { panic!(); }; @@ -173,9 +173,7 @@ fn parse_vsock() { assert_eq!(t.cid(), Some(12)); assert_eq!( - DBusAddr::try_from("vsock:port=abc") - .unwrap_err() - .to_string(), + Address::try_from("vsock:port=abc").unwrap_err().to_string(), "Invalid value for key: `port`" ); } diff --git a/zbus/src/address/transport/autolaunch.rs b/zbus/src/address/transport/autolaunch.rs index c69191336..d020fc2f9 100644 --- a/zbus/src/address/transport/autolaunch.rs +++ b/zbus/src/address/transport/autolaunch.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt}; -use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; /// Scope of autolaunch (Windows only) #[derive(Debug, PartialEq, Eq)] @@ -51,10 +51,10 @@ impl<'a> Autolaunch<'a> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Autolaunch<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Autolaunch<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let mut res = Autolaunch::default(); for (k, v) in s.key_val_iter() { diff --git a/zbus/src/address/transport/launchd.rs b/zbus/src/address/transport/launchd.rs index ffcb5c5cf..4f44486eb 100644 --- a/zbus/src/address/transport/launchd.rs +++ b/zbus/src/address/transport/launchd.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; /// `launchd:` D-Bus transport. /// @@ -20,10 +20,10 @@ impl<'a> Launchd<'a> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Launchd<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Launchd<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { for (k, v) in s.key_val_iter() { match (k, v) { ("env", Some(v)) => { diff --git a/zbus/src/address/transport/mod.rs b/zbus/src/address/transport/mod.rs index 9ba0cb2b6..24a7e7b2b 100644 --- a/zbus/src/address/transport/mod.rs +++ b/zbus/src/address/transport/mod.rs @@ -2,7 +2,7 @@ use std::fmt; -use super::{percent, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; mod autolaunch; pub use autolaunch::{Autolaunch, AutolaunchScope}; @@ -80,10 +80,10 @@ impl KeyValFmtAdd for Transport<'_> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Transport<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Transport<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let col = s.addr.find(':').ok_or(Error::MissingTransport)?; match &s.addr[..col] { "unix" => Ok(Self::Unix(s.try_into()?)), diff --git a/zbus/src/address/transport/nonce_tcp.rs b/zbus/src/address/transport/nonce_tcp.rs index 26cf077b2..f0240f50d 100644 --- a/zbus/src/address/transport/nonce_tcp.rs +++ b/zbus/src/address/transport/nonce_tcp.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, ffi::OsStr}; use super::{ percent::{decode_percents_os_str, decode_percents_str, EncOsStr}, tcp::TcpFamily, - DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result, + Address, Error, KeyValFmt, KeyValFmtAdd, Result, }; /// `nonce-tcp:` D-Bus transport. @@ -65,10 +65,10 @@ impl KeyValFmtAdd for NonceTcp<'_> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for NonceTcp<'a> { +impl<'a> TryFrom<&'a Address<'a>> for NonceTcp<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let mut res = NonceTcp::default(); for (k, v) in s.key_val_iter() { match (k, v) { diff --git a/zbus/src/address/transport/systemd.rs b/zbus/src/address/transport/systemd.rs index 7e9f1162e..5f6350f86 100644 --- a/zbus/src/address/transport/systemd.rs +++ b/zbus/src/address/transport/systemd.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use super::{DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{Address, Error, KeyValFmt, KeyValFmtAdd, Result}; /// `systemd:` D-Bus transport. /// @@ -11,10 +11,10 @@ pub struct Systemd<'a> { phantom: PhantomData<&'a ()>, } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Systemd<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Systemd<'a> { type Error = Error; - fn try_from(_s: &'a DBusAddr<'a>) -> Result { + fn try_from(_s: &'a Address<'a>) -> Result { Ok(Systemd { phantom: PhantomData, }) diff --git a/zbus/src/address/transport/tcp.rs b/zbus/src/address/transport/tcp.rs index 57aea6781..2f1969e2d 100644 --- a/zbus/src/address/transport/tcp.rs +++ b/zbus/src/address/transport/tcp.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt}; -use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; /// TCP IP address family #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -83,10 +83,10 @@ impl KeyValFmtAdd for Tcp<'_> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Tcp<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Tcp<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let mut res = Tcp::default(); for (k, v) in s.key_val_iter() { match (k, v) { diff --git a/zbus/src/address/transport/unix.rs b/zbus/src/address/transport/unix.rs index 8dfcefc7d..d105ce18b 100644 --- a/zbus/src/address/transport/unix.rs +++ b/zbus/src/address/transport/unix.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr}; use super::{ percent::{decode_percents, decode_percents_os_str, decode_percents_str, EncData, EncOsStr}, - DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result, + Address, Error, KeyValFmt, KeyValFmtAdd, Result, }; /// A sub-type of `unix:` transport. @@ -52,10 +52,10 @@ impl<'a> Unix<'a> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Unix<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Unix<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let mut kind = None; let mut iter = s.key_val_iter(); for (k, v) in &mut iter { diff --git a/zbus/src/address/transport/unixexec.rs b/zbus/src/address/transport/unixexec.rs index 740e65b1c..a74137049 100644 --- a/zbus/src/address/transport/unixexec.rs +++ b/zbus/src/address/transport/unixexec.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt}; use super::{ percent::{decode_percents_os_str, decode_percents_str, EncOsStr}, - DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result, + Address, Error, KeyValFmt, KeyValFmtAdd, Result, }; #[derive(Debug, PartialEq, Eq)] @@ -43,10 +43,10 @@ impl<'a> Unixexec<'a> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Unixexec<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Unixexec<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let mut path = None; let mut argv = Vec::new(); diff --git a/zbus/src/address/transport/vsock.rs b/zbus/src/address/transport/vsock.rs index 953d99b24..a160f00ba 100644 --- a/zbus/src/address/transport/vsock.rs +++ b/zbus/src/address/transport/vsock.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use super::{percent::decode_percents_str, DBusAddr, Error, KeyValFmt, KeyValFmtAdd, Result}; +use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; /// `vsock:` D-Bus transport. #[derive(Debug, PartialEq, Eq)] @@ -25,10 +25,10 @@ impl<'a> Vsock<'a> { } } -impl<'a> TryFrom<&'a DBusAddr<'a>> for Vsock<'a> { +impl<'a> TryFrom<&'a Address<'a>> for Vsock<'a> { type Error = Error; - fn try_from(s: &'a DBusAddr<'a>) -> Result { + fn try_from(s: &'a Address<'a>) -> Result { let mut port = None; let mut cid = None; diff --git a/zbus/src/blocking/connection/builder.rs b/zbus/src/blocking/connection/builder.rs index b9fe7dac4..b6d86d08f 100644 --- a/zbus/src/blocking/connection/builder.rs +++ b/zbus/src/blocking/connection/builder.rs @@ -15,7 +15,7 @@ use zvariant::{ObjectPath, Str}; #[cfg(feature = "p2p")] use crate::Guid; use crate::{ - address::ToDBusAddrs, blocking::Connection, connection::socket::BoxedSplit, + address::ToAddresses, blocking::Connection, connection::socket::BoxedSplit, names::WellKnownName, object_server::Interface, utils::block_on, AuthMechanism, Error, Result, }; @@ -42,7 +42,7 @@ impl<'a> Builder<'a> { /// [D-Bus bus address]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses pub fn address<'t, A>(address: &'t A) -> Result where - A: ToDBusAddrs<'t> + ?Sized, + A: ToAddresses<'t> + ?Sized, { crate::connection::Builder::address(address).map(Self) } diff --git a/zbus/src/connection/builder.rs b/zbus/src/connection/builder.rs index daaf94807..9e7083f44 100644 --- a/zbus/src/connection/builder.rs +++ b/zbus/src/connection/builder.rs @@ -24,7 +24,7 @@ use vsock::VsockStream; use zvariant::{ObjectPath, Str}; use crate::{ - address::{DBusAddr, ToDBusAddrs}, + address::{Address, ToAddresses}, names::{InterfaceName, WellKnownName}, object_server::{ArcInterface, Interface}, Connection, Error, Executor, Guid, OwnedGuid, Result, @@ -48,7 +48,7 @@ enum Target { feature = "tokio-vsock" ))] VsockStream(VsockStream), - Address(Vec>), + Address(Vec>), Socket(Split, Box>), AuthenticatedSocket(Split, Box>), } @@ -121,10 +121,10 @@ impl<'a> Builder<'a> { /// [D-Bus bus address]: https://dbus.freedesktop.org/doc/dbus-specification.html#addresses pub fn address<'t, A>(address: &'t A) -> Result where - A: ToDBusAddrs<'t> + ?Sized, + A: ToAddresses<'t> + ?Sized, { let addr = address - .to_dbus_addrs() + .to_addresses() .filter_map(std::result::Result::ok) .map(|a| a.to_owned()) .collect(); diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect.rs index adf17fb43..e137ba751 100644 --- a/zbus/src/connection/connect.rs +++ b/zbus/src/connection/connect.rs @@ -2,7 +2,7 @@ use std::{future::Future, pin::Pin}; use tracing::debug; use crate::{ - address::{transport::Transport, DBusAddr}, + address::{transport::Transport, Address}, Error, Guid, OwnedGuid, Result, }; @@ -13,7 +13,7 @@ mod win32; type ConnectResult = Result<(BoxedSplit, Option)>; -fn connect(addr: &DBusAddr<'_>) -> Pin>> { +fn connect(addr: &Address<'_>) -> Pin>> { let addr = addr.to_owned(); Box::pin(async move { let guid = match addr.guid() { @@ -45,7 +45,7 @@ fn connect(addr: &DBusAddr<'_>) -> Pin>> } pub(crate) async fn connect_address( - address: &[DBusAddr<'_>], + address: &[Address<'_>], ) -> Result<(BoxedSplit, Option)> { for addr in address { match connect(addr).await { diff --git a/zbus/src/connection/connect/macos.rs b/zbus/src/connection/connect/macos.rs index 4125285bb..21db9b20c 100644 --- a/zbus/src/connection/connect/macos.rs +++ b/zbus/src/connection/connect/macos.rs @@ -2,12 +2,12 @@ use super::socket; use crate::{ - address::{transport::Transport, DBusAddr}, + address::{transport::Transport, Address}, process::run, Error, Result, }; -async fn launchd_bus_address(env_key: &str) -> Result> { +async fn launchd_bus_address(env_key: &str) -> Result> { let output = run("launchctl", ["getenv", env_key]) .await .expect("failed to wait on launchctl output"); @@ -38,11 +38,11 @@ pub(crate) async fn connect( #[cfg(test)] mod tests { - use crate::address::{transport::Transport, DBusAddr}; + use crate::address::{transport::Transport, Address}; #[test] fn connect_launchd_session_bus() { - let addr: DBusAddr<'_> = "launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET" + let addr: Address<'_> = "launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET" .try_into() .unwrap(); let launchd = match addr.transport().unwrap() { diff --git a/zbus/src/connection/connect/win32.rs b/zbus/src/connection/connect/win32.rs index 8da717002..481e4098a 100644 --- a/zbus/src/connection/connect/win32.rs +++ b/zbus/src/connection/connect/win32.rs @@ -2,7 +2,7 @@ use super::BoxedSplit; use crate::{ - address::{transport::Transport, DBusAddr}, + address::{transport::Transport, Address}, win32::autolaunch_bus_address, Error, OwnedGuid, Result, }; @@ -16,7 +16,7 @@ pub(crate) async fn connect( )); } - let addr: DBusAddr<'_> = autolaunch_bus_address()?.try_into()?; + let addr: Address<'_> = autolaunch_bus_address()?.try_into()?; if let Transport::Autolaunch(_) = addr.transport()? { return Err(Error::Address("Recursive autolaunch: address".into())); @@ -29,9 +29,9 @@ pub(crate) async fn connect( mod tests { #[test] fn connect_autolaunch_session_bus() { - use crate::address::{transport::Transport, DBusAddr}; + use crate::address::{transport::Transport, Address}; - let addr: DBusAddr<'_> = "autolaunch:".try_into().unwrap(); + let addr: Address<'_> = "autolaunch:".try_into().unwrap(); let autolaunch = match addr.transport().unwrap() { Transport::Autolaunch(l) => l, _ => unreachable!(), diff --git a/zbus/src/connection/socket/tcp.rs b/zbus/src/connection/socket/tcp.rs index 05ccebb90..d66db8108 100644 --- a/zbus/src/connection/socket/tcp.rs +++ b/zbus/src/connection/socket/tcp.rs @@ -277,13 +277,13 @@ pub(crate) async fn connect_nonce( #[cfg(test)] mod tests { - use crate::address::{transport::Transport, DBusAddr}; + use crate::address::{transport::Transport, Address}; #[test] fn connect() { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let port = listener.local_addr().unwrap().port(); - let addr: DBusAddr<'_> = format!("tcp:host=localhost,port={port}") + let addr: Address<'_> = format!("tcp:host=localhost,port={port}") .try_into() .unwrap(); let tcp = match addr.transport().unwrap() { @@ -318,7 +318,7 @@ mod tests { PercentEncoded(cookie.path().to_str().unwrap().as_ref()) ); - let addr: DBusAddr<'_> = + let addr: Address<'_> = format!("nonce-tcp:host=localhost,port={port},noncefile={encoded_path}") .try_into() .unwrap(); diff --git a/zbus/src/lib.rs b/zbus/src/lib.rs index e9d37877a..b4abf950f 100644 --- a/zbus/src/lib.rs +++ b/zbus/src/lib.rs @@ -47,7 +47,7 @@ mod error; pub use error::*; pub mod address; -pub use address::DBusAddr; +pub use address::Address; mod guid; pub use guid::*; From 2dc80c449e1e8449c3a3f5bb833a63483485b539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 24 Sep 2024 10:30:14 +0400 Subject: [PATCH 20/21] =?UTF-8?q?=F0=9F=A9=B9=20zb:=20make=20address=20par?= =?UTF-8?q?sing=20target-specific?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some addresses should only be used on specific target OS. This makes the address handling specific to the target, which will make its usage impractical for some use cases (web, configuration etc) As requested by Zeeshan. --- zbus/src/address/tests.rs | 8 +++++++- zbus/src/address/transport/autolaunch.rs | 18 ++++++++++++++++-- zbus/src/address/transport/mod.rs | 16 +++++++++++++++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/zbus/src/address/tests.rs b/zbus/src/address/tests.rs index 9d11597ce..f76123a7e 100644 --- a/zbus/src/address/tests.rs +++ b/zbus/src/address/tests.rs @@ -1,7 +1,9 @@ use std::{borrow::Cow, ffi::OsStr}; +#[cfg(target_os = "windows")] +use super::transport::AutolaunchScope; use super::{ - transport::{AutolaunchScope, TcpFamily, Transport, UnixAddrKind}, + transport::{TcpFamily, Transport, UnixAddrKind}, Address, }; @@ -79,6 +81,7 @@ fn parse_unix() { Address::try_from(String::from("unix:path=/tmp/foo")).unwrap(); } +#[cfg(target_os = "macos")] #[test] fn parse_launchd() { let addr = Address::try_from("launchd:env=FOOBAR").unwrap(); @@ -93,6 +96,7 @@ fn parse_launchd() { ); } +#[cfg(target_os = "linux")] #[test] fn parse_systemd() { let addr = Address::try_from("systemd:").unwrap(); @@ -156,9 +160,11 @@ fn parse_unixexec() { #[test] fn parse_autolaunch() { let addr = Address::try_from("autolaunch:scope=*user").unwrap(); + #[allow(unused)] let Transport::Autolaunch(t) = addr.transport().unwrap() else { panic!(); }; + #[cfg(target_os = "windows")] assert_eq!(t.scope().unwrap(), &AutolaunchScope::User); } diff --git a/zbus/src/address/transport/autolaunch.rs b/zbus/src/address/transport/autolaunch.rs index d020fc2f9..3d56a907f 100644 --- a/zbus/src/address/transport/autolaunch.rs +++ b/zbus/src/address/transport/autolaunch.rs @@ -1,8 +1,13 @@ +use std::marker::PhantomData; +#[cfg(target_os = "windows")] use std::{borrow::Cow, fmt}; -use super::{percent::decode_percents_str, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; +#[cfg(target_os = "windows")] +use super::percent::decode_percents_str; +use super::{Address, Error, KeyValFmt, KeyValFmtAdd, Result}; /// Scope of autolaunch (Windows only) +#[cfg(target_os = "windows")] #[derive(Debug, PartialEq, Eq)] #[non_exhaustive] pub enum AutolaunchScope<'a> { @@ -14,6 +19,7 @@ pub enum AutolaunchScope<'a> { Other(Cow<'a, str>), } +#[cfg(target_os = "windows")] impl fmt::Display for AutolaunchScope<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -24,6 +30,7 @@ impl fmt::Display for AutolaunchScope<'_> { } } +#[cfg(target_os = "windows")] impl<'a> TryFrom> for AutolaunchScope<'a> { type Error = Error; @@ -41,10 +48,13 @@ impl<'a> TryFrom> for AutolaunchScope<'a> { /// #[derive(Debug, PartialEq, Eq, Default)] pub struct Autolaunch<'a> { + #[cfg(target_os = "windows")] scope: Option>, + phantom: PhantomData<&'a ()>, } impl<'a> Autolaunch<'a> { + #[cfg(target_os = "windows")] /// Scope of autolaunch (Windows only) pub fn scope(&self) -> Option<&AutolaunchScope<'a>> { self.scope.as_ref() @@ -55,10 +65,12 @@ impl<'a> TryFrom<&'a Address<'a>> for Autolaunch<'a> { type Error = Error; fn try_from(s: &'a Address<'a>) -> Result { + #[allow(unused_mut)] let mut res = Autolaunch::default(); for (k, v) in s.key_val_iter() { match (k, v) { + #[cfg(target_os = "windows")] ("scope", Some(v)) => { res.scope = Some(decode_percents_str(v)?.try_into()?); } @@ -72,6 +84,8 @@ impl<'a> TryFrom<&'a Address<'a>> for Autolaunch<'a> { impl KeyValFmtAdd for Autolaunch<'_> { fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { - kv.add("scope", self.scope()) + #[cfg(target_os = "windows")] + let kv = kv.add("scope", self.scope()); + kv } } diff --git a/zbus/src/address/transport/mod.rs b/zbus/src/address/transport/mod.rs index 24a7e7b2b..3d8a2585d 100644 --- a/zbus/src/address/transport/mod.rs +++ b/zbus/src/address/transport/mod.rs @@ -5,15 +5,21 @@ use std::fmt; use super::{percent, Address, Error, KeyValFmt, KeyValFmtAdd, Result}; mod autolaunch; -pub use autolaunch::{Autolaunch, AutolaunchScope}; +pub use autolaunch::Autolaunch; +#[cfg(target_os = "windows")] +pub use autolaunch::AutolaunchScope; +#[cfg(target_os = "macos")] mod launchd; +#[cfg(target_os = "macos")] pub use launchd::Launchd; mod nonce_tcp; pub use nonce_tcp::NonceTcp; +#[cfg(target_os = "linux")] mod systemd; +#[cfg(target_os = "linux")] pub use systemd::Systemd; mod tcp; @@ -34,8 +40,10 @@ pub use vsock::Vsock; pub enum Transport<'a> { /// Unix Domain Sockets transport. Unix(unix::Unix<'a>), + #[cfg(target_os = "macos")] /// launchd transport. Launchd(launchd::Launchd<'a>), + #[cfg(target_os = "linux")] /// systemd transport. Systemd(systemd::Systemd<'a>), /// TCP Sockets transport. @@ -54,7 +62,9 @@ impl fmt::Display for Transport<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Unix(_) => write!(f, "unix"), + #[cfg(target_os = "macos")] Self::Launchd(_) => write!(f, "launchd"), + #[cfg(target_os = "linux")] Self::Systemd(_) => write!(f, "systemd"), Self::Tcp(_) => write!(f, "tcp"), Self::NonceTcp(_) => write!(f, "nonce-tcp"), @@ -69,7 +79,9 @@ impl KeyValFmtAdd for Transport<'_> { fn key_val_fmt_add<'a: 'b, 'b>(&'a self, kv: KeyValFmt<'b>) -> KeyValFmt<'b> { match self { Self::Unix(t) => t.key_val_fmt_add(kv), + #[cfg(target_os = "macos")] Self::Launchd(t) => t.key_val_fmt_add(kv), + #[cfg(target_os = "linux")] Self::Systemd(t) => t.key_val_fmt_add(kv), Self::Tcp(t) => t.key_val_fmt_add(kv), Self::NonceTcp(t) => t.key_val_fmt_add(kv), @@ -87,7 +99,9 @@ impl<'a> TryFrom<&'a Address<'a>> for Transport<'a> { let col = s.addr.find(':').ok_or(Error::MissingTransport)?; match &s.addr[..col] { "unix" => Ok(Self::Unix(s.try_into()?)), + #[cfg(target_os = "macos")] "launchd" => Ok(Self::Launchd(s.try_into()?)), + #[cfg(target_os = "linux")] "systemd" => Ok(Self::Systemd(s.try_into()?)), "tcp" => Ok(Self::Tcp(s.try_into()?)), "nonce-tcp" => Ok(Self::NonceTcp(s.try_into()?)), From 0b4362bf667669461c5f7d19ef8f5a806e39c79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 30 Sep 2024 21:58:46 +0400 Subject: [PATCH 21/21] =?UTF-8?q?=F0=9F=9A=9A=20zb:=20src/connection/conne?= =?UTF-8?q?ct.rs=20=E2=9F=B6=20=20=20src/connection/connect/mod.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As requested by Zeenshan. --- zbus/src/connection/{connect.rs => connect/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename zbus/src/connection/{connect.rs => connect/mod.rs} (100%) diff --git a/zbus/src/connection/connect.rs b/zbus/src/connection/connect/mod.rs similarity index 100% rename from zbus/src/connection/connect.rs rename to zbus/src/connection/connect/mod.rs