1use std::fmt::Display;
2
3use ip::{Any, Prefix, PrefixSet};
4
5use irrc::{Connection, IrrClient, Query, ResponseItem};
6
7use rpsl::{
8 attr::{AttributeType, RpslAttribute},
9 expr::{
10 eval::{Evaluate, Evaluator, Resolver},
11 MpFilterExpr,
12 },
13 names::{AsSet, AutNum, FilterSet, RouteSet},
14 obj::{RpslObject, RpslObjectClass},
15 primitive::PeerAs,
16};
17
18use crate::error::Error;
19
20#[derive(Debug)]
38pub struct RpslEvaluator {
39 conn: Option<Connection>,
40}
41
42impl RpslEvaluator {
43 #[tracing::instrument(level = "debug")]
49 pub fn new(host: &str, port: u16) -> Result<Self, Error> {
50 let addr = format!("{host}:{port}");
51 let conn = IrrClient::new(addr).connect()?;
52 Ok(Self { conn: Some(conn) })
53 }
54
55 fn with_connection<F, T, E>(&mut self, f: F) -> Result<T, Error>
56 where
57 F: Fn(&mut Self, &mut Connection) -> Result<T, E>,
58 E: Into<Error>,
59 {
60 let mut conn = self.conn.take().ok_or(Error::AcquireConnection)?;
61 let result = f(self, &mut conn).map_err(Into::into);
62 self.conn = Some(conn);
63 result
64 }
65
66 #[tracing::instrument(skip(self, expr), fields(%expr), level = "debug")]
75 pub fn evaluate<'a, T>(
76 &mut self,
77 expr: T,
78 ) -> Result<<Self as Evaluator<'a>>::Output<T>, <Self as Evaluator<'a>>::Error>
79 where
80 T: Evaluate<'a, Self> + Display,
81 {
82 tracing::info!("evaluating RPSL mp-filter expression '{expr}'");
83 <Self as Evaluator>::evaluate(self, expr)
84 }
85}
86
87impl<'a> Evaluator<'a> for RpslEvaluator {
88 type Output<T> = <T as Evaluate<'a, Self>>::Output
89 where
90 T: Evaluate<'a, Self>;
91
92 type Error = Error;
93
94 fn finalise<T>(&mut self, output: T::Output) -> Result<Self::Output<T>, Self::Error>
95 where
96 T: Evaluate<'a, Self>,
97 {
98 Ok(output)
99 }
100
101 fn sink_error(&mut self, err: &(dyn std::error::Error + Send + Sync + 'static)) -> bool {
102 if let Some(irrc::Error::ResponseErr(
103 Query::Ipv4Routes(_) | Query::Ipv6Routes(_),
104 irrc::error::Response::KeyNotFound,
105 )) = err.downcast_ref()
106 {
107 tracing::debug!("{err:#}");
108 } else {
109 tracing::warn!("{err:#}");
110 }
111 true
112 }
113}
114
115impl Resolver<'_, FilterSet, MpFilterExpr> for RpslEvaluator {
116 type IError = Error;
117
118 #[tracing::instrument(skip(self), level = "debug")]
119 fn resolve(&mut self, filter_set: &FilterSet) -> Result<MpFilterExpr, Self::IError> {
120 self.with_connection(|this, conn| {
121 conn.pipeline()
122 .push(Query::RpslObject(
125 irrc::RpslObjectClass::FilterSet,
126 filter_set.to_string(),
127 ))
128 .map_err(Error::from)
129 .and_then(|pipeline| {
130 pipeline
131 .responses()
132 .find_map(|resp| {
133 this.collect_result(resp.map_err(Error::from).and_then(|item| {
134 let obj = item.into_content();
135 if let RpslObject::FilterSet(ref filter_set_obj) = obj {
136 filter_set_obj
137 .attrs()
138 .into_iter()
139 .find_map(|attr| {
140 if let RpslAttribute::MpFilter(expr) = attr {
141 Some(expr.clone())
143 } else {
144 None
145 }
146 })
147 .ok_or_else(|| {
148 Error::FindAttribute(AttributeType::MpFilter, obj)
149 })
150 } else {
151 Err(Error::RpslObjectClass(obj))
152 }
153 }))
154 .transpose()
155 })
156 .unwrap_or_else(|| Ok("NOT ANY".parse()?))
157 })
158 })
159 }
160}
161
162impl Resolver<'_, AsSet, PrefixSet<Any>> for RpslEvaluator {
163 type IError = Error;
164
165 #[tracing::instrument(skip(self), fields(%as_set), level = "debug")]
166 fn resolve(&mut self, as_set: &AsSet) -> Result<PrefixSet<Any>, Self::IError> {
167 self.with_connection(|this, conn| {
168 conn.pipeline_from_initial(Query::AsSetMembersRecursive(as_set.clone()), |resp| {
170 this.collect_result::<_, _, Error>(resp.map(|item| {
171 let autnum = item.into_content();
172 [Query::Ipv4Routes(autnum), Query::Ipv6Routes(autnum)]
173 }))
174 .unwrap_or_else(|err| {
176 _ = this.sink_error(&err);
177 None
178 })
179 })
180 .and_then(|mut pipeline| {
181 this.collect_results(
182 pipeline
183 .responses::<'_, Prefix<Any>>()
184 .map(|resp| resp.map(ResponseItem::into_content)),
185 )
186 })
187 })
188 }
189}
190
191impl Resolver<'_, RouteSet, PrefixSet<Any>> for RpslEvaluator {
192 type IError = Error;
193
194 #[tracing::instrument(skip(self), level = "debug")]
195 fn resolve(&mut self, route_set: &RouteSet) -> Result<PrefixSet<Any>, Self::IError> {
196 self.with_connection(|this, conn| {
197 conn.pipeline()
198 .push(Query::RouteSetMembersRecursive(route_set.clone()))
200 .map_err(Error::from)
201 .and_then(|pipeline| {
202 this.collect_results(
203 pipeline
204 .responses::<'_, Prefix<Any>>()
205 .map(|response| response.map(ResponseItem::into_content)),
206 )
207 })
208 })
209 }
210}
211
212impl Resolver<'_, AutNum, PrefixSet<Any>> for RpslEvaluator {
213 type IError = Error;
214
215 #[tracing::instrument(skip(self), fields(%autnum), level = "debug")]
216 fn resolve(&mut self, autnum: &AutNum) -> Result<PrefixSet<Any>, Self::IError> {
217 self.with_connection(|this, conn| {
218 conn.pipeline()
219 .push(Query::Ipv4Routes(*autnum))?
220 .push(Query::Ipv6Routes(*autnum))
221 .map_err(Error::from)
222 .and_then(|pipeline| {
223 this.collect_results(
224 pipeline
225 .responses::<'_, Prefix<Any>>()
226 .map(|response| response.map(ResponseItem::into_content)),
227 )
228 })
229 })
230 }
231}
232
233impl Resolver<'_, PeerAs, PrefixSet<Any>> for RpslEvaluator {
234 type IError = Error;
235
236 #[tracing::instrument(skip(self), level = "debug")]
237 fn resolve(&mut self, _: &PeerAs) -> Result<PrefixSet<Any>, Self::IError> {
238 unimplemented!()
239 }
240}