1use crate::chunk;
56use crate::error::{Error, Result};
57use crate::format;
58use crate::tls::{self, Revocations};
59use crate::volumes::{self, VolumeSet, HEADER_LEN, MAX_DESCRIPTOR};
60use rustls::{ClientConfig, ClientConnection, ServerConfig, ServerConnection, StreamOwned};
61use serde::{Deserialize, Serialize};
62use serde_bytes::ByteBuf;
63use std::cell::RefCell;
64use std::collections::{BTreeMap, HashMap, HashSet};
65use std::fs::{File, OpenOptions};
66use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
67use std::net::{IpAddr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
68use std::path::{Path, PathBuf};
69use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
70use std::sync::{Arc, Mutex};
71use std::time::{Duration, Instant};
72
73pub const PROTOCOL: &str = "TSXP/1";
74pub const MAX_LINE: usize = 256;
75pub const MAX_HAVE_RESPONSE: usize = 4 << 10;
76pub const MAX_SETS_RESPONSE: usize = 1 << 20;
77pub const TIMEOUT: Duration = Duration::from_secs(30);
78const STREAM_CHUNK: usize = 1 << 20;
79
80fn hash32(h: &[u8]) -> [u8; 32] {
81 let mut out = [0u8; 32];
82 out.copy_from_slice(&h[..32]);
83 out
84}
85
86fn parse_hex32(s: &str) -> Option<[u8; 32]> {
87 let v = hex::decode(s).ok()?;
88 (v.len() == 32).then(|| hash32(&v))
89}
90
91pub fn is_loopback(listen: &str) -> Result<bool> {
94 let addr = listen.to_socket_addrs().ok().and_then(|mut a| a.next()).ok_or_else(|| Error::Invalid(format!("listen address {listen}")))?;
95 Ok(addr.ip().is_loopback())
96}
97
98struct Served {
101 set: VolumeSet,
102 descriptor: Vec<u8>,
103 volumes: Vec<Option<PathBuf>>,
105}
106
107#[derive(Clone, Copy, Debug)]
109pub struct ServerLimits {
110 pub max_connections: usize,
112 pub max_connections_per_peer: usize,
114 pub timeout: Duration,
117 pub max_requests_per_second: u32,
122 pub min_rate: u64,
125 pub max_bandwidth: Option<u64>,
129 pub max_peers: usize,
132}
133
134impl Default for ServerLimits {
135 fn default() -> Self {
136 Self { max_connections: 64, max_connections_per_peer: 8, timeout: TIMEOUT, max_requests_per_second: 200, min_rate: 64 << 10, max_bandwidth: None, max_peers: 64 }
137 }
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142enum Admit {
143 Ok,
144 Busy,
145 RateLimited,
146}
147
148struct Bucket {
149 tokens: f64,
150 last: Instant,
151}
152
153struct Gate {
154 limits: ServerLimits,
155 total: AtomicUsize,
156 per_ip: Mutex<HashMap<IpAddr, usize>>,
157 buckets: Mutex<HashMap<IpAddr, Bucket>>,
158 throttle: Option<Mutex<Bucket>>,
160}
161
162impl Gate {
163 fn new(limits: ServerLimits) -> Gate {
164 let throttle = limits.max_bandwidth.map(|rate| Mutex::new(Bucket { tokens: rate as f64, last: Instant::now() }));
165 Gate { limits, total: AtomicUsize::new(0), per_ip: Mutex::new(HashMap::new()), buckets: Mutex::new(HashMap::new()), throttle }
166 }
167
168 fn enter(&self, ip: IpAddr) -> Admit {
169 let mut per_ip = self.per_ip.lock().unwrap_or_else(|e| e.into_inner());
170 let mine = per_ip.get(&ip).copied().unwrap_or(0);
171 if self.total.load(Ordering::Relaxed) >= self.limits.max_connections || mine >= self.limits.max_connections_per_peer {
172 return Admit::Busy;
173 }
174 if mine == 0 && per_ip.len() >= self.limits.max_peers {
177 return Admit::Busy;
178 }
179 let rate = f64::from(self.limits.max_requests_per_second.max(1));
181 let burst = rate * 2.0;
182 let now = Instant::now();
183 let mut buckets = self.buckets.lock().unwrap_or_else(|e| e.into_inner());
184 if buckets.len() > 4096 {
185 buckets.retain(|_, b| b.tokens + now.duration_since(b.last).as_secs_f64() * rate < burst);
187 }
188 let b = buckets.entry(ip).or_insert(Bucket { tokens: burst, last: now });
189 b.tokens = (b.tokens + now.duration_since(b.last).as_secs_f64() * rate).min(burst);
190 b.last = now;
191 if b.tokens < 1.0 {
192 return Admit::RateLimited;
193 }
194 b.tokens -= 1.0;
195 per_ip.insert(ip, mine + 1);
196 self.total.fetch_add(1, Ordering::Relaxed);
197 Admit::Ok
198 }
199
200 fn leave(&self, ip: IpAddr) {
201 let mut per_ip = self.per_ip.lock().unwrap_or_else(|e| e.into_inner());
202 if let Some(n) = per_ip.get_mut(&ip) {
203 *n = n.saturating_sub(1);
204 if *n == 0 {
205 per_ip.remove(&ip);
206 }
207 }
208 self.total.fetch_sub(1, Ordering::Relaxed);
209 }
210
211 fn slice_len(&self) -> Option<usize> {
214 self.limits.max_bandwidth.map(|rate| ((rate / 8) as usize).max(4 << 10))
215 }
216
217 fn take_bytes(&self, n: usize) -> Duration {
221 let (Some(bucket), Some(rate)) = (&self.throttle, self.limits.max_bandwidth) else { return Duration::ZERO };
222 let rate = rate.max(1) as f64;
223 let mut b = bucket.lock().unwrap_or_else(|e| e.into_inner());
224 let now = Instant::now();
225 b.tokens = (b.tokens + now.duration_since(b.last).as_secs_f64() * rate).min(rate);
226 b.last = now;
227 b.tokens -= n as f64;
228 if b.tokens >= 0.0 {
229 Duration::ZERO
230 } else {
231 Duration::from_secs_f64(-b.tokens / rate)
232 }
233 }
234}
235
236#[derive(Clone, Debug, Default)]
240pub struct Acl {
241 anyone: bool,
242 any_set: HashSet<[u8; 32]>,
243 per_set: HashMap<[u8; 32], HashSet<[u8; 32]>>,
244}
245
246impl Acl {
247 pub fn everyone() -> Acl {
250 Acl { anyone: true, ..Acl::default() }
251 }
252
253 pub fn new(any_set: impl IntoIterator<Item = [u8; 32]>, per_set: impl IntoIterator<Item = ([u8; 32], [u8; 32])>) -> Acl {
256 let mut acl = Acl { anyone: false, any_set: any_set.into_iter().collect(), per_set: HashMap::new() };
257 for (set, fp) in per_set {
258 acl.per_set.entry(set).or_default().insert(fp);
259 }
260 acl
261 }
262
263 pub fn admitted(&self) -> Vec<[u8; 32]> {
265 let mut all: Vec<[u8; 32]> = self.any_set.iter().copied().chain(self.per_set.values().flatten().copied()).collect();
266 all.sort_unstable();
267 all.dedup();
268 all
269 }
270
271 pub fn restricted(&self) -> Vec<[u8; 32]> {
273 let mut r: Vec<[u8; 32]> = self.per_set.values().flatten().copied().filter(|fp| !self.any_set.contains(fp)).collect();
274 r.sort_unstable();
275 r.dedup();
276 r
277 }
278
279 pub fn readers_of(&self, set: &[u8; 32]) -> Option<usize> {
281 if self.anyone {
282 return None;
283 }
284 Some(self.any_set.len() + self.per_set.get(set).map(|s| s.iter().filter(|fp| !self.any_set.contains(*fp)).count()).unwrap_or(0))
285 }
286
287 pub fn may_read(&self, client: Option<&[u8; 32]>, set: &[u8; 32]) -> bool {
288 self.anyone || client.is_some_and(|c| self.any_set.contains(c) || self.per_set.get(set).is_some_and(|s| s.contains(c)))
289 }
290}
291
292pub struct Server {
294 sets: Arc<HashMap<[u8; 32], Served>>,
295 listener: TcpListener,
296 gate: Arc<Gate>,
297 tls: Option<Arc<ServerConfig>>,
298 acl: Arc<Acl>,
299}
300
301#[derive(Clone, Debug, Serialize)]
302pub struct ServedSet {
303 pub set_id: String,
304 pub archive_name: String,
305 pub archive_size: u64,
306 pub volumes: Vec<usize>,
307}
308
309impl Server {
310 pub fn bind(paths: &[PathBuf], listen: &str) -> Result<Server> {
312 Self::bind_with(paths, listen, ServerLimits::default())
313 }
314
315 pub fn bind_with(paths: &[PathBuf], listen: &str, limits: ServerLimits) -> Result<Server> {
316 Self::bind_tls(paths, listen, limits, None)
317 }
318
319 pub fn bind_tls(paths: &[PathBuf], listen: &str, limits: ServerLimits, tls: Option<Arc<ServerConfig>>) -> Result<Server> {
323 Self::bind_acl(paths, listen, limits, tls, Acl::everyone())
324 }
325
326 pub fn bind_acl(paths: &[PathBuf], listen: &str, limits: ServerLimits, tls: Option<Arc<ServerConfig>>, acl: Acl) -> Result<Server> {
331 if tls.is_none() && !acl.anyone {
332 return Err(Error::Invalid("per-set client lists need TLS: a plain connection carries no client identity".into()));
333 }
334 let statuses = volumes::inspect(paths, true)?;
335 let mut sets = HashMap::new();
336 for st in statuses {
337 let mut id = [0u8; 32];
338 id.copy_from_slice(&hex::decode(&st.set_id).map_err(|_| Error::Corrupt("set id".into()))?);
339 let vols: Vec<Option<PathBuf>> = st.volumes.iter().map(|v| v.as_ref().filter(|f| f.usable() && (f.descriptor_ok || f.header_ok)).map(|f| f.path.clone())).collect();
340 let descriptor = format::cbor_encode(&st.set)?;
341 sets.insert(id, Served { set: st.set, descriptor, volumes: vols });
342 }
343 let listener = TcpListener::bind(listen).map_err(|e| Error::Io(std::io::Error::other(format!("bind {listen}: {e}"))))?;
344 Ok(Server { sets: Arc::new(sets), listener, gate: Arc::new(Gate::new(limits)), tls, acl: Arc::new(acl) })
345 }
346
347 pub fn local_addr(&self) -> Result<SocketAddr> {
348 Ok(self.listener.local_addr()?)
349 }
350
351 pub fn encrypted(&self) -> bool {
353 self.tls.is_some()
354 }
355
356 pub fn limits(&self) -> ServerLimits {
357 self.gate.limits
358 }
359
360 pub fn acl(&self) -> &Acl {
361 &self.acl
362 }
363
364 pub fn served(&self) -> Vec<ServedSet> {
365 let mut out: Vec<ServedSet> = self
366 .sets
367 .iter()
368 .map(|(id, s)| ServedSet {
369 set_id: hex::encode(id),
370 archive_name: s.set.archive_name.clone(),
371 archive_size: s.set.archive_size,
372 volumes: s.volumes.iter().enumerate().filter(|(_, p)| p.is_some()).map(|(i, _)| i).collect(),
373 })
374 .collect();
375 out.sort_by(|a, b| a.set_id.cmp(&b.set_id));
376 out
377 }
378
379 pub fn run(self, stop: Arc<AtomicBool>) -> Result<()> {
387 self.listener.set_nonblocking(true)?;
388 loop {
389 if stop.load(Ordering::Relaxed) {
390 return Ok(());
391 }
392 match self.listener.accept() {
393 Ok((mut stream, peer)) => {
394 let limits = self.gate.limits;
395 let _ = stream.set_nonblocking(false);
396 let _ = stream.set_read_timeout(Some(limits.timeout));
397 let _ = stream.set_write_timeout(Some(limits.timeout));
398 match self.gate.enter(peer.ip()) {
399 Admit::Ok => {}
400 refused => {
401 if self.tls.is_none() {
402 let _ = match refused {
403 Admit::Busy => send_err(&mut stream, 503, "busy: too many connections"),
404 _ => send_err(&mut stream, 429, "too many requests: slow down"),
405 };
406 }
407 continue;
408 }
409 }
410 let sets = self.sets.clone();
411 let gate = self.gate.clone();
412 let tls = self.tls.clone();
413 let acl = self.acl.clone();
414 std::thread::spawn(move || {
415 let mut stream = Deadline::new(stream, limits.timeout, gate.clone());
416 match tls {
417 None => {
418 let _ = handle(&mut stream, &sets, &limits, &acl, None);
419 }
420 Some(cfg) => {
421 if let Ok(conn) = ServerConnection::new(cfg) {
422 let mut session = StreamOwned::new(conn, stream);
423 if session.conn.complete_io(&mut session.sock).is_ok() && !session.conn.is_handshaking() {
426 let client = session.conn.peer_certificates().and_then(|c| c.first()).map(tls::fingerprint);
428 let _ = handle(&mut session, &sets, &limits, &acl, client);
429 session.conn.send_close_notify();
430 let _ = session.flush();
431 }
432 }
433 }
434 }
435 gate.leave(peer.ip());
436 });
437 }
438 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => std::thread::sleep(Duration::from_millis(2)),
439 Err(e) => return Err(Error::Io(e)),
440 }
441 }
442 }
443}
444
445struct Deadline {
449 inner: TcpStream,
450 deadline: Instant,
451 op: Duration,
452 gate: Arc<Gate>,
454}
455
456impl Deadline {
457 fn new(inner: TcpStream, op: Duration, gate: Arc<Gate>) -> Deadline {
458 Deadline { inner, deadline: Instant::now() + op, op, gate }
459 }
460
461 fn slice(&self) -> std::io::Result<Duration> {
462 let left = self.deadline.saturating_duration_since(Instant::now());
463 if left.is_zero() {
464 return Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "connection time budget exhausted"));
465 }
466 Ok(left.min(self.op).max(Duration::from_millis(1)))
467 }
468}
469
470impl Read for Deadline {
471 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
472 let t = self.slice()?;
473 self.inner.set_read_timeout(Some(t))?;
474 self.inner.read(buf)
475 }
476}
477
478impl Write for Deadline {
479 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
480 let mut n = buf.len();
485 if let Some(max) = self.gate.slice_len() {
486 n = n.min(max).max(1);
487 let wait = self.gate.take_bytes(n);
488 if !wait.is_zero() {
489 std::thread::sleep(wait);
490 self.deadline += wait;
491 }
492 }
493 let t = self.slice()?;
494 self.inner.set_write_timeout(Some(t))?;
495 self.inner.write(&buf[..n])
496 }
497
498 fn flush(&mut self) -> std::io::Result<()> {
499 self.inner.flush()
500 }
501}
502
503trait Budget {
505 fn allow(&mut self, bytes: u64, limits: &ServerLimits);
506}
507
508impl Budget for Deadline {
509 fn allow(&mut self, bytes: u64, limits: &ServerLimits) {
510 let secs = (bytes / limits.min_rate.max(1)).max(limits.timeout.as_secs());
511 self.deadline = Instant::now() + Duration::from_secs(secs);
512 }
513}
514
515impl<C, T: Read + Write + Budget> Budget for StreamOwned<C, T> {
516 fn allow(&mut self, bytes: u64, limits: &ServerLimits) {
517 self.sock.allow(bytes, limits)
518 }
519}
520
521fn read_line_bounded<R: BufRead>(r: &mut R) -> std::io::Result<String> {
526 let mut buf = Vec::with_capacity(64);
527 loop {
528 let available = r.fill_buf()?;
529 if available.is_empty() {
530 return Err(std::io::Error::other("request line too long or unterminated"));
531 }
532 let mut used = 0;
533 let mut complete = false;
534 for &b in available {
535 used += 1;
536 if b == b'\n' {
537 complete = true;
538 break;
539 }
540 if !(b == b'\t' || b == b'\r' || (0x20..0x7f).contains(&b)) {
541 r.consume(used);
542 return Err(std::io::Error::other("request line is not ASCII"));
543 }
544 buf.push(b);
545 if buf.len() >= MAX_LINE {
546 r.consume(used);
547 return Err(std::io::Error::other("request line too long or unterminated"));
548 }
549 }
550 r.consume(used);
551 if complete {
552 return String::from_utf8(buf).map_err(|_| std::io::Error::other("request line is not ASCII"));
553 }
554 }
555}
556
557fn send_err<W: Write>(stream: &mut W, code: u16, msg: &str) -> std::io::Result<()> {
558 stream.write_all(format!("ERR {code} {msg}\n").as_bytes())?;
559 stream.flush()
560}
561
562fn send_ok<W: Write>(stream: &mut W, payload: &[u8]) -> std::io::Result<()> {
563 stream.write_all(format!("OK {}\n", payload.len()).as_bytes())?;
564 stream.write_all(payload)?;
565 stream.flush()
566}
567
568fn send_ok_from_file<W: Write>(stream: &mut W, file: &mut File, off: u64, len: usize) -> std::io::Result<()> {
570 file.seek(SeekFrom::Start(off))?;
571 stream.write_all(format!("OK {len}\n").as_bytes())?;
572 let mut buf = vec![0u8; STREAM_CHUNK.min(len)];
573 let mut left = len;
574 while left > 0 {
575 let n = buf.len().min(left);
576 file.read_exact(&mut buf[..n])?;
577 stream.write_all(&buf[..n])?;
578 left -= n;
579 }
580 stream.flush()
581}
582
583fn handle<S: Read + Write + Budget>(stream: &mut S, sets: &HashMap<[u8; 32], Served>, limits: &ServerLimits, acl: &Acl, client: Option<[u8; 32]>) -> std::io::Result<()> {
588 let line = {
589 let mut reader = BufReader::new(&mut *stream);
590 read_line_bounded(&mut reader)
591 };
592 let line = match line {
593 Ok(l) => l,
594 Err(e) => return send_err(stream, 400, &e.to_string()),
595 };
596 let parts: Vec<&str> = line.split(' ').collect();
597 if parts.first() != Some(&PROTOCOL) || parts.len() < 2 {
598 return send_err(stream, 400, "expected a TSXP/1 request");
599 }
600 match parts[1] {
601 "SETS" => {
602 let list: Vec<(String, String, u64)> =
603 sets.iter().filter(|(id, _)| acl.may_read(client.as_ref(), id)).map(|(id, s)| (hex::encode(id), s.set.archive_name.clone(), s.set.archive_size)).collect();
604 let bytes = format::cbor_encode(&list).map_err(|e| std::io::Error::other(e.to_string()))?;
605 stream.allow(bytes.len() as u64, limits);
606 send_ok(stream, &bytes)
607 }
608 "DESCRIPTOR" | "HAVE" | "PIECE" => {
609 let Some(id) = parts.get(2).and_then(|h| parse_hex32(h)) else { return send_err(stream, 400, "bad set id") };
610 let Some(s) = sets.get(&id).filter(|_| acl.may_read(client.as_ref(), &id)) else { return send_err(stream, 404, "unknown set") };
612 match parts[1] {
613 "DESCRIPTOR" => {
614 stream.allow(s.descriptor.len() as u64, limits);
615 send_ok(stream, &s.descriptor)
616 }
617 "HAVE" => {
618 let have: Vec<u16> = s.volumes.iter().enumerate().filter(|(_, p)| p.is_some()).map(|(i, _)| i as u16).collect();
619 let bytes = format::cbor_encode(&have).map_err(|e| std::io::Error::other(e.to_string()))?;
620 stream.allow(bytes.len() as u64, limits);
621 send_ok(stream, &bytes)
622 }
623 _ => {
624 let (Some(vi), Some(st)) = (parts.get(3).and_then(|x| x.parse::<usize>().ok()), parts.get(4).and_then(|x| x.parse::<u64>().ok())) else {
625 return send_err(stream, 400, "bad piece address");
626 };
627 if parts.len() != 5 || vi >= s.set.total() || st >= s.set.volume_pieces(vi) {
628 return send_err(stream, 404, "no such piece");
629 }
630 let Some(path) = &s.volumes[vi] else { return send_err(stream, 404, "volume not available here") };
631 let ps = s.set.piece_size as usize;
632 let mut f = match File::open(path) {
633 Ok(f) => f,
634 Err(e) => return send_err(stream, 500, &e.to_string()),
635 };
636 stream.allow(ps as u64, limits);
637 send_ok_from_file(stream, &mut f, HEADER_LEN as u64 + st * ps as u64, ps)
638 }
639 }
640 }
641 _ => send_err(stream, 400, "unknown request"),
642 }
643}
644
645fn io_peer(peer: &str, e: std::io::Error) -> Error {
648 Error::Io(std::io::Error::new(e.kind(), format!("{peer}: {e}")))
649}
650
651fn closed_early(e: &std::io::Error) -> bool {
655 use std::io::ErrorKind::{BrokenPipe, ConnectionAborted, ConnectionReset, UnexpectedEof};
656 matches!(e.kind(), UnexpectedEof | ConnectionReset | ConnectionAborted | BrokenPipe)
657}
658
659#[derive(Clone, Copy, Debug, Default)]
661pub struct Timing {
662 pub requests: usize,
663 pub bytes: u64,
664 pub connect: Duration,
666 pub handshake: Duration,
668 pub transfer: Duration,
670}
671
672enum Reply {
674 Ok(Vec<u8>),
675 Refused(u16, String),
676}
677
678fn refusal(peer: &str, code: u16, msg: &str) -> Error {
679 Error::Missing(format!("{peer}: {code} {msg}"))
680}
681
682fn request(peer: &str, tls: Option<&Arc<ClientConfig>>, line: &str, max_len: usize, timing: &mut Timing) -> Result<Reply> {
687 let addr = peer.to_socket_addrs().ok().and_then(|mut a| a.next()).ok_or_else(|| Error::Invalid(format!("peer address {peer}")))?;
688 let t0 = Instant::now();
689 let stream = TcpStream::connect_timeout(&addr, TIMEOUT).map_err(|e| io_peer(peer, e))?;
690 stream.set_read_timeout(Some(TIMEOUT))?;
691 stream.set_write_timeout(Some(TIMEOUT))?;
692 let t1 = Instant::now();
693 timing.connect += t1 - t0;
694 match tls {
695 None => exchange(stream, peer, line, max_len, timing),
696 Some(cfg) => {
697 let conn = ClientConnection::new(cfg.clone(), tls::server_name()).map_err(|e| Error::Crypto(format!("{peer}: tls: {e}")))?;
698 let mut session = StreamOwned::new(conn, stream);
699 session.conn.complete_io(&mut session.sock).map_err(|e| io_peer(peer, e))?;
701 timing.handshake += t1.elapsed();
702 exchange(session, peer, line, max_len, timing)
703 }
704 }
705}
706
707fn exchange<S: Read + Write>(mut stream: S, peer: &str, line: &str, max_len: usize, timing: &mut Timing) -> Result<Reply> {
708 let t0 = Instant::now();
709 stream.write_all(format!("{PROTOCOL} {line}\n").as_bytes()).map_err(|e| io_peer(peer, e))?;
710 stream.flush().map_err(|e| io_peer(peer, e))?;
711 let mut reader = BufReader::new(stream);
712 let status = read_line_bounded(&mut reader).map_err(|e| io_peer(peer, e))?;
713 let mut words = status.splitn(3, ' ');
714 match (words.next(), words.next(), words.next()) {
715 (Some("OK"), Some(len), _) => {
716 let len: usize = len.parse().map_err(|_| Error::Corrupt(format!("{peer}: bad response length")))?;
717 if len > max_len {
718 return Err(Error::Limit(format!("{peer}: response of {len} bytes exceeds the {max_len}-byte limit")));
719 }
720 let mut buf = vec![0u8; len];
721 reader.read_exact(&mut buf).map_err(|e| io_peer(peer, e))?;
722 timing.transfer += t0.elapsed();
723 timing.requests += 1;
724 timing.bytes += len as u64;
725 Ok(Reply::Ok(buf))
726 }
727 (Some("ERR"), code, msg) => Ok(Reply::Refused(code.and_then(|c| c.parse().ok()).unwrap_or(0), msg.unwrap_or("").to_string())),
728 _ => Err(Error::Corrupt(format!("{peer}: unexpected response"))),
729 }
730}
731
732#[derive(Clone, Default)]
739pub struct ClientTls {
740 pub identity: Option<tls::Identity>,
741 pub peer_ids: Vec<[u8; 32]>,
742 pub revoked: Revocations,
743}
744
745struct Dial {
747 configs: HashMap<String, Option<Arc<ClientConfig>>>,
748 timing: RefCell<Timing>,
749}
750
751impl Dial {
752 fn new(peers: &[String], tls: Option<&ClientTls>) -> Result<Dial> {
753 if let Some(t) = tls {
754 if t.peer_ids.len() != peers.len() {
755 return Err(Error::Invalid(format!("{} peer addresses but {} expected peer identities: give one identity per peer, in the same order", peers.len(), t.peer_ids.len())));
756 }
757 }
758 let mut configs = HashMap::new();
759 for (i, peer) in peers.iter().enumerate() {
760 let cfg = match tls {
761 None => None,
762 Some(t) => {
763 if t.revoked.contains(&t.peer_ids[i]) {
764 return Err(Error::Policy(format!("peer identity {} ({peer}) is revoked; edit the command instead of skipping the peer", hex::encode(t.peer_ids[i]))));
765 }
766 Some(tls::client_config_with(t.identity.as_ref(), t.peer_ids[i], &t.revoked)?)
767 }
768 };
769 configs.insert(peer.clone(), cfg);
770 }
771 Ok(Dial { configs, timing: RefCell::new(Timing::default()) })
772 }
773
774 fn request(&self, peer: &str, line: &str, max_len: usize) -> Result<Vec<u8>> {
778 let cfg = self.configs.get(peer).and_then(|c| c.as_ref());
779 let mut attempt = 0u32;
780 loop {
781 let outcome = request(peer, cfg, line, max_len, &mut self.timing.borrow_mut());
782 let refused = match outcome {
783 Ok(Reply::Ok(bytes)) => return Ok(bytes),
784 Ok(Reply::Refused(code @ (429 | 503), msg)) => refusal(peer, code, &msg),
785 Ok(Reply::Refused(code, msg)) => return Err(refusal(peer, code, &msg)),
786 Err(Error::Io(e)) if closed_early(&e) => Error::Io(e),
787 Err(e) => return Err(e),
788 };
789 if attempt >= 16 {
790 return Err(refused);
791 }
792 attempt += 1;
793 std::thread::sleep(Duration::from_millis(u64::from(25 * attempt.min(8))));
794 }
795 }
796}
797
798#[derive(Clone, Debug, PartialEq, Eq)]
800pub enum Want {
801 Needed,
803 All,
805 Volumes(Vec<usize>),
807}
808
809pub struct FetchOptions {
810 pub set_id: [u8; 32],
811 pub descriptor_b3: Option<[u8; 32]>,
814 pub peers: Vec<String>,
815 pub out_dir: PathBuf,
816 pub local: Vec<PathBuf>,
818 pub want: Want,
819 pub max_pieces: Option<usize>,
821 pub tls: Option<ClientTls>,
823}
824
825#[derive(Clone, Debug, Serialize)]
826pub struct FetchReport {
827 pub set_id: String,
828 pub descriptor_b3: String,
829 pub verified_against: &'static str,
831 pub volumes_wanted: Vec<usize>,
832 pub volumes_completed: Vec<usize>,
833 pub volumes_partial: Vec<usize>,
834 pub pieces_received: usize,
835 pub pieces_rejected: usize,
837 pub pieces_reverified: usize,
839 pub pieces_reverify_failed: usize,
841 pub bytes_reserved: u64,
843 pub peers_used: Vec<String>,
844 pub reconstructible: bool,
846 pub stopped_early: bool,
847 pub encrypted: bool,
849 pub requests: usize,
851 pub bytes_received: u64,
852 pub seconds_connect: f64,
855 pub seconds_handshake: f64,
856 pub seconds_transfer: f64,
857 pub seconds_total: f64,
858}
859
860#[derive(Clone, Debug, Serialize, Deserialize)]
862struct PartialMap {
863 descriptor_b3: ByteBuf,
864 index: u16,
865 have: Vec<bool>,
866}
867
868fn partial_paths(out_dir: &Path, set: &VolumeSet, vi: usize) -> (PathBuf, PathBuf, PathBuf) {
869 let final_path = out_dir.join(set.volume_name(vi));
870 let partial = out_dir.join(format!("{}.partial", set.volume_name(vi)));
871 let map = out_dir.join(format!("{}.partial.map", set.volume_name(vi)));
872 (final_path, partial, map)
873}
874
875fn save_map(map_path: &Path, map: &PartialMap) -> Result<()> {
877 let tmp = map_path.with_extension("map.tmp");
878 std::fs::write(&tmp, format::cbor_encode(map)?)?;
879 if map_path.exists() {
880 std::fs::remove_file(map_path)?;
881 }
882 std::fs::rename(&tmp, map_path)?;
883 Ok(())
884}
885
886fn expected_piece_hash(set: &VolumeSet, vi: usize, s: usize) -> [u8; 32] {
887 let n = set.data as usize;
888 if set.is_parity(vi) {
889 set.parity_hash(s as u32, vi - n)
890 } else {
891 set.piece_hash((s as u64 * n as u64 + vi as u64) as u32)
892 }
893}
894
895fn piece_on_disk_ok(file: &mut File, set: &VolumeSet, vi: usize, s: usize, buf: &mut [u8]) -> bool {
897 let ps = set.piece_size as usize;
898 if file.seek(SeekFrom::Start(HEADER_LEN as u64 + s as u64 * ps as u64)).is_err() {
899 return false;
900 }
901 let mut got = 0;
902 while got < ps {
903 match file.read(&mut buf[got..]) {
904 Ok(0) | Err(_) => return false,
905 Ok(n) => got += n,
906 }
907 }
908 chunk::hash(&buf[..ps]) == expected_piece_hash(set, vi, s)
909}
910
911fn obtain_descriptor(opts: &FetchOptions, dial: &Dial, peers_used: &mut Vec<String>) -> Result<(VolumeSet, [u8; 32], &'static str)> {
913 let mut local_paths = opts.local.clone();
914 if opts.out_dir.is_dir() {
915 local_paths.push(opts.out_dir.clone());
916 }
917 if !local_paths.is_empty() {
918 if let Ok(sets) = volumes::inspect(&local_paths, false) {
919 for st in sets {
920 if st.set_id == hex::encode(opts.set_id) {
921 let bytes = format::cbor_encode(&st.set)?;
922 let h = chunk::hash(&bytes);
923 if let Some(expected) = opts.descriptor_b3 {
924 if h != expected {
925 return Err(Error::Corrupt("the local volumes carry a descriptor that does not match the expected descriptor hash".into()));
926 }
927 }
928 return Ok((st.set, h, if opts.descriptor_b3.is_some() { "descriptor" } else { "set id" }));
929 }
930 }
931 }
932 }
933 let mut last_err = None;
934 for peer in &opts.peers {
935 match dial.request(peer, &format!("DESCRIPTOR {}", hex::encode(opts.set_id)), MAX_DESCRIPTOR as usize) {
936 Ok(bytes) => match verify_descriptor(&bytes, opts) {
937 Ok((set, h, how)) => {
938 peers_used.push(peer.clone());
939 return Ok((set, h, how));
940 }
941 Err(e) => last_err = Some(format!("{peer}: {e}")),
942 },
943 Err(e) => last_err = Some(e.to_string()),
944 }
945 }
946 Err(Error::Missing(format!("no peer supplied an acceptable descriptor ({})", last_err.unwrap_or_else(|| "no peers".into()))))
947}
948
949fn verify_descriptor(bytes: &[u8], opts: &FetchOptions) -> Result<(VolumeSet, [u8; 32], &'static str)> {
950 let h = chunk::hash(bytes);
951 if let Some(expected) = opts.descriptor_b3 {
952 if h != expected {
953 return Err(Error::HashMismatch("descriptor hash".into()));
954 }
955 }
956 let set: VolumeSet = format::cbor_decode(bytes)?;
957 set.validate()?;
958 let recomputed = volumes::set_id_for(&hash32(&set.archive_b3), set.archive_size, set.piece_size, set.data, set.parity);
959 if recomputed != opts.set_id || set.set_id[..] != opts.set_id[..] {
960 return Err(Error::HashMismatch("descriptor does not belong to the expected set id".into()));
961 }
962 Ok((set, h, if opts.descriptor_b3.is_some() { "descriptor" } else { "set id" }))
963}
964
965pub fn fetch(opts: &FetchOptions) -> Result<FetchReport> {
967 if opts.peers.is_empty() {
968 return Err(Error::Invalid("at least one peer address is required".into()));
969 }
970 let started = Instant::now();
971 let dial = Dial::new(&opts.peers, opts.tls.as_ref())?;
972 std::fs::create_dir_all(&opts.out_dir)?;
973 let mut peers_used: Vec<String> = Vec::new();
974 let (set, descriptor_b3, verified_against) = obtain_descriptor(opts, &dial, &mut peers_used)?;
975 let descriptor_bytes = format::cbor_encode(&set)?;
976
977 let mut local_paths = opts.local.clone();
979 local_paths.push(opts.out_dir.clone());
980 let local_complete: Vec<usize> = match volumes::inspect(&local_paths, true) {
981 Ok(sets) => sets
982 .into_iter()
983 .find(|s| s.set_id == hex::encode(opts.set_id))
984 .map(|s| s.volumes.iter().enumerate().filter(|(_, v)| v.as_ref().is_some_and(|f| f.usable())).map(|(i, _)| i).collect())
985 .unwrap_or_default(),
986 Err(_) => Vec::new(),
987 };
988
989 let mut availability: BTreeMap<usize, Vec<String>> = BTreeMap::new();
991 for peer in &opts.peers {
992 if let Ok(bytes) = dial.request(peer, &format!("HAVE {}", hex::encode(opts.set_id)), MAX_HAVE_RESPONSE) {
993 if let Ok(have) = format::cbor_decode::<Vec<u16>>(&bytes) {
994 for vi in have {
995 if (vi as usize) < set.total() {
996 availability.entry(vi as usize).or_default().push(peer.clone());
997 }
998 }
999 }
1000 }
1001 }
1002
1003 let n = set.data as usize;
1004 let wanted: Vec<usize> = match &opts.want {
1005 Want::All => (0..set.total()).filter(|vi| !local_complete.contains(vi)).collect(),
1006 Want::Volumes(list) => list.iter().copied().filter(|vi| *vi < set.total() && !local_complete.contains(vi)).collect(),
1007 Want::Needed => {
1008 let mut chosen = Vec::new();
1009 let mut have = local_complete.len();
1010 for vi in (0..n).chain(n..set.total()) {
1011 if have >= n {
1012 break;
1013 }
1014 if !local_complete.contains(&vi) && availability.contains_key(&vi) {
1015 chosen.push(vi);
1016 have += 1;
1017 }
1018 }
1019 if have < n {
1020 return Err(Error::Missing(format!(
1021 "the peers hold only {} of the {n} volumes needed (local {}, available remotely {:?})",
1022 have,
1023 local_complete.len(),
1024 availability.keys().collect::<Vec<_>>()
1025 )));
1026 }
1027 chosen
1028 }
1029 };
1030
1031 let ps = set.piece_size as usize;
1032 let mut buf = vec![0u8; ps];
1033 let mut received = 0usize;
1034 let mut rejected = 0usize;
1035 let mut reverified = 0usize;
1036 let mut reverify_failed = 0usize;
1037 let mut bytes_reserved = 0u64;
1038 let mut completed = Vec::new();
1039 let mut partial = Vec::new();
1040 let mut stopped_early = false;
1041 'volumes: for &vi in &wanted {
1042 let (final_path, partial_path, map_path) = partial_paths(&opts.out_dir, &set, vi);
1043 let count = set.volume_pieces(vi) as usize;
1044 let full_len = HEADER_LEN as u64 + set.payload_len(vi);
1045 let hint: Option<Vec<bool>> = match std::fs::read(&map_path).ok().and_then(|b| format::cbor_decode::<PartialMap>(&b).ok()) {
1047 Some(m) if m.descriptor_b3[..] == descriptor_b3[..] && m.index as usize == vi && m.have.len() == count => Some(m.have),
1048 Some(_) => None, None => None, };
1051 let mut have = vec![false; count];
1052 if partial_path.is_file() {
1053 let mut f = OpenOptions::new().read(true).write(true).open(&partial_path)?;
1054 let mut header = [0u8; HEADER_LEN];
1055 let header_ok =
1056 f.read_exact(&mut header).is_ok() && volumes::decode_header(&header).is_ok_and(|h| h.index as usize == vi && h.set_id[..] == set.set_id[..] && h.piece_size == set.piece_size);
1057 if header_ok {
1058 for (s, slot) in have.iter_mut().enumerate() {
1059 let claimed = hint.as_ref().is_none_or(|h| h[s]);
1060 if !claimed {
1061 continue;
1062 }
1063 if piece_on_disk_ok(&mut f, &set, vi, s, &mut buf) {
1064 *slot = true;
1065 reverified += 1;
1066 } else if hint.is_some() {
1067 reverify_failed += 1;
1068 }
1069 }
1070 if f.metadata()?.len() != full_len {
1071 f.set_len(full_len)?;
1072 }
1073 } else {
1074 drop(f);
1075 std::fs::remove_file(&partial_path)?;
1076 }
1077 }
1078 if !partial_path.is_file() {
1079 let mut f = File::create(&partial_path)?;
1080 let mut id = [0u8; 32];
1081 id.copy_from_slice(&set.set_id);
1082 f.write_all(&volumes::encode_header(&volumes::VolumeHeader {
1083 index: vi as u16,
1084 data: set.data,
1085 parity: set.parity,
1086 piece_size: set.piece_size,
1087 archive_size: set.archive_size,
1088 set_id: id,
1089 payload_len: set.payload_len(vi),
1090 }))?;
1091 f.set_len(full_len)?;
1092 }
1093 bytes_reserved += full_len;
1094 let mut map = PartialMap { descriptor_b3: ByteBuf::from(descriptor_b3.to_vec()), index: vi as u16, have };
1095 save_map(&map_path, &map)?;
1096 let mut file = OpenOptions::new().read(true).write(true).open(&partial_path)?;
1097 let sources: Vec<String> = availability.get(&vi).cloned().unwrap_or_default();
1098 if sources.is_empty() && !map.have.iter().all(|&h| h) {
1099 partial.push(vi);
1100 continue;
1101 }
1102 let mut next_source = 0usize;
1103 for s in 0..count {
1104 if map.have[s] {
1105 continue;
1106 }
1107 if opts.max_pieces.is_some_and(|max| received >= max) {
1108 stopped_early = true;
1109 save_map(&map_path, &map)?;
1110 partial.push(vi);
1111 break 'volumes;
1112 }
1113 let expected = expected_piece_hash(&set, vi, s);
1114 let mut got = None;
1115 for attempt in 0..sources.len() {
1116 let peer = &sources[(next_source + attempt) % sources.len()];
1117 match dial.request(peer, &format!("PIECE {} {vi} {s}", hex::encode(opts.set_id)), ps) {
1118 Ok(bytes) if bytes.len() == ps && chunk::hash(&bytes) == expected => {
1119 got = Some(bytes);
1120 if !peers_used.contains(peer) {
1121 peers_used.push(peer.clone());
1122 }
1123 next_source = (next_source + attempt) % sources.len();
1124 break;
1125 }
1126 Ok(_) => rejected += 1,
1127 Err(_) => rejected += 1,
1128 }
1129 }
1130 let Some(bytes) = got else {
1131 save_map(&map_path, &map)?;
1132 return Err(Error::Missing(format!("piece {s} of {} could not be obtained from any peer", set.volume_name(vi))));
1133 };
1134 file.seek(SeekFrom::Start(HEADER_LEN as u64 + s as u64 * ps as u64))?;
1135 file.write_all(&bytes)?;
1136 file.flush()?;
1137 map.have[s] = true;
1138 received += 1;
1139 save_map(&map_path, &map)?;
1140 }
1141 if map.have.iter().all(|&h| h) {
1142 file.seek(SeekFrom::Start(HEADER_LEN as u64 + set.payload_len(vi)))?;
1143 file.write_all(&descriptor_bytes)?;
1144 file.write_all(&volumes::encode_trailer(HEADER_LEN as u64 + set.payload_len(vi), descriptor_bytes.len() as u64, &descriptor_b3, vi as u16))?;
1145 file.flush()?;
1146 drop(file);
1147 if final_path.exists() {
1148 std::fs::remove_file(&final_path)?;
1149 }
1150 std::fs::rename(&partial_path, &final_path)?;
1151 let _ = std::fs::remove_file(&map_path);
1152 completed.push(vi);
1153 }
1154 }
1155 let reconstructible = volumes::inspect(&local_paths, true).ok().and_then(|sets| sets.into_iter().find(|s| s.set_id == hex::encode(opts.set_id))).is_some_and(|s| s.reconstructible);
1156 let timing = *dial.timing.borrow();
1157 Ok(FetchReport {
1158 set_id: hex::encode(opts.set_id),
1159 descriptor_b3: hex::encode(descriptor_b3),
1160 verified_against,
1161 volumes_wanted: wanted,
1162 volumes_completed: completed,
1163 volumes_partial: partial,
1164 pieces_received: received,
1165 pieces_rejected: rejected,
1166 pieces_reverified: reverified,
1167 pieces_reverify_failed: reverify_failed,
1168 bytes_reserved,
1169 peers_used,
1170 reconstructible,
1171 stopped_early,
1172 encrypted: opts.tls.is_some(),
1173 requests: timing.requests,
1174 bytes_received: timing.bytes,
1175 seconds_connect: timing.connect.as_secs_f64(),
1176 seconds_handshake: timing.handshake.as_secs_f64(),
1177 seconds_transfer: timing.transfer.as_secs_f64(),
1178 seconds_total: started.elapsed().as_secs_f64(),
1179 })
1180}
1181
1182pub fn list_peer(peer: &str, identity: Option<&tls::Identity>, peer_id: Option<[u8; 32]>) -> Result<Vec<(String, String, u64)>> {
1185 list_peer_with(peer, identity, peer_id, &Revocations::default())
1186}
1187
1188pub fn list_peer_with(peer: &str, identity: Option<&tls::Identity>, peer_id: Option<[u8; 32]>, revoked: &Revocations) -> Result<Vec<(String, String, u64)>> {
1191 let cfg = match peer_id {
1192 Some(id) if revoked.contains(&id) => return Err(Error::Policy(format!("peer identity {} ({peer}) is revoked", hex::encode(id)))),
1193 Some(id) => Some(tls::client_config_with(identity, id, revoked)?),
1194 None => None,
1195 };
1196 match request(peer, cfg.as_ref(), "SETS", MAX_SETS_RESPONSE, &mut Timing::default())? {
1197 Reply::Ok(bytes) => format::cbor_decode(&bytes),
1198 Reply::Refused(code, msg) => Err(refusal(peer, code, &msg)),
1199 }
1200}
1201
1202#[cfg(test)]
1203mod tests {
1204 use super::*;
1205
1206 fn ip(last: u8) -> IpAddr {
1207 IpAddr::from([10, 0, 0, last])
1208 }
1209
1210 #[test]
1211 fn gate_refuses_a_new_address_beyond_max_peers() {
1212 let g = Gate::new(ServerLimits { max_peers: 2, ..ServerLimits::default() });
1213 assert_eq!(g.enter(ip(1)), Admit::Ok);
1214 assert_eq!(g.enter(ip(2)), Admit::Ok);
1215 assert_eq!(g.enter(ip(3)), Admit::Busy, "a third address needs a free address slot");
1216 assert_eq!(g.enter(ip(1)), Admit::Ok, "a known address is not a new one");
1217 g.leave(ip(2));
1218 assert_eq!(g.enter(ip(3)), Admit::Ok, "the slot of an address that left is free again");
1219 g.leave(ip(1));
1220 g.leave(ip(1));
1221 g.leave(ip(3));
1222 assert_eq!(g.total.load(Ordering::Relaxed), 0);
1223 assert!(g.per_ip.lock().unwrap().is_empty());
1224 }
1225
1226 #[test]
1227 fn bandwidth_bucket_grants_a_burst_then_meters_the_rate() {
1228 let g = Gate::new(ServerLimits { max_bandwidth: Some(1 << 20), ..ServerLimits::default() });
1229 assert_eq!(g.slice_len(), Some(128 << 10));
1230 assert!(g.take_bytes(1 << 20).is_zero());
1232 let w1 = g.take_bytes(1 << 20);
1234 let w2 = g.take_bytes(1 << 20);
1235 assert!(w1 > Duration::from_millis(900) && w1 <= Duration::from_millis(1100), "{w1:?}");
1236 assert!(w2 > Duration::from_millis(1900) && w2 <= Duration::from_millis(2100), "{w2:?}");
1237 let free = Gate::new(ServerLimits::default());
1239 assert_eq!(free.slice_len(), None);
1240 assert!(free.take_bytes(usize::MAX / 2).is_zero());
1241 assert_eq!(Gate::new(ServerLimits { max_bandwidth: Some(1024), ..ServerLimits::default() }).slice_len(), Some(4096));
1243 }
1244}