1use std::{error, fmt, fs, io};
4use std::path::Path;
5use std::str::FromStr;
6use std::sync::Arc;
7use log::error;
8use rpki::rtr::payload::{RouteOrigin, RouterKey};
9use rpki::slurm::{BgpsecFilter, PrefixFilter, SlurmFile};
10use crate::config::Config;
11use crate::error::Failed;
12
13
14#[derive(Clone, Debug, Default)]
17pub struct LocalExceptions {
18 origin_filters: Vec<PrefixFilter>,
19 router_key_filters: Vec<BgpsecFilter>,
20
21 origin_assertions: Vec<(RouteOrigin, Arc<ExceptionInfo>)>,
22 router_key_assertions: Vec<(RouterKey, Arc<ExceptionInfo>)>,
23}
24
25impl LocalExceptions {
26 pub fn empty() -> Self {
27 Self::default()
28 }
29
30 pub fn load(config: &Config, keep_comments: bool) -> Result<Self, Failed> {
31 let mut res = LocalExceptions::empty();
32 let mut ok = true;
33 for path in &config.exceptions {
34 if let Err(err) = res.extend_from_file(path, keep_comments) {
35 error!(
36 "Failed to load exceptions file {}: {}",
37 path.display(), err
38 );
39 ok = false;
40 }
41 }
42 if ok {
43 Ok(res)
44 }
45 else {
46 Err(Failed)
47 }
48 }
49
50 pub fn from_json(
51 json: &str,
52 keep_comments: bool
53 ) -> Result<Self, serde_json::Error> {
54 let mut res = LocalExceptions::empty();
55 res.extend_from_json(json, keep_comments)?;
56 Ok(res)
57 }
58
59 pub fn from_file<P: AsRef<Path>>(
60 path: P,
61 keep_comments: bool
62 ) -> Result<Self, LoadError> {
63 let mut res = Self::empty();
64 res.extend_from_file(path, keep_comments)?;
65 Ok(res)
66 }
67
68 pub fn extend_from_json(
69 &mut self,
70 json: &str,
71 keep_comments: bool
72 ) -> Result<(), serde_json::Error> {
73 self.extend_from_parsed(
74 SlurmFile::from_str(json)?, None, keep_comments
75 );
76 Ok(())
77 }
78
79 pub fn extend_from_file<P: AsRef<Path>>(
80 &mut self,
81 path: P,
82 keep_comments: bool
83 ) -> Result<(), LoadError> {
84 let buf = fs::read_to_string(&path)?;
85 self.extend_from_parsed(
86 SlurmFile::from_str(&buf)?,
87 Some(path.as_ref().into()), keep_comments
88 );
89 Ok(())
90 }
91
92 fn extend_from_parsed(
93 &mut self,
94 json: SlurmFile,
95 path: Option<Arc<Path>>,
96 keep_comments: bool,
97 ) {
98 let info = (!keep_comments).then(|| {
101 Arc::new(ExceptionInfo {
102 path: path.clone(),
103 comment: None
104 })
105 });
106 let info = info.as_ref(); self.origin_filters.extend(
109 json.filters.prefix.into_iter().map(|mut item| {
110 if !keep_comments {
111 item.comment = None
112 }
113 item
114 })
115 );
116 self.router_key_filters.extend(
117 json.filters.bgpsec.into_iter().map(|mut item| {
118 if !keep_comments {
119 item.comment = None
120 }
121 item
122 })
123 );
124 self.origin_assertions.extend(
125 json.assertions.prefix.into_iter().map(|item| {
126 (
127 RouteOrigin::new(item.prefix, item.asn),
128 info.cloned().unwrap_or_else(|| {
129 Arc::new(ExceptionInfo {
130 path: path.clone(),
131 comment: item.comment,
132 })
133 })
134 )
135 })
136 );
137 self.router_key_assertions.extend(
138 json.assertions.bgpsec.into_iter().map(|item| {
139 (
140 RouterKey::new(
141 item.ski, item.asn, item.router_public_key.into()
142 ),
143 info.cloned().unwrap_or_else(|| {
144 Arc::new(ExceptionInfo {
145 path: path.clone(),
146 comment: item.comment,
147 })
148 })
149 )
150 })
151 );
152 }
153
154 pub fn drop_origin(&self, origin: RouteOrigin) -> bool {
155 self.origin_filters.iter().any(|filter| filter.drop_origin(origin))
156 }
157
158 pub fn drop_router_key(&self, key: &RouterKey) -> bool {
159 self.router_key_filters.iter().any(|filter| {
160 filter.drop_router_key(key)
161 })
162 }
163
164 pub fn origin_assertions(
165 &self
166 ) -> impl Iterator<Item = (RouteOrigin, Arc<ExceptionInfo>)> + '_ {
167 self.origin_assertions.iter().map(|(origin, info)| {
168 (*origin, info.clone())
169 })
170 }
171
172 pub fn router_key_assertions(
173 &self
174 ) -> impl Iterator<Item = (RouterKey, Arc<ExceptionInfo>)> + '_ {
175 self.router_key_assertions.iter().map(|(key, info)| {
176 (key.clone(), info.clone())
177 })
178 }
179}
180
181
182#[derive(Clone, Debug, Default)]
185pub struct ExceptionInfo {
186 pub path: Option<Arc<Path>>,
187 pub comment: Option<String>,
188}
189
190#[cfg(feature = "arbitrary")]
191impl<'a> arbitrary::Arbitrary<'a> for ExceptionInfo {
192 fn arbitrary(
193 u: &mut arbitrary::Unstructured<'a>
194 ) -> arbitrary::Result<Self> {
195 Ok(Self {
196 path: if bool::arbitrary(u)? {
197 Some(
198 std::path::PathBuf::arbitrary(u)?.into_boxed_path().into()
199 )
200 }
201 else {
202 None
203 },
204 comment: Option::arbitrary(u)?,
205 })
206 }
207}
208
209
210#[derive(Debug)]
213pub enum LoadError {
214 Io(io::Error),
215 Json(serde_json::Error),
216}
217
218impl From<io::Error> for LoadError {
219 fn from(err: io::Error) -> LoadError {
220 LoadError::Io(err)
221 }
222}
223
224impl From<serde_json::Error> for LoadError {
225 fn from(err: serde_json::Error) -> LoadError {
226 LoadError::Json(err)
227 }
228}
229
230impl fmt::Display for LoadError {
231 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232 match *self {
233 LoadError::Io(ref err) => err.fmt(f),
234 LoadError::Json(ref err) => err.fmt(f),
235 }
236 }
237}
238
239impl error::Error for LoadError { }
240