#![cfg(feature = "std")]
use crate::connection::{Connection, StdConnection};
use crate::{Error, Fd, Result};
use alloc::format;
use core::fmt;
use std::io::{IoSlice, IoSliceMut};
use std::net::{Ipv4Addr, SocketAddr, TcpStream};
use alloc::{string::String, vec::Vec};
use x11rb_protocol::parse_display::{parse_display, ConnectAddress, ParsedDisplay};
use x11rb_protocol::xauth::Family;
cfg_std_unix! {
use crate::connection::SendmsgConnection;
use std::os::unix::{net::UnixStream, io::{AsRawFd, RawFd}};
}
cfg_std_windows! {
use std::os::windows::io::{AsRawSocket, RawSocket};
}
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::path::Path;
cfg_async! {
mod nb_connect;
use core::future::Future;
}
pub struct NameConnection {
inner: Inner,
}
#[derive(Debug)]
enum Inner {
Tcp(StdConnection<TcpStream>),
#[cfg(unix)]
Unix(SendmsgConnection),
}
impl fmt::Debug for NameConnection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
}
}
impl NameConnection {
pub fn new(name: Option<&str>) -> Result<Self> {
let parsed_display =
parse_display(name).ok_or_else(|| Error::couldnt_parse_display(name.is_none()))?;
Self::from_parsed_display(&parsed_display, name.is_none())
}
pub fn from_parsed_display(parsed_display: &ParsedDisplay, is_env: bool) -> Result<Self> {
let mut error: Option<Error> = None;
for connect_address in parsed_display.connect_instruction() {
match connect_address {
ConnectAddress::Hostname(hostname, port) => {
match TcpStream::connect((hostname, port)) {
Ok(stream) => return Ok(Self::from_tcp_stream(stream)),
Err(err) => error = Some(Error::io(err)),
}
}
ConnectAddress::Socket(path) => {
cfg_if::cfg_if! {
if #[cfg(unix)] {
#[cfg(any(target_os = "linux", target_os = "android"))]
match connect_abstract(&path) {
Ok(stream) => return Ok(Self::from_unix_stream(stream)),
Err(e) => {
tracing::error!("Encountered error connection to abstract socket: {:?}", e);
}
}
match UnixStream::connect(path) {
Ok(stream) => return Ok(Self::from_unix_stream(stream)),
Err(err) => { error = Some(Error::io(err)) },
}
} else {
let _ = path;
error = Some(Error::make_unsupported(crate::Unsupported::Socket));
}
}
}
addr => {
error = Some(Error::make_msg(format!(
"Unsupported connection address: {:?}",
addr,
)));
}
}
}
Err(error.unwrap_or_else(|| Error::couldnt_parse_display(is_env)))
}
pub(crate) fn from_tcp_stream(stream: TcpStream) -> Self {
NameConnection {
inner: Inner::Tcp(stream.into()),
}
}
pub fn get_address(&self) -> Result<(Family, Vec<u8>)> {
#[allow(irrefutable_let_patterns)]
if let Inner::Tcp(ref connection) = self.inner {
let ip = match connection.peer_addr().map_err(Error::io)? {
SocketAddr::V4(ip) => *ip.ip(),
SocketAddr::V6(ip) => {
let ip = *ip.ip();
if ip.is_loopback() {
Ipv4Addr::LOCALHOST
} else if let Some(ip) = ip.to_ipv4() {
ip
} else {
return Ok((Family::INTERNET6, ip.octets().to_vec()));
}
}
};
if !ip.is_loopback() {
return Ok((Family::INTERNET, ip.octets().to_vec()));
}
}
let hostname = gethostname::gethostname()
.into_string()
.map_or_else(|_| Vec::new(), String::into_bytes);
Ok((Family::LOCAL, hostname))
}
#[must_use]
pub fn take_error(&self) -> Option<Error> {
let taken_error = match self.inner {
Inner::Tcp(ref t) => t.take_error(),
#[cfg(unix)]
Inner::Unix(ref u) => u.as_ref().take_error(),
};
match taken_error {
Err(err) | Ok(Some(err)) => Some(Error::io(err)),
_ => None,
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn connect_abstract(path: &Path) -> Result<UnixStream> {
use alloc::vec;
use socket2::{Domain, SockAddr, Socket, Type};
use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
let bytes = path.as_os_str().as_bytes();
let mut abstract_socket_buf = vec![0; bytes.len() + 1];
abstract_socket_buf[1..].copy_from_slice(bytes);
let abstract_socket = OsStr::from_bytes(&abstract_socket_buf);
let sock_addr = SockAddr::unix(abstract_socket)?;
let sock = Socket::new(Domain::UNIX, Type::STREAM, None)?;
sock.connect(&sock_addr)?;
Ok(sock.into())
}
cfg_async! {
impl NameConnection {
pub fn from_parsed_display_async<'a, Fut, R: 'a>(
parsed_display: &'a ParsedDisplay,
is_env: bool,
resolv: impl FnMut(NameConnection) -> Fut + 'a,
) -> impl Future<Output = Result<R>> + 'a where Fut: Future<Output = Result<R>> + 'a {
nb_connect::nb_connect(parsed_display, is_env, resolv)
}
}
}
cfg_std_unix! {
impl NameConnection {
pub(crate) fn from_unix_stream(stream: UnixStream) -> Self {
NameConnection {
inner: Inner::Unix(stream.into()),
}
}
}
impl AsRawFd for NameConnection {
fn as_raw_fd(&self) -> RawFd {
match self.inner {
Inner::Tcp(ref connection) => connection.as_raw_fd(),
Inner::Unix(ref connection) => connection.as_raw_fd(),
}
}
}
}
cfg_std_windows! {
impl AsRawSocket for NameConnection {
fn as_raw_socket(&self) -> RawSocket {
match self.inner {
Inner::Tcp(ref connection) => connection.as_raw_socket(),
}
}
}
}
macro_rules! forward_impl {
(&$self: expr, $fn_name: ident, $($val: expr),*) => {
match ($self).inner {
Inner::Tcp(ref c) => {
#[allow(unused_mut)]
let mut c = c;
c.$fn_name($($val),*)
}
#[cfg(unix)]
Inner::Unix(ref c) => {
#[allow(unused_mut)]
let mut c = c;
c.$fn_name($($val),*)
}
}
};
(&mut $self: expr, $fn_name: ident, $($val: expr),*) => {
match ($self).inner {
Inner::Tcp(ref mut c) => c.$fn_name($($val),*),
#[cfg(unix)]
Inner::Unix(ref mut c) => c.$fn_name($($val),*),
}
}
}
impl Connection for NameConnection {
fn send_slices_and_fds(&mut self, slices: &[IoSlice<'_>], fds: &mut Vec<Fd>) -> Result<usize> {
forward_impl!(&mut self, send_slices_and_fds, slices, fds)
}
fn recv_slices_and_fds(
&mut self,
slices: &mut [IoSliceMut<'_>],
fds: &mut Vec<Fd>,
) -> Result<usize> {
forward_impl!(&mut self, recv_slices_and_fds, slices, fds)
}
fn send_slices(&mut self, slices: &[crate::connection::IoSlice<'_>]) -> Result<usize> {
forward_impl!(&mut self, send_slices, slices)
}
fn send_slice(&mut self, slice: &[u8]) -> Result<usize> {
forward_impl!(&mut self, send_slice, slice)
}
fn recv_slice_and_fds(&mut self, slice: &mut [u8], fds: &mut Vec<Fd>) -> Result<usize> {
forward_impl!(&mut self, recv_slice_and_fds, slice, fds)
}
fn recv_slice(&mut self, slice: &mut [u8]) -> Result<usize> {
forward_impl!(&mut self, recv_slice, slice)
}
fn flush(&mut self) -> Result<()> {
forward_impl!(&mut self, flush,)
}
fn shutdown(&self) -> Result<()> {
forward_impl!(&self, shutdown,)
}
fn non_blocking_recv_slice_and_fds(
&mut self,
slice: &mut [u8],
fds: &mut Vec<Fd>,
) -> Result<usize> {
forward_impl!(&mut self, non_blocking_recv_slice_and_fds, slice, fds)
}
fn non_blocking_recv_slices_and_fds(
&mut self,
slices: &mut [IoSliceMut<'_>],
fds: &mut Vec<Fd>,
) -> Result<usize> {
forward_impl!(&mut self, non_blocking_recv_slices_and_fds, slices, fds)
}
}
impl Connection for &NameConnection {
fn send_slices_and_fds(&mut self, slices: &[IoSlice<'_>], fds: &mut Vec<Fd>) -> Result<usize> {
forward_impl!(&self, send_slices_and_fds, slices, fds)
}
fn recv_slices_and_fds(
&mut self,
slices: &mut [IoSliceMut<'_>],
fds: &mut Vec<Fd>,
) -> Result<usize> {
forward_impl!(&self, recv_slices_and_fds, slices, fds)
}
fn send_slices(&mut self, slices: &[crate::connection::IoSlice<'_>]) -> Result<usize> {
forward_impl!(&self, send_slices, slices)
}
fn send_slice(&mut self, slice: &[u8]) -> Result<usize> {
forward_impl!(&self, send_slice, slice)
}
fn recv_slice_and_fds(&mut self, slice: &mut [u8], fds: &mut Vec<Fd>) -> Result<usize> {
forward_impl!(&self, recv_slice_and_fds, slice, fds)
}
fn recv_slice(&mut self, slice: &mut [u8]) -> Result<usize> {
forward_impl!(&self, recv_slice, slice)
}
fn flush(&mut self) -> Result<()> {
forward_impl!(&self, flush,)
}
fn shutdown(&self) -> Result<()> {
forward_impl!(&self, shutdown,)
}
fn non_blocking_recv_slice_and_fds(
&mut self,
slice: &mut [u8],
fds: &mut Vec<Fd>,
) -> Result<usize> {
forward_impl!(&self, non_blocking_recv_slice_and_fds, slice, fds)
}
fn non_blocking_recv_slices_and_fds(
&mut self,
slices: &mut [IoSliceMut<'_>],
fds: &mut Vec<Fd>,
) -> Result<usize> {
forward_impl!(&self, non_blocking_recv_slices_and_fds, slices, fds)
}
}