diff --git a/src/session.rs b/src/session.rs index d5de3d6..9f991b1 100644 --- a/src/session.rs +++ b/src/session.rs @@ -251,14 +251,22 @@ impl Session { if unset { self.unset_option(EnvOption::ListSetName); } - match ret? { - ListResult::Normal(_) => { + // Reset session data after list to avoid interfering with subsequent operations + unsafe { + binding::ipset_data_reset(self.data); + } + match ret { + Ok(ListResult::Normal(_)) => { unreachable!("normal should not return") } - ListResult::Terse(names) => { + Ok(ListResult::Terse(names)) => { let name = self.name.to_string_lossy().to_string(); Ok(names.contains(&name)) } + Err(Error::Cmd(msg, true)) if msg.contains("does not exist") => { + Ok(false) + } + Err(e) => Err(e), } } @@ -288,6 +296,7 @@ impl Session { let mut result = NormalListResult::default(); for line in &self.output { for s in line.split("\n") { + let s = s.trim_end_matches('\0'); if !s.is_empty() { result.update_from_str(s)?; } diff --git a/src/types.rs b/src/types.rs index 3f56e69..b30a1ea 100644 --- a/src/types.rs +++ b/src/types.rs @@ -392,6 +392,14 @@ impl_name!(PortDataType, "port"); impl_name!(IfaceDataType, "iface"); impl_name!(MarkDataType, "mark"); impl_name!(SetDataType, "set"); + +// Special case for single-element tuple +impl TypeName for (A,) { + fn name() -> String { + A::name() + } +} + impl_name!(A, B); impl_name!(A, B, C); @@ -410,6 +418,13 @@ macro_rules! impl_set_data { }; } +// Special case for single-element tuple +impl> SetData for (A,) { + fn set_data(&self, session: &Session, from: Option) -> Result<(), Error> { + self.0.set_data(session, from) + } +} + impl_set_data!(A, B); impl_set_data!(A, B, C); @@ -434,6 +449,13 @@ macro_rules! impl_parse { }; } +// Special case for single-element tuple +impl Parse for (A,) { + fn parse(&mut self, s: &str) -> Result<(), Error> { + self.0.parse(s) + } +} + impl_parse!(A, B); impl_parse!(A, B, C); @@ -877,10 +899,28 @@ impl NormalListResult { options.push(AddOption::Packets(fields[i + 1].parse()?)); } "bytes" => { - options.push(AddOption::Bytes(fields[i + 1].trim().replace("\0", "").parse()?)); + options.push(AddOption::Bytes(fields[i + 1].parse()?)); } "comment" => { - options.push(AddOption::Comment(fields[i + 1].to_string())); + // Collect all tokens between quotes + let mut comment = String::new(); + let mut j = i + 1; + while j < fields.len() { + if !comment.is_empty() { + comment.push(' '); + } + comment.push_str(fields[j]); + // Check if this token ends with a quote + if fields[j].ends_with('"') { + break; + } + j += 1; + } + // Remove quotes + comment = comment.trim_matches('"').to_string(); + options.push(AddOption::Comment(comment)); + i = j + 1; // Move past the last token of comment + continue; } "skbmark" => { let values: Vec<_> = fields[i + 1].split('/').collect(); @@ -927,6 +967,7 @@ pub struct ListHeader { hash_size: u32, bucket_size: Option, max_elem: u32, + timeout: Option, counters: bool, comment: bool, skbinfo: bool, @@ -956,6 +997,10 @@ impl ListHeader { header.max_elem = s[i + 1].parse().unwrap(); i += 2; } + "timeout" => { + header.timeout = Some(s[i + 1].parse().unwrap()); + i += 2; + } "counters" => { header.counters = true; i += 1;