node: Add timestamp to handshake

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-08-27 15:28:13 +02:00
parent 90486f3051
commit 5e0d4653c7
No known key found for this signature in database
3 changed files with 63 additions and 14 deletions

View File

@ -60,7 +60,11 @@ pub struct Envelope {
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Message { pub enum Message {
/// Say hello to a peer. This is the first message sent to a peer after connection. /// Say hello to a peer. This is the first message sent to a peer after connection.
Hello { version: u32, git: Url }, Hello {
timestamp: Timestamp,
version: u32,
git: Url,
},
/// Get node addresses from a peer. /// Get node addresses from a peer.
GetAddrs, GetAddrs,
/// Send node addresses to a peer. Sent in response to [`Message::GetAddrs`]. /// Send node addresses to a peer. Sent in response to [`Message::GetAddrs`].
@ -88,8 +92,9 @@ impl From<Message> for Envelope {
} }
impl Message { impl Message {
pub fn hello(git: Url) -> Self { pub fn hello(timestamp: Timestamp, git: Url) -> Self {
Self::Hello { Self::Hello {
timestamp,
version: PROTOCOL_VERSION, version: PROTOCOL_VERSION,
git, git,
} }
@ -480,8 +485,13 @@ where
let git = self.config.git_url.clone(); let git = self.config.git_url.clone();
if let Some(peer) = self.peers.get_mut(&id) { if let Some(peer) = self.peers.get_mut(&id) {
self.context self.context.write_all(
.write_all(peer.addr, [Message::hello(git), Message::get_inventory([])]); peer.addr,
[
Message::hello(self.context.timestamp(), git),
Message::get_inventory([]),
],
);
peer.attempts = 0; peer.attempts = 0;
} }
@ -669,7 +679,7 @@ impl<S, T> Context<S, T>
where where
T: storage::ReadStorage, T: storage::ReadStorage,
{ {
fn new( pub(crate) fn new(
config: Config, config: Config,
clock: RefClock, clock: RefClock,
storage: T, storage: T,
@ -687,6 +697,11 @@ where
} }
} }
/// Get current local timestamp.
pub(crate) fn timestamp(&self) -> Timestamp {
self.clock.local_time().as_secs()
}
/// Process a peer inventory announcement by updating our routing table. /// Process a peer inventory announcement by updating our routing table.
fn process_inventory(&mut self, inventory: &Inventory, from: PeerId) { fn process_inventory(&mut self, inventory: &Inventory, from: PeerId) {
for (proj_id, _refs) in inventory { for (proj_id, _refs) in inventory {
@ -702,11 +717,6 @@ where
} }
} }
/// Get current local timestamp.
fn timestamp(&self) -> Timestamp {
self.clock.local_time().as_secs()
}
/// Disconnect a peer. /// Disconnect a peer.
fn disconnect(&mut self, addr: net::SocketAddr, reason: DisconnectReason) { fn disconnect(&mut self, addr: net::SocketAddr, reason: DisconnectReason) {
self.io.push_back(Io::Disconnect(addr, reason)); self.io.push_back(Io::Disconnect(addr, reason));
@ -855,7 +865,16 @@ impl Peer {
debug!("Received {:?} from {}", &envelope.msg, self.id()); debug!("Received {:?} from {}", &envelope.msg, self.id());
match envelope.msg { match envelope.msg {
Message::Hello { version, git } => { Message::Hello {
timestamp,
version,
git,
} => {
let now = ctx.timestamp();
if timestamp.abs_diff(now) > MAX_TIME_DELTA.as_secs() {
return Err(PeerError::InvalidTimestamp(timestamp));
}
if version != PROTOCOL_VERSION { if version != PROTOCOL_VERSION {
return Err(PeerError::WrongVersion(version)); return Err(PeerError::WrongVersion(version));
} }
@ -864,8 +883,14 @@ impl Peer {
// extra "acknowledgment" message sent when the `Hello` is well received. // extra "acknowledgment" message sent when the `Hello` is well received.
if self.link.is_inbound() { if self.link.is_inbound() {
let git = ctx.config.git_url.clone(); let git = ctx.config.git_url.clone();
ctx.write_all(self.addr, [Message::hello(git), Message::get_inventory([])]); ctx.write_all(
self.addr,
[Message::hello(now, git), Message::get_inventory([])],
);
} }
// Nb. we don't set the peer timestamp here, since it is going to be
// set after the first message is received only. Setting it here would
// mean that messages received right after the handshake could be ignored.
self.state = PeerState::Negotiated { self.state = PeerState::Negotiated {
since: ctx.clock.local_time(), since: ctx.clock.local_time(),
git, git,

View File

@ -109,6 +109,14 @@ where
} }
} }
pub fn timestamp(&self) -> Timestamp {
self.protocol.timestamp()
}
pub fn git_url(&self) -> Url {
self.config().git_url.clone()
}
pub fn receive(&mut self, peer: &net::SocketAddr, msg: Message) { pub fn receive(&mut self, peer: &net::SocketAddr, msg: Message) {
let bytes = serde_json::to_vec(&Envelope { let bytes = serde_json::to_vec(&Envelope {
magic: NETWORK_MAGIC, magic: NETWORK_MAGIC,
@ -126,7 +134,7 @@ where
self.initialize(); self.initialize();
self.protocol.connected(*remote, &local, Link::Inbound); self.protocol.connected(*remote, &local, Link::Inbound);
self.receive(remote, Message::hello(git)); self.receive(remote, Message::hello(self.local_time().as_secs(), git));
let mut msgs = self.messages(remote); let mut msgs = self.messages(remote);
msgs.find(|m| matches!(m, Message::Hello { .. })) msgs.find(|m| matches!(m, Message::Hello { .. }))
@ -148,7 +156,7 @@ where
.expect("`get-inventory` is sent"); .expect("`get-inventory` is sent");
let git = self.config().git_url.clone(); let git = self.config().git_url.clone();
self.receive(remote, Message::hello(git)); self.receive(remote, Message::hello(self.local_time().as_secs(), git));
} }
/// Drain outgoing messages sent from this peer to the remote address. /// Drain outgoing messages sent from this peer to the remote address.

View File

@ -95,6 +95,22 @@ fn test_wrong_peer_version() {
// TODO // TODO
} }
#[test]
fn test_handshake_invalid_timestamp() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
let time_delta = MAX_TIME_DELTA.as_secs() + 1;
let local = std::net::SocketAddr::new(bob.ip, bob.rng.u16(..));
alice.initialize();
alice.connected(bob.addr(), &local, Link::Inbound);
alice.receive(
&bob.addr(),
Message::hello(alice.timestamp() - time_delta, bob.git_url()),
);
assert_matches!(alice.outbox().next(), Some(Io::Disconnect(addr, _)) if addr == bob.addr());
}
#[test] #[test]
#[ignore] #[ignore]
fn test_wrong_peer_magic() { fn test_wrong_peer_magic() {