From cc9bcb902c15eee2b6eb16e3ddc9f7f894d441d1 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Mon, 18 Nov 2024 17:05:48 -0500 Subject: [PATCH] feat: DhcpConfig can now effectively manage a config file to add a static map entry --- .../opnsense-config-xml/interfaces_expand.rs | 581 +------ .../opnsense-config-xml/src/data/dhcpd.rs | 12 +- .../src/data/interfaces.rs | 79 +- .../opnsense-config-xml/src/data/opnsense.rs | 43 +- .../src/xml_utils/generic_xml.rs | 256 --- .../src/xml_utils/maybe_string.rs | 173 -- .../opnsense-config-xml/src/xml_utils/mod.rs | 26 +- .../src/xml_utils/yaserde.rs | 20 - harmony-rs/opnsense-config/src/config.rs | 20 +- .../opnsense-config/src/modules/dhcp.rs | 20 +- .../src/tests/data/config-full-1.xml | 16 +- ...ig-structure-with-dhcp-staticmap-entry.xml | 1431 +++++++++++++++++ .../src/tests/data/config-structure.xml | 32 +- 13 files changed, 1595 insertions(+), 1114 deletions(-) delete mode 100644 harmony-rs/opnsense-config-xml/src/xml_utils/generic_xml.rs delete mode 100644 harmony-rs/opnsense-config-xml/src/xml_utils/maybe_string.rs delete mode 100644 harmony-rs/opnsense-config-xml/src/xml_utils/yaserde.rs create mode 100644 harmony-rs/opnsense-config/src/tests/data/config-structure-with-dhcp-staticmap-entry.xml diff --git a/harmony-rs/opnsense-config-xml/interfaces_expand.rs b/harmony-rs/opnsense-config-xml/interfaces_expand.rs index f5c03c7..3ca4b76 100644 --- a/harmony-rs/opnsense-config-xml/interfaces_expand.rs +++ b/harmony-rs/opnsense-config-xml/interfaces_expand.rs @@ -1,586 +1,9 @@ mod interfaces { - use xml::reader::XmlEvent; use yaserde::{ - raw_xml::RawXml, YaDeserialize as YaDeserializeTrait, - YaSerialize as YaSerializeTrait, + NamedList, YaDeserialize as YaDeserializeTrait, YaSerialize as YaSerializeTrait, }; use yaserde_derive::{YaDeserialize, YaSerialize}; - use std::collections::HashMap; - use crate::xml_utils::MaybeString; - pub struct Interfaces { - pub testraw: RawXml, - pub interfaces: HashMap, - } - #[automatically_derived] - impl ::core::default::Default for Interfaces { - #[inline] - fn default() -> Interfaces { - Interfaces { - testraw: ::core::default::Default::default(), - interfaces: ::core::default::Default::default(), - } - } - } - #[automatically_derived] - impl ::core::marker::StructuralPartialEq for Interfaces {} - #[automatically_derived] - impl ::core::cmp::PartialEq for Interfaces { - #[inline] - fn eq(&self, other: &Interfaces) -> bool { - self.testraw == other.testraw && self.interfaces == other.interfaces - } - } - #[automatically_derived] - impl ::core::fmt::Debug for Interfaces { - #[inline] - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_struct_field2_finish( - f, - "Interfaces", - "testraw", - &self.testraw, - "interfaces", - &&self.interfaces, - ) - } - } - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _IMPL_YA_SERIALIZE_FOR_Interfaces: () = { - use ::std::str::FromStr as _; - impl ::yaserde::YaSerialize for Interfaces { - #[allow(unused_variables)] - fn serialize( - &self, - writer: &mut ::yaserde::ser::Serializer, - ) -> ::std::result::Result<(), ::std::string::String> { - let skip = writer.skip_start_end(); - if !false && !skip { - let mut child_attributes = ::alloc::vec::Vec::new(); - let mut child_attributes_namespace = ::yaserde::__xml::namespace::Namespace::empty(); - let yaserde_label = writer - .get_start_event_name() - .unwrap_or_else(|| "Interfaces".to_string()); - let struct_start_event = ::yaserde::__xml::writer::XmlEvent::start_element( - yaserde_label.as_ref(), - ); - let event: ::yaserde::__xml::writer::events::XmlEvent = struct_start_event - .into(); - if let ::yaserde::__xml::writer::events::XmlEvent::StartElement { - name, - attributes, - namespace, - } = event { - let mut attributes: ::std::vec::Vec< - ::yaserde::__xml::attribute::OwnedAttribute, - > = attributes - .into_owned() - .to_vec() - .iter() - .map(|k| k.to_owned()) - .collect(); - attributes.extend(child_attributes); - let all_attributes = attributes - .iter() - .map(|ca| ca.borrow()) - .collect(); - let mut all_namespaces = namespace.into_owned(); - all_namespaces.extend(&child_attributes_namespace); - writer - .write(::yaserde::__xml::writer::events::XmlEvent::StartElement { - name, - attributes: ::std::borrow::Cow::Owned(all_attributes), - namespace: ::std::borrow::Cow::Owned(all_namespaces), - }) - .map_err(|e| e.to_string())?; - } else { - ::core::panicking::panic( - "internal error: entered unreachable code", - ) - } - } - if !false { - writer - .set_start_event_name( - ::std::option::Option::Some("testraw".to_string()), - ); - writer.set_skip_start_end(false); - ::yaserde::YaSerialize::serialize(&self.testraw, writer)?; - } - if !false { - writer - .set_start_event_name( - ::std::option::Option::Some("interfaces".to_string()), - ); - writer.set_skip_start_end(false); - ::yaserde::YaSerialize::serialize(&self.interfaces, writer)?; - } - if !false && !skip { - let struct_end_event = ::yaserde::__xml::writer::XmlEvent::end_element(); - writer.write(struct_end_event).map_err(|e| e.to_string())?; - } - ::std::result::Result::Ok(()) - } - fn serialize_attributes( - &self, - mut source_attributes: ::std::vec::Vec< - ::yaserde::__xml::attribute::OwnedAttribute, - >, - mut source_namespace: ::yaserde::__xml::namespace::Namespace, - ) -> ::std::result::Result< - ( - ::std::vec::Vec<::yaserde::__xml::attribute::OwnedAttribute>, - ::yaserde::__xml::namespace::Namespace, - ), - ::std::string::String, - > { - let mut child_attributes = ::std::vec::Vec::< - ::yaserde::__xml::attribute::OwnedAttribute, - >::new(); - let mut child_attributes_namespace = ::yaserde::__xml::namespace::Namespace::empty(); - let struct_start_event = ::yaserde::__xml::writer::XmlEvent::start_element( - "temporary_element_to_generate_attributes", - ); - let event: ::yaserde::__xml::writer::events::XmlEvent = struct_start_event - .into(); - if let ::yaserde::__xml::writer::events::XmlEvent::StartElement { - attributes, - namespace, - .. - } = event { - source_namespace.extend(&namespace.into_owned()); - source_namespace.extend(&child_attributes_namespace); - let a: ::std::vec::Vec< - ::yaserde::__xml::attribute::OwnedAttribute, - > = attributes - .into_owned() - .to_vec() - .iter() - .map(|k| k.to_owned()) - .collect(); - source_attributes.extend(a); - source_attributes.extend(child_attributes); - ::std::result::Result::Ok((source_attributes, source_namespace)) - } else { - ::core::panicking::panic("internal error: entered unreachable code"); - } - } - } - }; - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const _IMPL_YA_DESERIALIZE_FOR_Interfaces: () = { - use ::std::str::FromStr as _; - use ::yaserde::Visitor as _; - impl ::yaserde::YaDeserialize for Interfaces { - #[allow(unused_variables)] - fn deserialize( - reader: &mut ::yaserde::de::Deserializer, - ) -> ::std::result::Result { - let (named_element, struct_namespace) = if let ::yaserde::__xml::reader::XmlEvent::StartElement { - name, - .. - } = reader.peek()?.to_owned() - { - (name.local_name.to_owned(), name.namespace.clone()) - } else { - ( - ::std::string::String::from("Interfaces"), - ::std::option::Option::None, - ) - }; - let start_depth = reader.depth(); - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Struct {0} @ {1}: start to parse {2:?}", - "Interfaces", - start_depth, - named_element, - ), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - if reader.depth() == 0 { - if let Some(namespace) = struct_namespace { - match namespace.as_str() { - bad_namespace => { - let msg = ::alloc::__export::must_use({ - let res = ::alloc::fmt::format( - format_args!( - "bad namespace for {0}, found {1}", - named_element, - bad_namespace, - ), - ); - res - }); - return Err(msg); - } - } - } - } - #[allow(unused_mut)] - let mut __testraw_value: Option = None; - #[allow(unused_mut)] - let mut __interfaces_value: Option> = None; - let mut depth = 0; - loop { - let event = reader.peek()?.to_owned(); - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Struct {0} @ {1}: matching {2:?}", - "Interfaces", - start_depth, - event, - ), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - match event { - ::yaserde::__xml::reader::XmlEvent::StartElement { - ref name, - ref attributes, - .. - } => { - let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 && name.local_name == "Interfaces" - && namespace.as_str() == "" - { - let event = reader.next_event()?; - } else { - match (namespace.as_str(), name.local_name.as_str()) { - ("", "testraw") => { - if depth == 0 { - let _root = reader.next_event(); - } - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Looking at startElement"), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - if let Ok( - ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, - ) = reader.peek() - { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Found start element ?? {0}", "RawXml"), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - let value = ::deserialize( - reader, - )?; - __testraw_value = ::std::option::Option::Some(value); - let _event = reader.next_event()?; - } else { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "matching field type did not find substruct start element ? {0}", - "RawXml", - ), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - } - } - ("", "interfaces") => { - if depth == 0 { - let _root = reader.next_event(); - } - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Looking at startElement"), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - if let Ok( - ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, - ) = reader.peek() - { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "Found start element ?? {0}", - "HashMap < String, Interface >", - ), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - let value = as ::yaserde::YaDeserialize>::deserialize(reader)?; - __interfaces_value = ::std::option::Option::Some(value); - let _event = reader.next_event()?; - } else { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!( - "matching field type did not find substruct start element ? {0}", - "HashMap < String, Interface >", - ), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - } - } - _ => { - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Got StartElement {0:?}", name.local_name), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - let event = reader.next_event()?; - { - let lvl = ::log::Level::Trace; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("Next event {0:?}", event), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - if depth > 0 { - return Err( - ::alloc::__export::must_use({ - let res = ::alloc::fmt::format( - format_args!( - "Found unauthorized element {0}", - name.local_name, - ), - ); - res - }), - ); - reader.skip_element(|event| {})?; - } - } - } - } - if depth == 0 {} - depth += 1; - } - ::yaserde::__xml::reader::XmlEvent::EndElement { ref name } => { - { - let lvl = ::log::Level::Warn; - if lvl <= ::log::STATIC_MAX_LEVEL - && lvl <= ::log::max_level() - { - ::log::__private_api::log( - format_args!("endElement {0}", named_element), - lvl, - &( - "opnsense_config_xml::data::interfaces", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - if name.local_name == named_element - && reader.depth() == start_depth + 1 - { - break; - } - let event = reader.next_event()?; - depth -= 1; - } - ::yaserde::__xml::reader::XmlEvent::EndDocument => { - if false { - break; - } - } - ::yaserde::__xml::reader::XmlEvent::Characters( - ref text_content, - ) => { - let event = reader.next_event()?; - } - event => { - return ::std::result::Result::Err( - ::alloc::__export::must_use({ - let res = ::alloc::fmt::format( - format_args!("unknown event {0:?}", event), - ); - res - }), - ); - } - } - } - { - let lvl = ::log::Level::Debug; - if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { - ::log::__private_api::log( - format_args!( - "Struct {0} @ {1}: success", - "Interfaces", - start_depth, - ), - lvl, - &( - "yaserde_derive", - "opnsense_config_xml::data::interfaces", - ::log::__private_api::loc(), - ), - (), - ); - } - }; - ::std::result::Result::Ok(Interfaces { - testraw: __testraw_value - .ok_or_else(|| { - "testraw is a required field of Interfaces".to_string() - })?, - interfaces: __interfaces_value - .ok_or_else(|| { - "interfaces is a required field of Interfaces".to_string() - })?, - }) - } - } - }; + use yaserde::MaybeString; pub struct Interface { #[yaserde(rename = "if")] pub physical_interface_name: String, diff --git a/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs b/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs index 5f54437..9115e43 100644 --- a/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs +++ b/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs @@ -4,12 +4,12 @@ use yaserde::MaybeString; use super::opnsense::{NumberOption, Range, StaticMap}; -#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] -#[yaserde(rename = "dhcpd")] -pub struct Dhcpd { - #[yaserde(rename = "lan")] - pub lan: DhcpInterface, -} +// #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +// #[yaserde(rename = "dhcpd")] +// pub struct Dhcpd { +// #[yaserde(rename = "lan")] +// pub lan: DhcpInterface, +// } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct DhcpInterface { diff --git a/harmony-rs/opnsense-config-xml/src/data/interfaces.rs b/harmony-rs/opnsense-config-xml/src/data/interfaces.rs index af5bb2a..9d8104e 100644 --- a/harmony-rs/opnsense-config-xml/src/data/interfaces.rs +++ b/harmony-rs/opnsense-config-xml/src/data/interfaces.rs @@ -1,65 +1,106 @@ -use yaserde::{NamedList, YaDeserialize as YaDeserializeTrait, YaSerialize as YaSerializeTrait}; use yaserde_derive::{YaDeserialize, YaSerialize}; use yaserde::MaybeString; -#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] -pub struct Interfaces { - pub interfaces: NamedList, -} - #[derive(Default, PartialEq, Debug, YaDeserialize, YaSerialize)] pub struct Interface { + pub internal_dynamic: Option, #[yaserde(rename = "if")] pub physical_interface_name: String, pub descr: String, pub enable: MaybeString, + pub lock: Option, #[yaserde(rename = "spoofmac")] pub spoof_mac: Option, - pub internal_dynamic: Option, pub ipaddr: Option, #[yaserde(rename = "blockpriv")] pub block_priv: Option, #[yaserde(rename = "blockbogons")] pub block_bogons: Option, - pub lock: Option, #[yaserde(rename = "type")] pub r#type: Option, #[yaserde(rename = "virtual")] pub r#virtual: Option, pub subnet: Option, + pub ipaddrv6: Option, pub networks: Option, pub subnetv6: Option, - pub ipaddrv6: Option, #[yaserde(rename = "track6-interface")] pub track6_interface: Option, #[yaserde(rename = "track6-prefix-id")] pub track6_prefix_id: Option, } -pub trait MyDeserialize: YaDeserializeTrait {} -pub trait MySerialize: YaSerializeTrait {} - #[cfg(test)] mod test { + use crate::xml_utils::to_xml_str; + use super::*; use pretty_assertions::assert_eq; + use yaserde::NamedList; #[derive(Default, PartialEq, Debug, YaDeserialize, YaSerialize)] - pub struct TestStruct { + pub struct InterfacesParent { foo: String, + interfaces: NamedList, bar: String, } #[test] fn should_deserialize_interfaces() { - let test_struct = - yaserde::de::from_str::("aodisjbarbaba") - .unwrap(); - println!("test_struct : {:?}", test_struct); + let interfaces = + yaserde::de::from_str::>(FULL_INTERFACES_XML).unwrap(); + assert_eq!(interfaces.elements.len(), 6) + } - let interfaces = yaserde::de::from_str::(FULL_INTERFACES_XML).unwrap(); - assert_eq!(interfaces.interfaces.elements.len(), 6) + #[test] + fn should_serialize_interfaces() { + let named_list = NamedList { + elements: vec![ + (String::from("paul"), Interface::default()), + (String::from("anotherpaul"), Interface::default()), + (String::from("thirdone"), Interface::default()), + (String::from("andgofor4"), Interface::default()), + ], + }; + + let parent = InterfacesParent { + foo: String::from("foo"), + interfaces: named_list, + bar: String::from("foo"), + }; + + assert_eq!( + &to_xml_str(&parent).unwrap(), + r#" + + foo + + + + + + + + + + + + + + + + + + + + + + + foo + +"# + ) } const FULL_INTERFACES_XML: &str = " diff --git a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs index c331c45..6facf52 100644 --- a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs +++ b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs @@ -1,23 +1,9 @@ -use crate::data::dhcpd::Dhcpd; -use yaserde::{MaybeString, RawXml}; +use crate::{data::dhcpd::DhcpInterface, xml_utils::to_xml_str}; use log::error; +use yaserde::{MaybeString, NamedList, RawXml}; use yaserde_derive::{YaDeserialize, YaSerialize}; -impl From for OPNsense { - fn from(content: String) -> Self { - yaserde::de::from_str(&content) - .map_err(|e| error!("{}", e.to_string())) - .expect("OPNSense received invalid string, should be full XML") - } -} - -impl OPNsense { - pub fn to_xml(&self) -> String { - yaserde::ser::to_string(self) - .map_err(|e| error!("{}", e.to_string())) - .expect("OPNSense could not serialize to XML") - } -} +use super::Interface; #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] #[yaserde(rename = "opnsense")] @@ -25,8 +11,9 @@ pub struct OPNsense { pub theme: String, pub sysctl: Sysctl, pub system: RawXml, - pub interfaces: RawXml, - pub dhcpd: Dhcpd, + // pub interfaces: RawXml, + pub interfaces: NamedList, + pub dhcpd: NamedList, pub snmpd: Snmpd, pub syslog: Syslog, pub nat: Nat, @@ -56,6 +43,23 @@ pub struct OPNsense { pub ifgroups: Ifgroups, } +impl From for OPNsense { + fn from(content: String) -> Self { + yaserde::de::from_str(&content) + .map_err(|e| println!("{}", e.to_string())) + .expect("OPNSense received invalid string, should be full XML") + } +} + +impl OPNsense { + pub fn to_xml(&self) -> String { + to_xml_str(self) + .map_err(|e| error!("{}", e.to_string())) + .expect("OPNSense could not serialize to XML") + } +} + + #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct LoadBalancer { pub monitor_type: Vec, @@ -945,6 +949,7 @@ pub struct Acl { pub browser: MaybeString, #[yaserde(rename = "mimeType")] pub mime_type: MaybeString, + #[yaserde(rename = "googleapps")] pub google_apps: MaybeString, pub youtube: MaybeString, #[yaserde(rename = "safePorts")] diff --git a/harmony-rs/opnsense-config-xml/src/xml_utils/generic_xml.rs b/harmony-rs/opnsense-config-xml/src/xml_utils/generic_xml.rs deleted file mode 100644 index e670156..0000000 --- a/harmony-rs/opnsense-config-xml/src/xml_utils/generic_xml.rs +++ /dev/null @@ -1,256 +0,0 @@ -use xml::reader::XmlEvent as ReadEvent; -use yaserde::{ser, YaDeserialize as YaDeserializeTrait, YaSerialize as YaSerializeTrait}; - -#[derive(Debug, PartialEq, Default)] -pub struct RawXml(String); - -impl YaDeserializeTrait for RawXml { - fn deserialize( - reader: &mut yaserde::de::Deserializer, - ) -> Result { - let mut buffer = String::new(); - let mut depth = 0; - - let own_name = match reader.peek()? { - ReadEvent::StartElement { name, .. } => name.local_name.clone(), - _ => return Err("RawXml Should start deserializing with StartElement".to_string()), - }; - println!("RawXml deserialize from root element name : {own_name}"); - loop { - let current_event = reader.peek()?.to_owned(); - match current_event.clone() { - ReadEvent::StartElement { - name, attributes, .. - } => { - println!("StartElement {name} depth {depth}"); - depth += 1; - let mut attr_string = String::new(); - attributes.iter().for_each(|a| { - attr_string.push_str(&format!(r#" {}="{}""#, &a.name, &a.value)); - }); - buffer.push_str(&format!("<{}{}>", name, attr_string)); - let _event = reader.next_event()?; - } - ReadEvent::EndElement { name } => { - println!("EndElement {name} depth {depth}"); - depth -= 1; - buffer.push_str(&format!("", name)); - println!( - "Checking if name.local_name {} matches own_name {} at depth {depth}", - &name.local_name, &own_name - ); - if name.local_name == own_name && depth == 0 { - println!( - "Found next EndElement is closing my struct, breaking out of loop" - ); - break; - } else { - let _event = reader.next_event()?; - } - } - ReadEvent::Characters(content) => { - println!("Characters {content} depth {depth}"); - buffer.push_str(&content); - let _event = reader.next_event()?; - } - ReadEvent::StartDocument { - version, - encoding, - standalone, - } => todo!( - "StartDocument {:?} {:?} {:?}", - version, - encoding, - standalone - ), - ReadEvent::EndDocument => todo!(), - ReadEvent::ProcessingInstruction { name, data } => { - todo!("ProcessingInstruction {:?}, {:?}", name, data) - } - ReadEvent::CData(cdata) => todo!("CData, {:?}", cdata), - ReadEvent::Comment(comment) => todo!("Comment, {:?}", comment), - ReadEvent::Whitespace(whitespace) => todo!("Whitespace, {:?}", whitespace), - } - let next = reader.peek()?; - println!( - "Processing done on \ncurrent_event : {:?} \nnext : {:?}", - ¤t_event, &next - ); - } - - println!("buffered events {buffer}"); - let next = reader.peek()?; - println!("next : {:?}", &next); - - Ok(RawXml(buffer)) - } -} - -impl YaSerializeTrait for RawXml { - fn serialize(&self, writer: &mut ser::Serializer) -> Result<(), String> { - let content = self.0.clone(); - let content = xml::EventReader::from_str(content.as_str()); - let mut reader = yaserde::de::Deserializer::new(content); - loop { - let e = reader.next_event()?; - if let ReadEvent::EndDocument = e { - break; - } - writer - .write(e.as_writer_event().unwrap()) - .expect("Writer should write write event"); - } - Ok(()) - } - - fn serialize_attributes( - &self, - attributes: Vec, - namespace: xml::namespace::Namespace, - ) -> Result< - ( - Vec, - xml::namespace::Namespace, - ), - String, - > { - todo!() - } -} - -#[cfg(test)] -mod test { - use crate::xml_utils::to_xml_str; - - use super::*; - use pretty_assertions::assert_eq; - use yaserde_derive::YaDeserialize; - use yaserde_derive::YaSerialize; - - #[derive(Debug, PartialEq, Default, YaDeserialize)] - pub struct Parent { - // pub rawxml_child: RawXml, - pub string_child: String, - pub child_child: Child, - } - - #[derive(Debug, PartialEq, Default, YaDeserialize)] - pub struct Child { - pub child_val: String, - pub child_val2: String, - pub child_option: Option, - } - - #[test] - fn rawxml_should_buffer_empty_element() { - let rawxml: RawXml = yaserde::de::from_str("").unwrap(); - assert_eq!(rawxml.0, String::from("")); - } - - #[test] - fn rawxml_should_buffer_elements_with_different_case_as_they_are() { - let xml = ""; - let rawxml: RawXml = yaserde::de::from_str(xml).unwrap(); - assert_eq!(rawxml.0, String::from(xml)); - } - - #[test] - fn rawxml_should_buffer_elements_with_attributes() { - let xml = r#""#; - let rawxml: RawXml = yaserde::de::from_str(xml).unwrap(); - assert_eq!(rawxml.0, String::from(xml)); - } - - #[test] - fn rawxml_should_handle_complex_documents() { - let xml = r#"1060s00101024102401ignore204816384"#; - let rawxml: RawXml = yaserde::de::from_str(xml).unwrap(); - assert_eq!(rawxml.0, String::from(xml)); - } - - #[test] - fn rawxml_should_serialize_simple_documents() { - let xml = r#""#; - let rawxml: RawXml = yaserde::de::from_str(xml).unwrap(); - assert_eq!(yaserde::ser::to_string(&rawxml).unwrap(), xml); - } - - #[test] - fn rawxml_should_serialize_complex_documents() { - let xml = r#" - - - - - - - - - - 1 - 0 - 60s - - 0 - 0 - 1 - - 0 - - - 1024 - - - 1024 - - - 0 - - 1 - ignore - 2048 - 16384 - - - - -"#; - let rawxml: RawXml = yaserde::de::from_str(xml).unwrap(); - assert_eq!(to_xml_str(&rawxml).unwrap(), xml); - } - - #[test] - fn rawxml_should_allow_siblings_before() { - #[derive(YaDeserialize, YaSerialize)] - struct Config { - paul: Vec, - raw: RawXml, - } - let xml = r#"bobobpatateallo something"#; - let config: Config = yaserde::de::from_str(xml).unwrap(); - assert_eq!(yaserde::ser::to_string(&config).unwrap(), xml); - } - - #[test] - fn rawxml_should_allow_siblings_after() { - #[derive(YaDeserialize, YaSerialize)] - struct Config { - raw: RawXml, - paul: Vec, - } - let xml = r#"allo somethingbobobpatate"#; - let config: Config = yaserde::de::from_str(xml).unwrap(); - assert_eq!(config.paul.get(0).unwrap(), "bobob"); - assert_eq!(config.paul.get(1).unwrap(), "patate"); - assert_eq!(config.paul.len(), 2); - assert_eq!(config.raw.0, "allo something"); - assert_eq!(yaserde::ser::to_string(&config).unwrap(), xml); - } - - #[test] - fn rawxml_should_allow_being_end_of_document() { - let xml = r#"allo somethingbobobpatate"#; - let config: RawXml = yaserde::de::from_str(xml).unwrap(); - assert_eq!(yaserde::ser::to_string(&config).unwrap(), xml); - } -} diff --git a/harmony-rs/opnsense-config-xml/src/xml_utils/maybe_string.rs b/harmony-rs/opnsense-config-xml/src/xml_utils/maybe_string.rs deleted file mode 100644 index 7c7f4c9..0000000 --- a/harmony-rs/opnsense-config-xml/src/xml_utils/maybe_string.rs +++ /dev/null @@ -1,173 +0,0 @@ -use xml::reader::XmlEvent as ReadEvent; -use xml::writer::XmlEvent as WriteEvent; -use yaserde::{ser, YaDeserialize as YaDeserializeTrait, YaSerialize as YaSerializeTrait}; - -#[derive(Debug, PartialEq, Default)] -pub struct MaybeString { - field_name: String, - content: Option, -} - -impl YaDeserializeTrait for MaybeString { - fn deserialize( - reader: &mut yaserde::de::Deserializer, - ) -> Result { - let field_name = match reader.peek()? { - ReadEvent::StartElement { - name, attributes, .. - } => { - if attributes.len() > 0 { - return Err(String::from( - "Attributes not currently supported by MaybeString", - )); - } - - name.local_name.clone() - } - _ => return Err(String::from("Unsupporte ReadEvent type")), - }; - reader.next_event()?; - - let content = match reader.peek()? { - ReadEvent::Characters(content) => Some(content.clone()), - ReadEvent::EndElement { name } => { - if name.local_name != field_name { - return Err(format!( - "Invalid EndElement, expected {field_name} but got {}", - name.local_name - )); - } - None - } - _ => return Err(String::from("Unsupporte ReadEvent type")), - }; - - Ok(Self { - field_name, - content, - }) - } -} - -impl YaSerializeTrait for MaybeString { - fn serialize(&self, writer: &mut ser::Serializer) -> Result<(), String> { - let start_element_event = WriteEvent::start_element(self.field_name.as_str()); - writer.write(start_element_event).expect("Writer failed"); - match &self.content { - Some(content) => { - writer - .write(WriteEvent::characters(content)) - .expect("Writer failed"); - } - None => {} - }; - - writer - .write(WriteEvent::end_element()) - .expect("Writer failed"); - Ok(()) - } - - fn serialize_attributes( - &self, - _attributes: Vec, - _namespace: xml::namespace::Namespace, - ) -> Result< - ( - Vec, - xml::namespace::Namespace, - ), - String, - > { - unimplemented!("MaybeString does not currently support attributes") - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - use yaserde_derive::YaDeserialize; - use yaserde_derive::YaSerialize; - - #[derive(Debug, PartialEq, Default, YaDeserialize, YaSerialize)] - struct TestStruct { - maybe: MaybeString, - } - - #[test] - fn maybe_string_should_deserialize_empty_element() { - let initial_xml = ""; - let test_struct: TestStruct = - yaserde::de::from_str(initial_xml).expect("Shoudl deserialize teststruct"); - println!("Got test_struct {:?}", test_struct); - assert_eq!( - test_struct, - TestStruct { - maybe: MaybeString { - field_name: String::from("maybe"), - content: None - } - } - ); - } - - #[test] - fn maybe_string_should_deserialize_content() { - let initial_xml = "some content"; - let test_struct: TestStruct = - yaserde::de::from_str(initial_xml).expect("Shoudl deserialize teststruct"); - println!("Got test_struct {:?}", test_struct); - assert_eq!( - test_struct, - TestStruct { - maybe: MaybeString { - field_name: String::from("maybe"), - content: Some(String::from("some content")) - } - } - ); - } - - #[test] - fn maybe_string_should_deserialize_empty_long_format() { - let initial_xml = ""; - let test_struct: TestStruct = - yaserde::de::from_str(initial_xml).expect("Shoudl deserialize teststruct"); - println!("Got test_struct {:?}", test_struct); - assert_eq!( - test_struct, - TestStruct { - maybe: MaybeString { - field_name: String::from("maybe"), - content: None - } - } - ); - } - - #[test] - fn maybe_string_should_serialize_to_empty_element() { - let initial_xml = - r#""#; - let test_struct: TestStruct = - yaserde::de::from_str(initial_xml).expect("Shoudl deserialize teststruct"); - println!("Got test_struct {:?}", test_struct); - assert_eq!( - yaserde::ser::to_string(&test_struct).expect("should serialize teststruct"), - initial_xml - ); - } - - #[test] - fn maybe_string_should_serialize_content() { - let initial_xml = r#"some content"#; - let test_struct: TestStruct = - yaserde::de::from_str(initial_xml).expect("Shoudl deserialize teststruct"); - println!("Got test_struct {:?}", test_struct); - assert_eq!( - yaserde::ser::to_string(&test_struct).expect("should serialize teststruct"), - initial_xml - ); - } -} diff --git a/harmony-rs/opnsense-config-xml/src/xml_utils/mod.rs b/harmony-rs/opnsense-config-xml/src/xml_utils/mod.rs index ea92079..bdd2fcd 100644 --- a/harmony-rs/opnsense-config-xml/src/xml_utils/mod.rs +++ b/harmony-rs/opnsense-config-xml/src/xml_utils/mod.rs @@ -1,6 +1,20 @@ -//mod generic_xml; -//mod maybe_string; -mod yaserde; -//pub use generic_xml::*; -//pub use maybe_string::*; -pub use yaserde::*; +use yaserde::YaSerialize; + +pub fn to_xml_str(model: &T) -> Result { + let yaserde_cfg = yaserde::ser::Config { + perform_indent: true, + write_document_declaration: false, + pad_self_closing: false, + ..Default::default() + }; + let serialized = yaserde::ser::to_string_with_config::(model, &yaserde_cfg)?; + + // Opnsense does not specify encoding in the document declaration + // + // yaserde / xml-rs does not allow disabling the encoding attribute in the + // document declaration + // + // So here we just manually prefix the xml document with the exact document declaration + // that opnsense uses + Ok(format!("\n{serialized}\n")) +} diff --git a/harmony-rs/opnsense-config-xml/src/xml_utils/yaserde.rs b/harmony-rs/opnsense-config-xml/src/xml_utils/yaserde.rs deleted file mode 100644 index bdd2fcd..0000000 --- a/harmony-rs/opnsense-config-xml/src/xml_utils/yaserde.rs +++ /dev/null @@ -1,20 +0,0 @@ -use yaserde::YaSerialize; - -pub fn to_xml_str(model: &T) -> Result { - let yaserde_cfg = yaserde::ser::Config { - perform_indent: true, - write_document_declaration: false, - pad_self_closing: false, - ..Default::default() - }; - let serialized = yaserde::ser::to_string_with_config::(model, &yaserde_cfg)?; - - // Opnsense does not specify encoding in the document declaration - // - // yaserde / xml-rs does not allow disabling the encoding attribute in the - // document declaration - // - // So here we just manually prefix the xml document with the exact document declaration - // that opnsense uses - Ok(format!("\n{serialized}\n")) -} diff --git a/harmony-rs/opnsense-config/src/config.rs b/harmony-rs/opnsense-config/src/config.rs index 36681f6..0a40aa3 100644 --- a/harmony-rs/opnsense-config/src/config.rs +++ b/harmony-rs/opnsense-config/src/config.rs @@ -170,9 +170,11 @@ impl Config { #[cfg(test)] mod tests { + use crate::modules::dhcp::DhcpConfig; + use super::*; - use std::path::PathBuf; use pretty_assertions::assert_eq; + use std::path::PathBuf; #[tokio::test] async fn test_load_config_from_local_file() { @@ -199,23 +201,31 @@ mod tests { #[tokio::test] async fn test_add_dhcpd_static_entry() { let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - test_file_path.push("src/tests/data/config-full-1.xml"); + test_file_path.push("src/tests/data/config-structure.xml"); let config_file_path = test_file_path.to_str().unwrap().to_string(); println!("File path {config_file_path}"); let repository = Box::new(LocalFileConfigRepository::new(config_file_path)); - let config_file_str = repository.load().await.unwrap(); let mut config = Config::new(repository) .await .expect("Failed to load config"); println!("Config {:?}", config); + let mut dhcp_config = DhcpConfig::new(&mut config.opnsense); + dhcp_config.add_static_mapping("00:00:00:00:00:00", Ipv4Addr::new(192,168,20,100), "hostname").expect("Should add static mapping"); + let serialized = config.opnsense.to_xml(); fs::write("/tmp/serialized.xml", &serialized).unwrap(); - assert_eq!(config_file_str, serialized); - todo!(); + let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + test_file_path.push("src/tests/data/config-structure-with-dhcp-staticmap-entry.xml"); + + let config_file_path = test_file_path.to_str().unwrap().to_string(); + println!("File path {config_file_path}"); + let repository = Box::new(LocalFileConfigRepository::new(config_file_path)); + let expected_config_file_str = repository.load().await.unwrap(); + assert_eq!(expected_config_file_str, serialized); } } diff --git a/harmony-rs/opnsense-config/src/modules/dhcp.rs b/harmony-rs/opnsense-config/src/modules/dhcp.rs index 4b50d18..983c1d5 100644 --- a/harmony-rs/opnsense-config/src/modules/dhcp.rs +++ b/harmony-rs/opnsense-config/src/modules/dhcp.rs @@ -51,7 +51,16 @@ impl<'a> DhcpConfig<'a> { ) -> Result<(), DhcpError> { let mac = mac.to_string(); let hostname = hostname.to_string(); - let range = &self.opnsense.dhcpd.lan.range; + let lan_dhcpd = &mut self + .opnsense + .dhcpd + .elements + .iter_mut() + .find(|(name, _config)| return name == "lan") + .expect("Interface lan should have dhcpd activated") + .1; + + let range = &lan_dhcpd.range; if !Self::is_valid_mac(&mac) { return Err(DhcpError::InvalidMacAddress(mac)); @@ -61,7 +70,7 @@ impl<'a> DhcpConfig<'a> { return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string())); } - let existing_mappings = &self.opnsense.dhcpd.lan.staticmaps; + let existing_mappings: &mut Vec = &mut lan_dhcpd.staticmaps; if existing_mappings.iter().any(|m| { m.ipaddr @@ -86,14 +95,10 @@ impl<'a> DhcpConfig<'a> { ntpserver: Default::default(), }; - self.opnsense.dhcpd.lan.staticmaps.push(static_map); + existing_mappings.push(static_map); Ok(()) } - pub fn get_static_mappings(&self) -> &[StaticMap] { - &self.opnsense.dhcpd.lan.staticmaps - } - fn is_valid_mac(mac: &str) -> bool { let parts: Vec<&str> = mac.split(':').collect(); if parts.len() != 6 { @@ -158,5 +163,4 @@ mod test { let ip = "192.168.1.201".parse::().unwrap(); assert_eq!(DhcpConfig::is_ip_in_range(&ip, &range), false); } - } diff --git a/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml b/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml index fd2853d..33eff7a 100644 --- a/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml +++ b/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml @@ -320,9 +320,9 @@ 1 1 + pppoe 1 1 - pppoe em1 @@ -337,15 +337,15 @@ 1 + lo0 Loopback 1 - lo0 127.0.0.1 - ::1 - 8 - 128 none 1 + 8 + ::1 + 128 em5 @@ -358,18 +358,18 @@ 1 - WireGuard (Group) wireguard - 1 + WireGuard (Group) 1 group + 1 1 - 1 openvpn OpenVPN + 1 group 1 diff --git a/harmony-rs/opnsense-config/src/tests/data/config-structure-with-dhcp-staticmap-entry.xml b/harmony-rs/opnsense-config/src/tests/data/config-structure-with-dhcp-staticmap-entry.xml new file mode 100644 index 0000000..51d1a09 --- /dev/null +++ b/harmony-rs/opnsense-config/src/tests/data/config-structure-with-dhcp-staticmap-entry.xml @@ -0,0 +1,1431 @@ + + + opnsense + + + Increase UFS read-ahead speeds to match the state of hard drives and NCQ. + vfs.read_max + default + + + Set the ephemeral port range to be lower. + net.inet.ip.portrange.first + default + + + + + + 115200 + serial + video + normal + OPN1 + somedomain.yourlocal.mcd + + admins + System Administrators + system + 1999 + 0 + 2000 + page-all + + + root + System Administrator + system + admins + $2y$10$5555555555o8dj21980j1doiOIJDIOASJOID!jidjeue19812y + 0 + + + + + + + $2y$11$55555555556D8198uOASIDJaiojdjd1oijdijosaoijdaoidOIASJDoijdoiadOASdoiK + user + someuser + + + + + + /bin/sh + 2000 + + 2001 + 2000 + Etc/UTC + 0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org + + https + 6155aba4c9375 + + + + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + hadp + hadp + hadp + + monthly + + aesni + 1 + 1 + 1 + + admins + 1 + + + + + + enabled + 1 + + 1 + + + + + os-ddclient,os-dyndns,os-haproxy,os-wireguard + + + + 1 + admins + yes + basic + + + + + en_US + + none + none + none + none + none + none + none + none + 1 + + + + + pppoe0 + WAN + 1 + 1 + + pppoe + 1 + 1 + + + em1 + LAN + 1 + + 192.168.20.1 + 24 + track6 + + 0 + + + 1 + lo0 + Loopback + 1 + 127.0.0.1 + none + 1 + 8 + ::1 + 128 + + + em5 + backup_sync + 1 + 1 + + 10.10.5.1 + 24 + + + 1 + wireguard + WireGuard (Group) + 1 + group + 1 + + + + 1 + openvpn + OpenVPN + 1 + group + 1 + + + + + + 1 + 192.168.20.1 + somedomain.yourlocal.mcd + hmac-md5 + + + + + 192.168.20.50 + 192.168.20.200 + + + 192.168.20.1 + + + 55:55:55:55:55:1c + 192.168.20.160 + somehost983 + someservire8 + + + + + + 55:55:55:55:55:1c + 192.168.20.155 + somehost893 + + + + + + 55:55:55:55:55:1c + 192.168.20.50 + hostswitch2 + switch-2 (bottom) + + + + + + 00:00:00:00:00:00 + 192.168.20.100 + hostname + + + + + + + + + + + public + + + + 3 + + + + automatic + + + tcp + wan + + inet + + + + + nat_618812d37b8193.31302503 + host_3 + 22 + + 1 + + + wanip + 55555 + + + root@192.168.1.118 + + /firewall_nat_edit.php made changes + + + root@192.168.1.118 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_651ffc35e573d9.09092618 + 192.168.20.140 + 22 + + 1 + + + wanip + 30140 + + + root@172.12.0.11 + + /firewall_nat_edit.php made changes + + + root@172.12.0.11 + + /firewall_nat_edit.php made changes + + + + + + pass + wan + inet + keep state + allow public connections to vpn + in + wireguard + 1 + udp + + 1 + + + wanip + 51820 + + + root@192.168.1.118 + + /firewall_rules_edit.php made changes + + + root@192.168.1.118 + + /firewall_rules_edit.php made changes + + + + nat_670979b3279551.73601303 + wan + inet + keep state + port forwarding for virtual ip for someservice2 servers + + tcp + + 1 + + +
192.168.20.1
+ 55555 +
+ + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + 1 +
+
+ + + ICMP + icmp + ICMP + + + + TCP + tcp + Generic TCP + + + + HTTP + http + Generic HTTP + + / + + 200 + + + + + 0.opnsense.pool.ntp.org + + + system_information-container:00000000-col3:show,traffic_graphs-container:00000001-col3:show,thermal_sensors-container:00000002-col3:show,log-container:00000003-col3:show,services_status-container:00000004-col4:show,gateways-container:00000005-col4:show,interface_list-container:00000006-col4:show,carp_status-container:00000007-col4:show,wireguard-container:00000008-col4:show,dyn_dns_status-container:00000009-col4:show,system_log-container:00000010-col4:show + 2 + + + root@172.12.0.12 + + /firewall_nat.php made changes + + + + + + + + + + + + + + v9 + + + + 0 + + 1800 + 15 + + + + + + + + + wireguard + 1 + + + + + + + + + + + 1 + x3690_3 + host + + + 0 + + 192.168.1.136 + + + + + 1 + someservice2_vip + host + + + 0 + + 192.168.20.225 + + alias for someservice2 vip + + + + + + + + + + + + 0 + 0 + 0 + wan + 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 + + + W0D23 + 4 + ac + + medium + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 0 + 120 + 120 + 127.0.0.1 + 25 + + + 0 + auto + 1 + syslog facility log_daemon + + + + 0 + root + oiujds9889DSIJSDIJSDIjdj + 2812 + + + 5 + 1 + + + 0 + root@localhost.local + 0 + + + 10 + + + + 1 + $HOST + + system + + + + 300 + 30 +
+ + + + da6083fd-852c-44af-9ae7-8c9de443bbc9,4f18b847-c2ab-4707-9686-bf656e187ab8,62ea6632-3554-43be-bb0b-ceceab685338,f543f50a-4e52-4afd-85ce-95fe6d61dc54 + + + + + Ping + NetworkPing + failed ping + alert + + + + NetworkLink + NetworkInterface + failed link + alert + + + + + + + + + 0 + opnsense + + + + 1 + 1 + + + + + + 0 + on + strip + 1 + 1 + 0 + + admin@localhost.local + + + + 0 + /var/squid/cache + 256 + + + always + 100 + 16 + 256 + 0 + 0 + + + + 0 + 2048 + 1024 + 1024 + 256 + + + 0 + + 0 + username + password + + + + + + + lan + 3128 + 3129 + 0 + 0 + + + 4 + 5 + 0 + 3401 + public + + 2121 + 0 + 1 + 0 + + + + + + + + + + + 80:http,21:ftp,443:https,70:gopher,210:wais,1025-65535:unregistered ports,280:http-mgmt,488:gss-http,591:filemaker,777:multiling http + 443:https + + + + + + + 0 + icap://[::1]:1344/avscan + icap://[::1]:1344/avscan + 1 + 0 + 0 + X-Username + 1 + 1024 + 60 + + + + + + OPNsense proxy authentication + 2 + 5 + + + + +