Skip to content

Commit ad45d23

Browse files
authored
fix(identify): handle partial push messages
According to the [spec], all fields within push messages are optional. To handle missing fields, we deserialize push messages into a different struct, patch the previously received, full identify message with it and emit this as the new info. Previously, we failed to parse the message which causes incompatibility with js-libp2p nodes as they don't send the public key in push messages. See https://github.com/libp2p/js-libp2p/blob/88c47f51f9d67a6261e4ac65c494cd1e6e4ed8dd/packages/libp2p/src/identify/identify.ts#L205-L210. [spec]: https://github.com/libp2p/specs/tree/master/identify#identifypush Pull-Request: #4495.
1 parent 95890b5 commit ad45d23

File tree

6 files changed

+171
-58
lines changed

6 files changed

+171
-58
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ libp2p-deflate = { version = "0.40.0", path = "transports/deflate" }
8181
libp2p-dns = { version = "0.40.1", path = "transports/dns" }
8282
libp2p-floodsub = { version = "0.43.0", path = "protocols/floodsub" }
8383
libp2p-gossipsub = { version = "0.45.1", path = "protocols/gossipsub" }
84-
libp2p-identify = { version = "0.43.0", path = "protocols/identify" }
84+
libp2p-identify = { version = "0.43.1", path = "protocols/identify" }
8585
libp2p-identity = { version = "0.2.3" }
8686
libp2p-kad = { version = "0.44.5", path = "protocols/kad" }
8787
libp2p-mdns = { version = "0.44.0", path = "protocols/mdns" }

protocols/identify/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
## 0.43.0
1+
## 0.43.1 - unreleased
2+
3+
- Handle partial push messages.
4+
Previously, push messages with partial information were ignored.
5+
See [PR 4495].
6+
7+
[PR 4495]: https://github.com/libp2p/rust-libp2p/pull/4495
8+
9+
## 0.43.0
210

311
- Observed addresses (aka. external address candidates) of the local node, reported by a remote node via `libp2p-identify`, are no longer automatically considered confirmed external addresses, in other words they are no longer trusted by default.
412
Instead users need to confirm the reported observed address either manually, or by using `libp2p-autonat`.

protocols/identify/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "libp2p-identify"
33
edition = "2021"
44
rust-version = { workspace = true }
55
description = "Nodes identifcation protocol for libp2p"
6-
version = "0.43.0"
6+
version = "0.43.1"
77
authors = ["Parity Technologies <[email protected]>"]
88
license = "MIT"
99
repository = "https://github.com/libp2p/rust-libp2p"

protocols/identify/src/handler.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1919
// DEALINGS IN THE SOFTWARE.
2020

21-
use crate::protocol::{Identify, InboundPush, Info, OutboundPush, Push, UpgradeError};
21+
use crate::protocol::{Identify, InboundPush, OutboundPush, Push, UpgradeError};
22+
use crate::protocol::{Info, PushInfo};
2223
use either::Either;
2324
use futures::future::BoxFuture;
2425
use futures::prelude::*;
@@ -48,7 +49,7 @@ use std::{io, task::Context, task::Poll, time::Duration};
4849
/// permitting the underlying connection to be closed.
4950
pub struct Handler {
5051
remote_peer_id: PeerId,
51-
inbound_identify_push: Option<BoxFuture<'static, Result<Info, UpgradeError>>>,
52+
inbound_identify_push: Option<BoxFuture<'static, Result<PushInfo, UpgradeError>>>,
5253
/// Pending events to yield.
5354
events: SmallVec<
5455
[ConnectionHandlerEvent<Either<Identify, Push<OutboundPush>>, (), Event, io::Error>; 4],
@@ -80,6 +81,9 @@ pub struct Handler {
8081
/// Address observed by or for the remote.
8182
observed_addr: Multiaddr,
8283

84+
/// Identify information about the remote peer.
85+
remote_info: Option<Info>,
86+
8387
local_supported_protocols: SupportedProtocols,
8488
remote_supported_protocols: HashSet<StreamProtocol>,
8589
external_addresses: HashSet<Multiaddr>,
@@ -133,6 +137,7 @@ impl Handler {
133137
observed_addr,
134138
local_supported_protocols: SupportedProtocols::default(),
135139
remote_supported_protocols: HashSet::default(),
140+
remote_info: Default::default(),
136141
external_addresses,
137142
}
138143
}
@@ -176,7 +181,8 @@ impl Handler {
176181
) {
177182
match output {
178183
future::Either::Left(remote_info) => {
179-
self.update_supported_protocols_for_remote(&remote_info);
184+
self.handle_incoming_info(&remote_info);
185+
180186
self.events
181187
.push(ConnectionHandlerEvent::NotifyBehaviour(Event::Identified(
182188
remote_info,
@@ -213,6 +219,12 @@ impl Handler {
213219
}
214220
}
215221

222+
fn handle_incoming_info(&mut self, info: &Info) {
223+
self.remote_info.replace(info.clone());
224+
225+
self.update_supported_protocols_for_remote(info);
226+
}
227+
216228
fn update_supported_protocols_for_remote(&mut self, remote_info: &Info) {
217229
let new_remote_protocols = HashSet::from_iter(remote_info.protocols.clone());
218230

@@ -318,11 +330,15 @@ impl ConnectionHandler for Handler {
318330
{
319331
self.inbound_identify_push.take();
320332

321-
if let Ok(info) = res {
322-
self.update_supported_protocols_for_remote(&info);
323-
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Event::Identified(
324-
info,
325-
)));
333+
if let Ok(remote_push_info) = res {
334+
if let Some(mut info) = self.remote_info.clone() {
335+
info.merge(remote_push_info);
336+
self.handle_incoming_info(&info);
337+
338+
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
339+
Event::Identified(info),
340+
));
341+
};
326342
}
327343
}
328344

protocols/identify/src/protocol.rs

Lines changed: 135 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl Push<OutboundPush> {
6363
}
6464
}
6565

66-
/// Information of a peer sent in protocol messages.
66+
/// Identify information of a peer sent in protocol messages.
6767
#[derive(Debug, Clone)]
6868
pub struct Info {
6969
/// The public key of the local peer.
@@ -82,6 +82,41 @@ pub struct Info {
8282
pub observed_addr: Multiaddr,
8383
}
8484

85+
impl Info {
86+
pub fn merge(&mut self, info: PushInfo) {
87+
if let Some(public_key) = info.public_key {
88+
self.public_key = public_key;
89+
}
90+
if let Some(protocol_version) = info.protocol_version {
91+
self.protocol_version = protocol_version;
92+
}
93+
if let Some(agent_version) = info.agent_version {
94+
self.agent_version = agent_version;
95+
}
96+
if !info.listen_addrs.is_empty() {
97+
self.listen_addrs = info.listen_addrs;
98+
}
99+
if !info.protocols.is_empty() {
100+
self.protocols = info.protocols;
101+
}
102+
if let Some(observed_addr) = info.observed_addr {
103+
self.observed_addr = observed_addr;
104+
}
105+
}
106+
}
107+
108+
/// Identify push information of a peer sent in protocol messages.
109+
/// Note that missing fields should be ignored, as peers may choose to send partial updates containing only the fields whose values have changed.
110+
#[derive(Debug, Clone)]
111+
pub struct PushInfo {
112+
pub public_key: Option<PublicKey>,
113+
pub protocol_version: Option<String>,
114+
pub agent_version: Option<String>,
115+
pub listen_addrs: Vec<Multiaddr>,
116+
pub protocols: Vec<StreamProtocol>,
117+
pub observed_addr: Option<Multiaddr>,
118+
}
119+
85120
impl UpgradeInfo for Identify {
86121
type Info = StreamProtocol;
87122
type InfoIter = iter::Once<Self::Info>;
@@ -110,7 +145,7 @@ where
110145
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
111146

112147
fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future {
113-
recv(socket).boxed()
148+
recv_identify(socket).boxed()
114149
}
115150
}
116151

@@ -127,13 +162,13 @@ impl<C> InboundUpgrade<C> for Push<InboundPush>
127162
where
128163
C: AsyncRead + AsyncWrite + Unpin + Send + 'static,
129164
{
130-
type Output = BoxFuture<'static, Result<Info, UpgradeError>>;
165+
type Output = BoxFuture<'static, Result<PushInfo, UpgradeError>>;
131166
type Error = Void;
132167
type Future = future::Ready<Result<Self::Output, Self::Error>>;
133168

134169
fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future {
135170
// Lazily upgrade stream, thus allowing upgrade to happen within identify's handler.
136-
future::ok(recv(socket).boxed())
171+
future::ok(recv_push(socket).boxed())
137172
}
138173
}
139174

@@ -184,7 +219,29 @@ where
184219
Ok(())
185220
}
186221

187-
async fn recv<T>(socket: T) -> Result<Info, UpgradeError>
222+
async fn recv_push<T>(socket: T) -> Result<PushInfo, UpgradeError>
223+
where
224+
T: AsyncRead + AsyncWrite + Unpin,
225+
{
226+
let info = recv(socket).await?.try_into()?;
227+
228+
trace!("Received {:?}", info);
229+
230+
Ok(info)
231+
}
232+
233+
async fn recv_identify<T>(socket: T) -> Result<Info, UpgradeError>
234+
where
235+
T: AsyncRead + AsyncWrite + Unpin,
236+
{
237+
let info = recv(socket).await?.try_into()?;
238+
239+
trace!("Received {:?}", info);
240+
241+
Ok(info)
242+
}
243+
244+
async fn recv<T>(socket: T) -> Result<proto::Identify, UpgradeError>
188245
where
189246
T: AsyncRead + AsyncWrite + Unpin,
190247
{
@@ -199,61 +256,93 @@ where
199256
)
200257
.next()
201258
.await
202-
.ok_or(UpgradeError::StreamClosed)??
203-
.try_into()?;
204-
205-
trace!("Received: {:?}", info);
259+
.ok_or(UpgradeError::StreamClosed)??;
206260

207261
Ok(info)
208262
}
209263

210-
impl TryFrom<proto::Identify> for Info {
211-
type Error = UpgradeError;
264+
fn parse_listen_addrs(listen_addrs: Vec<Vec<u8>>) -> Vec<Multiaddr> {
265+
listen_addrs
266+
.into_iter()
267+
.filter_map(|bytes| match Multiaddr::try_from(bytes) {
268+
Ok(a) => Some(a),
269+
Err(e) => {
270+
debug!("Unable to parse multiaddr: {e:?}");
271+
None
272+
}
273+
})
274+
.collect()
275+
}
212276

213-
fn try_from(msg: proto::Identify) -> Result<Self, Self::Error> {
214-
fn parse_multiaddr(bytes: Vec<u8>) -> Result<Multiaddr, multiaddr::Error> {
215-
Multiaddr::try_from(bytes)
277+
fn parse_protocols(protocols: Vec<String>) -> Vec<StreamProtocol> {
278+
protocols
279+
.into_iter()
280+
.filter_map(|p| match StreamProtocol::try_from_owned(p) {
281+
Ok(p) => Some(p),
282+
Err(e) => {
283+
debug!("Received invalid protocol from peer: {e}");
284+
None
285+
}
286+
})
287+
.collect()
288+
}
289+
290+
fn parse_public_key(public_key: Option<Vec<u8>>) -> Option<PublicKey> {
291+
public_key.and_then(|key| match PublicKey::try_decode_protobuf(&key) {
292+
Ok(k) => Some(k),
293+
Err(e) => {
294+
debug!("Unable to decode public key: {e:?}");
295+
None
216296
}
297+
})
298+
}
217299

218-
let listen_addrs = {
219-
let mut addrs = Vec::new();
220-
for addr in msg.listenAddrs.into_iter() {
221-
match parse_multiaddr(addr) {
222-
Ok(a) => addrs.push(a),
223-
Err(e) => {
224-
debug!("Unable to parse multiaddr: {e:?}");
225-
}
226-
}
227-
}
228-
addrs
229-
};
300+
fn parse_observed_addr(observed_addr: Option<Vec<u8>>) -> Option<Multiaddr> {
301+
observed_addr.and_then(|bytes| match Multiaddr::try_from(bytes) {
302+
Ok(a) => Some(a),
303+
Err(e) => {
304+
debug!("Unable to parse observed multiaddr: {e:?}");
305+
None
306+
}
307+
})
308+
}
230309

231-
let public_key = PublicKey::try_decode_protobuf(&msg.publicKey.unwrap_or_default())?;
310+
impl TryFrom<proto::Identify> for Info {
311+
type Error = UpgradeError;
232312

233-
let observed_addr = match parse_multiaddr(msg.observedAddr.unwrap_or_default()) {
234-
Ok(a) => a,
235-
Err(e) => {
236-
debug!("Unable to parse multiaddr: {e:?}");
237-
Multiaddr::empty()
313+
fn try_from(msg: proto::Identify) -> Result<Self, Self::Error> {
314+
let public_key = {
315+
match parse_public_key(msg.publicKey) {
316+
Some(key) => key,
317+
// This will always produce a DecodingError if the public key is missing.
318+
None => PublicKey::try_decode_protobuf(Default::default())?,
238319
}
239320
};
321+
240322
let info = Info {
241323
public_key,
242324
protocol_version: msg.protocolVersion.unwrap_or_default(),
243325
agent_version: msg.agentVersion.unwrap_or_default(),
244-
listen_addrs,
245-
protocols: msg
246-
.protocols
247-
.into_iter()
248-
.filter_map(|p| match StreamProtocol::try_from_owned(p) {
249-
Ok(p) => Some(p),
250-
Err(e) => {
251-
debug!("Received invalid protocol from peer: {e}");
252-
None
253-
}
254-
})
255-
.collect(),
256-
observed_addr,
326+
listen_addrs: parse_listen_addrs(msg.listenAddrs),
327+
protocols: parse_protocols(msg.protocols),
328+
observed_addr: parse_observed_addr(msg.observedAddr).unwrap_or(Multiaddr::empty()),
329+
};
330+
331+
Ok(info)
332+
}
333+
}
334+
335+
impl TryFrom<proto::Identify> for PushInfo {
336+
type Error = UpgradeError;
337+
338+
fn try_from(msg: proto::Identify) -> Result<Self, Self::Error> {
339+
let info = PushInfo {
340+
public_key: parse_public_key(msg.publicKey),
341+
protocol_version: msg.protocolVersion,
342+
agent_version: msg.agentVersion,
343+
listen_addrs: parse_listen_addrs(msg.listenAddrs),
344+
protocols: parse_protocols(msg.protocols),
345+
observed_addr: parse_observed_addr(msg.observedAddr),
257346
};
258347

259348
Ok(info)
@@ -303,7 +392,7 @@ mod tests {
303392
),
304393
};
305394

306-
let info = Info::try_from(payload).expect("not to fail");
395+
let info = PushInfo::try_from(payload).expect("not to fail");
307396

308397
assert_eq!(info.listen_addrs, vec![valid_multiaddr])
309398
}

0 commit comments

Comments
 (0)