about summary refs log tree commit diff
path: root/library/std
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-09-10 20:29:04 +0200
committerGitHub <noreply@github.com>2025-09-10 20:29:04 +0200
commite79630da0b16a4514f34f6283bbfc17f2e239605 (patch)
treeb9473efac94154dab25d9be8d6d855b55b8a411e /library/std
parent565a9ca63e9df4b223fed0da01f15e578acfb538 (diff)
parent207a01e88f05f028f7a6c0db0d324fbedb8178a4 (diff)
downloadrust-e79630da0b16a4514f34f6283bbfc17f2e239605.tar.gz
rust-e79630da0b16a4514f34f6283bbfc17f2e239605.zip
Rollup merge of #145327 - joboet:net-addr-sgx-hack, r=tgross35
std: make address resolution weirdness local to SGX

Currently, the implementations of `TcpStream::connect` and its cousins take an `io::Result<&SocketAddr>` as argument, which is very weird, as most of them then `?`-try the result immediately to access the actual address. This weirdness is however necessitated by a peculiarity of the SGX networking implementation:

SGX doesn't support DNS resolution but rather accepts hostnames in the same place as socket addresses. So, to make e.g.
```rust
TcpStream::connect("example.com:80")`
```
work, the DNS lookup returns a special error (`NonIpSockAddr`) instead, which contains the hostname being looked up. When `.to_socket_addrs()` fails, the `each_addr` function used to select an address will pass the error to the inner `TcpStream::connect` implementation, which in SGX's case will inspect the error and try recover the hostname from it. If
that succeeds, it continues with the found hostname.

This is pretty obviously a terrible hack and leads to buggy code (for instance, when users use the result of `.to_socket_addrs()` in their own `ToSocketAddrs` implementation to select from a list of possible URLs, the only URL used will be that of the last item tried). Still, without changes to the SGX usercall ABI, it cannot be avoided.

Therefore, this PR aims to minimise the impact of that weirdness and remove it from all non-SGX platforms. The inner `TcpStream::connect`, et al. functions now receive the `ToSocketAddrs` type directly and call `each_addr` (which is moved to `sys::net::connection`) themselves. On SGX, the implementation uses a special `each_addr` which contains the whole pass-hostname-through-error hack.

As well as making the code cleaner, this also opens up the possibility of reusing newly created sockets even if a connection request fails – but I've left that for another PR.

CC `@raoulstrackx`
Diffstat (limited to 'library/std')
-rw-r--r--library/std/src/io/error.rs3
-rw-r--r--library/std/src/net/mod.rs21
-rw-r--r--library/std/src/net/tcp.rs4
-rw-r--r--library/std/src/net/tcp/tests.rs2
-rw-r--r--library/std/src/net/udp.rs4
-rw-r--r--library/std/src/net/udp/tests.rs1
-rw-r--r--library/std/src/sys/net/connection/mod.rs57
-rw-r--r--library/std/src/sys/net/connection/sgx.rs81
-rw-r--r--library/std/src/sys/net/connection/socket/mod.rs (renamed from library/std/src/sys/net/connection/socket.rs)108
-rw-r--r--library/std/src/sys/net/connection/uefi/mod.rs27
-rw-r--r--library/std/src/sys/net/connection/unsupported.rs10
-rw-r--r--library/std/src/sys/net/connection/wasip1.rs10
-rw-r--r--library/std/src/sys/net/connection/xous/tcplistener.rs26
-rw-r--r--library/std/src/sys/net/connection/xous/tcpstream.rs9
-rw-r--r--library/std/src/sys/net/connection/xous/udp.rs81
-rw-r--r--library/std/src/sys/net/mod.rs48
16 files changed, 276 insertions, 216 deletions
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index 57a980d6acd..21e82d43a80 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -95,6 +95,9 @@ impl Error {
 
     pub(crate) const ZERO_TIMEOUT: Self =
         const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout");
+
+    pub(crate) const NO_ADDRESSES: Self =
+        const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses");
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs
index ddd3b68dd2d..40f1a93e39d 100644
--- a/library/std/src/net/mod.rs
+++ b/library/std/src/net/mod.rs
@@ -34,7 +34,6 @@ pub use self::tcp::IntoIncoming;
 pub use self::tcp::{Incoming, TcpListener, TcpStream};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::udp::UdpSocket;
-use crate::io::{self, ErrorKind};
 
 mod ip_addr;
 mod socket_addr;
@@ -67,23 +66,3 @@ pub enum Shutdown {
     #[stable(feature = "rust1", since = "1.0.0")]
     Both,
 }
-
-fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T>
-where
-    F: FnMut(io::Result<&SocketAddr>) -> io::Result<T>,
-{
-    let addrs = match addr.to_socket_addrs() {
-        Ok(addrs) => addrs,
-        Err(e) => return f(Err(e)),
-    };
-    let mut last_err = None;
-    for addr in addrs {
-        match f(Ok(&addr)) {
-            Ok(l) => return Ok(l),
-            Err(e) => last_err = Some(e),
-        }
-    }
-    Err(last_err.unwrap_or_else(|| {
-        io::const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses")
-    }))
-}
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index 10685b49319..ae50f531a71 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -167,7 +167,7 @@ impl TcpStream {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
-        super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream)
+        net_imp::TcpStream::connect(addr).map(TcpStream)
     }
 
     /// Opens a TCP connection to a remote host with a timeout.
@@ -782,7 +782,7 @@ impl TcpListener {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
-        super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener)
+        net_imp::TcpListener::bind(addr).map(TcpListener)
     }
 
     /// Returns the local socket address of this listener.
diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs
index 03003037b29..7c7ef7b2f70 100644
--- a/library/std/src/net/tcp/tests.rs
+++ b/library/std/src/net/tcp/tests.rs
@@ -1,5 +1,5 @@
 use crate::io::prelude::*;
-use crate::io::{BorrowedBuf, IoSlice, IoSliceMut};
+use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut};
 use crate::mem::MaybeUninit;
 use crate::net::test::{next_test_ip4, next_test_ip6};
 use crate::net::*;
diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs
index a97b3299774..72e292e3d15 100644
--- a/library/std/src/net/udp.rs
+++ b/library/std/src/net/udp.rs
@@ -120,7 +120,7 @@ impl UdpSocket {
     /// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`].
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
-        super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket)
+        net_imp::UdpSocket::bind(addr).map(UdpSocket)
     }
 
     /// Receives a single datagram message on the socket. On success, returns the number
@@ -677,7 +677,7 @@ impl UdpSocket {
     /// on the platform.
     #[stable(feature = "net2_mutators", since = "1.9.0")]
     pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
-        super::each_addr(addr, |addr| self.0.connect(addr))
+        self.0.connect(addr)
     }
 
     /// Sends data on the socket to the remote address to which it is connected.
diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs
index 91da3135f97..0638b36c54f 100644
--- a/library/std/src/net/udp/tests.rs
+++ b/library/std/src/net/udp/tests.rs
@@ -1,3 +1,4 @@
+use crate::io::ErrorKind;
 use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6};
 use crate::net::*;
 use crate::sync::mpsc::channel;
diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs
new file mode 100644
index 00000000000..7f9636a8ccf
--- /dev/null
+++ b/library/std/src/sys/net/connection/mod.rs
@@ -0,0 +1,57 @@
+cfg_select! {
+    any(
+        all(target_family = "unix", not(target_os = "l4re")),
+        target_os = "windows",
+        target_os = "hermit",
+        all(target_os = "wasi", target_env = "p2"),
+        target_os = "solid_asp3",
+    ) => {
+        mod socket;
+        pub use socket::*;
+    }
+    all(target_vendor = "fortanix", target_env = "sgx") => {
+        mod sgx;
+        pub use sgx::*;
+    }
+    all(target_os = "wasi", target_env = "p1") => {
+        mod wasip1;
+        pub use wasip1::*;
+    }
+    target_os = "xous" => {
+        mod xous;
+        pub use xous::*;
+    }
+    target_os = "uefi" => {
+        mod uefi;
+        pub use uefi::*;
+    }
+    _ => {
+        mod unsupported;
+        pub use unsupported::*;
+    }
+}
+
+#[cfg_attr(
+    // Make sure that this is used on some platforms at least.
+    not(any(target_os = "linux", target_os = "windows")),
+    allow(dead_code)
+)]
+fn each_addr<A: crate::net::ToSocketAddrs, F, T>(addr: A, mut f: F) -> crate::io::Result<T>
+where
+    F: FnMut(&crate::net::SocketAddr) -> crate::io::Result<T>,
+{
+    use crate::io::Error;
+
+    let mut last_err = None;
+    for addr in addr.to_socket_addrs()? {
+        match f(&addr) {
+            Ok(l) => return Ok(l),
+            Err(e) => last_err = Some(e),
+        }
+    }
+
+    match last_err {
+        Some(err) => Err(err),
+        None => Err(Error::NO_ADDRESSES),
+    }
+}
diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs
index 2389fd1bcb6..9b54571997d 100644
--- a/library/std/src/sys/net/connection/sgx.rs
+++ b/library/std/src/sys/net/connection/sgx.rs
@@ -1,3 +1,5 @@
+use crate::error;
+use crate::fmt::{self, Write};
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
 use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
 use crate::sync::Arc;
@@ -5,7 +7,6 @@ use crate::sys::abi::usercalls;
 use crate::sys::fd::FileDesc;
 use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported};
 use crate::time::Duration;
-use crate::{error, fmt};
 
 const DEFAULT_FAKE_TTL: u32 = 64;
 
@@ -63,18 +64,52 @@ impl fmt::Debug for TcpStream {
     }
 }
 
-fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> {
-    match result {
-        Ok(saddr) => Ok(saddr.to_string()),
-        // need to downcast twice because io::Error::into_inner doesn't return the original
-        // value if the conversion fails
-        Err(e) => {
-            if e.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()).is_some() {
-                Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host)
-            } else {
-                Err(e)
+/// Converts each address in `addr` into a hostname.
+///
+/// SGX doesn't support DNS resolution but rather accepts hostnames in
+/// the same place as socket addresses. So, to make e.g.
+/// ```rust
+/// TcpStream::connect("example.com:80")`
+/// ```
+/// work, the DNS lookup returns a special error (`NonIpSockAddr`) instead,
+/// which contains the hostname being looked up. When `.to_socket_addrs()`
+/// fails, we inspect the error and try recover the hostname from it. If that
+/// succeeds, we thus continue with the hostname.
+///
+/// This is a terrible hack and leads to buggy code. For instance, when users
+/// use the result of `.to_socket_addrs()` in their own `ToSocketAddrs`
+/// implementation to select from a list of possible URLs, the only URL used
+/// will be that of the last item tried.
+// FIXME: This is a terrible, terrible hack. Fixing this requires Fortanix to
+// add a method for resolving addresses.
+fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T>
+where
+    F: FnMut(&str) -> io::Result<T>,
+{
+    match addr.to_socket_addrs() {
+        Ok(addrs) => {
+            let mut last_err = None;
+            let mut encoded = String::new();
+            for addr in addrs {
+                // Format the IP address as a string, reusing the buffer.
+                encoded.clear();
+                write!(encoded, "{}", &addr).unwrap();
+
+                match f(&encoded) {
+                    Ok(val) => return Ok(val),
+                    Err(err) => last_err = Some(err),
+                }
+            }
+
+            match last_err {
+                Some(err) => Err(err),
+                None => Err(io::Error::NO_ADDRESSES),
             }
         }
+        Err(err) => match err.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()) {
+            Some(NonIpSockAddr { host }) => f(host),
+            None => Err(err),
+        },
     }
 }
 
@@ -86,17 +121,18 @@ fn addr_to_sockaddr(addr: Option<&str>) -> io::Result<SocketAddr> {
 }
 
 impl TcpStream {
-    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        let addr = io_err_to_addr(addr)?;
-        let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?;
-        Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) })
+    pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
+        each_addr(addr, |addr| {
+            let (fd, local_addr, peer_addr) = usercalls::connect_stream(addr)?;
+            Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) })
+        })
     }
 
     pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> {
         if dur == Duration::default() {
             return Err(io::Error::ZERO_TIMEOUT);
         }
-        Self::connect(Ok(addr)) // FIXME: ignoring timeout
+        Self::connect(addr) // FIXME: ignoring timeout
     }
 
     pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
@@ -247,10 +283,11 @@ impl fmt::Debug for TcpListener {
 }
 
 impl TcpListener {
-    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
-        let addr = io_err_to_addr(addr)?;
-        let (fd, local_addr) = usercalls::bind_stream(&addr)?;
-        Ok(TcpListener { inner: Socket::new(fd, local_addr) })
+    pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
+        each_addr(addr, |addr| {
+            let (fd, local_addr) = usercalls::bind_stream(addr)?;
+            Ok(TcpListener { inner: Socket::new(fd, local_addr) })
+        })
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
@@ -316,7 +353,7 @@ impl FromInner<Socket> for TcpListener {
 pub struct UdpSocket(!);
 
 impl UdpSocket {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> {
         unsupported()
     }
 
@@ -436,7 +473,7 @@ impl UdpSocket {
         self.0
     }
 
-    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+    pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> {
         self.0
     }
 }
diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket/mod.rs
index aa83ed65d4c..564f2e3a01f 100644
--- a/library/std/src/sys/net/connection/socket.rs
+++ b/library/std/src/sys/net/connection/socket/mod.rs
@@ -3,8 +3,11 @@ mod tests;
 
 use crate::ffi::{c_int, c_void};
 use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut};
-use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6};
+use crate::net::{
+    Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs,
+};
 use crate::sys::common::small_c_string::run_with_cstr;
+use crate::sys::net::connection::each_addr;
 use crate::sys_common::{AsInner, FromInner};
 use crate::time::Duration;
 use crate::{cmp, fmt, mem, ptr};
@@ -342,14 +345,15 @@ pub struct TcpStream {
 }
 
 impl TcpStream {
-    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        let addr = addr?;
-
+    pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
         init();
+        return each_addr(addr, inner);
 
-        let sock = Socket::new(addr, c::SOCK_STREAM)?;
-        sock.connect(addr)?;
-        Ok(TcpStream { inner: sock })
+        fn inner(addr: &SocketAddr) -> io::Result<TcpStream> {
+            let sock = Socket::new(addr, c::SOCK_STREAM)?;
+            sock.connect(addr)?;
+            Ok(TcpStream { inner: sock })
+        }
     }
 
     pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> {
@@ -512,48 +516,45 @@ pub struct TcpListener {
 }
 
 impl TcpListener {
-    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
-        let addr = addr?;
-
+    pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
         init();
-
-        let sock = Socket::new(addr, c::SOCK_STREAM)?;
-
-        // On platforms with Berkeley-derived sockets, this allows to quickly
-        // rebind a socket, without needing to wait for the OS to clean up the
-        // previous one.
-        //
-        // On Windows, this allows rebinding sockets which are actively in use,
-        // which allows “socket hijacking”, so we explicitly don't set it here.
-        // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
-        #[cfg(not(windows))]
-        setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?;
-
-        // Bind our new socket
-        let (addr, len) = socket_addr_to_c(addr);
-        cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?;
-
-        cfg_select! {
-            target_os = "horizon" => {
+        return each_addr(addr, inner);
+
+        fn inner(addr: &SocketAddr) -> io::Result<TcpListener> {
+            let sock = Socket::new(addr, c::SOCK_STREAM)?;
+
+            // On platforms with Berkeley-derived sockets, this allows to quickly
+            // rebind a socket, without needing to wait for the OS to clean up the
+            // previous one.
+            //
+            // On Windows, this allows rebinding sockets which are actively in use,
+            // which allows “socket hijacking”, so we explicitly don't set it here.
+            // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
+            #[cfg(not(windows))]
+            setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?;
+
+            // Bind our new socket
+            let (addr, len) = socket_addr_to_c(addr);
+            cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?;
+
+            let backlog = if cfg!(target_os = "horizon") {
                 // The 3DS doesn't support a big connection backlog. Sometimes
                 // it allows up to about 37, but other times it doesn't even
                 // accept 32. There may be a global limitation causing this.
-                let backlog = 20;
-            }
-            target_os = "haiku" => {
+                20
+            } else if cfg!(target_os = "haiku") {
                 // Haiku does not support a queue length > 32
                 // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81
-                let backlog = 32;
-            }
-            _ => {
+                32
+            } else {
                 // The default for all other platforms
-                let backlog = 128;
-            }
-        }
+                128
+            };
 
-        // Start listening
-        cvt(unsafe { c::listen(sock.as_raw(), backlog) })?;
-        Ok(TcpListener { inner: sock })
+            // Start listening
+            cvt(unsafe { c::listen(sock.as_raw(), backlog) })?;
+            Ok(TcpListener { inner: sock })
+        }
     }
 
     #[inline]
@@ -639,15 +640,16 @@ pub struct UdpSocket {
 }
 
 impl UdpSocket {
-    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
-        let addr = addr?;
-
+    pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
         init();
+        return each_addr(addr, inner);
 
-        let sock = Socket::new(addr, c::SOCK_DGRAM)?;
-        let (addr, len) = socket_addr_to_c(addr);
-        cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?;
-        Ok(UdpSocket { inner: sock })
+        fn inner(addr: &SocketAddr) -> io::Result<UdpSocket> {
+            let sock = Socket::new(addr, c::SOCK_DGRAM)?;
+            let (addr, len) = socket_addr_to_c(addr);
+            cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?;
+            Ok(UdpSocket { inner: sock })
+        }
     }
 
     #[inline]
@@ -822,9 +824,13 @@ impl UdpSocket {
         Ok(ret as usize)
     }
 
-    pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> {
-        let (addr, len) = socket_addr_to_c(addr?);
-        cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop)
+    pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
+        return each_addr(addr, |addr| inner(self, addr));
+
+        fn inner(this: &UdpSocket, addr: &SocketAddr) -> io::Result<()> {
+            let (addr, len) = socket_addr_to_c(addr);
+            cvt_r(|| unsafe { c::connect(this.inner.as_raw(), addr.as_ptr(), len) }).map(drop)
+        }
     }
 }
 
diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs
index 16e3487a174..00368042873 100644
--- a/library/std/src/sys/net/connection/uefi/mod.rs
+++ b/library/std/src/sys/net/connection/uefi/mod.rs
@@ -1,6 +1,7 @@
+use super::each_addr;
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
-use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
 use crate::sync::{Arc, Mutex};
 use crate::sys::unsupported;
 use crate::time::Duration;
@@ -15,13 +16,17 @@ pub struct TcpStream {
 }
 
 impl TcpStream {
-    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        let inner = tcp::Tcp::connect(addr?, None)?;
-        Ok(Self {
-            inner,
-            read_timeout: Arc::new(Mutex::new(None)),
-            write_timeout: Arc::new(Mutex::new(None)),
-        })
+    pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
+        return each_addr(addr, inner);
+
+        fn inner(addr: &SocketAddr) -> io::Result<TcpStream> {
+            let inner = tcp::Tcp::connect(addr, None)?;
+            Ok(TcpStream {
+                inner,
+                read_timeout: Arc::new(Mutex::new(None)),
+                write_timeout: Arc::new(Mutex::new(None)),
+            })
+        }
     }
 
     pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> {
@@ -145,7 +150,7 @@ pub struct TcpListener {
 }
 
 impl TcpListener {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> {
         unsupported()
     }
 
@@ -195,7 +200,7 @@ impl fmt::Debug for TcpListener {
 pub struct UdpSocket(!);
 
 impl UdpSocket {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> {
         unsupported()
     }
 
@@ -315,7 +320,7 @@ impl UdpSocket {
         self.0
     }
 
-    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+    pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> {
         self.0
     }
 }
diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs
index da217439626..fbc86343272 100644
--- a/library/std/src/sys/net/connection/unsupported.rs
+++ b/library/std/src/sys/net/connection/unsupported.rs
@@ -1,13 +1,13 @@
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
-use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
 use crate::sys::unsupported;
 use crate::time::Duration;
 
 pub struct TcpStream(!);
 
 impl TcpStream {
-    pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+    pub fn connect<A: ToSocketAddrs>(_: A) -> io::Result<TcpStream> {
         unsupported()
     }
 
@@ -121,7 +121,7 @@ impl fmt::Debug for TcpStream {
 pub struct TcpListener(!);
 
 impl TcpListener {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> {
         unsupported()
     }
 
@@ -171,7 +171,7 @@ impl fmt::Debug for TcpListener {
 pub struct UdpSocket(!);
 
 impl UdpSocket {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> {
         unsupported()
     }
 
@@ -291,7 +291,7 @@ impl UdpSocket {
         self.0
     }
 
-    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+    pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> {
         self.0
     }
 }
diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs
index 951dc65e5b4..cdfa25c8a44 100644
--- a/library/std/src/sys/net/connection/wasip1.rs
+++ b/library/std/src/sys/net/connection/wasip1.rs
@@ -2,7 +2,7 @@
 
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
-use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
 use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::sys::fd::WasiFd;
 use crate::sys::{err2io, unsupported};
@@ -60,7 +60,7 @@ impl FromRawFd for Socket {
 }
 
 impl TcpStream {
-    pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+    pub fn connect<A: ToSocketAddrs>(_: A) -> io::Result<TcpStream> {
         unsupported()
     }
 
@@ -212,7 +212,7 @@ pub struct TcpListener {
 }
 
 impl TcpListener {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> {
         unsupported()
     }
 
@@ -316,7 +316,7 @@ pub struct UdpSocket {
 }
 
 impl UdpSocket {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+    pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> {
         unsupported()
     }
 
@@ -436,7 +436,7 @@ impl UdpSocket {
         unsupported()
     }
 
-    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+    pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> {
         unsupported()
     }
 
diff --git a/library/std/src/sys/net/connection/xous/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs
index bdf1fcd9302..8818ef2ca9a 100644
--- a/library/std/src/sys/net/connection/xous/tcplistener.rs
+++ b/library/std/src/sys/net/connection/xous/tcplistener.rs
@@ -2,9 +2,10 @@ use core::convert::TryInto;
 use core::sync::atomic::{Atomic, AtomicBool, AtomicU16, AtomicUsize, Ordering};
 
 use super::*;
-use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
+use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
 use crate::os::xous::services;
 use crate::sync::Arc;
+use crate::sys::net::connection::each_addr;
 use crate::{fmt, io};
 
 macro_rules! unimpl {
@@ -25,16 +26,19 @@ pub struct TcpListener {
 }
 
 impl TcpListener {
-    pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
-        let mut addr = *socketaddr?;
-
-        let fd = TcpListener::bind_inner(&mut addr)?;
-        return Ok(TcpListener {
-            fd: Arc::new(AtomicU16::new(fd)),
-            local: addr,
-            handle_count: Arc::new(AtomicUsize::new(1)),
-            nonblocking: Arc::new(AtomicBool::new(false)),
-        });
+    pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
+        return each_addr(addr, inner);
+
+        fn inner(addr: &SocketAddr) -> io::Result<TcpListener> {
+            let mut addr = *addr;
+            let fd = TcpListener::bind_inner(&mut addr)?;
+            Ok(TcpListener {
+                fd: Arc::new(AtomicU16::new(fd)),
+                local: addr,
+                handle_count: Arc::new(AtomicUsize::new(1)),
+                nonblocking: Arc::new(AtomicBool::new(false)),
+            })
+        }
     }
 
     /// This returns the raw fd of a Listener, so that it can also be used by the
diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs
index 54524767452..4df75453d1f 100644
--- a/library/std/src/sys/net/connection/xous/tcpstream.rs
+++ b/library/std/src/sys/net/connection/xous/tcpstream.rs
@@ -3,9 +3,12 @@ use core::sync::atomic::{Atomic, AtomicBool, AtomicU32, AtomicUsize, Ordering};
 use super::*;
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
-use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6};
+use crate::net::{
+    IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs,
+};
 use crate::os::xous::services;
 use crate::sync::Arc;
+use crate::sys::net::connection::each_addr;
 use crate::time::Duration;
 
 macro_rules! unimpl {
@@ -79,8 +82,8 @@ impl TcpStream {
         }
     }
 
-    pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        Self::connect_timeout(socketaddr?, Duration::ZERO)
+    pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
+        each_addr(addr, |addr| Self::connect_timeout(addr, Duration::ZERO))
     }
 
     pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
diff --git a/library/std/src/sys/net/connection/xous/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs
index 2127d3267ed..ce54ea3b79e 100644
--- a/library/std/src/sys/net/connection/xous/udp.rs
+++ b/library/std/src/sys/net/connection/xous/udp.rs
@@ -3,9 +3,10 @@ use core::sync::atomic::{Atomic, AtomicUsize, Ordering};
 
 use super::*;
 use crate::cell::Cell;
-use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
+use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
 use crate::os::xous::services;
 use crate::sync::Arc;
+use crate::sys::net::connection::each_addr;
 use crate::time::Duration;
 use crate::{fmt, io};
 
@@ -32,40 +33,45 @@ pub struct UdpSocket {
 }
 
 impl UdpSocket {
-    pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
-        let addr = socketaddr?;
-        // Construct the request
-        let mut connect_request = ConnectRequest { raw: [0u8; 4096] };
-
-        // Serialize the StdUdpBind structure. This is done "manually" because we don't want to
-        // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous.
-        let port_bytes = addr.port().to_le_bytes();
-        connect_request.raw[0] = port_bytes[0];
-        connect_request.raw[1] = port_bytes[1];
-        match addr.ip() {
-            IpAddr::V4(addr) => {
-                connect_request.raw[2] = 4;
-                for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
-                    *dest = src;
+    pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
+        return each_addr(addr, inner);
+
+        fn inner(addr: &SocketAddr) -> io::Result<UdpSocket> {
+            // Construct the request
+            let mut connect_request = ConnectRequest { raw: [0u8; 4096] };
+
+            // Serialize the StdUdpBind structure. This is done "manually" because we don't want to
+            // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous.
+            let port_bytes = addr.port().to_le_bytes();
+            connect_request.raw[0] = port_bytes[0];
+            connect_request.raw[1] = port_bytes[1];
+            match addr.ip() {
+                IpAddr::V4(addr) => {
+                    connect_request.raw[2] = 4;
+                    for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
+                        *dest = src;
+                    }
                 }
-            }
-            IpAddr::V6(addr) => {
-                connect_request.raw[2] = 6;
-                for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
-                    *dest = src;
+                IpAddr::V6(addr) => {
+                    connect_request.raw[2] = 6;
+                    for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
+                        *dest = src;
+                    }
                 }
             }
-        }
 
-        let response = crate::os::xous::ffi::lend_mut(
-            services::net_server(),
-            services::NetLendMut::StdUdpBind.into(),
-            &mut connect_request.raw,
-            0,
-            4096,
-        );
+            let response = crate::os::xous::ffi::lend_mut(
+                services::net_server(),
+                services::NetLendMut::StdUdpBind.into(),
+                &mut connect_request.raw,
+                0,
+                4096,
+            );
+
+            let Ok((_, valid)) = response else {
+                return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid response"));
+            };
 
-        if let Ok((_, valid)) = response {
             // The first four bytes should be zero upon success, and will be nonzero
             // for an error.
             let response = connect_request.raw;
@@ -87,8 +93,9 @@ impl UdpSocket {
                     ));
                 }
             }
+
             let fd = response[1] as u16;
-            return Ok(UdpSocket {
+            Ok(UdpSocket {
                 fd,
                 local: *addr,
                 remote: Cell::new(None),
@@ -96,9 +103,8 @@ impl UdpSocket {
                 write_timeout: Cell::new(0),
                 handle_count: Arc::new(AtomicUsize::new(1)),
                 nonblocking: Cell::new(false),
-            });
+            })
         }
-        Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid response"))
     }
 
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
@@ -198,10 +204,11 @@ impl UdpSocket {
         self.peek_from(buf).map(|(len, _addr)| len)
     }
 
-    pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> {
-        let addr = maybe_addr?;
-        self.remote.set(Some(*addr));
-        Ok(())
+    pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
+        each_addr(addr, |addr| {
+            self.remote.set(Some(*addr));
+            Ok(())
+        })
     }
 
     pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs
index 5df1fe138ab..dffc4ea7f81 100644
--- a/library/std/src/sys/net/mod.rs
+++ b/library/std/src/sys/net/mod.rs
@@ -1,46 +1,4 @@
-cfg_select! {
-    any(
-        all(target_family = "unix", not(target_os = "l4re")),
-        target_os = "windows",
-        target_os = "hermit",
-        all(target_os = "wasi", target_env = "p2"),
-        target_os = "solid_asp3",
-    ) => {
-        mod connection {
-            mod socket;
-            pub use socket::*;
-        }
-    }
-    all(target_vendor = "fortanix", target_env = "sgx") => {
-        mod connection {
-            mod sgx;
-            pub use sgx::*;
-        }
-    }
-    all(target_os = "wasi", target_env = "p1") => {
-        mod connection {
-            mod wasip1;
-            pub use wasip1::*;
-        }
-    }
-    target_os = "xous" => {
-        mod connection {
-            mod xous;
-            pub use xous::*;
-        }
-    }
-    target_os = "uefi" => {
-        mod connection {
-            mod uefi;
-            pub use uefi::*;
-        }
-    }
-    _ => {
-        mod connection {
-            mod unsupported;
-            pub use unsupported::*;
-        }
-    }
-}
-
+/// This module contains the implementations of `TcpStream`, `TcpListener` and
+/// `UdpSocket` as well as related functionality like DNS resolving.
+mod connection;
 pub use connection::*;