1#![allow(clippy::too_many_arguments)]
2#![deny(clippy::unwrap_used)]
3pub mod command;
4pub use command::{Command, QueryState};
5
6pub mod filter;
7pub mod gossip;
8pub mod io;
9pub mod limiter;
10pub mod message;
11pub mod session;
12
13use std::collections::hash_map::Entry;
14use std::collections::{BTreeSet, HashMap, HashSet};
15use std::net::IpAddr;
16use std::ops::{Deref, DerefMut};
17use std::sync::Arc;
18use std::sync::mpsc;
19use std::{fmt, net, time};
20
21use fastrand::Rng;
22use localtime::{LocalDuration, LocalTime};
23use log::*;
24use nonempty::NonEmpty;
25
26use radicle::identity::Doc;
27use radicle::node;
28use radicle::node::address;
29use radicle::node::address::Store as _;
30use radicle::node::address::{AddressBook, AddressType, KnownAddress};
31use radicle::node::config::{PeerConfig, RateLimit};
32use radicle::node::refs::Store as _;
33use radicle::node::routing::Store as _;
34use radicle::node::seed;
35use radicle::node::seed::Store as _;
36use radicle::node::{Penalty, Severity};
37use radicle::storage::refs::{FeatureLevel, SIGREFS_BRANCH};
38use radicle::storage::{RepositoryError, RepositoryInfo, SignedRefsInfo};
39use radicle_fetch::policy::SeedingPolicy;
40
41use crate::fetcher;
42use crate::fetcher::FetcherState;
43use crate::fetcher::RefsToFetch;
44use crate::fetcher::service::FetcherService;
45use crate::service::gossip::Store as _;
46use crate::service::message::{
47 Announcement, AnnouncementMessage, Info, NodeAnnouncement, Ping, RefsAnnouncement, RefsStatus,
48};
49use crate::service::policy::{Scope, store::Write};
50use radicle::identity::RepoId;
51use radicle::node::events::Emitter;
52use radicle::node::routing;
53use radicle::node::routing::InsertResult;
54use radicle::node::{Address, Features, FetchResult, HostName, Seed, Seeds, SyncStatus, SyncedAt};
55use radicle::prelude::*;
56use radicle::storage;
57use radicle::storage::{Namespaces, ReadStorage, refs::RefsAt};
58use radicle::crypto;
61use radicle::crypto::Signer as _;
62use radicle::node::Link;
63use radicle::node::PROTOCOL_VERSION;
64
65use crate::bounded::BoundedVec;
66use crate::service::filter::Filter;
67pub use crate::service::message::{Message, ZeroBytes};
68pub use crate::service::session::{QueuedFetch, Session};
69use crate::worker::FetchError;
70use radicle::node::events::{Event, Events};
71use radicle::node::{Config, NodeId};
72
73use radicle::node::policy::config as policy;
74
75use self::io::Outbox;
76use self::limiter::RateLimiter;
77use self::message::InventoryAnnouncement;
78use self::policy::NamespacesError;
79
80pub const IDLE_INTERVAL: LocalDuration = LocalDuration::from_secs(30);
82pub const GOSSIP_INTERVAL: LocalDuration = LocalDuration::from_secs(6);
84pub const ANNOUNCE_INTERVAL: LocalDuration = LocalDuration::from_mins(60);
86pub const SYNC_INTERVAL: LocalDuration = LocalDuration::from_secs(60);
88pub const PRUNE_INTERVAL: LocalDuration = LocalDuration::from_mins(30);
90pub const STALE_CONNECTION_TIMEOUT: LocalDuration = LocalDuration::from_mins(2);
92pub const KEEP_ALIVE_DELTA: LocalDuration = LocalDuration::from_mins(1);
94pub const MAX_LATENCIES: usize = 16;
96pub const MAX_TIME_DELTA: LocalDuration = LocalDuration::from_mins(60);
98pub const MAX_CONNECTION_ATTEMPTS: usize = 3;
100pub const INITIAL_SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(60 * 24);
103pub const SUBSCRIBE_BACKLOG_DELTA: LocalDuration = LocalDuration::from_mins(3);
106pub const MIN_RECONNECTION_DELTA: LocalDuration = LocalDuration::from_secs(3);
108pub const MAX_RECONNECTION_DELTA: LocalDuration = LocalDuration::from_mins(60);
110pub const CONNECTION_RETRY_DELTA: LocalDuration = LocalDuration::from_mins(10);
112pub const FETCH_TIMEOUT: time::Duration = time::Duration::from_secs(30);
114pub const TARGET_OUTBOUND_PEERS: usize = 8;
116
117pub use message::ADDRESS_LIMIT;
119pub use message::INVENTORY_LIMIT;
121pub use message::REF_REMOTE_LIMIT;
123
124#[derive(Clone, Debug, Default, serde::Serialize)]
126#[serde(rename_all = "camelCase")]
127pub struct Metrics {
128 pub peers: HashMap<NodeId, PeerMetrics>,
130 pub worker_queue_size: usize,
132 pub open_channels: usize,
134}
135
136impl Metrics {
137 pub fn peer(&mut self, nid: NodeId) -> &mut PeerMetrics {
139 self.peers.entry(nid).or_default()
140 }
141}
142
143#[derive(Clone, Debug, Default, serde::Serialize)]
145#[serde(rename_all = "camelCase")]
146pub struct PeerMetrics {
147 pub received_git_bytes: usize,
148 pub received_fetch_requests: usize,
149 pub received_bytes: usize,
150 pub received_gossip_messages: usize,
151 pub sent_bytes: usize,
152 pub sent_fetch_requests: usize,
153 pub sent_git_bytes: usize,
154 pub sent_gossip_messages: usize,
155 pub streams_opened: usize,
156 pub inbound_connection_attempts: usize,
157 pub outbound_connection_attempts: usize,
158 pub disconnects: usize,
159}
160
161#[derive(Default)]
163struct SyncedRouting {
164 added: Vec<RepoId>,
166 removed: Vec<RepoId>,
168 updated: Vec<RepoId>,
170}
171
172impl SyncedRouting {
173 fn is_empty(&self) -> bool {
174 self.added.is_empty() && self.removed.is_empty() && self.updated.is_empty()
175 }
176}
177
178#[derive(Debug, Clone)]
180struct Peer {
181 nid: NodeId,
182 addresses: Vec<KnownAddress>,
183 penalty: Penalty,
184}
185
186#[derive(thiserror::Error, Debug)]
188pub enum Error {
189 #[error(transparent)]
190 Git(#[from] radicle::git::raw::Error),
191 #[error(transparent)]
192 Storage(#[from] storage::Error),
193 #[error(transparent)]
194 Gossip(#[from] gossip::Error),
195 #[error(transparent)]
196 Refs(#[from] storage::refs::Error),
197 #[error(transparent)]
198 Routing(#[from] routing::Error),
199 #[error(transparent)]
200 Address(#[from] address::Error),
201 #[error(transparent)]
202 Database(#[from] node::db::Error),
203 #[error(transparent)]
204 Seeds(#[from] seed::Error),
205 #[error(transparent)]
206 Policy(#[from] policy::Error),
207 #[error(transparent)]
208 Repository(#[from] radicle::storage::RepositoryError),
209 #[error("namespaces error: {0}")]
210 Namespaces(Box<NamespacesError>),
211}
212
213impl From<NamespacesError> for Error {
214 fn from(e: NamespacesError) -> Self {
215 Self::Namespaces(Box::new(e))
216 }
217}
218
219#[derive(thiserror::Error, Debug)]
220pub enum ConnectError {
221 #[error("attempted connection to peer {nid} which already has a session")]
222 SessionExists { nid: NodeId },
223 #[error("attempted connection to self")]
224 SelfConnection,
225 #[error("outbound connection limit reached when attempting {nid} ({addr})")]
226 LimitReached { nid: NodeId, addr: Address },
227 #[error(
228 "attempted connection to {nid}, via {addr} but addresses of this kind are not supported"
229 )]
230 UnsupportedAddress { nid: NodeId, addr: Address },
231 #[error("attempted connection with blocked peer {nid}")]
232 Blocked { nid: NodeId },
233}
234
235pub trait Store:
237 address::Store + gossip::Store + routing::Store + seed::Store + node::refs::Store
238{
239}
240
241impl Store for radicle::node::Database {}
242
243#[derive(Debug)]
245pub struct FetchState {
246 pub from: NodeId,
248 pub refs_at: Vec<RefsAt>,
250 pub subscribers: Vec<mpsc::Sender<FetchResult>>,
252}
253
254#[derive(Debug)]
256pub struct Stores<D>(D);
257
258impl<D> Stores<D>
259where
260 D: Store,
261{
262 pub fn routing(&self) -> &impl routing::Store {
264 &self.0
265 }
266
267 pub fn routing_mut(&mut self) -> &mut impl routing::Store {
269 &mut self.0
270 }
271
272 pub fn addresses(&self) -> &impl address::Store {
274 &self.0
275 }
276
277 pub fn addresses_mut(&mut self) -> &mut impl address::Store {
279 &mut self.0
280 }
281
282 pub fn gossip(&self) -> &impl gossip::Store {
284 &self.0
285 }
286
287 pub fn gossip_mut(&mut self) -> &mut impl gossip::Store {
289 &mut self.0
290 }
291
292 pub fn seeds(&self) -> &impl seed::Store {
294 &self.0
295 }
296
297 pub fn seeds_mut(&mut self) -> &mut impl seed::Store {
299 &mut self.0
300 }
301
302 pub fn refs(&self) -> &impl node::refs::Store {
304 &self.0
305 }
306
307 pub fn refs_mut(&mut self) -> &mut impl node::refs::Store {
309 &mut self.0
310 }
311}
312
313impl<D> AsMut<D> for Stores<D> {
314 fn as_mut(&mut self) -> &mut D {
315 &mut self.0
316 }
317}
318
319impl<D> From<D> for Stores<D> {
320 fn from(db: D) -> Self {
321 Self(db)
322 }
323}
324
325#[derive(Debug)]
327pub struct Service<D, S> {
328 config: Config,
330 secret_key: crypto::SigningKey,
332 storage: S,
334 db: Stores<D>,
336 policies: policy::Config<Write>,
338 sessions: Sessions,
340 clock: LocalTime,
342 relayed_by: HashMap<gossip::AnnouncementId, Vec<NodeId>>,
345 outbox: Outbox,
347 node: NodeAnnouncement,
349 inventory: InventoryAnnouncement,
351 rng: Rng,
353 fetcher: FetcherService<command::Responder<FetchResult>>,
354 limiter: RateLimiter,
356 filter: Filter,
358 last_idle: LocalTime,
360 last_gossip: LocalTime,
362 last_sync: LocalTime,
364 last_prune: LocalTime,
366 last_announce: LocalTime,
368 last_inventory: LocalTime,
370 last_timestamp: Timestamp,
372 started_at: Option<LocalTime>,
374 last_online_at: Option<LocalTime>,
376 emitter: Emitter<Event>,
378 listening: Vec<net::SocketAddr>,
380 metrics: Metrics,
382}
383
384impl<D, S> Service<D, S> {
385 pub fn local_time(&self) -> LocalTime {
387 self.clock
388 }
389
390 pub fn emitter(&self) -> Emitter<Event> {
391 self.emitter.clone()
392 }
393}
394
395impl<D, S> Service<D, S>
396where
397 D: Store,
398 S: WriteStorage + 'static,
399{
400 pub fn initialize(&mut self, time: LocalTime) -> Result<(), Error> {
402 debug!(target: "service", "Init @{}", time.as_millis());
403 assert_ne!(time, LocalTime::default());
404
405 let nid = *self.nid();
406
407 self.clock = time;
408 self.started_at = Some(time);
409 self.last_online_at = match self.db.gossip().last() {
410 Ok(Some(last)) => Some(last.to_local_time()),
411 Ok(None) => None,
412 Err(e) => {
413 warn!(target: "service", "Failed to get the latest gossip message from db: {e}");
414 None
415 }
416 };
417
418 match self.db.refs().count() {
421 Ok(0) => {
422 info!(target: "service", "Empty refs database, populating from storage..");
423 if let Err(e) = self.db.refs_mut().populate(&self.storage) {
424 warn!(target: "service", "Failed to populate refs database: {e}");
425 }
426 }
427 Ok(n) => debug!(target: "service", "Refs database has {n} cached references"),
428 Err(e) => {
429 warn!(target: "service", "Failed to retrieve count of refs from database: {e}")
430 }
431 }
432
433 let announced = self
434 .db
435 .seeds()
436 .seeded_by(&nid)?
437 .collect::<Result<HashMap<_, _>, _>>()?;
438 let mut inventory = BTreeSet::new();
439 let mut private = BTreeSet::new();
440
441 for repo in self.storage.repositories()? {
442 let repo = self.upgrade_sigrefs(repo)?;
443 let rid = repo.rid;
444
445 if !self.policies.is_seeding(&rid)? {
447 debug!(target: "service", "Local repository {rid} is not seeded");
448 continue;
449 }
450 if repo.doc.is_public() {
452 inventory.insert(rid);
453 } else {
454 private.insert(rid);
455 }
456 let Some(updated_at) = repo.synced_at else {
458 continue;
459 };
460 if let Some(announced) = announced.get(&rid)
462 && updated_at.oid == announced.oid
463 {
464 continue;
465 }
466 if self.db.seeds_mut().synced(
468 &rid,
469 &nid,
470 updated_at.oid,
471 updated_at.timestamp.into(),
472 )? {
473 debug!(target: "service", "Saved local sync status for {rid}..");
474 }
475 if let Ok((ann, _)) = self.refs_announcement_for(rid, [nid]) {
479 debug!(target: "service", "Adding refs announcement for {rid} to historical gossip messages..");
480 self.db.gossip_mut().announced(&nid, &ann)?;
481 }
482 }
483
484 self.db
488 .routing_mut()
489 .add_inventory(inventory.iter(), nid, time.into())?;
490 self.inventory = gossip::inventory(self.timestamp(), inventory);
491
492 self.db
495 .routing_mut()
496 .remove_inventories(private.iter(), &nid)?;
497
498 self.filter = Filter::allowed_by(self.policies.seed_policies()?);
500 let addrs = self.config.connect.clone();
502 for (id, addr) in addrs.into_iter().map(|ca| ca.into()) {
503 if let Err(e) = self.connect(id, addr) {
504 debug!(target: "service", "Service::initialization connection error: {e}");
505 }
506 }
507 self.maintain_connections();
509 self.outbox.wakeup(IDLE_INTERVAL);
511 self.outbox.wakeup(GOSSIP_INTERVAL);
512
513 Ok(())
514 }
515
516 fn upgrade_sigrefs(&mut self, mut info: RepositoryInfo) -> Result<RepositoryInfo, Error> {
517 if !matches!(info.refs, SignedRefsInfo::NeedsMigration) {
518 return Ok(info);
519 }
520
521 let rid = info.rid;
522
523 log::info!(
524 "Migrating `rad/sigrefs` of {rid} to force feature level {}.",
525 FeatureLevel::LATEST
526 );
527
528 let repo = self.storage.repository_mut(rid)?;
529 let refs = repo.force_sign_refs(&self.secret_key)?;
531
532 let repo = self.storage.repository(rid)?;
533 let synced_at = SyncedAt::new(refs.at, &repo)?;
534
535 info.synced_at = Some(synced_at);
536 info.refs = SignedRefsInfo::Some(refs);
537
538 Ok(info)
539 }
540}
541
542impl<D, S> Service<D, S>
543where
544 D: Store,
545 S: ReadStorage + 'static,
546{
547 pub fn new(
548 config: Config,
549 db: Stores<D>,
550 storage: S,
551 policies: policy::Config<Write>,
552 secret_key: crypto::SigningKey,
553 rng: Rng,
554 node: NodeAnnouncement,
555 emitter: Emitter<Event>,
556 ) -> Self {
557 let sessions = Sessions::new(rng.clone());
558 let limiter = RateLimiter::new(config.peers());
559 let last_timestamp = node.timestamp;
560 let clock = LocalTime::default(); let inventory = gossip::inventory(clock.into(), []); let fetcher = {
563 let config = fetcher::Config::new()
564 .with_max_concurrency(
565 std::num::NonZeroUsize::new(config.limits.fetch_concurrency.into())
566 .expect("fetch concurrency was zero, must be at least 1"),
567 )
568 .with_max_capacity(fetcher::MaxQueueSize::default());
569 FetcherService::new(config)
570 };
571 Self {
572 config,
573 storage,
574 policies,
575 secret_key,
576 rng,
577 inventory,
578 node,
579 clock,
580 db,
581 outbox: Outbox::default(),
582 limiter,
583 sessions,
584 fetcher,
585 filter: Filter::empty(),
586 relayed_by: HashMap::default(),
587 last_idle: LocalTime::default(),
588 last_gossip: LocalTime::default(),
589 last_sync: LocalTime::default(),
590 last_prune: LocalTime::default(),
591 last_timestamp,
592 last_announce: LocalTime::default(),
593 last_inventory: LocalTime::default(),
594 started_at: None, last_online_at: None, emitter,
597 listening: vec![],
598 metrics: Metrics::default(),
599 }
600 }
601
602 pub fn started(&self) -> Option<LocalTime> {
604 self.started_at
605 }
606
607 #[allow(clippy::should_implement_trait)]
609 pub fn next(&mut self) -> Option<io::Io> {
610 self.outbox.next()
611 }
612
613 pub fn seed(&mut self, id: &RepoId, scope: Scope) -> Result<bool, policy::Error> {
616 let updated = self.policies.seed(id, scope)?;
617 self.filter.insert(id);
618
619 Ok(updated)
620 }
621
622 pub fn unseed(&mut self, id: &RepoId) -> Result<bool, policy::Error> {
627 let updated = self.policies.unseed(id)?;
628
629 if updated {
630 self.filter = Filter::allowed_by(self.policies.seed_policies()?);
634 if let Err(e) = self.remove_inventory(id) {
636 warn!(target: "service", "Failed to update inventory after unseed: {e}");
637 }
638 }
639 Ok(updated)
640 }
641
642 #[allow(unused)]
646 pub fn closest_peers(&self, n: usize) -> Vec<NodeId> {
647 todo!()
648 }
649
650 pub fn database(&self) -> &Stores<D> {
652 &self.db
653 }
654
655 pub fn database_mut(&mut self) -> &mut Stores<D> {
657 &mut self.db
658 }
659
660 pub fn storage(&self) -> &S {
662 &self.storage
663 }
664
665 pub fn storage_mut(&mut self) -> &mut S {
667 &mut self.storage
668 }
669
670 pub fn policies(&self) -> &policy::Config<Write> {
672 &self.policies
673 }
674
675 #[cfg(any(test, feature = "test"))]
677 pub fn secret_key(&self) -> &radicle::crypto::SigningKey {
678 &self.secret_key
679 }
680
681 pub fn events(&mut self) -> Events {
683 Events::from(self.emitter.subscribe())
684 }
685
686 pub fn fetcher(&self) -> &FetcherState {
687 self.fetcher.state()
688 }
689
690 pub fn outbox(&mut self) -> &mut Outbox {
692 &mut self.outbox
693 }
694
695 pub fn config(&self) -> &Config {
697 &self.config
698 }
699
700 pub fn lookup(&self, rid: RepoId) -> Result<Lookup, LookupError> {
702 let this = self.nid();
703 let local = self.storage.get(rid)?;
704 let remote = self
705 .db
706 .routing()
707 .get(&rid)?
708 .iter()
709 .filter(|nid| nid != &this)
710 .cloned()
711 .collect();
712
713 Ok(Lookup { local, remote })
714 }
715
716 pub fn tick(&mut self, now: LocalTime, metrics: &Metrics) {
717 trace!(
718 target: "service",
719 "Tick +{}",
720 now - self.started_at.expect("Service::tick: service must be initialized")
721 );
722 if now >= self.clock {
723 self.clock = now;
724 } else {
725 #[cfg(not(test))]
728 warn!(
729 target: "service",
730 "System clock is not monotonic: {now} is not greater or equal to {}", self.clock
731 );
732 }
733 self.metrics = metrics.clone();
734 }
735
736 pub fn wake(&mut self) {
737 let now = self.clock;
738
739 trace!(
740 target: "service",
741 "Wake +{}",
742 now - self.started_at.expect("Service::wake: service must be initialized")
743 );
744
745 if now - self.last_idle >= IDLE_INTERVAL {
746 trace!(target: "service", "Running 'idle' task…");
747
748 self.keep_alive(&now);
749 self.disconnect_unresponsive_peers(&now);
750 self.idle_connections();
751 self.maintain_connections();
752 self.dequeue_fetches();
753 self.outbox.wakeup(IDLE_INTERVAL);
754 self.last_idle = now;
755 }
756 if now - self.last_gossip >= GOSSIP_INTERVAL {
757 trace!(target: "service", "Running 'gossip' task…");
758
759 if let Err(e) = self.relay_announcements() {
760 warn!(target: "service", "Failed to relay stored announcements: {e}");
761 }
762 self.outbox.wakeup(GOSSIP_INTERVAL);
763 self.last_gossip = now;
764 }
765 if now - self.last_sync >= SYNC_INTERVAL {
766 trace!(target: "service", "Running 'sync' task…");
767
768 if let Err(e) = self.fetch_missing_repositories() {
769 warn!(target: "service", "Failed to fetch missing inventory: {e}");
770 }
771 self.outbox.wakeup(SYNC_INTERVAL);
772 self.last_sync = now;
773 }
774 if now - self.last_announce >= ANNOUNCE_INTERVAL {
775 trace!(target: "service", "Running 'announce' task…");
776
777 self.announce_inventory();
778 self.outbox.wakeup(ANNOUNCE_INTERVAL);
779 self.last_announce = now;
780 }
781 if now - self.last_prune >= PRUNE_INTERVAL {
782 trace!(target: "service", "Running 'prune' task…");
783
784 if let Err(err) = self.prune_routing_entries(&now) {
785 warn!(target: "service", "Failed to prune routing entries: {err}");
786 }
787 if let Err(err) = self
788 .db
789 .gossip_mut()
790 .prune((now - LocalDuration::from(self.config.limits.gossip_max_age)).into())
791 {
792 warn!(target: "service", "Failed to prune gossip entries: {err}");
793 }
794
795 self.outbox.wakeup(PRUNE_INTERVAL);
796 self.last_prune = now;
797 }
798
799 self.maintain_persistent();
801 }
802
803 pub fn command(&mut self, cmd: Command) {
804 info!(target: "service", "Received command {cmd:?}");
805
806 match cmd {
807 Command::Connect(nid, addr, opts) => {
808 if opts.persistent {
809 self.config.connect.insert((nid, addr.clone()).into());
810 }
811 if let Err(e) = self.connect(nid, addr) {
812 match e {
813 ConnectError::SessionExists { nid } => {
814 self.emitter.emit(Event::PeerConnected { nid });
815 }
816 e => {
817 self.emitter.emit(Event::PeerDisconnected {
819 nid,
820 reason: e.to_string(),
821 });
822 }
823 }
824 }
825 }
826 Command::Disconnect(nid) => {
827 self.outbox.disconnect(nid, DisconnectReason::Command);
828 }
829 Command::Config(resp) => {
830 resp.ok(self.config.clone()).ok();
831 }
832 Command::ListenAddrs(resp) => {
833 resp.ok(self.listening.clone()).ok();
834 }
835 Command::Seeds(rid, namespaces, resp) => match self.seeds(&rid, namespaces) {
836 Ok(seeds) => {
837 let (connected, disconnected) = seeds.partition();
838 debug!(
839 target: "service",
840 "Found {} connected seed(s) and {} disconnected seed(s) for {}",
841 connected.len(), disconnected.len(), rid
842 );
843 resp.ok(seeds).ok();
844 }
845 Err(e) => {
846 warn!(target: "service", "Failed to get seeds for {rid}: {e}");
847 resp.err(e).ok();
848 }
849 },
850 Command::Fetch(rid, seed, timeout, signed_references_minimum_feature_level, resp) => {
851 let feature_level = signed_references_minimum_feature_level
852 .unwrap_or(self.config.fetch.feature_level_min());
853 let config = self
854 .fetch_config()
855 .with_timeout(timeout)
856 .with_minimum_feature_level(feature_level);
857 self.fetch(rid, seed, vec![], config, Some(resp));
858 }
859 Command::Seed(rid, scope, resp) => {
860 let seeded = self
862 .seed(&rid, scope)
863 .expect("Service::command: error seeding repository");
864 resp.ok(seeded).ok();
865
866 self.outbox.broadcast(
868 Message::subscribe(self.filter(), self.clock.into(), Timestamp::MAX),
869 self.sessions.connected().map(|(_, s)| s),
870 );
871 }
872 Command::Unseed(id, resp) => {
873 let updated = self
874 .unseed(&id)
875 .expect("Service::command: error unseeding repository");
876 resp.ok(updated).ok();
877 }
878 Command::Follow(id, alias, resp) => {
879 let seeded = self
880 .policies
881 .follow(&id, alias.as_ref())
882 .expect("Service::command: error following node");
883 resp.ok(seeded).ok();
884 }
885 Command::Unfollow(id, resp) => {
886 let updated = self
887 .policies
888 .unfollow(&id)
889 .expect("Service::command: error unfollowing node");
890 resp.ok(updated).ok();
891 }
892 Command::Block(id, resp) => {
893 let updated = self
894 .policies
895 .set_follow_policy(&id, policy::Policy::Block)
896 .expect("Service::command: error blocking node");
897 if updated {
898 self.outbox.disconnect(id, DisconnectReason::Policy);
899 }
900 resp.ok(updated).ok();
901 }
902 Command::AnnounceRefs(id, namespaces, resp) => {
903 let doc = match self.storage.get(id) {
904 Ok(Some(doc)) => doc,
905 Ok(None) => {
906 warn!(target: "service", "Failed to announce refs: repository {id} not found");
907 resp.err(command::Error::custom(format!("repository {id} not found")))
908 .ok();
909 return;
910 }
911 Err(e) => {
912 warn!(target: "service", "Failed to announce refs: doc error: {e}");
913 resp.err(e).ok();
914 return;
915 }
916 };
917
918 match self.announce_own_refs(id, doc, namespaces) {
919 Ok((refs, _timestamp)) => {
920 if let Some(refs) = refs.first() {
924 resp.ok(*refs).ok();
925 } else {
926 resp.err(command::Error::custom(format!(
927 "no refs were announced for {id}"
928 )))
929 .ok();
930 }
931 }
932 Err(err) => {
933 warn!(target: "service", "Failed to announce refs: {err}");
934 resp.err(err).ok();
935 }
936 }
937 }
938 Command::AnnounceInventory => {
939 self.announce_inventory();
940 }
941 Command::AddInventory(rid, resp) => match self.add_inventory(rid) {
942 Ok(updated) => {
943 resp.ok(updated).ok();
944 }
945 Err(e) => {
946 warn!(target: "service", "Failed to add {rid} to inventory: {e}");
947 resp.err(e).ok();
948 }
949 },
950 Command::QueryState(query, sender) => {
951 sender.send(query(self)).ok();
952 }
953 }
954 }
955
956 fn fetch_refs_at(
959 &mut self,
960 rid: RepoId,
961 from: NodeId,
962 refs: NonEmpty<RefsAt>,
963 scope: Scope,
964 config: fetcher::FetchConfig,
965 ) -> bool {
966 match self.refs_status_of(rid, refs, &scope) {
967 Ok(status) => {
968 if status.want.is_empty() {
969 debug!(target: "service", "Skipping fetch for {rid}, all refs are already in storage");
970 } else {
971 self.fetch(rid, from, status.want, config, None);
972 return true;
973 }
974 }
975 Err(e) => {
976 warn!(target: "service", "Failed to get the refs status of {rid}: {e}");
977 }
978 }
979 false
981 }
982
983 fn fetch(
984 &mut self,
985 rid: RepoId,
986 from: NodeId,
987 refs_at: Vec<RefsAt>,
988 config: fetcher::FetchConfig,
989 channel: Option<command::Responder<FetchResult>>,
990 ) {
991 let session = {
992 let reason = format!("peer {from} is not connected; cannot initiate fetch");
993 let Some(session) = self.sessions.get_mut(&from) else {
994 if let Some(c) = channel {
995 c.ok(FetchResult::Failed { reason }).ok();
996 }
997 return;
998 };
999 if !session.is_connected() {
1000 if let Some(c) = channel {
1001 c.ok(FetchResult::Failed { reason }).ok();
1002 }
1003 return;
1004 }
1005 session
1006 };
1007
1008 let cmd = fetcher::state::command::Fetch {
1009 from,
1010 rid,
1011 refs: refs_at.into(),
1012 config,
1013 };
1014 let fetcher::service::FetchInitiated { event, rejected } = self.fetcher.fetch(cmd, channel);
1015
1016 if let Some(c) = rejected {
1017 c.ok(FetchResult::Failed {
1018 reason: "fetch queue at capacity".to_string(),
1019 })
1020 .ok();
1021 }
1022
1023 match event {
1024 fetcher::state::event::Fetch::Started {
1025 rid,
1026 from,
1027 refs: refs_at,
1028 config,
1029 } => {
1030 debug!(target: "service", "Starting fetch for {rid} from {from}");
1031 self.outbox.fetch(
1032 session,
1033 rid,
1034 refs_at.into(),
1035 self.config.limits.fetch_pack_receive,
1036 config,
1037 );
1038 }
1039 fetcher::state::event::Fetch::Queued { rid, from } => {
1040 debug!(target: "service", "Queued fetch for {rid} from {from}");
1041 }
1042 fetcher::state::event::Fetch::AlreadyFetching { rid, from } => {
1043 debug!(target: "service", "Already fetching {rid} from {from}");
1044 }
1045 fetcher::state::event::Fetch::QueueAtCapacity { rid, from, .. } => {
1046 debug!(target: "service", "Queue at capacity for {from}, rejected {rid}");
1047 }
1048 }
1049 }
1050
1051 pub fn fetched(
1052 &mut self,
1053 rid: RepoId,
1054 from: NodeId,
1055 result: Result<crate::worker::fetch::FetchResult, crate::worker::FetchError>,
1056 ) {
1057 let cmd = fetcher::state::command::Fetched { from, rid };
1058 let fetcher::service::FetchCompleted { event, subscribers } = self.fetcher.fetched(cmd);
1059
1060 self.dequeue_fetches();
1062
1063 match event {
1064 fetcher::state::event::Fetched::NotFound { from, rid } => {
1065 debug!(target: "service", "Unexpected fetch result for {rid} from {from}");
1066 }
1067 fetcher::state::event::Fetched::Completed { from, rid, refs: _ } => {
1068 let fetch_result = match &result {
1070 Ok(success) => FetchResult::Success {
1071 updated: success.updated.clone(),
1072 namespaces: success.namespaces.clone(),
1073 clone: success.clone,
1074 },
1075 Err(e) => FetchResult::Failed {
1076 reason: e.to_string(),
1077 },
1078 };
1079 for responder in subscribers {
1080 responder.ok(fetch_result.clone()).ok();
1081 }
1082 match result {
1083 Ok(crate::worker::fetch::FetchResult {
1084 updated,
1085 canonical,
1086 namespaces,
1087 clone,
1088 doc,
1089 }) => {
1090 info!(target: "service", "Fetched {rid} from {from} successfully");
1091 self.seed_discovered(rid, from, self.clock.into());
1094
1095 for update in &updated {
1096 if update.is_skipped() {
1097 trace!(target: "service", "Ref skipped: {update} for {rid}");
1098 } else {
1099 debug!(target: "service", "Ref updated: {update} for {rid}");
1100 }
1101 }
1102 self.emitter.emit(Event::RefsFetched {
1103 remote: from,
1104 rid,
1105 updated: updated.clone(),
1106 });
1107 self.emitter
1108 .emit_all(canonical.into_iter().map(|(refname, target)| {
1109 Event::CanonicalRefUpdated {
1110 rid,
1111 refname,
1112 target,
1113 }
1114 }));
1115
1116 if clone && doc.is_public() {
1119 debug!(target: "service", "Updating and announcing inventory for cloned repository {rid}..");
1120
1121 if let Err(e) = self.add_inventory(rid) {
1122 warn!(target: "service", "Failed to announce inventory for {rid}: {e}");
1123 }
1124 }
1125
1126 if updated.is_empty() || updated.iter().all(|u| u.is_skipped()) {
1128 debug!(target: "service", "Nothing to announce, no refs were updated..");
1129 } else {
1130 if let Err(e) = self.announce_refs(rid, doc.into(), namespaces, false) {
1133 warn!(target: "service", "Failed to announce new refs: {e}");
1134 }
1135 }
1136 }
1137 Err(err) => {
1138 warn!(target: "service", "Fetch failed for {rid} from {from}: {err}");
1139
1140 if err.is_timeout() {
1143 self.outbox.disconnect(from, DisconnectReason::Fetch(err));
1144 }
1145 }
1146 }
1147 }
1148 }
1149 }
1150
1151 pub fn dequeue_fetches(&mut self) {
1159 let sessions = self
1160 .sessions
1161 .shuffled()
1162 .map(|(k, _)| *k)
1163 .collect::<Vec<_>>();
1164
1165 for nid in sessions {
1166 #[allow(clippy::unwrap_used)]
1167 let sess = self.sessions.get_mut(&nid).unwrap();
1168 if !sess.is_connected() {
1169 continue;
1170 }
1171
1172 let Some(fetcher::QueuedFetch {
1173 rid,
1174 refs: refs_at,
1175 config,
1176 }) = self.fetcher.dequeue(&nid)
1177 else {
1178 continue;
1179 };
1180
1181 let repo_entry = self
1183 .policies
1184 .seed_policy(&rid)
1185 .expect("error accessing repo seeding configuration");
1186
1187 let SeedingPolicy::Allow { scope } = repo_entry.policy else {
1188 debug!(target: "service", "Repository {} no longer seeded, skipping", rid);
1189 continue;
1190 };
1191
1192 debug!(target: "service", "Dequeued fetch for {} from {}", rid, nid);
1193
1194 match refs_at {
1195 RefsToFetch::Refs(refs) => {
1196 self.fetch_refs_at(rid, nid, refs, scope, config);
1197 }
1198 RefsToFetch::All => {
1199 self.fetch(rid, nid, vec![], config, None);
1202 }
1203 }
1204 }
1205 }
1206
1207 pub fn accepted(&mut self, ip: IpAddr) -> bool {
1209 if ip.is_loopback() || ip.is_unspecified() {
1212 return true;
1213 }
1214 if self.sessions.inbound().count() >= self.config.limits.connection.inbound.into() {
1216 return false;
1217 }
1218 match self.db.addresses().is_ip_banned(ip) {
1219 Ok(banned) => {
1220 if banned {
1221 debug!(target: "service", "Rejecting inbound connection from banned ip {ip}");
1222 return false;
1223 }
1224 }
1225 Err(e) => warn!(target: "service", "Failed to query ban status for {ip}: {e}"),
1226 }
1227 let host: HostName = ip.into();
1228 let tokens = self.config.limits.rate.inbound;
1229
1230 if self.limiter.limit(host.clone(), None, &tokens, self.clock) {
1231 trace!(target: "service", "Rate limiting inbound connection from {host}..");
1232 return false;
1233 }
1234 true
1235 }
1236
1237 pub fn attempted(&mut self, nid: NodeId, addr: Address) {
1238 debug!(target: "service", "Attempted connection to {nid} ({addr})");
1239
1240 if let Some(sess) = self.sessions.get_mut(&nid) {
1241 sess.to_attempted();
1242 } else {
1243 #[cfg(debug_assertions)]
1244 panic!("Service::attempted: unknown session {nid}@{addr}");
1245 }
1246 }
1247
1248 pub fn listening(&mut self, local_addr: net::SocketAddr) {
1249 info!(target: "node", "Listening on {local_addr}..");
1250
1251 self.listening.push(local_addr);
1252 }
1253
1254 pub fn connected(&mut self, remote: NodeId, addr: Address, link: Link) {
1255 if let Ok(true) = self.policies.is_blocked(&remote) {
1256 self.emitter.emit(Event::PeerDisconnected {
1257 nid: remote,
1258 reason: format!("{remote} is blocked"),
1259 });
1260 info!(target: "service", "Disconnecting blocked inbound peer {remote}");
1261 self.outbox.disconnect(remote, DisconnectReason::Policy);
1262 return;
1263 }
1264
1265 info!(target: "service", "Connected to {remote} ({addr}) ({link:?})");
1266 self.emitter.emit(Event::PeerConnected { nid: remote });
1267
1268 let msgs = self.initial(link);
1269
1270 if link.is_outbound() {
1271 if let Some(peer) = self.sessions.get_mut(&remote) {
1272 peer.to_connected(self.clock);
1273 self.outbox.write_all(peer, msgs);
1274 }
1275 } else {
1276 match self.sessions.entry(remote) {
1277 Entry::Occupied(mut e) => {
1278 let peer = e.get_mut();
1288 debug!(
1289 target: "service",
1290 "Connecting peer {remote} already has a session open ({peer})"
1291 );
1292 peer.link = link;
1293 peer.to_connected(self.clock);
1294 self.outbox.write_all(peer, msgs);
1295 }
1296 Entry::Vacant(e) => {
1297 if let HostName::Ip(ip) = addr.host
1298 && !address::is_local(&ip)
1299 && let Err(e) =
1300 self.db
1301 .addresses_mut()
1302 .record_ip(&remote, ip, self.clock.into())
1303 {
1304 log::debug!(target: "service", "Failed to record IP address for {remote}: {e}");
1305 }
1306 let peer = e.insert(Session::inbound(
1307 remote,
1308 addr,
1309 self.config.is_persistent(&remote),
1310 self.rng.clone(),
1311 self.clock,
1312 ));
1313 self.outbox.write_all(peer, msgs);
1314 }
1315 }
1316 }
1317 }
1318
1319 pub fn disconnected(&mut self, remote: NodeId, link: Link, reason: &DisconnectReason) {
1320 let since = self.local_time();
1321 let Some(session) = self.sessions.get_mut(&remote) else {
1322 trace!(target: "service", "Redundant disconnection for {remote} ({reason})");
1325 return;
1326 };
1327 if session.link != link {
1330 return;
1331 }
1332
1333 info!(target: "service", "Disconnected from {remote} ({reason})");
1334 self.emitter.emit(Event::PeerDisconnected {
1335 nid: remote,
1336 reason: reason.to_string(),
1337 });
1338
1339 let link = session.link;
1340 let addr = session.addr.clone();
1341
1342 let cmd = fetcher::state::command::Cancel { from: remote };
1343 let fetcher::service::FetchesCancelled { event, orphaned } = self.fetcher.cancel(cmd);
1344
1345 match event {
1346 fetcher::state::event::Cancel::Unexpected { from } => {
1347 debug!(target: "service", "No fetches to cancel for {from}");
1348 }
1349 fetcher::state::event::Cancel::Canceled {
1350 from,
1351 active,
1352 queued,
1353 } => {
1354 debug!(target: "service", "Cancelled {} ongoing, {} queued for {from}", active.len(), queued.len());
1355 }
1356 }
1357
1358 for (rid, responder) in orphaned {
1360 responder
1361 .ok(FetchResult::Failed {
1362 reason: format!("failed fetch to {rid}, peer disconnected: {reason}"),
1363 })
1364 .ok();
1365 }
1366
1367 if self.config.is_persistent(&remote) {
1369 let delay = LocalDuration::from_secs(2u64.saturating_pow(session.attempts() as u32))
1370 .clamp(MIN_RECONNECTION_DELTA, MAX_RECONNECTION_DELTA);
1371
1372 session.to_disconnected(since, since + delay);
1375
1376 debug!(target: "service", "Reconnecting to {remote} in {delay}..");
1377
1378 self.outbox.wakeup(delay);
1379 } else {
1380 debug!(target: "service", "Dropping peer {remote}..");
1381 self.sessions.remove(&remote);
1382
1383 let severity = match reason {
1384 DisconnectReason::Dial(_)
1385 | DisconnectReason::Fetch(_)
1386 | DisconnectReason::Connection(_) => {
1387 if self.is_online() {
1388 Severity::Medium
1391 } else {
1392 Severity::Low
1393 }
1394 }
1395 DisconnectReason::Session(e) => e.severity(),
1396 DisconnectReason::Command
1397 | DisconnectReason::Conflict
1398 | DisconnectReason::Policy
1399 | DisconnectReason::SelfConnection => Severity::Low,
1400 };
1401
1402 if let Err(e) = self
1403 .db
1404 .addresses_mut()
1405 .disconnected(&remote, &addr, severity)
1406 {
1407 debug!(target: "service", "Failed to update address store: {e}");
1408 }
1409 if link.is_outbound() {
1412 self.maintain_connections();
1413 }
1414 }
1415 self.dequeue_fetches();
1416 }
1417
1418 pub fn received_message(&mut self, remote: NodeId, message: Message) {
1419 if let Err(err) = self.handle_message(&remote, message) {
1420 self.outbox
1423 .disconnect(remote, DisconnectReason::Session(err));
1424
1425 }
1428 }
1429
1430 pub fn handle_announcement(
1435 &mut self,
1436 relayer: &NodeId,
1437 relayer_addr: &Address,
1438 announcement: &Announcement,
1439 ) -> Result<Option<gossip::AnnouncementId>, session::Error> {
1440 if !announcement.verify() {
1441 return Err(session::Error::Misbehavior);
1442 }
1443 let Announcement {
1444 node: announcer,
1445 message,
1446 ..
1447 } = announcement;
1448
1449 if announcer == self.nid() {
1451 return Ok(None);
1452 }
1453 let now = self.clock;
1454 let timestamp = message.timestamp();
1455
1456 if timestamp.saturating_sub(now.as_millis()) > MAX_TIME_DELTA.as_millis() as u64 {
1458 return Err(session::Error::future_timestamp(timestamp, now.into()));
1459 }
1460
1461 if let AnnouncementMessage::Inventory(_) | AnnouncementMessage::Refs(_) = message {
1470 match self.db.addresses().get(announcer) {
1471 Ok(node) => {
1472 if node.is_none() {
1473 debug!(target: "service", "Ignoring announcement from unknown node {announcer} (t={timestamp})");
1474 return Ok(None);
1475 }
1476 }
1477 Err(e) => {
1478 debug!(target: "service", "Failed to look up node in address book: {e}");
1479 return Ok(None);
1480 }
1481 }
1482 }
1483
1484 let relay = match self.db.gossip_mut().announced(announcer, announcement) {
1486 Ok(Some(id)) => {
1487 log::debug!(
1488 target: "service",
1489 "Stored announcement from {announcer} to be broadcast in {} (t={timestamp})",
1490 (self.last_gossip + GOSSIP_INTERVAL) - self.clock
1491 );
1492 self.relayed_by.entry(id).or_default().push(*relayer);
1494
1495 let relay = message.is_node_announcement()
1500 || now - timestamp.to_local_time() <= MAX_TIME_DELTA;
1501 relay.then_some(id)
1502 }
1503 Ok(None) => {
1504 debug!(target: "service", "Ignoring stale announcement from {announcer} (t={timestamp})");
1508 return Ok(None);
1509 }
1510 Err(e) => {
1511 debug!(target: "service", "Failed to update gossip entry from {announcer}: {e}");
1512 return Ok(None);
1513 }
1514 };
1515
1516 match message {
1517 AnnouncementMessage::Inventory(message) => {
1519 self.emitter.emit(Event::InventoryAnnounced {
1520 nid: *announcer,
1521 inventory: message.inventory.to_vec(),
1522 timestamp: message.timestamp,
1523 });
1524 match self.sync_routing(
1525 message.inventory.iter().cloned(),
1526 *announcer,
1527 message.timestamp,
1528 ) {
1529 Ok(synced) => {
1530 if synced.is_empty() {
1531 trace!(target: "service", "No routes updated by inventory announcement from {announcer}");
1532 return Ok(None);
1533 }
1534 }
1535 Err(e) => {
1536 debug!(target: "service", "Failed to process inventory from {announcer}: {e}");
1537 return Ok(None);
1538 }
1539 }
1540 let mut missing = Vec::new();
1541 let nid = *self.nid();
1542
1543 if let Some(sess) = self.sessions.get_mut(announcer) {
1546 for id in message.inventory.as_slice() {
1547 if let Some(sub) = &mut sess.subscribe {
1551 sub.filter.insert(id);
1552 }
1553
1554 if self.policies.is_seeding(id).expect(
1557 "Service::handle_announcement: error accessing seeding configuration",
1558 ) {
1559 match self.db.routing().entry(id, &nid) {
1562 Ok(entry) => {
1563 if entry.is_none() {
1564 missing.push(*id);
1565 }
1566 }
1567 Err(e) => debug!(
1568 target: "service",
1569 "Error checking local inventory for {id}: {e}"
1570 ),
1571 }
1572 }
1573 }
1574 }
1575 self.rng.shuffle(&mut missing);
1580
1581 for rid in missing {
1582 debug!(target: "service", "Missing seeded inventory {rid}; initiating fetch..");
1583 self.fetch(rid, *announcer, vec![], self.fetch_config(), None);
1584 }
1585 return Ok(relay);
1586 }
1587 AnnouncementMessage::Refs(message) => {
1588 self.emitter.emit(Event::RefsAnnounced {
1589 nid: *announcer,
1590 rid: message.rid,
1591 refs: message.refs.to_vec(),
1592 timestamp: message.timestamp,
1593 });
1594 let Some(refs) = NonEmpty::from_vec(message.refs.to_vec()) else {
1596 debug!(target: "service", "Skipping fetch, no refs in announcement for {} (t={timestamp})", message.rid);
1597 return Ok(None);
1598 };
1599 self.seed_discovered(message.rid, *announcer, message.timestamp);
1602
1603 if let Some(refs) = refs.iter().find(|r| &r.remote == self.nid()) {
1605 debug!(
1606 target: "service",
1607 "Refs announcement of {announcer} for {} contains our own remote at {} (t={})",
1608 message.rid, refs.at, message.timestamp
1609 );
1610 match self.db.seeds_mut().synced(
1611 &message.rid,
1612 announcer,
1613 refs.at,
1614 message.timestamp,
1615 ) {
1616 Ok(updated) => {
1617 if updated {
1618 debug!(
1619 target: "service",
1620 "Updating sync status of {announcer} for {} to {}",
1621 message.rid, refs.at
1622 );
1623 self.emitter.emit(Event::RefsSynced {
1624 rid: message.rid,
1625 remote: *announcer,
1626 at: refs.at,
1627 });
1628 } else {
1629 debug!(
1630 target: "service",
1631 "Sync status of {announcer} was not updated for {}",
1632 message.rid,
1633 );
1634 }
1635 }
1636 Err(e) => {
1637 debug!(target: "service", "Failed to update sync status for {}: {e}", message.rid);
1638 }
1639 }
1640 }
1641 let repo_entry = self.policies.seed_policy(&message.rid).expect(
1642 "Service::handle_announcement: error accessing repo seeding configuration",
1643 );
1644 let SeedingPolicy::Allow { scope } = repo_entry.policy else {
1645 debug!(
1646 target: "service",
1647 "Ignoring refs announcement from {announcer}: repository {} isn't seeded (t={timestamp})",
1648 message.rid
1649 );
1650 return Ok(None);
1651 };
1652 let Some(remote) = self.sessions.get(announcer).cloned() else {
1665 trace!(
1666 target: "service",
1667 "Skipping fetch of {}, no sessions connected to {announcer}",
1668 message.rid
1669 );
1670 return Ok(relay);
1671 };
1672 self.fetch_refs_at(message.rid, remote.id, refs, scope, self.fetch_config());
1674
1675 return Ok(relay);
1676 }
1677 AnnouncementMessage::Node(
1678 ann @ NodeAnnouncement {
1679 features,
1680 addresses,
1681 ..
1682 },
1683 ) => {
1684 self.emitter.emit(Event::NodeAnnounced {
1685 nid: *announcer,
1686 alias: ann.alias.clone(),
1687 timestamp: ann.timestamp,
1688 features: *features,
1689 addresses: addresses.to_vec(),
1690 });
1691 if !features.has(Features::SEED) {
1694 return Ok(relay);
1695 }
1696
1697 match self.db.addresses_mut().insert(
1698 announcer,
1699 ann.version,
1700 ann.features,
1701 &ann.alias,
1702 ann.work(),
1703 &ann.agent,
1704 timestamp,
1705 addresses
1706 .iter()
1707 .filter(|a| a.is_routable() || relayer_addr.is_local())
1710 .map(|a| KnownAddress::new(a.clone(), address::Source::Peer)),
1711 ) {
1712 Ok(updated) => {
1713 if updated {
1715 debug!(
1716 target: "service",
1717 "Address store entry for node {announcer} updated at {timestamp}"
1718 );
1719 return Ok(relay);
1720 }
1721 }
1722 Err(err) => {
1723 warn!(target: "service", "Failed to process node announcement from {announcer}: {err}");
1725 }
1726 }
1727 }
1728 }
1729 Ok(None)
1730 }
1731
1732 pub fn handle_info(&mut self, remote: NodeId, info: &Info) -> Result<(), session::Error> {
1733 match info {
1734 Info::RefsAlreadySynced { rid, at } => {
1736 debug!(target: "service", "Refs already synced for {rid} by {remote}");
1737 self.emitter.emit(Event::RefsSynced {
1738 rid: *rid,
1739 remote,
1740 at: *at,
1741 });
1742 }
1743 }
1744
1745 Ok(())
1746 }
1747
1748 pub fn handle_message(
1749 &mut self,
1750 remote: &NodeId,
1751 message: Message,
1752 ) -> Result<(), session::Error> {
1753 let local = *self.nid();
1754 let relay = self.config.is_relay();
1755 let Some(peer) = self.sessions.get_mut(remote) else {
1756 debug!(target: "service", "Session not found for {remote}");
1757 return Ok(());
1758 };
1759 peer.last_active = self.clock;
1760
1761 let limit: RateLimit = match peer.link {
1762 Link::Outbound => self.config.limits.rate.outbound.into(),
1763 Link::Inbound => self.config.limits.rate.inbound.into(),
1764 };
1765 if self
1766 .limiter
1767 .limit(peer.addr.clone().into(), Some(remote), &limit, self.clock)
1768 {
1769 debug!(target: "service", "Rate limiting message from {remote} ({})", peer.addr);
1770 return Ok(());
1771 }
1772 message.log(log::Level::Debug, remote, Link::Inbound);
1773
1774 let connected = match &mut peer.state {
1775 session::State::Disconnected { .. } => {
1776 debug!(target: "service", "Ignoring message from disconnected peer {}", peer.id);
1777 return Ok(());
1778 }
1779 session::State::Attempted | session::State::Initial => {
1786 debug!(target: "service", "Received unexpected message from connecting peer {}", peer.id);
1787 debug!(target: "service", "Transitioning peer {} to 'connected' state", peer.id);
1788
1789 peer.to_connected(self.clock);
1790
1791 None
1792 }
1793 session::State::Connected {
1794 ping, latencies, ..
1795 } => Some((ping, latencies)),
1796 };
1797
1798 trace!(target: "service", "Received message {message:?} from {remote}");
1799
1800 match message {
1801 Message::Announcement(ann) => {
1803 let relayer = remote;
1804 let relayer_addr = peer.addr.clone();
1805
1806 if let Some(id) = self.handle_announcement(relayer, &relayer_addr, &ann)?
1807 && self.config.is_relay()
1808 {
1809 if let AnnouncementMessage::Inventory(_) = ann.message {
1810 if let Err(e) = self
1811 .database_mut()
1812 .gossip_mut()
1813 .set_relay(id, gossip::RelayStatus::Relay)
1814 {
1815 warn!(target: "service", "Failed to set relay flag for message: {e}");
1816 return Ok(());
1817 }
1818 } else {
1819 self.relay(id, ann);
1820 }
1821 }
1822 }
1823 Message::Subscribe(subscribe) => {
1824 match self
1826 .db
1827 .gossip()
1828 .filtered(&subscribe.filter, subscribe.since, subscribe.until)
1829 {
1830 Ok(anns) => {
1831 for ann in anns {
1832 let ann = match ann {
1833 Ok(a) => a,
1834 Err(e) => {
1835 debug!(target: "service", "Failed to read gossip message from store: {e}");
1836 continue;
1837 }
1838 };
1839 if ann.node == *remote {
1841 continue;
1842 }
1843 if relay || ann.node == local {
1845 self.outbox.write(peer, ann.into());
1846 }
1847 }
1848 }
1849 Err(e) => {
1850 warn!(target: "service", "Failed to query gossip messages from store: {e}");
1851 }
1852 }
1853 peer.subscribe = Some(subscribe);
1854 }
1855 Message::Info(info) => {
1856 self.handle_info(*remote, &info)?;
1857 }
1858 Message::Ping(Ping { ponglen, .. }) => {
1859 if ponglen > Ping::MAX_PONG_ZEROES {
1861 return Ok(());
1862 }
1863 self.outbox.write(
1864 peer,
1865 Message::Pong {
1866 zeroes: ZeroBytes::new(ponglen),
1867 },
1868 );
1869 }
1870 Message::Pong { zeroes } => {
1871 if let Some((ping, latencies)) = connected
1872 && let session::PingState::AwaitingResponse {
1873 len: ponglen,
1874 since,
1875 } = *ping
1876 && (ponglen as usize) == zeroes.len()
1877 {
1878 *ping = session::PingState::Ok;
1879 latencies.push_back(self.clock - since);
1881 if latencies.len() > MAX_LATENCIES {
1882 latencies.pop_front();
1883 }
1884 }
1885 }
1886 }
1887 Ok(())
1888 }
1889
1890 fn refs_status_of(
1892 &self,
1893 rid: RepoId,
1894 refs: NonEmpty<RefsAt>,
1895 scope: &policy::Scope,
1896 ) -> Result<RefsStatus, Error> {
1897 let mut refs = RefsStatus::new(rid, refs, self.db.refs())?;
1898 if refs.want.is_empty() {
1900 return Ok(refs);
1901 }
1902 let mut refs = match scope {
1904 policy::Scope::All => refs,
1905 policy::Scope::Followed => match self.policies.namespaces_for(&self.storage, &rid) {
1906 Ok(Namespaces::All) => refs,
1907 Ok(Namespaces::Followed(followed)) => {
1908 refs.want.retain(|r| followed.contains(&r.remote));
1909 refs
1910 }
1911 Err(e) => return Err(e.into()),
1912 },
1913 };
1914 refs.want.retain(|r| r.remote != *self.nid());
1916
1917 Ok(refs)
1918 }
1919
1920 fn seed_discovered(&mut self, rid: RepoId, nid: NodeId, time: Timestamp) {
1922 if let Ok(result) = self.db.routing_mut().add_inventory([&rid], nid, time)
1923 && let &[(_, InsertResult::SeedAdded)] = result.as_slice()
1924 {
1925 self.emitter.emit(Event::SeedDiscovered { rid, nid });
1926 debug!(target: "service", "Routing table updated for {rid} with seed {nid}");
1927 }
1928 }
1929
1930 fn initial(&mut self, _link: Link) -> Vec<Message> {
1932 let now = self.clock();
1933 let filter = self.filter();
1934
1935 let since = if let Some(last) = self.last_online_at {
1945 Timestamp::from(last - SUBSCRIBE_BACKLOG_DELTA)
1946 } else {
1947 (*now - INITIAL_SUBSCRIBE_BACKLOG_DELTA).into()
1948 };
1949 debug!(target: "service", "Subscribing to messages since timestamp {since}..");
1950
1951 vec![
1952 Message::node(self.node.clone(), &self.secret_key),
1953 Message::inventory(self.inventory.clone(), &self.secret_key),
1954 Message::subscribe(filter, since, Timestamp::MAX),
1955 ]
1956 }
1957
1958 fn is_online(&self) -> bool {
1960 self.sessions
1961 .connected()
1962 .filter(|(_, s)| s.addr.is_routable() && s.last_active >= self.clock - IDLE_INTERVAL)
1963 .count()
1964 > 0
1965 }
1966
1967 fn remove_inventory(&mut self, rid: &RepoId) -> Result<bool, Error> {
1969 let node = *self.nid();
1970 let now = self.timestamp();
1971
1972 let removed = self.db.routing_mut().remove_inventory(rid, &node)?;
1973 if removed {
1974 self.refresh_and_announce_inventory(now)?;
1975 }
1976 Ok(removed)
1977 }
1978
1979 fn add_inventory(&mut self, rid: RepoId) -> Result<bool, Error> {
1981 let node = *self.nid();
1982 let now = self.timestamp();
1983
1984 if !self.storage.contains(&rid)? {
1985 debug!(target: "service", "Attempt to add non-existing inventory {rid}: repository not found in storage");
1986 return Ok(false);
1987 }
1988 let updates = self.db.routing_mut().add_inventory([&rid], node, now)?;
1990 let updated = !updates.is_empty();
1991
1992 if updated {
1993 self.refresh_and_announce_inventory(now)?;
1994 }
1995 Ok(updated)
1996 }
1997
1998 fn refresh_and_announce_inventory(&mut self, time: Timestamp) -> Result<(), Error> {
2000 let inventory = self.inventory()?;
2001
2002 self.inventory = gossip::inventory(time, inventory);
2003 self.announce_inventory();
2004
2005 Ok(())
2006 }
2007
2008 fn inventory(&self) -> Result<HashSet<RepoId>, Error> {
2019 self.db
2020 .routing()
2021 .get_inventory(self.nid())
2022 .map_err(Error::from)
2023 }
2024
2025 fn sync_routing(
2029 &mut self,
2030 inventory: impl IntoIterator<Item = RepoId>,
2031 from: NodeId,
2032 timestamp: Timestamp,
2033 ) -> Result<SyncedRouting, Error> {
2034 let mut synced = SyncedRouting::default();
2035 let included = inventory.into_iter().collect::<BTreeSet<_>>();
2036 let mut events = Vec::new();
2037
2038 for (rid, result) in
2039 self.db
2040 .routing_mut()
2041 .add_inventory(included.iter(), from, timestamp)?
2042 {
2043 match result {
2044 InsertResult::SeedAdded => {
2045 debug!(target: "service", "Routing table updated for {rid} with seed {from}");
2046 events.push(Event::SeedDiscovered { rid, nid: from });
2047
2048 if self
2049 .policies
2050 .is_seeding(&rid)
2051 .expect("Service::process_inventory: error accessing seeding configuration")
2052 {
2053 }
2056 synced.added.push(rid);
2057 }
2058 InsertResult::TimeUpdated => {
2059 synced.updated.push(rid);
2060 }
2061 InsertResult::NotUpdated => {}
2062 }
2063 }
2064
2065 synced.removed.extend(
2066 self.db
2067 .routing()
2068 .get_inventory(&from)?
2069 .into_iter()
2070 .filter(|rid| !included.contains(rid)),
2071 );
2072 self.db
2073 .routing_mut()
2074 .remove_inventories(&synced.removed, &from)?;
2075 events.extend(
2076 synced
2077 .removed
2078 .iter()
2079 .map(|&rid| Event::SeedDropped { rid, nid: from }),
2080 );
2081
2082 self.emitter.emit_all(events);
2083
2084 Ok(synced)
2085 }
2086
2087 fn refs_announcement_for(
2089 &mut self,
2090 rid: RepoId,
2091 remotes: impl IntoIterator<Item = NodeId>,
2092 ) -> Result<(Announcement, Vec<RefsAt>), Error> {
2093 let repo = self.storage.repository(rid)?;
2094 let timestamp = self.timestamp();
2095 let mut refs = BoundedVec::<_, REF_REMOTE_LIMIT>::new();
2096
2097 for remote_id in remotes.into_iter() {
2098 let refs_at = RefsAt::new(&repo, remote_id).map_err(|err| {
2099 radicle::storage::Error::Refs(radicle::storage::refs::Error::Read(err))
2100 })?;
2101
2102 if refs.push(refs_at).is_err() {
2103 warn!(
2104 target: "service",
2105 "refs announcement limit ({REF_REMOTE_LIMIT}) exceeded, peers will see only some of your repository references",
2106 );
2107 break;
2108 }
2109 }
2110
2111 let msg = AnnouncementMessage::from(RefsAnnouncement {
2112 rid,
2113 refs: refs.clone(),
2114 timestamp,
2115 });
2116 Ok((msg.signed(&self.secret_key), refs.into()))
2117 }
2118
2119 fn announce_own_refs(
2121 &mut self,
2122 rid: RepoId,
2123 doc: Doc,
2124 namespaces: impl IntoIterator<Item = NodeId>,
2125 ) -> Result<(Vec<RefsAt>, Timestamp), Error> {
2126 let (refs, timestamp) = self.announce_refs(rid, doc, namespaces, true)?;
2127
2128 for r in refs.iter() {
2132 self.emitter.emit(Event::LocalRefsAnnounced {
2133 rid,
2134 refs: *r,
2135 timestamp,
2136 });
2137 if let Err(e) = self.database_mut().refs_mut().set(
2138 &rid,
2139 &r.remote,
2140 &SIGREFS_BRANCH,
2141 r.at,
2142 timestamp.to_local_time(),
2143 ) {
2144 warn!(
2145 target: "service",
2146 "Error updating refs database for `rad/sigrefs` of {} in {rid}: {e}",
2147 r.remote
2148 );
2149 }
2150 }
2151 Ok((refs, timestamp))
2152 }
2153
2154 fn announce_refs(
2156 &mut self,
2157 rid: RepoId,
2158 doc: Doc,
2159 remotes: impl IntoIterator<Item = NodeId>,
2160 own: bool,
2161 ) -> Result<(Vec<RefsAt>, Timestamp), Error> {
2162 let (ann, refs) = self.refs_announcement_for(rid, remotes)?;
2163 let timestamp = ann.timestamp();
2164 let peers = self.sessions.connected().map(|(_, p)| p);
2165
2166 for r in refs.iter().filter(|r| own || r.remote == ann.node) {
2169 info!(
2170 target: "service",
2171 "Announcing refs {rid}/{r} to peers (t={timestamp})..",
2172 );
2173 if let Err(e) = self.db.seeds_mut().synced(&rid, &ann.node, r.at, timestamp) {
2175 warn!(target: "service", "Failed to update sync status for local node: {e}");
2176 } else {
2177 debug!(target: "service", "Saved local sync status for {rid}..");
2178 }
2179 }
2180
2181 self.outbox.announce(
2182 ann,
2183 peers.filter(|p| {
2184 doc.is_visible_to(&p.id.into())
2186 }),
2187 self.db.gossip_mut(),
2188 );
2189 Ok((refs, timestamp))
2190 }
2191
2192 fn reconnect(&mut self, nid: NodeId, addr: Address) -> bool {
2193 if let Some(sess) = self.sessions.get_mut(&nid) {
2194 sess.to_initial();
2195 self.outbox.connect(nid, addr);
2196
2197 return true;
2198 }
2199 false
2200 }
2201
2202 fn connect(&mut self, nid: NodeId, addr: Address) -> Result<(), ConnectError> {
2203 debug!(target: "service", "Connecting to {nid} ({addr})..");
2204
2205 if nid == *self.nid() {
2206 return Err(ConnectError::SelfConnection);
2207 }
2208 if let Ok(true) = self.policies.is_blocked(&nid) {
2209 return Err(ConnectError::Blocked { nid });
2210 }
2211 if !self.is_supported_address(&addr) {
2212 return Err(ConnectError::UnsupportedAddress { nid, addr });
2213 }
2214 if self.sessions.contains_key(&nid) {
2215 return Err(ConnectError::SessionExists { nid });
2216 }
2217 if self.sessions.outbound().count() >= self.config.limits.connection.outbound.into() {
2218 return Err(ConnectError::LimitReached { nid, addr });
2219 }
2220 let persistent = self.config.is_persistent(&nid);
2221 let timestamp: Timestamp = self.clock.into();
2222
2223 if let Err(e) = self.db.addresses_mut().attempted(&nid, &addr, timestamp) {
2224 warn!(target: "service", "Failed to update address book with connection attempt: {e}");
2225 }
2226 self.sessions.insert(
2227 nid,
2228 Session::outbound(nid, addr.clone(), persistent, self.rng.clone()),
2229 );
2230 self.outbox.connect(nid, addr);
2231
2232 Ok(())
2233 }
2234
2235 fn seeds(&self, rid: &RepoId, namespaces: HashSet<PublicKey>) -> Result<Seeds, Error> {
2236 let mut seeds = Seeds::new(self.rng.clone());
2237
2238 if let Ok(repo) = self.storage.repository(*rid) {
2243 for namespace in namespaces.iter() {
2244 let Ok(local) = RefsAt::new(&repo, *namespace) else {
2245 continue;
2246 };
2247
2248 for seed in self.db.seeds().seeds_for(rid)? {
2249 let seed = seed?;
2250 let state = self.sessions.get(&seed.nid).map(|s| s.state.clone());
2251 let synced = if local.at == seed.synced_at.oid {
2252 SyncStatus::Synced { at: seed.synced_at }
2253 } else {
2254 let local = SyncedAt::new(local.at, &repo)?;
2255
2256 SyncStatus::OutOfSync {
2257 local,
2258 remote: seed.synced_at,
2259 }
2260 };
2261 seeds.insert(Seed::new(seed.nid, seed.addresses, state, Some(synced)));
2262 }
2263 }
2264 }
2265
2266 for nid in self.db.routing().get(rid)? {
2270 if namespaces.contains(&nid) {
2271 continue;
2272 }
2273 if seeds.contains(&nid) {
2274 continue;
2276 }
2277 let addrs = self.db.addresses().addresses_of(&nid)?;
2278 let state = self.sessions.get(&nid).map(|s| s.state.clone());
2279
2280 seeds.insert(Seed::new(nid, addrs, state, None));
2281 }
2282 Ok(seeds)
2283 }
2284
2285 fn filter(&self) -> Filter {
2287 if self.config.seeding_policy.is_allow() {
2288 Filter::default()
2290 } else {
2291 self.filter.clone()
2292 }
2293 }
2294
2295 fn timestamp(&mut self) -> Timestamp {
2298 let now = Timestamp::from(self.clock);
2299 if *now > *self.last_timestamp {
2300 self.last_timestamp = now;
2301 } else {
2302 self.last_timestamp = self.last_timestamp + 1;
2303 }
2304 self.last_timestamp
2305 }
2306
2307 fn relay(&mut self, id: gossip::AnnouncementId, ann: Announcement) {
2308 let announcer = ann.node;
2309 let relayed_by = self.relayed_by.get(&id);
2310 let rid = if let AnnouncementMessage::Refs(RefsAnnouncement { rid, .. }) = ann.message {
2311 Some(rid)
2312 } else {
2313 None
2314 };
2315 let relay_to = self
2319 .sessions
2320 .connected()
2321 .filter(|(id, _)| {
2322 relayed_by
2323 .map(|relayers| !relayers.contains(id))
2324 .unwrap_or(true) })
2326 .filter(|(id, _)| **id != announcer)
2327 .filter(|(id, _)| {
2328 if let Some(rid) = rid {
2329 self.storage
2333 .get(rid)
2334 .ok()
2335 .flatten()
2336 .map(|doc| doc.is_visible_to(&(*id).into()))
2337 .unwrap_or(false)
2338 } else {
2339 true
2341 }
2342 })
2343 .map(|(_, p)| p);
2344
2345 self.outbox.relay(ann, relay_to);
2346 }
2347
2348 fn relay_announcements(&mut self) -> Result<(), Error> {
2353 let now = self.clock.into();
2354 let rows = self.database_mut().gossip_mut().relays(now)?;
2355 let local = *self.nid();
2356
2357 for (id, msg) in rows {
2358 let announcer = msg.node;
2359 if announcer == local {
2360 continue;
2362 }
2363 self.relay(id, msg);
2364 }
2365 Ok(())
2366 }
2367
2368 fn announce_inventory(&mut self) {
2370 let timestamp = self.inventory.timestamp.to_local_time();
2371
2372 if self.last_inventory == timestamp {
2373 debug!(target: "service", "Skipping redundant inventory announcement (t={})", self.inventory.timestamp);
2374 return;
2375 }
2376 let msg = AnnouncementMessage::from(self.inventory.clone());
2377
2378 self.outbox.announce(
2379 msg.signed(&self.secret_key),
2380 self.sessions.connected().map(|(_, p)| p),
2381 self.db.gossip_mut(),
2382 );
2383 self.last_inventory = timestamp;
2384 }
2385
2386 fn prune_routing_entries(&mut self, now: &LocalTime) -> Result<(), routing::Error> {
2387 let count = self.db.routing().len()?;
2388 if count <= self.config.limits.routing_max_size.into() {
2389 return Ok(());
2390 }
2391
2392 let delta = count - usize::from(self.config.limits.routing_max_size);
2393 let nid = *self.nid();
2394 self.db.routing_mut().prune(
2395 (*now - LocalDuration::from(self.config.limits.routing_max_age)).into(),
2396 Some(delta),
2397 &nid,
2398 )?;
2399 Ok(())
2400 }
2401
2402 fn disconnect_unresponsive_peers(&mut self, now: &LocalTime) {
2403 let stale = self
2404 .sessions
2405 .connected()
2406 .filter(|(_, session)| *now - session.last_active >= STALE_CONNECTION_TIMEOUT);
2407
2408 for (_, session) in stale {
2409 debug!(target: "service", "Disconnecting unresponsive peer {}..", session.id);
2410
2411 self.outbox.disconnect(
2415 session.id,
2416 DisconnectReason::Session(session::Error::Timeout),
2417 );
2418 }
2419 }
2420
2421 fn keep_alive(&mut self, now: &LocalTime) {
2423 let inactive_sessions = self
2424 .sessions
2425 .connected_mut()
2426 .filter(|(_, session)| *now - session.last_active >= KEEP_ALIVE_DELTA)
2427 .map(|(_, session)| session);
2428 for session in inactive_sessions {
2429 session.ping(self.clock, &mut self.outbox).ok();
2430 }
2431 }
2432
2433 fn available_peers(&mut self) -> Vec<Peer> {
2435 match self.db.addresses().entries() {
2436 Ok(entries) => {
2437 let mut peers = entries
2440 .filter(|entry| entry.version == PROTOCOL_VERSION)
2441 .filter(|entry| !entry.address.banned)
2442 .filter(|entry| !entry.penalty.is_connect_threshold_reached())
2443 .filter(|entry| !self.sessions.contains_key(&entry.node))
2444 .filter(|entry| !self.policies.is_blocked(&entry.node).unwrap_or(false))
2445 .filter(|entry| !self.config.external_addresses.contains(&entry.address.addr))
2446 .filter(|entry| &entry.node != self.nid())
2447 .filter(|entry| self.is_supported_address(&entry.address.addr))
2448 .fold(HashMap::new(), |mut acc, entry| {
2449 acc.entry(entry.node)
2450 .and_modify(|e: &mut Peer| e.addresses.push(entry.address.clone()))
2451 .or_insert_with(|| Peer {
2452 nid: entry.node,
2453 addresses: vec![entry.address],
2454 penalty: entry.penalty,
2455 });
2456 acc
2457 })
2458 .into_values()
2459 .collect::<Vec<_>>();
2460 peers.sort_by_key(|p| p.penalty);
2461 peers
2462 }
2463 Err(e) => {
2464 warn!(target: "service", "Unable to lookup available peers in address book: {e}");
2465 Vec::new()
2466 }
2467 }
2468 }
2469
2470 fn fetch_missing_repositories(&mut self) -> Result<(), Error> {
2472 let policies = self.policies.seed_policies()?.collect::<Vec<_>>();
2473 for policy in policies {
2474 let policy = match policy {
2475 Ok(policy) => policy,
2476 Err(err) => {
2477 debug!(target: "protocol::filter", "Failed to read seed policy: {err}");
2478 continue;
2479 }
2480 };
2481
2482 let rid = policy.rid;
2483
2484 if !policy.is_allow() {
2485 continue;
2486 }
2487 match self.storage.contains(&rid) {
2488 Ok(exists) => {
2489 if exists {
2490 continue;
2491 }
2492 }
2493 Err(err) => {
2494 log::debug!(target: "protocol::filter", "Failed to check if {rid} exists: {err}");
2495 continue;
2496 }
2497 }
2498 match self.seeds(&rid, [*self.nid()].into()) {
2499 Ok(seeds) => {
2500 if let Some(connected) = NonEmpty::from_vec(seeds.connected().collect()) {
2501 for seed in connected {
2502 self.fetch(rid, seed.nid, vec![], self.fetch_config(), None);
2503 }
2504 } else {
2505 debug!(target: "service", "No connected seeds found for {rid}..");
2515 }
2516 }
2517 Err(e) => {
2518 debug!(target: "service", "Couldn't fetch missing repo {rid}: failed to lookup seeds: {e}");
2519 }
2520 }
2521 }
2522 Ok(())
2523 }
2524
2525 fn idle_connections(&mut self) {
2527 for sess in self.sessions.values_mut() {
2528 sess.idle(self.clock);
2529
2530 if sess.is_stable() {
2531 if let Err(e) =
2533 self.db
2534 .addresses_mut()
2535 .connected(&sess.id, &sess.addr, self.clock.into())
2536 {
2537 warn!(target: "service", "Failed to update address book with connection: {e}");
2538 }
2539 }
2540 }
2541 }
2542
2543 fn maintain_connections(&mut self) {
2545 let PeerConfig::Dynamic = self.config.peers else {
2546 return;
2547 };
2548 trace!(target: "service", "Maintaining connections..");
2549
2550 let target = TARGET_OUTBOUND_PEERS;
2551 let now = self.clock;
2552 let outbound = self
2553 .sessions
2554 .values()
2555 .filter(|s| s.link.is_outbound())
2556 .filter(|s| s.is_connected() || s.is_connecting())
2557 .count();
2558 let wanted = target.saturating_sub(outbound);
2559
2560 if wanted == 0 {
2562 return;
2563 }
2564
2565 let available = self
2567 .available_peers()
2568 .into_iter()
2569 .filter_map(|peer| {
2570 peer.addresses
2571 .into_iter()
2572 .find(|ka| match (ka.last_success, ka.last_attempt) {
2573 (Some(success), Some(attempt)) => {
2576 success > attempt || now - attempt >= CONNECTION_RETRY_DELTA
2577 }
2578 (None, Some(attempt)) => now - attempt >= CONNECTION_RETRY_DELTA,
2580 (_, None) => true,
2582 })
2583 .map(|ka| (peer.nid, ka))
2584 })
2585 .filter(|(_, ka)| self.is_supported_address(&ka.addr));
2586
2587 let connect = available.take(wanted).collect::<Vec<_>>();
2589 if connect.len() < wanted {
2590 log::debug!(
2591 target: "service",
2592 "Not enough available peers to connect to (available={}, wanted={wanted})",
2593 connect.len()
2594 );
2595 }
2596 for (id, ka) in connect {
2597 if let Err(e) = self.connect(id, ka.addr.clone()) {
2598 warn!(target: "service", "Service::maintain_connections connection error: {e}");
2599 }
2600 }
2601 }
2602
2603 fn maintain_persistent(&mut self) {
2605 trace!(target: "service", "Maintaining persistent peers..");
2606
2607 let now = self.local_time();
2608 let mut reconnect = Vec::new();
2609
2610 for (nid, session) in self.sessions.iter_mut() {
2611 if self.config.is_persistent(nid) {
2612 if self.policies.is_blocked(nid).unwrap_or(false) {
2613 continue;
2614 }
2615 if let session::State::Disconnected { retry_at, .. } = &mut session.state {
2616 if now >= *retry_at {
2620 reconnect.push((*nid, session.addr.clone(), session.attempts()));
2621 }
2622 }
2623 }
2624 }
2625
2626 for (nid, addr, attempts) in reconnect {
2627 if self.reconnect(nid, addr) {
2628 debug!(target: "service", "Reconnecting to {nid} (attempts={attempts})…");
2629 }
2630 }
2631 }
2632
2633 fn is_supported_address(&self, address: &Address) -> bool {
2649 match AddressType::from(address) {
2650 #[cfg(feature = "tor")]
2652 AddressType::Onion => self.config.onion != radicle::node::config::AddressConfig::Drop,
2653 #[cfg(feature = "i2p")]
2654 AddressType::I2p => self.config.i2p != radicle::node::config::AddressConfig::Drop,
2655 AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
2656 _ => false,
2657 }
2658 }
2659
2660 fn fetch_config(&self) -> fetcher::FetchConfig {
2661 let timeout: LocalDuration = self.config.limits.fetch_timeout.into();
2662 fetcher::FetchConfig::default()
2663 .with_timeout(timeout.into())
2664 .with_minimum_feature_level(self.config.fetch.feature_level_min())
2665 }
2666}
2667
2668pub trait ServiceState {
2670 fn nid(&self) -> &NodeId;
2672 fn sessions(&self) -> &Sessions;
2674 fn fetching(&self) -> &FetcherState;
2676 fn outbox(&self) -> &Outbox;
2678 fn limiter(&self) -> &RateLimiter;
2680 fn emitter(&self) -> &Emitter<Event>;
2682 fn get(&self, rid: RepoId) -> Result<Option<Doc>, RepositoryError>;
2684 fn clock(&self) -> &LocalTime;
2686 fn clock_mut(&mut self) -> &mut LocalTime;
2688 fn config(&self) -> &Config;
2690 fn metrics(&self) -> &Metrics;
2692}
2693
2694impl<D, S> ServiceState for Service<D, S>
2695where
2696 D: routing::Store,
2697 S: ReadStorage,
2698{
2699 fn nid(&self) -> &NodeId {
2700 self.secret_key.public_key()
2701 }
2702
2703 fn sessions(&self) -> &Sessions {
2704 &self.sessions
2705 }
2706
2707 fn fetching(&self) -> &FetcherState {
2708 self.fetcher.state()
2709 }
2710
2711 fn outbox(&self) -> &Outbox {
2712 &self.outbox
2713 }
2714
2715 fn limiter(&self) -> &RateLimiter {
2716 &self.limiter
2717 }
2718
2719 fn emitter(&self) -> &Emitter<Event> {
2720 &self.emitter
2721 }
2722
2723 fn get(&self, rid: RepoId) -> Result<Option<Doc>, RepositoryError> {
2724 self.storage.get(rid)
2725 }
2726
2727 fn clock(&self) -> &LocalTime {
2728 &self.clock
2729 }
2730
2731 fn clock_mut(&mut self) -> &mut LocalTime {
2732 &mut self.clock
2733 }
2734
2735 fn config(&self) -> &Config {
2736 &self.config
2737 }
2738
2739 fn metrics(&self) -> &Metrics {
2740 &self.metrics
2741 }
2742}
2743
2744#[derive(Debug)]
2746pub enum DisconnectReason {
2747 Dial(Arc<dyn std::error::Error + Sync + Send>),
2750 Connection(Arc<dyn std::error::Error + Sync + Send>),
2753 Fetch(FetchError),
2755 Session(session::Error),
2757 Conflict,
2759 SelfConnection,
2761 Policy,
2763 Command,
2765}
2766
2767impl DisconnectReason {
2768 pub fn is_dial_err(&self) -> bool {
2769 matches!(self, Self::Dial(_))
2770 }
2771
2772 pub fn is_connection_err(&self) -> bool {
2773 matches!(self, Self::Connection(_))
2774 }
2775
2776 pub fn connection() -> Self {
2777 DisconnectReason::Connection(Arc::new(std::io::Error::from(
2778 std::io::ErrorKind::ConnectionReset,
2779 )))
2780 }
2781}
2782
2783impl fmt::Display for DisconnectReason {
2784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2785 match self {
2786 Self::Dial(err) => write!(f, "{err}"),
2787 Self::Connection(err) => write!(f, "{err}"),
2788 Self::Command => write!(f, "command"),
2789 Self::SelfConnection => write!(f, "self-connection"),
2790 Self::Conflict => write!(f, "conflict"),
2791 Self::Policy => write!(f, "policy"),
2792 Self::Session(err) => write!(f, "{err}"),
2793 Self::Fetch(err) => write!(f, "fetch: {err}"),
2794 }
2795 }
2796}
2797
2798#[derive(Debug)]
2800pub struct Lookup {
2801 pub local: Option<Doc>,
2803 pub remote: Vec<NodeId>,
2805}
2806
2807#[derive(thiserror::Error, Debug)]
2808pub enum LookupError {
2809 #[error(transparent)]
2810 Routing(#[from] routing::Error),
2811 #[error(transparent)]
2812 Repository(#[from] RepositoryError),
2813}
2814
2815#[derive(Debug, Clone)]
2816pub struct Sessions(AddressBook<NodeId, Session>);
2818
2819impl Sessions {
2820 pub fn new(rng: Rng) -> Self {
2821 Self(AddressBook::new(rng))
2822 }
2823
2824 pub fn connected(&self) -> impl Iterator<Item = (&NodeId, &Session)> + Clone {
2826 self.0
2827 .iter()
2828 .filter_map(move |(id, sess)| match &sess.state {
2829 session::State::Connected { .. } => Some((id, sess)),
2830 _ => None,
2831 })
2832 }
2833
2834 pub fn inbound(&self) -> impl Iterator<Item = (&NodeId, &Session)> + Clone {
2836 self.connected().filter(|(_, s)| s.link.is_inbound())
2837 }
2838
2839 pub fn outbound(&self) -> impl Iterator<Item = (&NodeId, &Session)> + Clone {
2841 self.connected().filter(|(_, s)| s.link.is_outbound())
2842 }
2843
2844 pub fn connected_mut(&mut self) -> impl Iterator<Item = (&NodeId, &mut Session)> {
2846 self.0.iter_mut().filter(move |(_, s)| s.is_connected())
2847 }
2848
2849 pub fn disconnected_mut(&mut self) -> impl Iterator<Item = (&NodeId, &mut Session)> {
2851 self.0.iter_mut().filter(move |(_, s)| s.is_disconnected())
2852 }
2853
2854 pub fn is_connected(&self, id: &NodeId) -> bool {
2856 self.0.get(id).map(|s| s.is_connected()).unwrap_or(false)
2857 }
2858
2859 pub fn is_disconnected(&self, id: &NodeId) -> bool {
2861 self.0.get(id).map(|s| s.is_disconnected()).unwrap_or(true)
2862 }
2863}
2864
2865impl Deref for Sessions {
2866 type Target = AddressBook<NodeId, Session>;
2867
2868 fn deref(&self) -> &Self::Target {
2869 &self.0
2870 }
2871}
2872
2873impl DerefMut for Sessions {
2874 fn deref_mut(&mut self) -> &mut Self::Target {
2875 &mut self.0
2876 }
2877}