Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce rrdp::NotificationFile::parse_limited. #293

Merged
merged 2 commits into from
Jun 10, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 131 additions & 28 deletions src/rrdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ pub struct NotificationFile {
snapshot: SnapshotInfo,

/// The list of available delta updates.
deltas: Vec<DeltaInfo>,
///
/// If parsing the list fails for a “soft” reason, this is set to the
/// error variant.
deltas: Result<Vec<DeltaInfo>, DeltaListError>,
}

/// # Data Access
Expand All @@ -70,7 +73,12 @@ impl NotificationFile {
snapshot: UriAndHash,
deltas: Vec<DeltaInfo>,
) -> Self {
NotificationFile { session_id, serial, snapshot, deltas }
NotificationFile {
session_id,
serial,
snapshot,
deltas: Ok(deltas),
}
}

/// Returns the identifier of the current session of the server.
Expand Down Expand Up @@ -99,24 +107,41 @@ impl NotificationFile {
/// Returns the list of available delta updates.
///
/// Deltas can be processed using the [`ProcessDelta`] trait.
///
/// If `delta_status` returns an error, this list will be empty.
pub fn deltas(&self) -> &[DeltaInfo] {
&self.deltas
match self.deltas {
Ok(ref deltas) => deltas.as_slice(),
Err(_) => &[]
}
}

/// Returns the status of the delta list.
pub fn delta_status(&self) -> Result<(), DeltaListError> {
match self.deltas {
Ok(_) => Ok(()),
Err(err) => Err(err)
}
}

/// Sorts the deltas by increasing serial numbers.
///
/// In other words, the delta with the smallest serial number will
/// appear at the beginning of the sequence.
pub fn sort_deltas(&mut self) {
self.deltas.sort_by_key(|delta| delta.serial());
if let Ok(ref mut deltas) = self.deltas {
deltas.sort_by_key(|delta| delta.serial());
}
}

/// Sorts the deltas by decreasing serial numbers.
///
/// In other words, the delta with the largest serial number will
/// appear at the beginning of the sequence.
pub fn reverse_sort_deltas(&mut self) {
self.deltas.sort_by(|a,b| b.serial.cmp(&a.serial));
if let Ok(ref mut deltas) = self.deltas {
deltas.sort_by(|a,b| b.serial.cmp(&a.serial));
}
}

/// Sorts, verifies, and optionally limits the list of deltas.
Expand All @@ -126,22 +151,24 @@ impl NotificationFile {
///
/// Returns whether there are no gaps in the retained deltas.
pub fn sort_and_verify_deltas(&mut self, limit: Option<usize>) -> bool {
if !self.deltas.is_empty() {
self.sort_deltas();

if let Some(limit) = limit {
if limit < self.deltas.len() {
let offset = self.deltas.len() - limit;
self.deltas.drain(..offset);
if let Ok(ref mut deltas) = self.deltas {
if !deltas.is_empty() {
deltas.sort_by_key(|delta| delta.serial());

if let Some(limit) = limit {
if limit < deltas.len() {
let offset = deltas.len() - limit;
deltas.drain(..offset);
}
}
}

let mut last_seen = self.deltas[0].serial();
for delta in &self.deltas[1..] {
if last_seen + 1 != delta.serial() {
return false;
} else {
last_seen = delta.serial()
let mut last_seen = deltas[0].serial();
for delta in &deltas[1..] {
if last_seen + 1 != delta.serial() {
return false;
} else {
last_seen = delta.serial()
}
}
}
}
Expand All @@ -154,8 +181,10 @@ impl NotificationFile {
if !base.eq_authority(self.snapshot().uri()) {
return false
}
if self.deltas().iter().any(|delta| !base.eq_authority(delta.uri())) {
return false
if let Ok(ref deltas) = self.deltas {
if deltas.iter().any(|delta| !base.eq_authority(delta.uri())) {
return false
}
}
true
}
Expand All @@ -165,7 +194,28 @@ impl NotificationFile {
///
impl NotificationFile {
/// Parses the notification file from its XML representation.
pub fn parse<R: io::BufRead>(reader: R) -> Result<Self, XmlError> {
pub fn parse<R: io::BufRead>(
reader: R,
) -> Result<Self, XmlError> {
Self::_parse(reader, None)
}

/// Parses the notification file with a limit on the delta_list.
///
/// If there are more delta entries than the given limit, the list of the
/// returned value will be empty and [`Self::delta_status`] will return
/// an error.
pub fn parse_limited<R: io::BufRead>(
reader: R,
delta_limit: usize,
) -> Result<Self, XmlError> {
Self::_parse(reader, Some(delta_limit))
}

pub fn _parse<R: io::BufRead>(
reader: R,
delta_limit: Option<usize>,
) -> Result<Self, XmlError> {
let mut reader = Reader::new(reader);

let mut session_id = None;
Expand Down Expand Up @@ -196,7 +246,7 @@ impl NotificationFile {

let mut snapshot = None;

let mut deltas = vec![];
let mut deltas = Ok(vec![]);

while let Some(mut content) = outer.take_opt_element(&mut reader,
|element| {
Expand Down Expand Up @@ -245,13 +295,24 @@ impl NotificationFile {
}
_ => Err(XmlError::Malformed)
})?;
match (serial, uri, hash) {
(Some(serial), Some(uri), Some(hash)) => {
deltas.push(DeltaInfo::new(serial, uri, hash));
Ok(())
let (serial, uri, hash) = match (serial, uri, hash) {
(Some(serial), Some(uri), Some(hash)) => {
(serial, uri, hash)
}
_ => Err(XmlError::Malformed)
_ => return Err(XmlError::Malformed)
};
if let Some(limit) = delta_limit {
let len = deltas.as_ref().map(|deltas| {
deltas.len()
}).unwrap_or(0);
if len >= limit {
deltas = Err(DeltaListError::Oversized);
}
}
if let Ok(ref mut deltas) = deltas {
deltas.push(DeltaInfo::new(serial, uri, hash))
}
Ok(())
}
_ => Err(XmlError::Malformed)
}
Expand Down Expand Up @@ -1477,6 +1538,26 @@ impl fmt::Display for ParseHashError {
impl error::Error for ParseHashError { }


//------------ DeltaListError ------------------------------------------------

/// An error happened when parsing the delta list of a notification file.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeltaListError {
/// The delta list was larger than a given limit.
Oversized,
}

impl fmt::Display for DeltaListError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Oversized => write!(f, "excessively large delta list")
}
}
}

impl error::Error for DeltaListError { }


//------------ ProcessError --------------------------------------------------

/// An error occurred while processing RRDP data.
Expand Down Expand Up @@ -1525,6 +1606,7 @@ impl error::Error for ProcessError { }
#[cfg(test)]
mod test {
use std::str::from_utf8_unchecked;
use std::str::FromStr;

use super::*;

Expand Down Expand Up @@ -1656,6 +1738,27 @@ mod test {
assert_eq!(from_sorted, from_unsorted);
}

#[test]
fn notification_parse_limited() {
let bytes = include_bytes!(
"../test-data/rrdp/ripe-notification.xml"
).as_ref();
let full = NotificationFile::parse(bytes).unwrap();
assert!(!full.deltas().is_empty());

let limited = NotificationFile::parse_limited(
bytes, full.deltas().len()
).unwrap();
assert_eq!(limited.deltas().len(), full.deltas().len());
assert!(limited.delta_status().is_ok());

let limited = NotificationFile::parse_limited(
bytes, full.deltas().len() - 1
).unwrap();
assert_eq!(limited.deltas().len(), 0);
assert!(limited.delta_status().is_err());
}

#[test]
fn ripe_snapshot() {
<Test as ProcessSnapshot>::process(
Expand Down
Loading