Skip to content

Commit f328e2c

Browse files
authored
Unrolled build for #145327
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`
2 parents 565a9ca + 207a01e commit f328e2c

File tree

16 files changed

+276
-216
lines changed

16 files changed

+276
-216
lines changed

library/std/src/io/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ impl Error {
9595

9696
pub(crate) const ZERO_TIMEOUT: Self =
9797
const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout");
98+
99+
pub(crate) const NO_ADDRESSES: Self =
100+
const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses");
98101
}
99102

100103
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/net/mod.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ pub use self::tcp::IntoIncoming;
3434
pub use self::tcp::{Incoming, TcpListener, TcpStream};
3535
#[stable(feature = "rust1", since = "1.0.0")]
3636
pub use self::udp::UdpSocket;
37-
use crate::io::{self, ErrorKind};
3837

3938
mod ip_addr;
4039
mod socket_addr;
@@ -67,23 +66,3 @@ pub enum Shutdown {
6766
#[stable(feature = "rust1", since = "1.0.0")]
6867
Both,
6968
}
70-
71-
fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T>
72-
where
73-
F: FnMut(io::Result<&SocketAddr>) -> io::Result<T>,
74-
{
75-
let addrs = match addr.to_socket_addrs() {
76-
Ok(addrs) => addrs,
77-
Err(e) => return f(Err(e)),
78-
};
79-
let mut last_err = None;
80-
for addr in addrs {
81-
match f(Ok(&addr)) {
82-
Ok(l) => return Ok(l),
83-
Err(e) => last_err = Some(e),
84-
}
85-
}
86-
Err(last_err.unwrap_or_else(|| {
87-
io::const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses")
88-
}))
89-
}

library/std/src/net/tcp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ impl TcpStream {
167167
/// ```
168168
#[stable(feature = "rust1", since = "1.0.0")]
169169
pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
170-
super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream)
170+
net_imp::TcpStream::connect(addr).map(TcpStream)
171171
}
172172

173173
/// Opens a TCP connection to a remote host with a timeout.
@@ -782,7 +782,7 @@ impl TcpListener {
782782
/// ```
783783
#[stable(feature = "rust1", since = "1.0.0")]
784784
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
785-
super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener)
785+
net_imp::TcpListener::bind(addr).map(TcpListener)
786786
}
787787

788788
/// Returns the local socket address of this listener.

library/std/src/net/tcp/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::io::prelude::*;
2-
use crate::io::{BorrowedBuf, IoSlice, IoSliceMut};
2+
use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut};
33
use crate::mem::MaybeUninit;
44
use crate::net::test::{next_test_ip4, next_test_ip6};
55
use crate::net::*;

library/std/src/net/udp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl UdpSocket {
120120
/// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`].
121121
#[stable(feature = "rust1", since = "1.0.0")]
122122
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
123-
super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket)
123+
net_imp::UdpSocket::bind(addr).map(UdpSocket)
124124
}
125125

126126
/// Receives a single datagram message on the socket. On success, returns the number
@@ -677,7 +677,7 @@ impl UdpSocket {
677677
/// on the platform.
678678
#[stable(feature = "net2_mutators", since = "1.9.0")]
679679
pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
680-
super::each_addr(addr, |addr| self.0.connect(addr))
680+
self.0.connect(addr)
681681
}
682682

683683
/// Sends data on the socket to the remote address to which it is connected.

library/std/src/net/udp/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::io::ErrorKind;
12
use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6};
23
use crate::net::*;
34
use crate::sync::mpsc::channel;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
cfg_select! {
2+
any(
3+
all(target_family = "unix", not(target_os = "l4re")),
4+
target_os = "windows",
5+
target_os = "hermit",
6+
all(target_os = "wasi", target_env = "p2"),
7+
target_os = "solid_asp3",
8+
) => {
9+
mod socket;
10+
pub use socket::*;
11+
}
12+
all(target_vendor = "fortanix", target_env = "sgx") => {
13+
mod sgx;
14+
pub use sgx::*;
15+
}
16+
all(target_os = "wasi", target_env = "p1") => {
17+
mod wasip1;
18+
pub use wasip1::*;
19+
}
20+
target_os = "xous" => {
21+
mod xous;
22+
pub use xous::*;
23+
}
24+
target_os = "uefi" => {
25+
mod uefi;
26+
pub use uefi::*;
27+
}
28+
_ => {
29+
mod unsupported;
30+
pub use unsupported::*;
31+
}
32+
}
33+
34+
#[cfg_attr(
35+
// Make sure that this is used on some platforms at least.
36+
not(any(target_os = "linux", target_os = "windows")),
37+
allow(dead_code)
38+
)]
39+
fn each_addr<A: crate::net::ToSocketAddrs, F, T>(addr: A, mut f: F) -> crate::io::Result<T>
40+
where
41+
F: FnMut(&crate::net::SocketAddr) -> crate::io::Result<T>,
42+
{
43+
use crate::io::Error;
44+
45+
let mut last_err = None;
46+
for addr in addr.to_socket_addrs()? {
47+
match f(&addr) {
48+
Ok(l) => return Ok(l),
49+
Err(e) => last_err = Some(e),
50+
}
51+
}
52+
53+
match last_err {
54+
Some(err) => Err(err),
55+
None => Err(Error::NO_ADDRESSES),
56+
}
57+
}

library/std/src/sys/net/connection/sgx.rs

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use crate::error;
2+
use crate::fmt::{self, Write};
13
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
24
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
35
use crate::sync::Arc;
46
use crate::sys::abi::usercalls;
57
use crate::sys::fd::FileDesc;
68
use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported};
79
use crate::time::Duration;
8-
use crate::{error, fmt};
910

1011
const DEFAULT_FAKE_TTL: u32 = 64;
1112

@@ -63,18 +64,52 @@ impl fmt::Debug for TcpStream {
6364
}
6465
}
6566

66-
fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> {
67-
match result {
68-
Ok(saddr) => Ok(saddr.to_string()),
69-
// need to downcast twice because io::Error::into_inner doesn't return the original
70-
// value if the conversion fails
71-
Err(e) => {
72-
if e.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()).is_some() {
73-
Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host)
74-
} else {
75-
Err(e)
67+
/// Converts each address in `addr` into a hostname.
68+
///
69+
/// SGX doesn't support DNS resolution but rather accepts hostnames in
70+
/// the same place as socket addresses. So, to make e.g.
71+
/// ```rust
72+
/// TcpStream::connect("example.com:80")`
73+
/// ```
74+
/// work, the DNS lookup returns a special error (`NonIpSockAddr`) instead,
75+
/// which contains the hostname being looked up. When `.to_socket_addrs()`
76+
/// fails, we inspect the error and try recover the hostname from it. If that
77+
/// succeeds, we thus continue with the hostname.
78+
///
79+
/// This is a terrible hack and leads to buggy code. For instance, when users
80+
/// use the result of `.to_socket_addrs()` in their own `ToSocketAddrs`
81+
/// implementation to select from a list of possible URLs, the only URL used
82+
/// will be that of the last item tried.
83+
// FIXME: This is a terrible, terrible hack. Fixing this requires Fortanix to
84+
// add a method for resolving addresses.
85+
fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T>
86+
where
87+
F: FnMut(&str) -> io::Result<T>,
88+
{
89+
match addr.to_socket_addrs() {
90+
Ok(addrs) => {
91+
let mut last_err = None;
92+
let mut encoded = String::new();
93+
for addr in addrs {
94+
// Format the IP address as a string, reusing the buffer.
95+
encoded.clear();
96+
write!(encoded, "{}", &addr).unwrap();
97+
98+
match f(&encoded) {
99+
Ok(val) => return Ok(val),
100+
Err(err) => last_err = Some(err),
101+
}
102+
}
103+
104+
match last_err {
105+
Some(err) => Err(err),
106+
None => Err(io::Error::NO_ADDRESSES),
76107
}
77108
}
109+
Err(err) => match err.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()) {
110+
Some(NonIpSockAddr { host }) => f(host),
111+
None => Err(err),
112+
},
78113
}
79114
}
80115

@@ -86,17 +121,18 @@ fn addr_to_sockaddr(addr: Option<&str>) -> io::Result<SocketAddr> {
86121
}
87122

88123
impl TcpStream {
89-
pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
90-
let addr = io_err_to_addr(addr)?;
91-
let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?;
92-
Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) })
124+
pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
125+
each_addr(addr, |addr| {
126+
let (fd, local_addr, peer_addr) = usercalls::connect_stream(addr)?;
127+
Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) })
128+
})
93129
}
94130

95131
pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> {
96132
if dur == Duration::default() {
97133
return Err(io::Error::ZERO_TIMEOUT);
98134
}
99-
Self::connect(Ok(addr)) // FIXME: ignoring timeout
135+
Self::connect(addr) // FIXME: ignoring timeout
100136
}
101137

102138
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
@@ -247,10 +283,11 @@ impl fmt::Debug for TcpListener {
247283
}
248284

249285
impl TcpListener {
250-
pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
251-
let addr = io_err_to_addr(addr)?;
252-
let (fd, local_addr) = usercalls::bind_stream(&addr)?;
253-
Ok(TcpListener { inner: Socket::new(fd, local_addr) })
286+
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
287+
each_addr(addr, |addr| {
288+
let (fd, local_addr) = usercalls::bind_stream(addr)?;
289+
Ok(TcpListener { inner: Socket::new(fd, local_addr) })
290+
})
254291
}
255292

256293
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
@@ -316,7 +353,7 @@ impl FromInner<Socket> for TcpListener {
316353
pub struct UdpSocket(!);
317354

318355
impl UdpSocket {
319-
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
356+
pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> {
320357
unsupported()
321358
}
322359

@@ -436,7 +473,7 @@ impl UdpSocket {
436473
self.0
437474
}
438475

439-
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
476+
pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> {
440477
self.0
441478
}
442479
}

0 commit comments

Comments
 (0)