Skip to content

Commit e04e95a

Browse files
mxindenwngr
andauthored
transports/tcp/: Call take_error on TCP socket (#2458)
Within `Provider::new_stream` we wait for the socket to become writable (`stream.writable`), before returning it as a stream. In other words, we are waiting for the socket to connect before returning it as a new TCP connection. Waiting to connect before returning it as a new TCP connection allows us to catch TCP connection establishment errors early. While `stream.writable` drives the process of connecting, it does not surface potential connection errors themselves. These need to be explicitly collected via `TcpSocket::take_error`. If not explicitly collected, they will surface on future operations on the socket. For now this commit explicitly calls `TcpSocket::take_error` when using `async-io` only. `tokio` introduced the method (`take_error`) in tokio-rs/tokio#4364 though later reverted it in tokio-rs/tokio#4392. Once re-reverted, the same patch can be applied when using `libp2p-tcp` with tokio. --- One example on how this bug surfaces today: A `/dnsaddr/xxx` `Multiaddr` can potentially resolve to multiple IP addresses, e.g. to the IPv4 and the IPv6 addresses of a node. `libp2p-dns` tries dialing each of them in sequence using `libp2p-tcp`, returning the first that `libp2p-tcp` reports as successful. Say that the local node tries the IPv6 address first. In the scenario where the local node's networking stack does not support IPv6, e.g. has no IPv6 route, the connection attempt to the resolved IPv6 address of the remote node fails. Given that `libp2p-tcp` does not call `TcpSocket::take_error`, it would falsly report the TCP connection attempt as successful. `libp2p-dns` would receive the "successful" TCP connection for the IPv6 address from `libp2p-tcp` and would not attempt to dial the IPv4 address, even though it supports IPv4, and instead bubble up the "successful" IPv6 TCP connection. Only later, when writing or reading from the "successful" IPv6 TCP connection, would the IPv6 error surface. Co-authored-by: Oliver Wangler <oliver@wngr.de>
1 parent 6338b25 commit e04e95a

File tree

6 files changed

+19
-3
lines changed

6 files changed

+19
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
- Update individual crates.
4848
- `libp2p-relay`
49+
- `libp2p-tcp`
4950

5051
## Version 0.42.0 [2022-01-27]
5152

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ smallvec = "1.6.1"
106106
libp2p-deflate = { version = "0.31.0", path = "transports/deflate", optional = true }
107107
libp2p-dns = { version = "0.31.0", path = "transports/dns", optional = true, default-features = false }
108108
libp2p-mdns = { version = "0.34.0", path = "protocols/mdns", optional = true }
109-
libp2p-tcp = { version = "0.31.0", path = "transports/tcp", default-features = false, optional = true }
109+
libp2p-tcp = { version = "0.31.1", path = "transports/tcp", default-features = false, optional = true }
110110
libp2p-websocket = { version = "0.33.0", path = "transports/websocket", optional = true }
111111

112112
[dev-dependencies]

transports/tcp/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.31.1 [unreleased]
2+
3+
- Call `TcpSocket::take_error` to report connection establishment errors early.
4+
15
# 0.31.0 [2022-01-27]
26

37
- Update dependencies.

transports/tcp/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "libp2p-tcp"
33
edition = "2021"
44
rust-version = "1.56.1"
55
description = "TCP/IP transport protocol for libp2p"
6-
version = "0.31.0"
6+
version = "0.31.1"
77
authors = ["Parity Technologies <admin@parity.io>"]
88
license = "MIT"
99
repository = "https://github.com/libp2p/rust-libp2p"

transports/tcp/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,8 @@ mod tests {
852852
panic!("No TCP port in address: {}", a)
853853
}
854854
ready_tx.send(a).await.ok();
855+
}
856+
ListenerEvent::Upgrade { .. } => {
855857
return;
856858
}
857859
_ => {}

transports/tcp/src/provider/async_io.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,18 @@ impl Provider for Tcp {
4444

4545
fn new_stream(s: net::TcpStream) -> BoxFuture<'static, io::Result<Self::Stream>> {
4646
async move {
47+
// Taken from [`Async::connect`].
48+
4749
let stream = Async::new(s)?;
50+
51+
// The stream becomes writable when connected.
4852
stream.writable().await?;
49-
Ok(stream)
53+
54+
// Check if there was an error while connecting.
55+
match stream.get_ref().take_error()? {
56+
None => Ok(stream),
57+
Some(err) => Err(err),
58+
}
5059
}
5160
.boxed()
5261
}

0 commit comments

Comments
 (0)