#[cfg(feature = "build_docs")]
pub(crate) mod build;
mod md;
#[cfg(feature = "ns_consensus")]
mod ns;
use super::{NetstatusKwd, RelayFlags, RelayWeight};
use crate::parse::parser::Section;
use crate::types::misc::*;
use crate::{Error, Result};
use std::{net, time};
use tor_llcrypto::pk::rsa::RsaIdentity;
use tor_protover::Protocols;
pub use md::MdConsensusRouterStatus;
#[cfg(feature = "ns_consensus")]
pub use ns::NsConsensusRouterStatus;
#[derive(Debug, Clone)]
struct GenericRouterStatus<D> {
nickname: String,
identity: RsaIdentity,
addrs: Vec<net::SocketAddr>,
#[allow(dead_code)] or_port: u16,
doc_digest: D,
flags: RelayFlags,
version: Option<String>,
protos: Protocols,
weight: RelayWeight,
}
macro_rules! implement_accessors {
($name:ident) => {
impl $name {
pub fn orport_addrs(&self) -> impl Iterator<Item = &net::SocketAddr> {
self.rs.addrs.iter()
}
pub fn weight(&self) -> &RelayWeight {
&self.rs.weight
}
pub fn addrs(&self) -> &[net::SocketAddr] {
&self.rs.addrs[..]
}
pub fn protovers(&self) -> &Protocols {
&self.rs.protos
}
pub fn nickname(&self) -> &String {
&self.rs.nickname
}
pub fn flags(&self) -> &RelayFlags {
&self.rs.flags
}
pub fn version(&self) -> &Option<String> {
&self.rs.version
}
pub fn ed25519_id_is_usable(&self) -> bool {
!self.rs.flags.contains(RelayFlags::NO_ED_CONSENSUS)
}
pub fn is_flagged_bad_exit(&self) -> bool {
self.rs.flags.contains(RelayFlags::BAD_EXIT)
}
pub fn is_flagged_v2dir(&self) -> bool {
self.rs.flags.contains(RelayFlags::V2DIR)
}
pub fn is_flagged_exit(&self) -> bool {
self.rs.flags.contains(RelayFlags::EXIT)
}
pub fn is_flagged_guard(&self) -> bool {
self.rs.flags.contains(RelayFlags::GUARD)
}
}
};
}
pub(crate) use implement_accessors;
trait FromRsString: Sized {
fn decode(s: &str) -> Result<Self>;
}
impl<D> GenericRouterStatus<D>
where
D: FromRsString,
{
fn from_section(
sec: &Section<'_, NetstatusKwd>,
microdesc_format: bool,
) -> Result<GenericRouterStatus<D>> {
use NetstatusKwd::*;
let r_item = sec.required(RS_R)?;
let nickname = r_item.required_arg(0)?.to_string();
let ident = r_item.required_arg(1)?.parse::<B64>()?;
let identity = RsaIdentity::from_bytes(ident.as_bytes())
.ok_or_else(|| Error::BadArgument(r_item.pos(), "Wrong identity length".to_string()))?;
let skip = if microdesc_format { 0 } else { 1 };
let _ignore_published: time::SystemTime = {
let mut p = r_item.required_arg(2 + skip)?.to_string();
p.push(' ');
p.push_str(r_item.required_arg(3 + skip)?);
p.parse::<Iso8601TimeSp>()?.into()
};
let ipv4addr = r_item.required_arg(4 + skip)?.parse::<net::Ipv4Addr>()?;
let or_port = r_item.required_arg(5 + skip)?.parse::<u16>()?;
let _ = r_item.required_arg(6 + skip)?.parse::<u16>()?;
let mut addrs: Vec<net::SocketAddr> = vec![net::SocketAddr::V4(net::SocketAddrV4::new(
ipv4addr, or_port,
))];
for a_item in sec.slice(RS_A) {
addrs.push(a_item.required_arg(0)?.parse::<net::SocketAddr>()?);
}
let flags = RelayFlags::from_item(sec.required(RS_S)?)?;
let version = sec.maybe(RS_V).args_as_str().map(str::to_string);
let protos = {
let tok = sec.required(RS_PR)?;
tok.args_as_str()
.parse::<Protocols>()
.map_err(|e| Error::BadArgument(tok.pos(), e.to_string()))?
};
let weight = sec
.get(RS_W)
.map(RelayWeight::from_item)
.transpose()?
.unwrap_or_default();
let doc_digest: D = if microdesc_format {
let m_item = sec.required(RS_M)?;
D::decode(m_item.required_arg(0)?)?
} else {
D::decode(r_item.required_arg(2)?)?
};
Ok(GenericRouterStatus {
nickname,
identity,
addrs,
or_port,
doc_digest,
flags,
version,
protos,
weight,
})
}
}