From aae68654f25ad5189ae3357c8c782ea847abdbf5 Mon Sep 17 00:00:00 2001 From: Mattbazooka Date: Mon, 19 Sep 2022 06:50:15 +0000 Subject: [PATCH 01/12] Create v6::OROCodes (#1) --- src/v6/mod.rs | 2 + src/v6/options.rs | 6 +- src/v6/oro_codes.rs | 214 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 src/v6/oro_codes.rs diff --git a/src/v6/mod.rs b/src/v6/mod.rs index 67434b2..df0ed3b 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -53,6 +53,7 @@ //! ``` //! mod options; +mod oro_codes; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -61,6 +62,7 @@ use std::{convert::TryInto, fmt, net::Ipv6Addr}; // re-export submodules from proto::msg pub use self::options::*; +pub use self::oro_codes::*; pub use crate::{ decoder::{Decodable, Decoder}, diff --git a/src/v6/options.rs b/src/v6/options.rs index 95db2ac..2a48ab0 100644 --- a/src/v6/options.rs +++ b/src/v6/options.rs @@ -13,7 +13,7 @@ use crate::{ encoder::{Encodable, Encoder}, error::{DecodeResult, EncodeResult}, v4::HType, - v6::{MessageType, RelayMessage}, + v6::{MessageType, OROCode, RelayMessage}, }; // server can send multiple IA_NA options to request multiple addresses @@ -417,7 +417,7 @@ impl Decodable for Authentication { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ORO { // 2 * num opts - pub opts: Vec, + pub opts: Vec, } impl Decodable for ORO { @@ -429,7 +429,7 @@ impl Decodable for ORO { .read_slice(len)? .chunks_exact(2) // TODO: use .array_chunks::<2>() when stable - .map(|code| OptionCode::from(u16::from_be_bytes([code[0], code[1]]))) + .map(|code| OROCode::from(u16::from_be_bytes([code[0], code[1]]))) .collect() }, }) diff --git a/src/v6/oro_codes.rs b/src/v6/oro_codes.rs new file mode 100644 index 0000000..0f350c2 --- /dev/null +++ b/src/v6/oro_codes.rs @@ -0,0 +1,214 @@ +///Valid Option Codes for ORO +///https://datatracker.ietf.org/doc/html/rfc8415#section-24 +#[allow(non_camel_case_types)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum OROCode { + ///Optional + VENDOR_OPTS, + SIP_SERVER_D, + SIP_SERVER_A, + DNS_SERVERS, + DOMAIN_LIST, + NIS_SERVERS, + NISP_SERVERS, + NIS_DOMAIN_NAME, + NISP_DOMAIN_NAME, + SNTP_SERVERS, + ///Required for Information-request + INFORMATION_REFRESH_TIME, + BCMCS_SERVER_D, + BCMCS_SERVER_A, + GEOCONF_CIVIC, + CLIENT_FQDN, + PANA_AGENT, + NEW_POSIX_TIMEZONE, + NEW_TZDB_TIMEZONE, + MIP6_HNIDF, + MIP6_VDINF, + V6_LOST, + CAPWAP_AC_V6, + IPv6_Address_MoS, + IPv6_FQDN_MoS, + NTP_SERVER, + V6_ACCESS_DOMAIN, + SIP_UA_CS_LIST, + OPT_BOOTFILE_URL, + OPT_BOOTFILE_PARAM, + NII, + GEOLOCATION, + AFTR_NAME, + ERP_LOCAL_DOMAIN_NAME, + PD_EXCLUDE, + MIP6_IDINF, + MIP6_UDINF, + MIP6_HNP, + MIP6_HAA, + MIP6_HAF, + RDNSS_SELECTION, + KRB_PRINCIPAL_NAME, + KRB_REALM_NAME, + KRB_DEFAULT_REALM_NAME, + KRB_KDC, + ///Required for Solicit + SOL_MAX_RT, + ///Required for Information-request + INF_MAX_RT, + ADDRSEL, + ADDRSEL_TABLE, + V6_PCP_SERVER, + DHCP4_O_DHCP6_SERVER, + S46_CONT_MAPE, + S46_CONT_MAPT, + S46_CONT_LW, + _4RD, + _4RD_MAP_RULE, + _4RD_NON_MAP_RULE, + DHCP_Captive_Portal, + MPL_PARAMETERS, + S46_PRIORITY, + V6_PREFIX64, + IPv6_Address_ANDSF, + ///Avalible for future codes. + Unknown(u16), +} + +impl From for u16 { + fn from(opt: OROCode) -> Self { + use OROCode::*; + match opt { + VENDOR_OPTS => 17, + SIP_SERVER_D => 21, + SIP_SERVER_A => 22, + DNS_SERVERS => 23, + DOMAIN_LIST => 24, + NIS_SERVERS => 27, + NISP_SERVERS => 28, + NIS_DOMAIN_NAME => 29, + NISP_DOMAIN_NAME => 30, + SNTP_SERVERS => 31, + INFORMATION_REFRESH_TIME => 32, + BCMCS_SERVER_D => 33, + BCMCS_SERVER_A => 34, + GEOCONF_CIVIC => 36, + CLIENT_FQDN => 39, + PANA_AGENT => 40, + NEW_POSIX_TIMEZONE => 41, + NEW_TZDB_TIMEZONE => 42, + MIP6_HNIDF => 49, + MIP6_VDINF => 50, + V6_LOST => 51, + CAPWAP_AC_V6 => 52, + IPv6_Address_MoS => 54, + IPv6_FQDN_MoS => 55, + NTP_SERVER => 56, + V6_ACCESS_DOMAIN => 57, + SIP_UA_CS_LIST => 58, + OPT_BOOTFILE_URL => 59, + OPT_BOOTFILE_PARAM => 60, + NII => 62, + GEOLOCATION => 63, + AFTR_NAME => 64, + ERP_LOCAL_DOMAIN_NAME => 65, + PD_EXCLUDE => 67, + MIP6_IDINF => 69, + MIP6_UDINF => 70, + MIP6_HNP => 71, + MIP6_HAA => 72, + MIP6_HAF => 73, + RDNSS_SELECTION => 74, + KRB_PRINCIPAL_NAME => 75, + KRB_REALM_NAME => 76, + KRB_DEFAULT_REALM_NAME => 77, + KRB_KDC => 78, + SOL_MAX_RT => 82, + INF_MAX_RT => 83, + ADDRSEL => 84, + ADDRSEL_TABLE => 85, + V6_PCP_SERVER => 86, + DHCP4_O_DHCP6_SERVER => 88, + S46_CONT_MAPE => 94, + S46_CONT_MAPT => 95, + S46_CONT_LW => 96, + _4RD => 97, + _4RD_MAP_RULE => 98, + _4RD_NON_MAP_RULE => 99, + DHCP_Captive_Portal => 103, + MPL_PARAMETERS => 104, + S46_PRIORITY => 111, + V6_PREFIX64 => 113, + IPv6_Address_ANDSF => 143, + Unknown(n) => n, + } + } +} + +impl From for OROCode { + fn from(opt: u16) -> Self { + use OROCode::*; + match opt { + 17 => VENDOR_OPTS, + 21 => SIP_SERVER_D, + 22 => SIP_SERVER_A, + 23 => DNS_SERVERS, + 24 => DOMAIN_LIST, + 27 => NIS_SERVERS, + 28 => NISP_SERVERS, + 29 => NIS_DOMAIN_NAME, + 30 => NISP_DOMAIN_NAME, + 31 => SNTP_SERVERS, + 32 => INFORMATION_REFRESH_TIME, + 33 => BCMCS_SERVER_D, + 34 => BCMCS_SERVER_A, + 36 => GEOCONF_CIVIC, + 39 => CLIENT_FQDN, + 40 => PANA_AGENT, + 41 => NEW_POSIX_TIMEZONE, + 42 => NEW_TZDB_TIMEZONE, + 49 => MIP6_HNIDF, + 50 => MIP6_VDINF, + 51 => V6_LOST, + 52 => CAPWAP_AC_V6, + 54 => IPv6_Address_MoS, + 55 => IPv6_FQDN_MoS, + 56 => NTP_SERVER, + 57 => V6_ACCESS_DOMAIN, + 58 => SIP_UA_CS_LIST, + 59 => OPT_BOOTFILE_URL, + 60 => OPT_BOOTFILE_PARAM, + 62 => NII, + 63 => GEOLOCATION, + 64 => AFTR_NAME, + 65 => ERP_LOCAL_DOMAIN_NAME, + 67 => PD_EXCLUDE, + 69 => MIP6_IDINF, + 70 => MIP6_UDINF, + 71 => MIP6_HNP, + 72 => MIP6_HAA, + 73 => MIP6_HAF, + 74 => RDNSS_SELECTION, + 75 => KRB_PRINCIPAL_NAME, + 76 => KRB_REALM_NAME, + 77 => KRB_DEFAULT_REALM_NAME, + 78 => KRB_KDC, + 82 => SOL_MAX_RT, + 83 => INF_MAX_RT, + 84 => ADDRSEL, + 85 => ADDRSEL_TABLE, + 86 => V6_PCP_SERVER, + 88 => DHCP4_O_DHCP6_SERVER, + 94 => S46_CONT_MAPE, + 95 => S46_CONT_MAPT, + 96 => S46_CONT_LW, + 97 => _4RD, + 98 => _4RD_MAP_RULE, + 99 => _4RD_NON_MAP_RULE, + 103 => DHCP_Captive_Portal, + 104 => MPL_PARAMETERS, + 111 => S46_PRIORITY, + 113 => V6_PREFIX64, + 143 => IPv6_Address_ANDSF, + n => Unknown(n), + } + } +} From 94d947f8e5527eff7b34230b75123276320cadfe Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 1 Oct 2022 12:55:00 +1000 Subject: [PATCH 02/12] v6::OROCodes change naming to PascalCase --- src/v6/oro_codes.rs | 366 ++++++++++++++++++++++---------------------- 1 file changed, 183 insertions(+), 183 deletions(-) diff --git a/src/v6/oro_codes.rs b/src/v6/oro_codes.rs index 0f350c2..7bb14e2 100644 --- a/src/v6/oro_codes.rs +++ b/src/v6/oro_codes.rs @@ -5,70 +5,70 @@ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum OROCode { ///Optional - VENDOR_OPTS, - SIP_SERVER_D, - SIP_SERVER_A, - DNS_SERVERS, - DOMAIN_LIST, - NIS_SERVERS, - NISP_SERVERS, - NIS_DOMAIN_NAME, - NISP_DOMAIN_NAME, - SNTP_SERVERS, + VendorOpts, + SipServerD, + SipServerA, + DnsServers, + DomainList, + NisServers, + NispServers, + NisDomainName, + NispDomainName, + SntpServers, ///Required for Information-request - INFORMATION_REFRESH_TIME, - BCMCS_SERVER_D, - BCMCS_SERVER_A, - GEOCONF_CIVIC, - CLIENT_FQDN, - PANA_AGENT, - NEW_POSIX_TIMEZONE, - NEW_TZDB_TIMEZONE, - MIP6_HNIDF, - MIP6_VDINF, - V6_LOST, - CAPWAP_AC_V6, - IPv6_Address_MoS, - IPv6_FQDN_MoS, - NTP_SERVER, - V6_ACCESS_DOMAIN, - SIP_UA_CS_LIST, - OPT_BOOTFILE_URL, - OPT_BOOTFILE_PARAM, - NII, - GEOLOCATION, - AFTR_NAME, - ERP_LOCAL_DOMAIN_NAME, - PD_EXCLUDE, - MIP6_IDINF, - MIP6_UDINF, - MIP6_HNP, - MIP6_HAA, - MIP6_HAF, - RDNSS_SELECTION, - KRB_PRINCIPAL_NAME, - KRB_REALM_NAME, - KRB_DEFAULT_REALM_NAME, - KRB_KDC, + InformationRefreshTime, + BcmcsServerD, + BcmcsServerA, + GeoconfCivic, + ClientFqdn, + PanaAgent, + NewPosixTimezone, + NewTzdbTimezone, + Mip6Hnidf, + Mip6Vdinf, + V6Lost, + CapwapAcV6, + Ipv6AddressMos, + Ipv6FqdnMos, + NtpServer, + V6AccessDomain, + SipUaCsList, + OptBootfileUrl, + OptBootfileParam, + Nii, + Geolocation, + AftrName, + ErpLocalDomainName, + PdExclude, + Mip6Idinf, + Mip6Udinf, + Mip6Hnp, + Mip6Haa, + Mip6Haf, + RdnssSelection, + KrbPrincipalName, + KrbRealmName, + KrbDefaultRealmName, + KrbKdc, ///Required for Solicit - SOL_MAX_RT, + SolMaxRt, ///Required for Information-request - INF_MAX_RT, - ADDRSEL, - ADDRSEL_TABLE, - V6_PCP_SERVER, - DHCP4_O_DHCP6_SERVER, - S46_CONT_MAPE, - S46_CONT_MAPT, - S46_CONT_LW, - _4RD, - _4RD_MAP_RULE, - _4RD_NON_MAP_RULE, - DHCP_Captive_Portal, - MPL_PARAMETERS, - S46_PRIORITY, - V6_PREFIX64, - IPv6_Address_ANDSF, + InfMaxRt, + Addrsel, + AddrselTable, + V6PcpServer, + Dhcp4ODhcp6Server, + S46ContMape, + S46ContMapt, + S46ContLw, + _4rd, + _4rdMapRule, + _4rdNonMapRule, + DhcpCaptivePortal, + MplParameters, + S46Priority, + V6Prefix64, + IPv6AddressAndsf, ///Avalible for future codes. Unknown(u16), } @@ -77,67 +77,67 @@ impl From for u16 { fn from(opt: OROCode) -> Self { use OROCode::*; match opt { - VENDOR_OPTS => 17, - SIP_SERVER_D => 21, - SIP_SERVER_A => 22, - DNS_SERVERS => 23, - DOMAIN_LIST => 24, - NIS_SERVERS => 27, - NISP_SERVERS => 28, - NIS_DOMAIN_NAME => 29, - NISP_DOMAIN_NAME => 30, - SNTP_SERVERS => 31, - INFORMATION_REFRESH_TIME => 32, - BCMCS_SERVER_D => 33, - BCMCS_SERVER_A => 34, - GEOCONF_CIVIC => 36, - CLIENT_FQDN => 39, - PANA_AGENT => 40, - NEW_POSIX_TIMEZONE => 41, - NEW_TZDB_TIMEZONE => 42, - MIP6_HNIDF => 49, - MIP6_VDINF => 50, - V6_LOST => 51, - CAPWAP_AC_V6 => 52, - IPv6_Address_MoS => 54, - IPv6_FQDN_MoS => 55, - NTP_SERVER => 56, - V6_ACCESS_DOMAIN => 57, - SIP_UA_CS_LIST => 58, - OPT_BOOTFILE_URL => 59, - OPT_BOOTFILE_PARAM => 60, - NII => 62, - GEOLOCATION => 63, - AFTR_NAME => 64, - ERP_LOCAL_DOMAIN_NAME => 65, - PD_EXCLUDE => 67, - MIP6_IDINF => 69, - MIP6_UDINF => 70, - MIP6_HNP => 71, - MIP6_HAA => 72, - MIP6_HAF => 73, - RDNSS_SELECTION => 74, - KRB_PRINCIPAL_NAME => 75, - KRB_REALM_NAME => 76, - KRB_DEFAULT_REALM_NAME => 77, - KRB_KDC => 78, - SOL_MAX_RT => 82, - INF_MAX_RT => 83, - ADDRSEL => 84, - ADDRSEL_TABLE => 85, - V6_PCP_SERVER => 86, - DHCP4_O_DHCP6_SERVER => 88, - S46_CONT_MAPE => 94, - S46_CONT_MAPT => 95, - S46_CONT_LW => 96, - _4RD => 97, - _4RD_MAP_RULE => 98, - _4RD_NON_MAP_RULE => 99, - DHCP_Captive_Portal => 103, - MPL_PARAMETERS => 104, - S46_PRIORITY => 111, - V6_PREFIX64 => 113, - IPv6_Address_ANDSF => 143, + VendorOpts => 17, + SipServerD => 21, + SipServerA => 22, + DnsServers => 23, + DomainList => 24, + NisServers => 27, + NispServers => 28, + NisDomainName => 29, + NispDomainName => 30, + SntpServers => 31, + InformationRefreshTime => 32, + BcmcsServerD => 33, + BcmcsServerA => 34, + GeoconfCivic => 36, + ClientFqdn => 39, + PanaAgent => 40, + NewPosixTimezone => 41, + NewTzdbTimezone => 42, + Mip6Hnidf => 49, + Mip6Vdinf => 50, + V6Lost => 51, + CapwapAcV6 => 52, + Ipv6AddressMos => 54, + Ipv6FqdnMos => 55, + NtpServer => 56, + V6AccessDomain => 57, + SipUaCsList => 58, + OptBootfileUrl => 59, + OptBootfileParam => 60, + Nii => 62, + Geolocation => 63, + AftrName => 64, + ErpLocalDomainName => 65, + PdExclude => 67, + Mip6Idinf => 69, + Mip6Udinf => 70, + Mip6Hnp => 71, + Mip6Haa => 72, + Mip6Haf => 73, + RdnssSelection => 74, + KrbPrincipalName => 75, + KrbRealmName => 76, + KrbDefaultRealmName => 77, + KrbKdc => 78, + SolMaxRt => 82, + InfMaxRt => 83, + Addrsel => 84, + AddrselTable => 85, + V6PcpServer => 86, + Dhcp4ODhcp6Server => 88, + S46ContMape => 94, + S46ContMapt => 95, + S46ContLw => 96, + _4rd => 97, + _4rdMapRule => 98, + _4rdNonMapRule => 99, + DhcpCaptivePortal => 103, + MplParameters => 104, + S46Priority => 111, + V6Prefix64 => 113, + IPv6AddressAndsf => 143, Unknown(n) => n, } } @@ -147,67 +147,67 @@ impl From for OROCode { fn from(opt: u16) -> Self { use OROCode::*; match opt { - 17 => VENDOR_OPTS, - 21 => SIP_SERVER_D, - 22 => SIP_SERVER_A, - 23 => DNS_SERVERS, - 24 => DOMAIN_LIST, - 27 => NIS_SERVERS, - 28 => NISP_SERVERS, - 29 => NIS_DOMAIN_NAME, - 30 => NISP_DOMAIN_NAME, - 31 => SNTP_SERVERS, - 32 => INFORMATION_REFRESH_TIME, - 33 => BCMCS_SERVER_D, - 34 => BCMCS_SERVER_A, - 36 => GEOCONF_CIVIC, - 39 => CLIENT_FQDN, - 40 => PANA_AGENT, - 41 => NEW_POSIX_TIMEZONE, - 42 => NEW_TZDB_TIMEZONE, - 49 => MIP6_HNIDF, - 50 => MIP6_VDINF, - 51 => V6_LOST, - 52 => CAPWAP_AC_V6, - 54 => IPv6_Address_MoS, - 55 => IPv6_FQDN_MoS, - 56 => NTP_SERVER, - 57 => V6_ACCESS_DOMAIN, - 58 => SIP_UA_CS_LIST, - 59 => OPT_BOOTFILE_URL, - 60 => OPT_BOOTFILE_PARAM, - 62 => NII, - 63 => GEOLOCATION, - 64 => AFTR_NAME, - 65 => ERP_LOCAL_DOMAIN_NAME, - 67 => PD_EXCLUDE, - 69 => MIP6_IDINF, - 70 => MIP6_UDINF, - 71 => MIP6_HNP, - 72 => MIP6_HAA, - 73 => MIP6_HAF, - 74 => RDNSS_SELECTION, - 75 => KRB_PRINCIPAL_NAME, - 76 => KRB_REALM_NAME, - 77 => KRB_DEFAULT_REALM_NAME, - 78 => KRB_KDC, - 82 => SOL_MAX_RT, - 83 => INF_MAX_RT, - 84 => ADDRSEL, - 85 => ADDRSEL_TABLE, - 86 => V6_PCP_SERVER, - 88 => DHCP4_O_DHCP6_SERVER, - 94 => S46_CONT_MAPE, - 95 => S46_CONT_MAPT, - 96 => S46_CONT_LW, - 97 => _4RD, - 98 => _4RD_MAP_RULE, - 99 => _4RD_NON_MAP_RULE, - 103 => DHCP_Captive_Portal, - 104 => MPL_PARAMETERS, - 111 => S46_PRIORITY, - 113 => V6_PREFIX64, - 143 => IPv6_Address_ANDSF, + 17 => VendorOpts, + 21 => SipServerD, + 22 => SipServerA, + 23 => DnsServers, + 24 => DomainList, + 27 => NisServers, + 28 => NispServers, + 29 => NisDomainName, + 30 => NispDomainName, + 31 => SntpServers, + 32 => InformationRefreshTime, + 33 => BcmcsServerD, + 34 => BcmcsServerA, + 36 => GeoconfCivic, + 39 => ClientFqdn, + 40 => PanaAgent, + 41 => NewPosixTimezone, + 42 => NewTzdbTimezone, + 49 => Mip6Hnidf, + 50 => Mip6Vdinf, + 51 => V6Lost, + 52 => CapwapAcV6, + 54 => Ipv6AddressMos, + 55 => Ipv6FqdnMos, + 56 => NtpServer, + 57 => V6AccessDomain, + 58 => SipUaCsList, + 59 => OptBootfileUrl, + 60 => OptBootfileParam, + 62 => Nii, + 63 => Geolocation, + 64 => AftrName, + 65 => ErpLocalDomainName, + 67 => PdExclude, + 69 => Mip6Idinf, + 70 => Mip6Udinf, + 71 => Mip6Hnp, + 72 => Mip6Haa, + 73 => Mip6Haf, + 74 => RdnssSelection, + 75 => KrbPrincipalName, + 76 => KrbRealmName, + 77 => KrbDefaultRealmName, + 78 => KrbKdc, + 82 => SolMaxRt, + 83 => InfMaxRt, + 84 => Addrsel, + 85 => AddrselTable, + 86 => V6PcpServer, + 88 => Dhcp4ODhcp6Server, + 94 => S46ContMape, + 95 => S46ContMapt, + 96 => S46ContLw, + 97 => _4rd, + 98 => _4rdMapRule, + 99 => _4rdNonMapRule, + 103 => DhcpCaptivePortal, + 104 => MplParameters, + 111 => S46Priority, + 113 => V6Prefix64, + 143 => IPv6AddressAndsf, n => Unknown(n), } } From c800484a75dda3c3c9ae84cdf19155741e214897 Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 1 Oct 2022 13:01:31 +1000 Subject: [PATCH 03/12] fix compiling with serde feature --- src/v6/oro_codes.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/v6/oro_codes.rs b/src/v6/oro_codes.rs index 7bb14e2..5cff8b3 100644 --- a/src/v6/oro_codes.rs +++ b/src/v6/oro_codes.rs @@ -1,6 +1,8 @@ ///Valid Option Codes for ORO ///https://datatracker.ietf.org/doc/html/rfc8415#section-24 -#[allow(non_camel_case_types)] + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum OROCode { From c76823d9111b155b45c04ea7ff29cc393edfa13b Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 9 Oct 2022 13:36:37 +1000 Subject: [PATCH 04/12] Seperate option codes into its own file. --- src/v6/mod.rs | 2 + src/v6/option_codes.rs | 167 ++++++++++++++++++++++++++++++++++++++++ src/v6/options.rs | 170 ++--------------------------------------- 3 files changed, 176 insertions(+), 163 deletions(-) create mode 100644 src/v6/option_codes.rs diff --git a/src/v6/mod.rs b/src/v6/mod.rs index df0ed3b..aeaf55c 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -54,6 +54,7 @@ //! mod options; mod oro_codes; +mod option_codes; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -63,6 +64,7 @@ use std::{convert::TryInto, fmt, net::Ipv6Addr}; // re-export submodules from proto::msg pub use self::options::*; pub use self::oro_codes::*; +pub use self::option_codes::*; pub use crate::{ decoder::{Decodable, Decoder}, diff --git a/src/v6/option_codes.rs b/src/v6/option_codes.rs new file mode 100644 index 0000000..225302e --- /dev/null +++ b/src/v6/option_codes.rs @@ -0,0 +1,167 @@ +use crate::v6::DhcpOption; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// option code type +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum OptionCode { + /// 1 + ClientId, // should duid for this be bytes or string? + /// 2 + ServerId, + /// 3 + IANA, + /// 4 + IATA, + /// 5 + IAAddr, + /// 6 + ORO, + /// 7 + Preference, + /// 8 + ElapsedTime, + /// 9 + RelayMsg, + /// 11 + Authentication, + /// 12 + ServerUnicast, + /// 13 + StatusCode, + /// 14 + RapidCommit, + /// 15 + UserClass, + /// 16 + VendorClass, + /// 17 + VendorOpts, + /// 18 + InterfaceId, + /// 19 + ReconfMsg, + /// 20 + ReconfAccept, + /// 23 + DNSNameServer, + /// 24 + DomainSearchList, + /// 25 + IAPD, + /// 26 + IAPDPrefix, + /// an unknown or unimplemented option type + Unknown(u16), +} + +impl PartialOrd for OptionCode { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for OptionCode { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + u16::from(*self).cmp(&u16::from(*other)) + } +} + +impl From for u16 { + fn from(opt: OptionCode) -> Self { + use OptionCode::*; + match opt { + ClientId => 1, + ServerId => 2, + IANA => 3, + IATA => 4, + IAAddr => 5, + ORO => 6, + Preference => 7, + ElapsedTime => 8, + RelayMsg => 9, + Authentication => 11, + ServerUnicast => 12, + StatusCode => 13, + RapidCommit => 14, + UserClass => 15, + VendorClass => 16, + VendorOpts => 17, + InterfaceId => 18, + ReconfMsg => 19, + ReconfAccept => 20, + DNSNameServer => 23, + DomainSearchList => 24, + IAPD => 25, + IAPDPrefix => 26, + Unknown(n) => n, + } + } +} + +impl From for OptionCode { + fn from(n: u16) -> Self { + use OptionCode::*; + match n { + 1 => ClientId, + 2 => ServerId, + 3 => IANA, + 4 => IATA, + 5 => IAAddr, + 6 => ORO, + 7 => Preference, + 8 => ElapsedTime, + 9 => RelayMsg, + 11 => Authentication, + 12 => ServerUnicast, + 13 => StatusCode, + 14 => RapidCommit, + 15 => UserClass, + 16 => VendorClass, + 17 => VendorOpts, + 18 => InterfaceId, + 19 => ReconfMsg, + 20 => ReconfAccept, + 23 => DNSNameServer, + 24 => DomainSearchList, + 25 => IAPD, + 26 => IAPDPrefix, + _ => Unknown(n), + } + } +} + +impl From<&DhcpOption> for OptionCode { + fn from(opt: &DhcpOption) -> Self { + use DhcpOption::*; + match opt { + ClientId(_) => OptionCode::ClientId, + ServerId(_) => OptionCode::ServerId, + IANA(_) => OptionCode::IANA, + IATA(_) => OptionCode::IATA, + IAAddr(_) => OptionCode::IAAddr, + ORO(_) => OptionCode::ORO, + Preference(_) => OptionCode::Preference, + ElapsedTime(_) => OptionCode::ElapsedTime, + RelayMsg(_) => OptionCode::RelayMsg, + Authentication(_) => OptionCode::Authentication, + ServerUnicast(_) => OptionCode::ServerUnicast, + StatusCode(_) => OptionCode::StatusCode, + RapidCommit => OptionCode::RapidCommit, + UserClass(_) => OptionCode::UserClass, + VendorClass(_) => OptionCode::VendorClass, + VendorOpts(_) => OptionCode::VendorOpts, + InterfaceId(_) => OptionCode::InterfaceId, + ReconfMsg(_) => OptionCode::ReconfMsg, + ReconfAccept => OptionCode::ReconfAccept, + DNSNameServer(_) => OptionCode::DNSNameServer, + DomainSearchList(_) => OptionCode::DomainSearchList, + IAPD(_) => OptionCode::IAPD, + IAPDPrefix(_) => OptionCode::IAPDPrefix, + Unknown(unknown) => unknown.into(), + } + } +} + diff --git a/src/v6/options.rs b/src/v6/options.rs index 2a48ab0..eb0f245 100644 --- a/src/v6/options.rs +++ b/src/v6/options.rs @@ -13,7 +13,7 @@ use crate::{ encoder::{Encodable, Encoder}, error::{DecodeResult, EncodeResult}, v4::HType, - v6::{MessageType, OROCode, RelayMessage}, + v6::{MessageType, OROCode, OptionCode, RelayMessage}, }; // server can send multiple IA_NA options to request multiple addresses @@ -577,6 +577,12 @@ impl UnknownOption { } } +impl From<&UnknownOption> for OptionCode{ + fn from(opt: &UnknownOption) -> Self{ + opt.code.into() + } +} + impl Decodable for DhcpOptions { fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { let mut opts = Vec::new(); @@ -864,168 +870,6 @@ impl Encodable for DhcpOption { } } -/// option code type -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum OptionCode { - /// 1 - ClientId, // should duid for this be bytes or string? - /// 2 - ServerId, - /// 3 - IANA, - /// 4 - IATA, - /// 5 - IAAddr, - /// 6 - ORO, - /// 7 - Preference, - /// 8 - ElapsedTime, - /// 9 - RelayMsg, - /// 11 - Authentication, - /// 12 - ServerUnicast, - /// 13 - StatusCode, - /// 14 - RapidCommit, - /// 15 - UserClass, - /// 16 - VendorClass, - /// 17 - VendorOpts, - /// 18 - InterfaceId, - /// 19 - ReconfMsg, - /// 20 - ReconfAccept, - /// 23 - DNSNameServer, - /// 24 - DomainSearchList, - /// 25 - IAPD, - /// 26 - IAPDPrefix, - /// an unknown or unimplemented option type - Unknown(u16), -} - -impl PartialOrd for OptionCode { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for OptionCode { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - u16::from(*self).cmp(&u16::from(*other)) - } -} - -impl From for u16 { - fn from(opt: OptionCode) -> Self { - use OptionCode::*; - match opt { - ClientId => 1, - ServerId => 2, - IANA => 3, - IATA => 4, - IAAddr => 5, - ORO => 6, - Preference => 7, - ElapsedTime => 8, - RelayMsg => 9, - Authentication => 11, - ServerUnicast => 12, - StatusCode => 13, - RapidCommit => 14, - UserClass => 15, - VendorClass => 16, - VendorOpts => 17, - InterfaceId => 18, - ReconfMsg => 19, - ReconfAccept => 20, - DNSNameServer => 23, - DomainSearchList => 24, - IAPD => 25, - IAPDPrefix => 26, - Unknown(n) => n, - } - } -} - -impl From for OptionCode { - fn from(n: u16) -> Self { - use OptionCode::*; - match n { - 1 => ClientId, - 2 => ServerId, - 3 => IANA, - 4 => IATA, - 5 => IAAddr, - 6 => ORO, - 7 => Preference, - 8 => ElapsedTime, - 9 => RelayMsg, - 11 => Authentication, - 12 => ServerUnicast, - 13 => StatusCode, - 14 => RapidCommit, - 15 => UserClass, - 16 => VendorClass, - 17 => VendorOpts, - 18 => InterfaceId, - 19 => ReconfMsg, - 20 => ReconfAccept, - 23 => DNSNameServer, - 24 => DomainSearchList, - 25 => IAPD, - 26 => IAPDPrefix, - _ => Unknown(n), - } - } -} - -impl From<&DhcpOption> for OptionCode { - fn from(opt: &DhcpOption) -> Self { - use DhcpOption::*; - match opt { - ClientId(_) => OptionCode::ClientId, - ServerId(_) => OptionCode::ServerId, - IANA(_) => OptionCode::IANA, - IATA(_) => OptionCode::IATA, - IAAddr(_) => OptionCode::IAAddr, - ORO(_) => OptionCode::ORO, - Preference(_) => OptionCode::Preference, - ElapsedTime(_) => OptionCode::ElapsedTime, - RelayMsg(_) => OptionCode::RelayMsg, - Authentication(_) => OptionCode::Authentication, - ServerUnicast(_) => OptionCode::ServerUnicast, - StatusCode(_) => OptionCode::StatusCode, - RapidCommit => OptionCode::RapidCommit, - UserClass(_) => OptionCode::UserClass, - VendorClass(_) => OptionCode::VendorClass, - VendorOpts(_) => OptionCode::VendorOpts, - InterfaceId(_) => OptionCode::InterfaceId, - ReconfMsg(_) => OptionCode::ReconfMsg, - ReconfAccept => OptionCode::ReconfAccept, - DNSNameServer(_) => OptionCode::DNSNameServer, - DomainSearchList(_) => OptionCode::DomainSearchList, - IAPD(_) => OptionCode::IAPD, - IAPDPrefix(_) => OptionCode::IAPDPrefix, - Unknown(UnknownOption { code, .. }) => OptionCode::Unknown(*code), - } - } -} - #[inline] fn first(arr: &[T], f: F) -> Option where From b9f423f17cc1e878454f92cfe2a6e11838ad6b11 Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 9 Oct 2022 17:52:44 +1000 Subject: [PATCH 05/12] add missing option codes and rename previously misnamed codes --- src/v6/option_codes.rs | 424 +++++++++++++++++++++++++++++++++++------ src/v6/options.rs | 54 +++--- 2 files changed, 395 insertions(+), 83 deletions(-) diff --git a/src/v6/option_codes.rs b/src/v6/option_codes.rs index 225302e..c14e9b9 100644 --- a/src/v6/option_codes.rs +++ b/src/v6/option_codes.rs @@ -7,68 +7,143 @@ use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum OptionCode { - /// 1 - ClientId, // should duid for this be bytes or string? - /// 2 + ClientId, ServerId, - /// 3 IANA, - /// 4 IATA, - /// 5 IAAddr, - /// 6 ORO, - /// 7 Preference, - /// 8 ElapsedTime, - /// 9 RelayMsg, - /// 11 - Authentication, - /// 12 - ServerUnicast, - /// 13 + Auth, + Unicast, StatusCode, - /// 14 RapidCommit, - /// 15 UserClass, - /// 16 VendorClass, - /// 17 VendorOpts, - /// 18 InterfaceId, - /// 19 ReconfMsg, - /// 20 ReconfAccept, - /// 23 - DNSNameServer, - /// 24 - DomainSearchList, - /// 25 + SipServerD, + SipServerA, + DNSServers, + DomainList, IAPD, - /// 26 - IAPDPrefix, - /// an unknown or unimplemented option type + IAPrefix, + NisServers, + NispServers, + NisDomainName, + NispDomainName, + SntpServers, + InformationRefreshTime, + BcmcsServerD, + BcmcsServerA, + GeoconfCivic, + RemoteId, + SubscriberId, + ClientFqdn, + PanaAgent, + NewPosixTimezone, + NewTzdbTimezone, + ERO, + LqQuery, + ClientData, + CltTime, + LqRelayData, + LqClientLink, + Mip6Hnidf, + Mip6Vdinf, + V6Lost, + CapwapAcV6, + RelayId, + Ipv6AddressMoS, + Ipv6FQDNMoS, + NtpServer, + V6AccessDomain, + SipUaCsList, + OptBootfileUrl, + OptBootfileParam, + ClientArchType, + Nii, + Geolocation, + AftrName, + ErpLocalDomainName, + Rsoo, + PdExclude, + Vss, + Mip6Idinf, + Mip6Udinf, + Mip6Hnp, + Mip6Haa, + Mip6Haf, + RdnssSelection, + KrbPrincipalName, + KrbRealmName, + KrbDefaultRealmName, + KrbKdc, + ClientLinklayerAddr, + LinkAddress, + Radius, + SolMaxRt, + InfMaxRt, + Addrsel, + AddrselTable, + V6PcpServer, + Dhcpv4Msg, + Dhcp4ODhcp6Server, + S46Rule, + S46Br, + S46Dmr, + S46V4v6bind, + S46Portparams, + S46ContMape, + S46ContMapt, + S46ContLw, + _4Rd, + _4RdMapRule, + _4RdNonMapRule, + LqBaseTime, + LqStartTime, + LqEndTime, + DhcpCaptivePortal, + MplParameters, + AniAtt, + AniNetworkName, + AniApName, + AniApBssid, + AniOperatorId, + AniOperatorRealm, + S46Priority, + MudUrlV6, + V6Prefix64, + FBindingStatus, + FConnectFlags, + Fdnsremovalinfo, + FDNSHostName, + FDNSZoneName, + Fdnsflags, + Fexpirationtime, + FMaxUnackedBndupd, + FMclt, + FPartnerLifetime, + FPartnerLifetimeSent, + FPartnerDownTime, + FPartnerRawCltTime, + FProtocolVersion, + FKeepaliveTime, + FReconfigureData, + FRelationshipName, + FServerFlags, + FServerState, + FStartTimeOfState, + FStateExpirationTime, + RelayPort, + Ipv6AddressANDSF, Unknown(u16), } -impl PartialOrd for OptionCode { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for OptionCode { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - u16::from(*self).cmp(&u16::from(*other)) - } -} - impl From for u16 { fn from(opt: OptionCode) -> Self { use OptionCode::*; @@ -82,8 +157,8 @@ impl From for u16 { Preference => 7, ElapsedTime => 8, RelayMsg => 9, - Authentication => 11, - ServerUnicast => 12, + Auth => 11, + Unicast => 12, StatusCode => 13, RapidCommit => 14, UserClass => 15, @@ -92,10 +167,121 @@ impl From for u16 { InterfaceId => 18, ReconfMsg => 19, ReconfAccept => 20, - DNSNameServer => 23, - DomainSearchList => 24, + SipServerD => 21, + SipServerA => 22, + DNSServers => 23, + DomainList => 24, IAPD => 25, - IAPDPrefix => 26, + IAPrefix => 26, + NisServers => 27, + NispServers => 28, + NisDomainName => 29, + NispDomainName => 30, + SntpServers => 31, + InformationRefreshTime => 32, + BcmcsServerD => 33, + BcmcsServerA => 34, + GeoconfCivic => 36, + RemoteId => 37, + SubscriberId => 38, + ClientFqdn => 39, + PanaAgent => 40, + NewPosixTimezone => 41, + NewTzdbTimezone => 42, + ERO => 43, + LqQuery => 44, + ClientData => 45, + CltTime => 46, + LqRelayData => 47, + LqClientLink => 48, + Mip6Hnidf => 49, + Mip6Vdinf => 50, + V6Lost => 51, + CapwapAcV6 => 52, + RelayId => 53, + Ipv6AddressMoS => 54, + Ipv6FQDNMoS => 55, + NtpServer => 56, + V6AccessDomain => 57, + SipUaCsList => 58, + OptBootfileUrl => 59, + OptBootfileParam => 60, + ClientArchType => 61, + Nii => 62, + Geolocation => 63, + AftrName => 64, + ErpLocalDomainName => 65, + Rsoo => 66, + PdExclude => 67, + Vss => 68, + Mip6Idinf => 69, + Mip6Udinf => 70, + Mip6Hnp => 71, + Mip6Haa => 72, + Mip6Haf => 73, + RdnssSelection => 74, + KrbPrincipalName => 75, + KrbRealmName => 76, + KrbDefaultRealmName => 77, + KrbKdc => 78, + ClientLinklayerAddr => 79, + LinkAddress => 80, + Radius => 81, + SolMaxRt => 82, + InfMaxRt => 83, + Addrsel => 84, + AddrselTable => 85, + V6PcpServer => 86, + Dhcpv4Msg => 87, + Dhcp4ODhcp6Server => 88, + S46Rule => 89, + S46Br => 90, + S46Dmr => 91, + S46V4v6bind => 92, + S46Portparams => 93, + S46ContMape => 94, + S46ContMapt => 95, + S46ContLw => 96, + _4Rd => 97, + _4RdMapRule => 98, + _4RdNonMapRule => 99, + LqBaseTime => 100, + LqStartTime => 101, + LqEndTime => 102, + DhcpCaptivePortal => 103, + MplParameters => 104, + AniAtt => 105, + AniNetworkName => 106, + AniApName => 107, + AniApBssid => 108, + AniOperatorId => 109, + AniOperatorRealm => 110, + S46Priority => 111, + MudUrlV6 => 112, + V6Prefix64 => 113, + FBindingStatus => 114, + FConnectFlags => 115, + Fdnsremovalinfo => 116, + FDNSHostName => 117, + FDNSZoneName => 118, + Fdnsflags => 119, + Fexpirationtime => 120, + FMaxUnackedBndupd => 121, + FMclt => 122, + FPartnerLifetime => 123, + FPartnerLifetimeSent => 124, + FPartnerDownTime => 125, + FPartnerRawCltTime => 126, + FProtocolVersion => 127, + FKeepaliveTime => 128, + FReconfigureData => 129, + FRelationshipName => 130, + FServerFlags => 131, + FServerState => 132, + FStartTimeOfState => 133, + FStateExpirationTime => 134, + RelayPort => 135, + Ipv6AddressANDSF => 143, Unknown(n) => n, } } @@ -114,8 +300,8 @@ impl From for OptionCode { 7 => Preference, 8 => ElapsedTime, 9 => RelayMsg, - 11 => Authentication, - 12 => ServerUnicast, + 11 => Auth, + 12 => Unicast, 13 => StatusCode, 14 => RapidCommit, 15 => UserClass, @@ -124,15 +310,138 @@ impl From for OptionCode { 18 => InterfaceId, 19 => ReconfMsg, 20 => ReconfAccept, - 23 => DNSNameServer, - 24 => DomainSearchList, + 21 => SipServerD, + 22 => SipServerA, + 23 => DNSServers, + 24 => DomainList, 25 => IAPD, - 26 => IAPDPrefix, + 26 => IAPrefix, + 27 => NisServers, + 28 => NispServers, + 29 => NisDomainName, + 30 => NispDomainName, + 31 => SntpServers, + 32 => InformationRefreshTime, + 33 => BcmcsServerD, + 34 => BcmcsServerA, + 36 => GeoconfCivic, + 37 => RemoteId, + 38 => SubscriberId, + 39 => ClientFqdn, + 40 => PanaAgent, + 41 => NewPosixTimezone, + 42 => NewTzdbTimezone, + 43 => ERO, + 44 => LqQuery, + 45 => ClientData, + 46 => CltTime, + 47 => LqRelayData, + 48 => LqClientLink, + 49 => Mip6Hnidf, + 50 => Mip6Vdinf, + 51 => V6Lost, + 52 => CapwapAcV6, + 53 => RelayId, + 54 => Ipv6AddressMoS, + 55 => Ipv6FQDNMoS, + 56 => NtpServer, + 57 => V6AccessDomain, + 58 => SipUaCsList, + 59 => OptBootfileUrl, + 60 => OptBootfileParam, + 61 => ClientArchType, + 62 => Nii, + 63 => Geolocation, + 64 => AftrName, + 65 => ErpLocalDomainName, + 66 => Rsoo, + 67 => PdExclude, + 68 => Vss, + 69 => Mip6Idinf, + 70 => Mip6Udinf, + 71 => Mip6Hnp, + 72 => Mip6Haa, + 73 => Mip6Haf, + 74 => RdnssSelection, + 75 => KrbPrincipalName, + 76 => KrbRealmName, + 77 => KrbDefaultRealmName, + 78 => KrbKdc, + 79 => ClientLinklayerAddr, + 80 => LinkAddress, + 81 => Radius, + 82 => SolMaxRt, + 83 => InfMaxRt, + 84 => Addrsel, + 85 => AddrselTable, + 86 => V6PcpServer, + 87 => Dhcpv4Msg, + 88 => Dhcp4ODhcp6Server, + 89 => S46Rule, + 90 => S46Br, + 91 => S46Dmr, + 92 => S46V4v6bind, + 93 => S46Portparams, + 94 => S46ContMape, + 95 => S46ContMapt, + 96 => S46ContLw, + 97 => _4Rd, + 98 => _4RdMapRule, + 99 => _4RdNonMapRule, + 100 => LqBaseTime, + 101 => LqStartTime, + 102 => LqEndTime, + 103 => DhcpCaptivePortal, + 104 => MplParameters, + 105 => AniAtt, + 106 => AniNetworkName, + 107 => AniApName, + 108 => AniApBssid, + 109 => AniOperatorId, + 110 => AniOperatorRealm, + 111 => S46Priority, + 112 => MudUrlV6, + 113 => V6Prefix64, + 114 => FBindingStatus, + 115 => FConnectFlags, + 116 => Fdnsremovalinfo, + 117 => FDNSHostName, + 118 => FDNSZoneName, + 119 => Fdnsflags, + 120 => Fexpirationtime, + 121 => FMaxUnackedBndupd, + 122 => FMclt, + 123 => FPartnerLifetime, + 124 => FPartnerLifetimeSent, + 125 => FPartnerDownTime, + 126 => FPartnerRawCltTime, + 127 => FProtocolVersion, + 128 => FKeepaliveTime, + 129 => FReconfigureData, + 130 => FRelationshipName, + 131 => FServerFlags, + 132 => FServerState, + 133 => FStartTimeOfState, + 134 => FStateExpirationTime, + 135 => RelayPort, + 143 => Ipv6AddressANDSF, _ => Unknown(n), } } } +impl PartialOrd for OptionCode { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for OptionCode { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + u16::from(*self).cmp(&u16::from(*other)) + } +} + impl From<&DhcpOption> for OptionCode { fn from(opt: &DhcpOption) -> Self { use DhcpOption::*; @@ -146,8 +455,8 @@ impl From<&DhcpOption> for OptionCode { Preference(_) => OptionCode::Preference, ElapsedTime(_) => OptionCode::ElapsedTime, RelayMsg(_) => OptionCode::RelayMsg, - Authentication(_) => OptionCode::Authentication, - ServerUnicast(_) => OptionCode::ServerUnicast, + Auth(_) => OptionCode::Auth, + Unicast(_) => OptionCode::Unicast, StatusCode(_) => OptionCode::StatusCode, RapidCommit => OptionCode::RapidCommit, UserClass(_) => OptionCode::UserClass, @@ -156,12 +465,11 @@ impl From<&DhcpOption> for OptionCode { InterfaceId(_) => OptionCode::InterfaceId, ReconfMsg(_) => OptionCode::ReconfMsg, ReconfAccept => OptionCode::ReconfAccept, - DNSNameServer(_) => OptionCode::DNSNameServer, - DomainSearchList(_) => OptionCode::DomainSearchList, + DNSServers(_) => OptionCode::DNSServers, + DomainList(_) => OptionCode::DomainList, IAPD(_) => OptionCode::IAPD, - IAPDPrefix(_) => OptionCode::IAPDPrefix, + IAPrefix(_) => OptionCode::IAPrefix, Unknown(unknown) => unknown.into(), } } } - diff --git a/src/v6/options.rs b/src/v6/options.rs index eb0f245..d2b6124 100644 --- a/src/v6/options.rs +++ b/src/v6/options.rs @@ -194,9 +194,9 @@ pub enum DhcpOption { /// 9 - RelayMsg(RelayMessage), /// 11 - - Authentication(Authentication), + Auth(Auth), /// 12 - - ServerUnicast(Ipv6Addr), + Unicast(Ipv6Addr), /// 13 - StatusCode(StatusCode), /// 14 - @@ -214,13 +214,13 @@ pub enum DhcpOption { /// 20 - ReconfAccept, /// 23 - - DNSNameServer(Vec), + DNSServers(Vec), /// 24 - - DomainSearchList(Vec), + DomainList(Vec), /// 25 - IAPD(IAPD), /// 26 - - IAPDPrefix(IAPDPrefix), + IAPrefix(IAPrefix), /// An unknown or unimplemented option type Unknown(UnknownOption), } @@ -386,10 +386,10 @@ impl From for u16 { } } -/// Authentication +/// Auth #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Authentication { +pub struct Auth { pub proto: u8, pub algo: u8, pub rdm: u8, @@ -398,10 +398,10 @@ pub struct Authentication { pub info: Vec, } -impl Decodable for Authentication { +impl Decodable for Auth { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { let len = decoder.buffer().len(); - Ok(Authentication { + Ok(Auth { proto: decoder.read_u8()?, algo: decoder.read_u8()?, rdm: decoder.read_u8()?, @@ -503,7 +503,7 @@ impl Decodable for IAPD { /// Identity Association Prefix Delegation Prefix Option #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] -pub struct IAPDPrefix { +pub struct IAPrefix { pub preferred_lifetime: u32, pub valid_lifetime: u32, pub prefix_len: u8, @@ -512,9 +512,9 @@ pub struct IAPDPrefix { pub opts: DhcpOptions, } -impl Decodable for IAPDPrefix { +impl Decodable for IAPrefix { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - Ok(IAPDPrefix { + Ok(IAPrefix { preferred_lifetime: decoder.read_u32()?, valid_lifetime: decoder.read_u32()?, prefix_len: decoder.read_u8()?, @@ -630,11 +630,11 @@ impl Decodable for DhcpOption { let mut relay_dec = Decoder::new(decoder.read_slice(len)?); DhcpOption::RelayMsg(RelayMessage::decode(&mut relay_dec)?) } - OptionCode::Authentication => { + OptionCode::Auth => { let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::Authentication(Authentication::decode(&mut dec)?) + DhcpOption::Auth(Auth::decode(&mut dec)?) } - OptionCode::ServerUnicast => DhcpOption::ServerUnicast(decoder.read::<16>()?.into()), + OptionCode::Unicast => DhcpOption::Unicast(decoder.read::<16>()?.into()), OptionCode::StatusCode => DhcpOption::StatusCode(StatusCode { status: decoder.read_u16()?.into(), msg: decoder.read_string(len - 1)?, @@ -664,28 +664,32 @@ impl Decodable for DhcpOption { OptionCode::InterfaceId => DhcpOption::InterfaceId(decoder.read_slice(len)?.to_vec()), OptionCode::ReconfMsg => DhcpOption::ReconfMsg(decoder.read_u8()?.into()), OptionCode::ReconfAccept => DhcpOption::ReconfAccept, - OptionCode::DNSNameServer => DhcpOption::DNSNameServer(decoder.read_ipv6s(len)?), + OptionCode::DNSServers => DhcpOption::DNSServers(decoder.read_ipv6s(len)?), OptionCode::IAPD => { let mut dec = Decoder::new(decoder.read_slice(len)?); DhcpOption::IAPD(IAPD::decode(&mut dec)?) } - OptionCode::IAPDPrefix => { + OptionCode::IAPrefix => { let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::IAPDPrefix(IAPDPrefix::decode(&mut dec)?) + DhcpOption::IAPrefix(IAPrefix::decode(&mut dec)?) } - OptionCode::DomainSearchList => { + OptionCode::DomainList => { let mut name_decoder = BinDecoder::new(decoder.read_slice(len as usize)?); let mut names = Vec::new(); while let Ok(name) = Name::read(&mut name_decoder) { names.push(Domain(name)); } - DhcpOption::DomainSearchList(names) + DhcpOption::DomainList(names) } // not yet implemented OptionCode::Unknown(code) => DhcpOption::Unknown(UnknownOption { code, data: decoder.read_slice(len)?.to_vec(), + }), + unimplemented => DhcpOption::Unknown(UnknownOption { + code: unimplemented.into(), + data: decoder.read_slice(len)?.to_vec(), }), }) } @@ -766,7 +770,7 @@ impl Encodable for DhcpOption { e.write_u16(buf.len() as u16)?; e.write_slice(&buf)?; } - DhcpOption::Authentication(Authentication { + DhcpOption::Auth(Auth { proto, algo, rdm, @@ -780,7 +784,7 @@ impl Encodable for DhcpOption { e.write_u64(*replay_detection)?; e.write_slice(info)?; } - DhcpOption::ServerUnicast(addr) => { + DhcpOption::Unicast(addr) => { e.write_u16(16)?; e.write_u128((*addr).into())?; } @@ -827,13 +831,13 @@ impl Encodable for DhcpOption { DhcpOption::ReconfAccept => { e.write_u16(0)?; } - DhcpOption::DNSNameServer(addrs) => { + DhcpOption::DNSServers(addrs) => { e.write_u16(addrs.len() as u16 * 16)?; for addr in addrs { e.write_u128((*addr).into())?; } } - DhcpOption::DomainSearchList(names) => { + DhcpOption::DomainList(names) => { let mut buf = Vec::new(); let mut name_encoder = BinEncoder::new(&mut buf); for name in names { @@ -842,7 +846,7 @@ impl Encodable for DhcpOption { e.write_u16(buf.len() as u16)?; e.write_slice(&buf)?; } - DhcpOption::IAPDPrefix(IAPDPrefix { + DhcpOption::IAPrefix(IAPrefix { preferred_lifetime, valid_lifetime, prefix_len, From b6e69448d52fff28fe25523f71c4a2258eea485f Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 9 Oct 2022 19:18:48 +1000 Subject: [PATCH 06/12] V6: define OROCode as OptionCode and change some nameing --- src/v6/oro_codes.rs | 287 +++++++++++++++++++++++--------------------- 1 file changed, 152 insertions(+), 135 deletions(-) diff --git a/src/v6/oro_codes.rs b/src/v6/oro_codes.rs index 5cff8b3..d0cc9f9 100644 --- a/src/v6/oro_codes.rs +++ b/src/v6/oro_codes.rs @@ -3,6 +3,8 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; + +use crate::v6::OptionCode; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum OROCode { @@ -10,7 +12,7 @@ pub enum OROCode { VendorOpts, SipServerD, SipServerA, - DnsServers, + DNSServers, DomainList, NisServers, NispServers, @@ -30,8 +32,8 @@ pub enum OROCode { Mip6Vdinf, V6Lost, CapwapAcV6, - Ipv6AddressMos, - Ipv6FqdnMos, + Ipv6AddressMoS, + Ipv6FQDNMoS, NtpServer, V6AccessDomain, SipUaCsList, @@ -63,154 +65,169 @@ pub enum OROCode { S46ContMape, S46ContMapt, S46ContLw, - _4rd, - _4rdMapRule, - _4rdNonMapRule, + _4Rd, + _4RdMapRule, + _4RdNonMapRule, DhcpCaptivePortal, MplParameters, S46Priority, V6Prefix64, - IPv6AddressAndsf, + Ipv6AddressANDSF, ///Avalible for future codes. Unknown(u16), } impl From for u16 { fn from(opt: OROCode) -> Self { - use OROCode::*; - match opt { - VendorOpts => 17, - SipServerD => 21, - SipServerA => 22, - DnsServers => 23, - DomainList => 24, - NisServers => 27, - NispServers => 28, - NisDomainName => 29, - NispDomainName => 30, - SntpServers => 31, - InformationRefreshTime => 32, - BcmcsServerD => 33, - BcmcsServerA => 34, - GeoconfCivic => 36, - ClientFqdn => 39, - PanaAgent => 40, - NewPosixTimezone => 41, - NewTzdbTimezone => 42, - Mip6Hnidf => 49, - Mip6Vdinf => 50, - V6Lost => 51, - CapwapAcV6 => 52, - Ipv6AddressMos => 54, - Ipv6FqdnMos => 55, - NtpServer => 56, - V6AccessDomain => 57, - SipUaCsList => 58, - OptBootfileUrl => 59, - OptBootfileParam => 60, - Nii => 62, - Geolocation => 63, - AftrName => 64, - ErpLocalDomainName => 65, - PdExclude => 67, - Mip6Idinf => 69, - Mip6Udinf => 70, - Mip6Hnp => 71, - Mip6Haa => 72, - Mip6Haf => 73, - RdnssSelection => 74, - KrbPrincipalName => 75, - KrbRealmName => 76, - KrbDefaultRealmName => 77, - KrbKdc => 78, - SolMaxRt => 82, - InfMaxRt => 83, - Addrsel => 84, - AddrselTable => 85, - V6PcpServer => 86, - Dhcp4ODhcp6Server => 88, - S46ContMape => 94, - S46ContMapt => 95, - S46ContLw => 96, - _4rd => 97, - _4rdMapRule => 98, - _4rdNonMapRule => 99, - DhcpCaptivePortal => 103, - MplParameters => 104, - S46Priority => 111, - V6Prefix64 => 113, - IPv6AddressAndsf => 143, - Unknown(n) => n, - } + OptionCode::from(opt).into() } } +//should this be a TryFrom? impl From for OROCode { fn from(opt: u16) -> Self { - use OROCode::*; + OptionCode::from(opt) + .try_into() + .unwrap_or(OROCode::Unknown(opt)) + } +} + +impl TryFrom for OROCode { + type Error = &'static str; + fn try_from(opt: OptionCode) -> Result { + match opt { + OptionCode::VendorOpts => Ok(OROCode::VendorOpts), + OptionCode::SipServerD => Ok(OROCode::SipServerD), + OptionCode::SipServerA => Ok(OROCode::SipServerA), + OptionCode::DNSServers => Ok(OROCode::DNSServers), + OptionCode::DomainList => Ok(OROCode::DomainList), + OptionCode::NisServers => Ok(OROCode::NisServers), + OptionCode::NispServers => Ok(OROCode::NispServers), + OptionCode::NisDomainName => Ok(OROCode::NisDomainName), + OptionCode::NispDomainName => Ok(OROCode::NispDomainName), + OptionCode::SntpServers => Ok(OROCode::SntpServers), + OptionCode::InformationRefreshTime => Ok(OROCode::InformationRefreshTime), + OptionCode::BcmcsServerD => Ok(OROCode::BcmcsServerD), + OptionCode::BcmcsServerA => Ok(OROCode::BcmcsServerA), + OptionCode::GeoconfCivic => Ok(OROCode::GeoconfCivic), + OptionCode::ClientFqdn => Ok(OROCode::ClientFqdn), + OptionCode::PanaAgent => Ok(OROCode::PanaAgent), + OptionCode::NewPosixTimezone => Ok(OROCode::NewPosixTimezone), + OptionCode::NewTzdbTimezone => Ok(OROCode::NewTzdbTimezone), + OptionCode::Mip6Hnidf => Ok(OROCode::Mip6Hnidf), + OptionCode::Mip6Vdinf => Ok(OROCode::Mip6Vdinf), + OptionCode::V6Lost => Ok(OROCode::V6Lost), + OptionCode::CapwapAcV6 => Ok(OROCode::CapwapAcV6), + OptionCode::Ipv6AddressMoS => Ok(OROCode::Ipv6AddressMoS), + OptionCode::Ipv6FQDNMoS => Ok(OROCode::Ipv6FQDNMoS), + OptionCode::NtpServer => Ok(OROCode::NtpServer), + OptionCode::V6AccessDomain => Ok(OROCode::V6AccessDomain), + OptionCode::SipUaCsList => Ok(OROCode::SipUaCsList), + OptionCode::OptBootfileUrl => Ok(OROCode::OptBootfileUrl), + OptionCode::OptBootfileParam => Ok(OROCode::OptBootfileParam), + OptionCode::Nii => Ok(OROCode::Nii), + OptionCode::Geolocation => Ok(OROCode::Geolocation), + OptionCode::AftrName => Ok(OROCode::AftrName), + OptionCode::ErpLocalDomainName => Ok(OROCode::ErpLocalDomainName), + OptionCode::PdExclude => Ok(OROCode::PdExclude), + OptionCode::Mip6Idinf => Ok(OROCode::Mip6Idinf), + OptionCode::Mip6Udinf => Ok(OROCode::Mip6Udinf), + OptionCode::Mip6Hnp => Ok(OROCode::Mip6Hnp), + OptionCode::Mip6Haa => Ok(OROCode::Mip6Haa), + OptionCode::Mip6Haf => Ok(OROCode::Mip6Haf), + OptionCode::RdnssSelection => Ok(OROCode::RdnssSelection), + OptionCode::KrbPrincipalName => Ok(OROCode::KrbPrincipalName), + OptionCode::KrbRealmName => Ok(OROCode::KrbRealmName), + OptionCode::KrbDefaultRealmName => Ok(OROCode::KrbDefaultRealmName), + OptionCode::KrbKdc => Ok(OROCode::KrbKdc), + OptionCode::SolMaxRt => Ok(OROCode::SolMaxRt), + OptionCode::InfMaxRt => Ok(OROCode::InfMaxRt), + OptionCode::Addrsel => Ok(OROCode::Addrsel), + OptionCode::AddrselTable => Ok(OROCode::AddrselTable), + OptionCode::V6PcpServer => Ok(OROCode::V6PcpServer), + OptionCode::Dhcp4ODhcp6Server => Ok(OROCode::Dhcp4ODhcp6Server), + OptionCode::S46ContMape => Ok(OROCode::S46ContMape), + OptionCode::S46ContMapt => Ok(OROCode::S46ContMapt), + OptionCode::S46ContLw => Ok(OROCode::S46ContLw), + OptionCode::_4Rd => Ok(OROCode::_4Rd), + OptionCode::_4RdMapRule => Ok(OROCode::_4RdMapRule), + OptionCode::_4RdNonMapRule => Ok(OROCode::_4RdNonMapRule), + OptionCode::DhcpCaptivePortal => Ok(OROCode::DhcpCaptivePortal), + OptionCode::MplParameters => Ok(OROCode::MplParameters), + OptionCode::S46Priority => Ok(OROCode::S46Priority), + OptionCode::V6Prefix64 => Ok(OROCode::V6Prefix64), + OptionCode::Ipv6AddressANDSF => Ok(OROCode::Ipv6AddressANDSF), + OptionCode::Unknown(u16) => Ok(OROCode::Unknown(u16)), + _ => Err("conversion error, is not a valid OROCode"), + } + } +} + +impl From for OptionCode { + fn from(opt: OROCode) -> OptionCode { match opt { - 17 => VendorOpts, - 21 => SipServerD, - 22 => SipServerA, - 23 => DnsServers, - 24 => DomainList, - 27 => NisServers, - 28 => NispServers, - 29 => NisDomainName, - 30 => NispDomainName, - 31 => SntpServers, - 32 => InformationRefreshTime, - 33 => BcmcsServerD, - 34 => BcmcsServerA, - 36 => GeoconfCivic, - 39 => ClientFqdn, - 40 => PanaAgent, - 41 => NewPosixTimezone, - 42 => NewTzdbTimezone, - 49 => Mip6Hnidf, - 50 => Mip6Vdinf, - 51 => V6Lost, - 52 => CapwapAcV6, - 54 => Ipv6AddressMos, - 55 => Ipv6FqdnMos, - 56 => NtpServer, - 57 => V6AccessDomain, - 58 => SipUaCsList, - 59 => OptBootfileUrl, - 60 => OptBootfileParam, - 62 => Nii, - 63 => Geolocation, - 64 => AftrName, - 65 => ErpLocalDomainName, - 67 => PdExclude, - 69 => Mip6Idinf, - 70 => Mip6Udinf, - 71 => Mip6Hnp, - 72 => Mip6Haa, - 73 => Mip6Haf, - 74 => RdnssSelection, - 75 => KrbPrincipalName, - 76 => KrbRealmName, - 77 => KrbDefaultRealmName, - 78 => KrbKdc, - 82 => SolMaxRt, - 83 => InfMaxRt, - 84 => Addrsel, - 85 => AddrselTable, - 86 => V6PcpServer, - 88 => Dhcp4ODhcp6Server, - 94 => S46ContMape, - 95 => S46ContMapt, - 96 => S46ContLw, - 97 => _4rd, - 98 => _4rdMapRule, - 99 => _4rdNonMapRule, - 103 => DhcpCaptivePortal, - 104 => MplParameters, - 111 => S46Priority, - 113 => V6Prefix64, - 143 => IPv6AddressAndsf, - n => Unknown(n), + OROCode::VendorOpts => OptionCode::VendorOpts, + OROCode::SipServerD => OptionCode::SipServerD, + OROCode::SipServerA => OptionCode::SipServerA, + OROCode::DNSServers => OptionCode::DNSServers, + OROCode::DomainList => OptionCode::DomainList, + OROCode::NisServers => OptionCode::NisServers, + OROCode::NispServers => OptionCode::NispServers, + OROCode::NisDomainName => OptionCode::NisDomainName, + OROCode::NispDomainName => OptionCode::NispDomainName, + OROCode::SntpServers => OptionCode::SntpServers, + OROCode::InformationRefreshTime => OptionCode::InformationRefreshTime, + OROCode::BcmcsServerD => OptionCode::BcmcsServerD, + OROCode::BcmcsServerA => OptionCode::BcmcsServerA, + OROCode::GeoconfCivic => OptionCode::GeoconfCivic, + OROCode::ClientFqdn => OptionCode::ClientFqdn, + OROCode::PanaAgent => OptionCode::PanaAgent, + OROCode::NewPosixTimezone => OptionCode::NewPosixTimezone, + OROCode::NewTzdbTimezone => OptionCode::NewTzdbTimezone, + OROCode::Mip6Hnidf => OptionCode::Mip6Hnidf, + OROCode::Mip6Vdinf => OptionCode::Mip6Vdinf, + OROCode::V6Lost => OptionCode::V6Lost, + OROCode::CapwapAcV6 => OptionCode::CapwapAcV6, + OROCode::Ipv6AddressMoS => OptionCode::Ipv6AddressMoS, + OROCode::Ipv6FQDNMoS => OptionCode::Ipv6FQDNMoS, + OROCode::NtpServer => OptionCode::NtpServer, + OROCode::V6AccessDomain => OptionCode::V6AccessDomain, + OROCode::SipUaCsList => OptionCode::SipUaCsList, + OROCode::OptBootfileUrl => OptionCode::OptBootfileUrl, + OROCode::OptBootfileParam => OptionCode::OptBootfileParam, + OROCode::Nii => OptionCode::Nii, + OROCode::Geolocation => OptionCode::Geolocation, + OROCode::AftrName => OptionCode::AftrName, + OROCode::ErpLocalDomainName => OptionCode::ErpLocalDomainName, + OROCode::PdExclude => OptionCode::PdExclude, + OROCode::Mip6Idinf => OptionCode::Mip6Idinf, + OROCode::Mip6Udinf => OptionCode::Mip6Udinf, + OROCode::Mip6Hnp => OptionCode::Mip6Hnp, + OROCode::Mip6Haa => OptionCode::Mip6Haa, + OROCode::Mip6Haf => OptionCode::Mip6Haf, + OROCode::RdnssSelection => OptionCode::RdnssSelection, + OROCode::KrbPrincipalName => OptionCode::KrbPrincipalName, + OROCode::KrbRealmName => OptionCode::KrbRealmName, + OROCode::KrbDefaultRealmName => OptionCode::KrbDefaultRealmName, + OROCode::KrbKdc => OptionCode::KrbKdc, + OROCode::SolMaxRt => OptionCode::SolMaxRt, + OROCode::InfMaxRt => OptionCode::InfMaxRt, + OROCode::Addrsel => OptionCode::Addrsel, + OROCode::AddrselTable => OptionCode::AddrselTable, + OROCode::V6PcpServer => OptionCode::V6PcpServer, + OROCode::Dhcp4ODhcp6Server => OptionCode::Dhcp4ODhcp6Server, + OROCode::S46ContMape => OptionCode::S46ContMape, + OROCode::S46ContMapt => OptionCode::S46ContMapt, + OROCode::S46ContLw => OptionCode::S46ContLw, + OROCode::_4Rd => OptionCode::_4Rd, + OROCode::_4RdMapRule => OptionCode::_4RdMapRule, + OROCode::_4RdNonMapRule => OptionCode::_4RdNonMapRule, + OROCode::DhcpCaptivePortal => OptionCode::DhcpCaptivePortal, + OROCode::MplParameters => OptionCode::MplParameters, + OROCode::S46Priority => OptionCode::S46Priority, + OROCode::V6Prefix64 => OptionCode::V6Prefix64, + OROCode::Ipv6AddressANDSF => OptionCode::Ipv6AddressANDSF, + OROCode::Unknown(u16) => OptionCode::Unknown(u16), } } } From 8aae6665447d503349f02809a339c051fbcabcb6 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 11 Oct 2022 20:28:25 +1000 Subject: [PATCH 07/12] V6 rewrite part 1 --- src/decoder.rs | 5 + src/v6/duid.rs | 78 +++ src/v6/mod.rs | 2 + src/v6/options/auth.rs | 73 +++ src/v6/options/iaaddr.rs | 83 +++ src/v6/options/iana.rs | 83 +++ src/v6/options/iapd.rs | 85 +++ src/v6/options/iaprefix.rs | 86 ++++ src/v6/options/iata.rs | 75 +++ src/v6/{options.rs => options/mod.rs} | 715 +++++++++----------------- src/v6/options/oro.rs | 66 +++ src/v6/options/status.rs | 149 ++++++ 12 files changed, 1029 insertions(+), 471 deletions(-) create mode 100644 src/v6/duid.rs create mode 100644 src/v6/options/auth.rs create mode 100644 src/v6/options/iaaddr.rs create mode 100644 src/v6/options/iana.rs create mode 100644 src/v6/options/iapd.rs create mode 100644 src/v6/options/iaprefix.rs create mode 100644 src/v6/options/iata.rs rename src/v6/{options.rs => options/mod.rs} (51%) create mode 100644 src/v6/options/oro.rs create mode 100644 src/v6/options/status.rs diff --git a/src/decoder.rs b/src/decoder.rs index 84230e9..875e776 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -39,6 +39,11 @@ impl<'a> Decoder<'a> { Ok(u8::from_be_bytes(self.peek::<{ mem::size_of::() }>()?)) } + /// peek at the next u16 without advancing the internal pointer + pub fn peek_u16(&self) -> DecodeResult { + Ok(u16::from_be_bytes(self.peek::<{mem::size_of::() }>()?)) + } + /// read a u8 pub fn read_u8(&mut self) -> DecodeResult { Ok(u8::from_be_bytes(self.read::<{ mem::size_of::() }>()?)) diff --git a/src/v6/duid.rs b/src/v6/duid.rs new file mode 100644 index 0000000..6e69d4a --- /dev/null +++ b/src/v6/duid.rs @@ -0,0 +1,78 @@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::{Encodable,Decodable,Encoder,Decoder}; +use crate::v6::{Ipv6Addr,EncodeError,DecodeError}; +use crate::v4::HType; + + +/// Duid helper type +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Duid(Vec); +// TODO: define specific duid types + +impl Duid { + /// new DUID link layer address with time + pub fn link_layer_time(htype: HType, time: u32, addr: Ipv6Addr) -> Self { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.write_u16(1).unwrap(); // duid type + e.write_u16(u8::from(htype) as u16).unwrap(); + e.write_u32(time).unwrap(); + e.write_u128(addr.into()).unwrap(); + Self(buf) + } + /// new DUID enterprise number + pub fn enterprise(enterprise: u32, id: &[u8]) -> Self { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.write_u16(2).unwrap(); // duid type + e.write_u32(enterprise).unwrap(); + e.write_slice(id).unwrap(); + Self(buf) + } + /// new link layer DUID + pub fn link_layer(htype: HType, addr: Ipv6Addr) -> Self { + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.write_u16(3).unwrap(); // duid type + e.write_u16(u8::from(htype) as u16).unwrap(); + e.write_u128(addr.into()).unwrap(); + Self(buf) + } + /// new DUID-UUID + /// `uuid` must be 16 bytes long + pub fn uuid(uuid: &[u8]) -> Self { + assert!(uuid.len() == 16); + let mut buf = Vec::new(); + let mut e = Encoder::new(&mut buf); + e.write_u16(4).unwrap(); // duid type + e.write_slice(uuid).unwrap(); + Self(buf) + } + /// create a DUID of unknown type + pub fn unknown(duid: &[u8]) -> Self { + Self(duid.to_vec()) + } + /// total length of contained DUID + pub fn len(&self) -> usize { + self.0.len() + } + /// is contained DUID empty + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl AsRef<[u8]> for Duid { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From> for Duid { + fn from(v: Vec) -> Self { + Self(v) + } +} diff --git a/src/v6/mod.rs b/src/v6/mod.rs index aeaf55c..4428ef4 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -55,6 +55,7 @@ mod options; mod oro_codes; mod option_codes; +mod duid; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -65,6 +66,7 @@ use std::{convert::TryInto, fmt, net::Ipv6Addr}; pub use self::options::*; pub use self::oro_codes::*; pub use self::option_codes::*; +pub use self::duid::*; pub use crate::{ decoder::{Decodable, Decoder}, diff --git a/src/v6/options/auth.rs b/src/v6/options/auth.rs new file mode 100644 index 0000000..93ca6dc --- /dev/null +++ b/src/v6/options/auth.rs @@ -0,0 +1,73 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Auth +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Auth { + pub proto: u8, + pub algo: u8, + pub rdm: u8, + pub replay_detection: u64, + // 11 + len + pub info: Vec, +} + +impl Decodable for Auth { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + Ok(Auth { + proto: decoder.read_u8()?, + algo: decoder.read_u8()?, + rdm: decoder.read_u8()?, + replay_detection: decoder.read_u64()?, + info: decoder.read_slice(len - 11)?.to_vec(), + }) + } +} + +impl Encodable for Auth { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::Auth.into())?; + e.write_u16(11 + self.info.len() as u16)?; + e.write_u8(self.proto)?; + e.write_u8(self.algo)?; + e.write_u8(self.rdm)?; + e.write_u64(self.replay_detection)?; + e.write_slice(&self.info)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iata_encode_decode() { + let option = Auth { + proto: 0xC, + algo: 0xB, + rdm: 0xA, + replay_detection: 0xABCD, + info: vec![1,2,3], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = Auth::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = Auth::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/iaaddr.rs b/src/v6/options/iaaddr.rs new file mode 100644 index 0000000..614e2a6 --- /dev/null +++ b/src/v6/options/iaaddr.rs @@ -0,0 +1,83 @@ +use crate::v6::DhcpOption; +use crate::v6::{DecodeResult, EncodeResult, Ipv6Addr, OptionCode, StatusCode, option_builder}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association Address +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IAAddr { + pub addr: Ipv6Addr, + pub preferred_life: u32, + pub valid_life: u32, + // 24 + opts.len() + // should this be DhcpOptions ? + // the RFC suggests it 'encapsulates options' + pub opts: IAAddrOptions, +} + +impl Decodable for IAAddr { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + Ok(IAAddr { + addr: decoder.read::<16>()?.into(), + preferred_life: decoder.read_u32()?, + valid_life: decoder.read_u32()?, + opts: IAAddrOptions::decode(&mut decoder)?, + }) + } +} + +impl Encodable for IAAddr { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.opts.encode(&mut opt_enc)?; + e.write_u16(OptionCode::IAAddr.into())?; + // buf now has total len + e.write_u16(24 + buf.len() as u16)?; + // data + e.write_u128((self.addr).into())?; + e.write_u32(self.preferred_life)?; + e.write_u32(self.valid_life)?; + e.write_slice(&buf)?; + Ok(()) + } +} + +option_builder!(IAAddrOption, IAAddrOptions, DhcpOption, StatusCode); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iaaddr_encode_decode() { + let option = IAAddr { + addr: "FE:80::AB".parse().unwrap(), + preferred_life: 0xEF12, + valid_life: 0xABCD, + opts: IAAddrOptions(vec![StatusCode { + status: 0xABCDu16.into(), + msg: "message".into(), + } + .into()]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = IAAddr::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = IAAddr::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/iana.rs b/src/v6/options/iana.rs new file mode 100644 index 0000000..132e19c --- /dev/null +++ b/src/v6/options/iana.rs @@ -0,0 +1,83 @@ +use super::{ + option_builder, DecodeResult, DhcpOption, EncodeResult, IAAddr, OptionCode, StatusCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IANA { + pub id: u32, + pub t1: u32, + pub t2: u32, + // 12 + opts.len() + pub opts: IANAOptions, +} + +impl Decodable for IANA { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + Ok(IANA { + id: decoder.read_u32()?, + t1: decoder.read_u32()?, + t2: decoder.read_u32()?, + opts: IANAOptions::decode(&mut decoder)?, + }) + } +} + +impl Encodable for IANA { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.opts.encode(&mut opt_enc)?; + // buf now has total len + e.write_u16(OptionCode::IANA.into())?; + e.write_u16(12 + buf.len() as u16)?; + // write data + e.write_u32(self.id)?; + e.write_u32(self.t1)?; + e.write_u32(self.t2)?; + e.write_slice(&buf)?; + Ok(()) + } +} + +option_builder!(IANAOption, IANAOptions, DhcpOption, IAAddr, StatusCode); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iana_encode_decode() { + let option = IANA { + id: 0xAABB, + t1: 0xCCDDEEFF, + t2: 0x11223344, + // 12 + opts.len() + opts: IANAOptions(vec![StatusCode { + status: 0xABCDu16.into(), + msg: "message".into(), + } + .into()]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = IANA::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = IANA::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/iapd.rs b/src/v6/options/iapd.rs new file mode 100644 index 0000000..734df84 --- /dev/null +++ b/src/v6/options/iapd.rs @@ -0,0 +1,85 @@ +use super::{ + option_builder, DecodeResult, DhcpOption, EncodeResult, IAPrefix, OptionCode, StatusCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association Prefix Delegation +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IAPD { + pub id: u32, + pub t1: u32, + pub t2: u32, + // 12 + opts.len() + pub opts: IAPDOptions, +} + +impl Decodable for IAPD { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + Ok(IAPD { + id: decoder.read_u32()?, + t1: decoder.read_u32()?, + t2: decoder.read_u32()?, + opts: { + let mut dec = Decoder::new(decoder.read_slice(len-12)?); + IAPDOptions::decode(&mut dec)? + }, + }) + } +} + +impl Encodable for IAPD { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::IAPD.into())?; + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.opts.encode(&mut opt_enc)?; + // buf now has total len + e.write_u16(12 + buf.len() as u16)?; + // write data + e.write_u32(self.id)?; + e.write_u32(self.t1)?; + e.write_u32(self.t2)?; + e.write_slice(&buf)?; + Ok(()) + } +} + +option_builder!(IAPDOption, IAPDOptions, DhcpOption, IAPrefix, StatusCode); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iapd_encode_decode() { + let option = IAPD { + id: 0xAABB, + t1: 0xCCDDEEFF, + t2: 0x11223344, + // 12 + opts.len() + opts: IAPDOptions(vec![StatusCode { + status: 0xABCDu16.into(), + msg: "message".into(), + } + .into()]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = IAPD::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = IAPD::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/iaprefix.rs b/src/v6/options/iaprefix.rs new file mode 100644 index 0000000..addd85b --- /dev/null +++ b/src/v6/options/iaprefix.rs @@ -0,0 +1,86 @@ +use super::{ + option_builder, DecodeResult, DhcpOption, EncodeResult, Ipv6Addr, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association Prefix Delegation Prefix Option +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IAPrefix { + pub preferred_lifetime: u32, + pub valid_lifetime: u32, + pub prefix_len: u8, + pub prefix_ip: Ipv6Addr, + // 25 + opts.len() + pub opts: IAPrefixOptions, +} + +impl Decodable for IAPrefix { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + Ok(IAPrefix { + preferred_lifetime: decoder.read_u32()?, + valid_lifetime: decoder.read_u32()?, + prefix_len: decoder.read_u8()?, + prefix_ip: decoder.read::<16>()?.into(), + opts: { + let mut dec = Decoder::new(decoder.read_slice(len-25)?); + IAPrefixOptions::decode(&mut dec)? + } + }) + } +} + + +impl Encodable for IAPrefix { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::IAPrefix.into())?; + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.opts.encode(&mut opt_enc)?; + // buf now has total len + e.write_u16(25 + buf.len() as u16)?; + // write data + e.write_u32(self.preferred_lifetime)?; + e.write_u32(self.valid_lifetime)?; + e.write_u8(self.prefix_len)?; + e.write_u128(self.prefix_ip.into())?; + e.write_slice(&buf)?; + Ok(()) + } +} + +option_builder!(IAPrefixOption, IAPrefixOptions, DhcpOption,); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iapd_encode_decode() { + let option = IAPrefix { + preferred_lifetime: 0, + valid_lifetime: 0, + prefix_len: 0, + prefix_ip: "FE80::".parse().unwrap(), + // 12 + opts.len() + opts: IAPrefixOptions(vec![]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = IAPrefix::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = IAPrefix::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/iata.rs b/src/v6/options/iata.rs new file mode 100644 index 0000000..b80fb08 --- /dev/null +++ b/src/v6/options/iata.rs @@ -0,0 +1,75 @@ +use super::{ + option_builder, DecodeResult, DhcpOption, EncodeResult, IAAddr, OptionCode, StatusCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IATA { + pub id: u32, + // 4 + opts.len() + pub opts: IATAOptions, +} + +impl Decodable for IATA { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + Ok(IATA { + id: decoder.read_u32()?, + opts: IATAOptions::decode(&mut decoder)?, + }) + } +} + +impl Encodable for IATA { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.opts.encode(&mut opt_enc)?; + // buf now has total len + e.write_u16(OptionCode::IATA.into())?; + e.write_u16(4 + buf.len() as u16)?; + // write data + e.write_u32(self.id)?; + e.write_slice(&buf)?; + Ok(()) + } +} + +option_builder!(IATAOption, IATAOptions, DhcpOption, IAAddr, StatusCode); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iata_encode_decode() { + let option = IATA { + id: 0, + // 12 + opts.len() + opts: IATAOptions(vec![StatusCode { + status: 0xABCDu16.into(), + msg: "message".into(), + } + .into()]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = IATA::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = IATA::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options.rs b/src/v6/options/mod.rs similarity index 51% rename from src/v6/options.rs rename to src/v6/options/mod.rs index d2b6124..5deceed 100644 --- a/src/v6/options.rs +++ b/src/v6/options/mod.rs @@ -1,5 +1,23 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; + +mod iana; +pub use iana::*; +mod iaaddr; +pub use iaaddr::*; +mod status; +pub use status::*; +mod iapd; +pub use iapd::*; +mod iaprefix; +pub use iaprefix::*; +mod iata; +pub use iata::*; +mod auth; +pub use auth::*; +mod oro; +pub use oro::*; + use trust_dns_proto::{ rr::Name, serialize::binary::{BinDecodable, BinDecoder, BinEncodable, BinEncoder}, @@ -12,9 +30,113 @@ use crate::{ decoder::{Decodable, Decoder}, encoder::{Encodable, Encoder}, error::{DecodeResult, EncodeResult}, - v4::HType, - v6::{MessageType, OROCode, OptionCode, RelayMessage}, + v6::{Duid, MessageType, OROCode, OptionCode, RelayMessage}, }; +//helper macro for implementing sub-options (IANAOptions, ect) +//useage: option_builder!(IANAOption, IANAOptions, DhcpOption, IAAddr, StatusCode); +// option_builder!(name , names , master , subname... ); +macro_rules! option_builder{ + ($name: ident, $names: ident, $mastername: ident, $($subnames: ident),*) => { + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub enum $name { + $( + $subnames($subnames), + )* + Unknown($mastername), + } + $( + impl From<$subnames> for $name { + fn from(sc: $subnames) -> Self{ + $name :: $subnames(sc) + } + } + )* + impl From<&$name> for $mastername{ + fn from(option: &$name) -> Self{ + match option { + $( + $name :: $subnames(u) => $mastername :: $subnames(u.clone()), + )* + $name::Unknown(other) => other.clone(), + } + } + } + impl TryFrom<&$mastername> for $name{ + type Error=&'static str; + + fn try_from(option: &$mastername) -> Result{ + match option{ + $( + $mastername :: $subnames(u) => Ok($name :: $subnames(u.clone())), + )* + _ => Err("invalid or unknown option"), + } + } + } + impl Encodable for $name { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + $mastername::from(self).encode(e) + } + } + + impl Decodable for $name { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + let option = $mastername::decode(decoder)?; + match (&option).try_into() { + Ok(n) => Ok(n), + _ => Ok($name::Unknown(option)), + } + } + } + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Debug, Clone, PartialEq, Eq, Default)] + pub struct $names(Vec<$name>); + impl $names { + /// insert a new option into the list of opts + pub fn insert>(&mut self, opt: T){ + self.0.push(opt.into()) + } + /// return a mutable ref to an iterator + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } + /// return a mutable ref to an iterator + pub fn iter_mut(&mut self) -> impl Iterator { + self.0.iter_mut() + } + } + impl Encodable for $names { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + self.0.iter().try_for_each(|opt| opt.encode(e)) + } + } + impl Decodable for $names { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + let mut opts = Vec::new(); + while let Ok(opt) = $name::decode(decoder) { + opts.push(opt); + } + Ok($names(opts)) + } + } + impl IntoIterator for $names { + type Item = $name; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator<$name> for $names{ + fn from_iter>(iter: T) -> Self { + let opts = iter.into_iter().collect::>(); + $names(opts) + } + } + }; +} + +pub(crate) use option_builder; // server can send multiple IA_NA options to request multiple addresses // so we must be able to handle multiple of the same option type @@ -99,77 +221,6 @@ impl FromIterator for DhcpOptions { } } -/// Duid helper type -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Duid(Vec); -// TODO: define specific duid types - -impl Duid { - /// new DUID link layer address with time - pub fn link_layer_time(htype: HType, time: u32, addr: Ipv6Addr) -> Self { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.write_u16(1).unwrap(); // duid type - e.write_u16(u8::from(htype) as u16).unwrap(); - e.write_u32(time).unwrap(); - e.write_u128(addr.into()).unwrap(); - Self(buf) - } - /// new DUID enterprise number - pub fn enterprise(enterprise: u32, id: &[u8]) -> Self { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.write_u16(2).unwrap(); // duid type - e.write_u32(enterprise).unwrap(); - e.write_slice(id).unwrap(); - Self(buf) - } - /// new link layer DUID - pub fn link_layer(htype: HType, addr: Ipv6Addr) -> Self { - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.write_u16(3).unwrap(); // duid type - e.write_u16(u8::from(htype) as u16).unwrap(); - e.write_u128(addr.into()).unwrap(); - Self(buf) - } - /// new DUID-UUID - /// `uuid` must be 16 bytes long - pub fn uuid(uuid: &[u8]) -> Self { - assert!(uuid.len() == 16); - let mut buf = Vec::new(); - let mut e = Encoder::new(&mut buf); - e.write_u16(4).unwrap(); // duid type - e.write_slice(uuid).unwrap(); - Self(buf) - } - /// create a DUID of unknown type - pub fn unknown(duid: &[u8]) -> Self { - Self(duid.to_vec()) - } - /// total length of contained DUID - pub fn len(&self) -> usize { - self.0.len() - } - /// is contained DUID empty - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl AsRef<[u8]> for Duid { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl From> for Duid { - fn from(v: Vec) -> Self { - Self(v) - } -} - /// DHCPv6 option types #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] @@ -283,271 +334,6 @@ fn decode_data(decoder: &'_ mut Decoder<'_>) -> Vec> { data } -/// Server Unicast -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StatusCode { - pub status: Status, - // 2 + len - pub msg: String, -} - -/// Status code for Server Unicast -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Status { - Success, - UnspecFail, - NoAddrsAvail, - NoBinding, - NotOnLink, - UseMulticast, - NoPrefixAvail, - UnknownQueryType, - MalformedQuery, - NotConfigured, - NotAllowed, - QueryTerminated, - DataMissing, - CatchUpComplete, - NotSupported, - TLSConnectionRefused, - AddressInUse, - ConfigurationConflict, - MissingBindingInformation, - OutdatedBindingInformation, - ServerShuttingDown, - DNSUpdateNotSupported, - ExcessiveTimeSkew, - /// unknown/unimplemented message type - Unknown(u16), -} - -impl From for Status { - fn from(n: u16) -> Self { - use Status::*; - match n { - 0 => Success, - 1 => UnspecFail, - 2 => NoAddrsAvail, - 3 => NoBinding, - 4 => NotOnLink, - 5 => UseMulticast, - 6 => NoPrefixAvail, - 7 => UnknownQueryType, - 8 => MalformedQuery, - 9 => NotConfigured, - 10 => NotAllowed, - 11 => QueryTerminated, - 12 => DataMissing, - 13 => CatchUpComplete, - 14 => NotSupported, - 15 => TLSConnectionRefused, - 16 => AddressInUse, - 17 => ConfigurationConflict, - 18 => MissingBindingInformation, - 19 => OutdatedBindingInformation, - 20 => ServerShuttingDown, - 21 => DNSUpdateNotSupported, - 22 => ExcessiveTimeSkew, - _ => Unknown(n), - } - } -} -impl From for u16 { - fn from(n: Status) -> Self { - use Status::*; - match n { - Success => 0, - UnspecFail => 1, - NoAddrsAvail => 2, - NoBinding => 3, - NotOnLink => 4, - UseMulticast => 5, - NoPrefixAvail => 6, - UnknownQueryType => 7, - MalformedQuery => 8, - NotConfigured => 9, - NotAllowed => 10, - QueryTerminated => 11, - DataMissing => 12, - CatchUpComplete => 13, - NotSupported => 14, - TLSConnectionRefused => 15, - AddressInUse => 16, - ConfigurationConflict => 17, - MissingBindingInformation => 18, - OutdatedBindingInformation => 19, - ServerShuttingDown => 20, - DNSUpdateNotSupported => 21, - ExcessiveTimeSkew => 22, - Unknown(n) => n, - } - } -} - -/// Auth -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Auth { - pub proto: u8, - pub algo: u8, - pub rdm: u8, - pub replay_detection: u64, - // 11 + len - pub info: Vec, -} - -impl Decodable for Auth { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - let len = decoder.buffer().len(); - Ok(Auth { - proto: decoder.read_u8()?, - algo: decoder.read_u8()?, - rdm: decoder.read_u8()?, - replay_detection: decoder.read_u64()?, - info: decoder.read_slice(len - 11)?.to_vec(), - }) - } -} - -/// Option Request Option -/// -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ORO { - // 2 * num opts - pub opts: Vec, -} - -impl Decodable for ORO { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - let len = decoder.buffer().len(); - Ok(ORO { - opts: { - decoder - .read_slice(len)? - .chunks_exact(2) - // TODO: use .array_chunks::<2>() when stable - .map(|code| OROCode::from(u16::from_be_bytes([code[0], code[1]]))) - .collect() - }, - }) - } -} - -/// Identity Association for Temporary Addresses -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IATA { - pub id: u32, - // 4 + opts.len() - // should this be Vec ? - // the RFC suggests it 'encapsulates options' - pub opts: DhcpOptions, -} - -impl Decodable for IATA { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - Ok(IATA { - id: decoder.read_u32()?, - opts: DhcpOptions::decode(decoder)?, - }) - } -} - -/// Identity Association for Non-Temporary Addresses -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IANA { - pub id: u32, - pub t1: u32, - pub t2: u32, - // 12 + opts.len() - pub opts: DhcpOptions, -} - -impl Decodable for IANA { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - Ok(IANA { - id: decoder.read_u32()?, - t1: decoder.read_u32()?, - t2: decoder.read_u32()?, - opts: DhcpOptions::decode(decoder)?, - }) - } -} - -/// Identity Association Prefix Delegation -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IAPD { - pub id: u32, - pub t1: u32, - pub t2: u32, - // 12 + opts.len() - pub opts: DhcpOptions, -} - -impl Decodable for IAPD { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - Ok(IAPD { - id: decoder.read_u32()?, - t1: decoder.read_u32()?, - t2: decoder.read_u32()?, - opts: DhcpOptions::decode(decoder)?, - }) - } -} - -/// Identity Association Prefix Delegation Prefix Option -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IAPrefix { - pub preferred_lifetime: u32, - pub valid_lifetime: u32, - pub prefix_len: u8, - pub prefix_ip: Ipv6Addr, - // 25 + opts.len() - pub opts: DhcpOptions, -} - -impl Decodable for IAPrefix { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - Ok(IAPrefix { - preferred_lifetime: decoder.read_u32()?, - valid_lifetime: decoder.read_u32()?, - prefix_len: decoder.read_u8()?, - prefix_ip: decoder.read::<16>()?.into(), - opts: DhcpOptions::decode(decoder)?, - }) - } -} - -/// Identity Association Address -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IAAddr { - pub addr: Ipv6Addr, - pub preferred_life: u32, - pub valid_life: u32, - // 24 + opts.len() - // should this be DhcpOptions ? - // the RFC suggests it 'encapsulates options' - pub opts: DhcpOptions, -} - -impl Decodable for IAAddr { - fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - Ok(IAAddr { - addr: decoder.read::<16>()?.into(), - preferred_life: decoder.read_u32()?, - valid_life: decoder.read_u32()?, - opts: DhcpOptions::decode(decoder)?, - }) - } -} - /// fallback for options not yet implemented #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -577,10 +363,10 @@ impl UnknownOption { } } -impl From<&UnknownOption> for OptionCode{ - fn from(opt: &UnknownOption) -> Self{ - opt.code.into() - } +impl From<&UnknownOption> for OptionCode { + fn from(opt: &UnknownOption) -> Self { + opt.code.into() + } } impl Decodable for DhcpOptions { @@ -603,50 +389,68 @@ impl Encodable for DhcpOptions { impl Decodable for DhcpOption { fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { - let code = decoder.read_u16()?.into(); - let len = decoder.read_u16()? as usize; + let code = decoder.peek_u16()?.into(); + let tmp = Decoder::new(&decoder.buffer()[2..]); + let len = tmp.peek_u16()? as usize; + Ok(match code { - OptionCode::ClientId => DhcpOption::ClientId(decoder.read_slice(len)?.to_vec()), - OptionCode::ServerId => DhcpOption::ServerId(decoder.read_slice(len)?.to_vec()), + OptionCode::ClientId => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::ClientId(decoder.read_slice(len)?.to_vec()) + } + OptionCode::ServerId => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::ServerId(decoder.read_slice(len)?.to_vec()) + } OptionCode::IANA => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::IANA(IANA::decode(&mut dec)?) + DhcpOption::IANA(IANA::decode(decoder)?) } OptionCode::IATA => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::IATA(IATA::decode(&mut dec)?) + DhcpOption::IATA(IATA::decode(decoder)?) } OptionCode::IAAddr => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::IAAddr(IAAddr::decode(&mut dec)?) + + DhcpOption::IAAddr(IAAddr::decode(decoder)?) } OptionCode::ORO => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::ORO(ORO::decode(&mut dec)?) + DhcpOption::ORO(ORO::decode(decoder)?) + } + OptionCode::Preference => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::Preference(decoder.read_u8()?) + } + OptionCode::ElapsedTime => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::ElapsedTime(decoder.read_u16()?) } - OptionCode::Preference => DhcpOption::Preference(decoder.read_u8()?), - OptionCode::ElapsedTime => DhcpOption::ElapsedTime(decoder.read_u16()?), OptionCode::RelayMsg => { + decoder.read_u16()?;decoder.read_u16()?; let mut relay_dec = Decoder::new(decoder.read_slice(len)?); DhcpOption::RelayMsg(RelayMessage::decode(&mut relay_dec)?) } OptionCode::Auth => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::Auth(Auth::decode(&mut dec)?) - } - OptionCode::Unicast => DhcpOption::Unicast(decoder.read::<16>()?.into()), - OptionCode::StatusCode => DhcpOption::StatusCode(StatusCode { - status: decoder.read_u16()?.into(), - msg: decoder.read_string(len - 1)?, - }), - OptionCode::RapidCommit => DhcpOption::RapidCommit, + DhcpOption::Auth(Auth::decode(decoder)?) + } + OptionCode::Unicast => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::Unicast(decoder.read::<16>()?.into()) + } + OptionCode::StatusCode => { + DhcpOption::StatusCode(StatusCode::decode(decoder)?) + } + OptionCode::RapidCommit => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::RapidCommit + } OptionCode::UserClass => { + decoder.read_u16()?;decoder.read_u16()?; let buf = decoder.read_slice(len)?; DhcpOption::UserClass(UserClass { data: decode_data(&mut Decoder::new(buf)), }) } OptionCode::VendorClass => { + decoder.read_u16()?;decoder.read_u16()?; let num = decoder.read_u32()?; let buf = decoder.read_slice(len - 4)?; DhcpOption::VendorClass(VendorClass { @@ -654,26 +458,40 @@ impl Decodable for DhcpOption { data: decode_data(&mut Decoder::new(buf)), }) } - OptionCode::VendorOpts => DhcpOption::VendorOpts(VendorOpts { - num: decoder.read_u32()?, - opts: { - let mut opt_decoder = Decoder::new(decoder.read_slice(len - 4)?); - DhcpOptions::decode(&mut opt_decoder)? - }, - }), - OptionCode::InterfaceId => DhcpOption::InterfaceId(decoder.read_slice(len)?.to_vec()), - OptionCode::ReconfMsg => DhcpOption::ReconfMsg(decoder.read_u8()?.into()), - OptionCode::ReconfAccept => DhcpOption::ReconfAccept, - OptionCode::DNSServers => DhcpOption::DNSServers(decoder.read_ipv6s(len)?), + OptionCode::VendorOpts => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::VendorOpts(VendorOpts { + num: decoder.read_u32()?, + opts: { + let mut opt_decoder = Decoder::new(decoder.read_slice(len - 4)?); + DhcpOptions::decode(&mut opt_decoder)? + }, + }) + } + OptionCode::InterfaceId => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::InterfaceId(decoder.read_slice(len)?.to_vec()) + } + OptionCode::ReconfMsg => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::ReconfMsg(decoder.read_u8()?.into()) + } + OptionCode::ReconfAccept => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::ReconfAccept + } + OptionCode::DNSServers => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::DNSServers(decoder.read_ipv6s(len)?) + } OptionCode::IAPD => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::IAPD(IAPD::decode(&mut dec)?) + DhcpOption::IAPD(IAPD::decode(decoder)?) } OptionCode::IAPrefix => { - let mut dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::IAPrefix(IAPrefix::decode(&mut dec)?) + DhcpOption::IAPrefix(IAPrefix::decode(decoder)?) } OptionCode::DomainList => { + decoder.read_u16()?;decoder.read_u16()?; let mut name_decoder = BinDecoder::new(decoder.read_slice(len as usize)?); let mut names = Vec::new(); while let Ok(name) = Name::read(&mut name_decoder) { @@ -683,86 +501,59 @@ impl Decodable for DhcpOption { DhcpOption::DomainList(names) } // not yet implemented - OptionCode::Unknown(code) => DhcpOption::Unknown(UnknownOption { - code, - data: decoder.read_slice(len)?.to_vec(), - }), - unimplemented => DhcpOption::Unknown(UnknownOption { - code: unimplemented.into(), - data: decoder.read_slice(len)?.to_vec(), - }), + OptionCode::Unknown(code) => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::Unknown(UnknownOption { + code, + data: decoder.read_slice(len)?.to_vec(), + }) + } + unimplemented => { + decoder.read_u16()?;decoder.read_u16()?; + DhcpOption::Unknown(UnknownOption { + code: unimplemented.into(), + data: decoder.read_slice(len)?.to_vec(), + }) + } }) } } impl Encodable for DhcpOption { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { let code: OptionCode = self.into(); - e.write_u16(code.into())?; match self { DhcpOption::ClientId(duid) | DhcpOption::ServerId(duid) => { + e.write_u16(code.into())?; e.write_u16(duid.len() as u16)?; e.write_slice(duid)?; } - DhcpOption::IANA(IANA { id, t1, t2, opts }) - | DhcpOption::IAPD(IAPD { id, t1, t2, opts }) => { - // write len - let mut buf = Vec::new(); - let mut opt_enc = Encoder::new(&mut buf); - opts.encode(&mut opt_enc)?; - // buf now has total len - e.write_u16(12 + buf.len() as u16)?; - // write data - e.write_u32(*id)?; - e.write_u32(*t1)?; - e.write_u32(*t2)?; - e.write_slice(&buf)?; + DhcpOption::IANA(iana) => { + iana.encode(e)?; } - DhcpOption::IATA(IATA { id, opts }) => { - // write len - let mut buf = Vec::new(); - let mut opt_enc = Encoder::new(&mut buf); - opts.encode(&mut opt_enc)?; - // buf now has total len - e.write_u16(4 + buf.len() as u16)?; - // data - e.write_u32(*id)?; - e.write_slice(&buf)?; + DhcpOption::IAPD(iapd) => { + iapd.encode(e)?; } - DhcpOption::IAAddr(IAAddr { - addr, - preferred_life, - valid_life, - opts, - }) => { - // write len - let mut buf = Vec::new(); - let mut opt_enc = Encoder::new(&mut buf); - opts.encode(&mut opt_enc)?; - // buf now has total len - e.write_u16(24 + buf.len() as u16)?; - // data - e.write_u128((*addr).into())?; - e.write_u32(*preferred_life)?; - e.write_u32(*valid_life)?; - e.write_slice(&buf)?; + DhcpOption::IATA(iata) => { + iata.encode(e)?; } - DhcpOption::ORO(ORO { opts }) => { - // write len - e.write_u16(2 * opts.len() as u16)?; - // data - for code in opts { - e.write_u16(u16::from(*code))?; - } + DhcpOption::IAAddr(iaaddr) => { + iaaddr.encode(e)?; + } + DhcpOption::ORO(oro) => { + oro.encode(e)?; } DhcpOption::Preference(pref) => { + e.write_u16(code.into())?; e.write_u16(1)?; e.write_u8(*pref)?; } DhcpOption::ElapsedTime(elapsed) => { + e.write_u16(code.into())?; e.write_u16(2)?; e.write_u16(*elapsed)?; } DhcpOption::RelayMsg(msg) => { + e.write_u16(code.into())?; let mut buf = Vec::new(); let mut relay_enc = Encoder::new(&mut buf); msg.encode(&mut relay_enc)?; @@ -770,33 +561,23 @@ impl Encodable for DhcpOption { e.write_u16(buf.len() as u16)?; e.write_slice(&buf)?; } - DhcpOption::Auth(Auth { - proto, - algo, - rdm, - replay_detection, - info, - }) => { - e.write_u16(11 + info.len() as u16)?; - e.write_u8(*proto)?; - e.write_u8(*algo)?; - e.write_u8(*rdm)?; - e.write_u64(*replay_detection)?; - e.write_slice(info)?; + DhcpOption::Auth(auth) => { + auth.encode(e)?; } DhcpOption::Unicast(addr) => { + e.write_u16(code.into())?; e.write_u16(16)?; e.write_u128((*addr).into())?; } - DhcpOption::StatusCode(StatusCode { status, msg }) => { - e.write_u16(2 + msg.len() as u16)?; - e.write_u16((*status).into())?; - e.write_slice(msg.as_bytes())?; + DhcpOption::StatusCode(status) => { + status.encode(e)?; } DhcpOption::RapidCommit => { + e.write_u16(code.into())?; e.write_u16(0)?; } DhcpOption::UserClass(UserClass { data }) => { + e.write_u16(code.into())?; e.write_u16(data.len() as u16)?; for s in data { e.write_u16(s.len() as u16)?; @@ -804,6 +585,7 @@ impl Encodable for DhcpOption { } } DhcpOption::VendorClass(VendorClass { num, data }) => { + e.write_u16(code.into())?; e.write_u16(4 + data.len() as u16)?; e.write_u32(*num)?; for s in data { @@ -812,6 +594,7 @@ impl Encodable for DhcpOption { } } DhcpOption::VendorOpts(VendorOpts { num, opts }) => { + e.write_u16(code.into())?; let mut buf = Vec::new(); let mut opt_enc = Encoder::new(&mut buf); opts.encode(&mut opt_enc)?; @@ -821,23 +604,28 @@ impl Encodable for DhcpOption { e.write_slice(&buf)?; } DhcpOption::InterfaceId(id) => { + e.write_u16(code.into())?; e.write_u16(id.len() as u16)?; e.write_slice(id)?; } DhcpOption::ReconfMsg(msg_type) => { + e.write_u16(code.into())?; e.write_u16(1)?; e.write_u8((*msg_type).into())?; } DhcpOption::ReconfAccept => { + e.write_u16(code.into())?; e.write_u16(0)?; } DhcpOption::DNSServers(addrs) => { + e.write_u16(code.into())?; e.write_u16(addrs.len() as u16 * 16)?; for addr in addrs { e.write_u128((*addr).into())?; } } DhcpOption::DomainList(names) => { + e.write_u16(code.into())?; let mut buf = Vec::new(); let mut name_encoder = BinEncoder::new(&mut buf); for name in names { @@ -846,26 +634,11 @@ impl Encodable for DhcpOption { e.write_u16(buf.len() as u16)?; e.write_slice(&buf)?; } - DhcpOption::IAPrefix(IAPrefix { - preferred_lifetime, - valid_lifetime, - prefix_len, - prefix_ip, - opts, - }) => { - let mut buf = Vec::new(); - let mut opt_enc = Encoder::new(&mut buf); - opts.encode(&mut opt_enc)?; - // buf now has total len - e.write_u16(25 + buf.len() as u16)?; - // write data - e.write_u32(*preferred_lifetime)?; - e.write_u32(*valid_lifetime)?; - e.write_u8(*prefix_len)?; - e.write_u128((*prefix_ip).into())?; - e.write_slice(&buf)?; + DhcpOption::IAPrefix(iaprefix) => { + iaprefix.encode(e)?; } DhcpOption::Unknown(UnknownOption { data, .. }) => { + e.write_u16(code.into())?; e.write_u16(data.len() as u16)?; e.write_slice(data)?; } diff --git a/src/v6/options/oro.rs b/src/v6/options/oro.rs new file mode 100644 index 0000000..0da2eca --- /dev/null +++ b/src/v6/options/oro.rs @@ -0,0 +1,66 @@ +use super::{DecodeResult, DhcpOption, EncodeResult, OROCode, OptionCode}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Option Request Option +/// +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ORO { + pub opts: Vec, +} + +impl Decodable for ORO { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read_u16()?; + let len = decoder.read_u16()? as usize; + Ok(ORO { + opts: { + decoder + .read_slice(len)? + .chunks_exact(2) + // TODO: use .array_chunks::<2>() when stable + .map(|code| OROCode::from(u16::from_be_bytes([code[0], code[1]]))) + .collect() + }, + }) + } +} + +impl Encodable for ORO { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::ORO.into())?; + // write len + e.write_u16(2 * self.opts.len() as u16)?; + // data + for &code in self.opts.iter() { + e.write_u16(code.into())?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_iata_encode_decode() { + let option = ORO { + opts: vec![OROCode::SolMaxRt], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = ORO::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = ORO::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/status.rs b/src/v6/options/status.rs new file mode 100644 index 0000000..82cdf0e --- /dev/null +++ b/src/v6/options/status.rs @@ -0,0 +1,149 @@ +use crate::v6::{DecodeResult, EncodeResult, OptionCode}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StatusCode { + pub status: Status, + // 2 + len + pub msg: String, +} + +impl Decodable for StatusCode { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + let _code = decoder.read_u16()?; + let len = decoder.read_u16()? as usize; + Ok(StatusCode { + status: decoder.read_u16()?.into(), + msg: decoder.read_string(len - 2)?, + }) + } +} + +impl Encodable for StatusCode { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::StatusCode.into())?; + e.write_u16(2 + self.msg.len() as u16)?; + e.write_u16(self.status.into())?; + e.write_slice(self.msg.as_bytes())?; + Ok(()) + } +} + +/// Status code +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Status { + Success, + UnspecFail, + NoAddrsAvail, + NoBinding, + NotOnLink, + UseMulticast, + NoPrefixAvail, + UnknownQueryType, + MalformedQuery, + NotConfigured, + NotAllowed, + QueryTerminated, + DataMissing, + CatchUpComplete, + NotSupported, + TLSConnectionRefused, + AddressInUse, + ConfigurationConflict, + MissingBindingInformation, + OutdatedBindingInformation, + ServerShuttingDown, + DNSUpdateNotSupported, + ExcessiveTimeSkew, + /// unknown/unimplemented message type + Unknown(u16), +} + +impl From for Status { + fn from(n: u16) -> Self { + use Status::*; + match n { + 0 => Success, + 1 => UnspecFail, + 2 => NoAddrsAvail, + 3 => NoBinding, + 4 => NotOnLink, + 5 => UseMulticast, + 6 => NoPrefixAvail, + 7 => UnknownQueryType, + 8 => MalformedQuery, + 9 => NotConfigured, + 10 => NotAllowed, + 11 => QueryTerminated, + 12 => DataMissing, + 13 => CatchUpComplete, + 14 => NotSupported, + 15 => TLSConnectionRefused, + 16 => AddressInUse, + 17 => ConfigurationConflict, + 18 => MissingBindingInformation, + 19 => OutdatedBindingInformation, + 20 => ServerShuttingDown, + 21 => DNSUpdateNotSupported, + 22 => ExcessiveTimeSkew, + _ => Unknown(n), + } + } +} +impl From for u16 { + fn from(n: Status) -> Self { + use Status::*; + match n { + Success => 0, + UnspecFail => 1, + NoAddrsAvail => 2, + NoBinding => 3, + NotOnLink => 4, + UseMulticast => 5, + NoPrefixAvail => 6, + UnknownQueryType => 7, + MalformedQuery => 8, + NotConfigured => 9, + NotAllowed => 10, + QueryTerminated => 11, + DataMissing => 12, + CatchUpComplete => 13, + NotSupported => 14, + TLSConnectionRefused => 15, + AddressInUse => 16, + ConfigurationConflict => 17, + MissingBindingInformation => 18, + OutdatedBindingInformation => 19, + ServerShuttingDown => 20, + DNSUpdateNotSupported => 21, + ExcessiveTimeSkew => 22, + Unknown(n) => n, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_status_code_encode_decode() { + let sc = StatusCode{status: 0xABCDu16.into(), msg: "message".into()}; + let mut encoder = vec![]; + + sc.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = StatusCode::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(sc, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = StatusCode::decode(&mut decoder).unwrap(); + assert_eq!(sc, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + + } +} From 63abed9678a28eae0b0dec788fe5ab4d88f50d92 Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 12 Oct 2022 18:50:37 +1000 Subject: [PATCH 08/12] V6 rewrite part 2 --- src/v6/duid.rs | 18 +- src/v6/mod.rs | 28 +-- src/v6/option_codes.rs | 7 +- src/v6/options/clientid.rs | 62 +++++ src/v6/options/dnsservers.rs | 66 +++++ src/v6/options/domainlist.rs | 50 ++++ src/v6/options/elapsedtime.rs | 56 +++++ src/v6/options/informationrefreshtime.rs | 32 +++ src/v6/options/interfaceid.rs | 56 +++++ src/v6/options/maxrt.rs | 57 +++++ src/v6/options/mod.rs | 300 +++++++++-------------- src/v6/options/oro.rs | 2 +- src/v6/options/preference.rs | 56 +++++ src/v6/options/rapidcommit.rs | 49 ++++ src/v6/options/reconfmsg.rs | 93 +++++++ src/v6/options/relaymsg.rs | 59 +++++ src/v6/options/serverid.rs | 62 +++++ src/v6/options/unicast.rs | 58 +++++ src/v6/options/userclass.rs | 93 +++++++ src/v6/options/vendorclass.rs | 93 +++++++ src/v6/options/vendoropts.rs | 107 ++++++++ 21 files changed, 1201 insertions(+), 203 deletions(-) create mode 100644 src/v6/options/clientid.rs create mode 100644 src/v6/options/dnsservers.rs create mode 100644 src/v6/options/domainlist.rs create mode 100644 src/v6/options/elapsedtime.rs create mode 100644 src/v6/options/informationrefreshtime.rs create mode 100644 src/v6/options/interfaceid.rs create mode 100644 src/v6/options/maxrt.rs create mode 100644 src/v6/options/preference.rs create mode 100644 src/v6/options/rapidcommit.rs create mode 100644 src/v6/options/reconfmsg.rs create mode 100644 src/v6/options/relaymsg.rs create mode 100644 src/v6/options/serverid.rs create mode 100644 src/v6/options/unicast.rs create mode 100644 src/v6/options/userclass.rs create mode 100644 src/v6/options/vendorclass.rs create mode 100644 src/v6/options/vendoropts.rs diff --git a/src/v6/duid.rs b/src/v6/duid.rs index 6e69d4a..229c34d 100644 --- a/src/v6/duid.rs +++ b/src/v6/duid.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{Encodable,Decodable,Encoder,Decoder}; -use crate::v6::{Ipv6Addr,EncodeError,DecodeError}; +use crate::v6::{Ipv6Addr, EncodeResult, DecodeResult}; use crate::v4::HType; @@ -76,3 +76,19 @@ impl From> for Duid { Self(v) } } + +impl Decodable for Duid { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + + Ok(Duid ( + decoder.buffer().into() + )) + } +} + +impl Encodable for Duid { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_slice(&self.0)?; + Ok(()) + } +} diff --git a/src/v6/mod.rs b/src/v6/mod.rs index 4428ef4..448c250 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -8,14 +8,14 @@ //! # fn main() -> Result<(), Box> { //! use dhcproto::{v6, Encodable, Encoder}; //! // arbitrary DUID -//! let duid = vec![ +//! let duid = v6::Duid::from(vec![ //! 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, -//! ]; +//! ]); //! // construct a new Message with a random xid //! let mut msg = v6::Message::new(v6::MessageType::Solicit); //! // set an option //! msg.opts_mut() -//! .insert(v6::DhcpOption::ClientId(duid)); +//! .insert(v6::DhcpOption::ClientId(v6::ClientId{id: duid})); //! //! // now encode to bytes //! let mut buf = Vec::new(); @@ -131,7 +131,7 @@ pub struct Message { xid: [u8; 3], /// Options /// - opts: DhcpOptions, + opts: MessageOptions, } impl Default for Message { @@ -139,7 +139,7 @@ impl Default for Message { Self { msg_type: MessageType::Solicit, xid: rand::random(), - opts: DhcpOptions::new(), + opts: MessageOptions::new(), } } } @@ -199,18 +199,18 @@ impl Message { } /// Get a reference to the message's options. - pub fn opts(&self) -> &DhcpOptions { + pub fn opts(&self) -> &MessageOptions { &self.opts } /// Set DHCP opts - pub fn set_opts(&mut self, opts: DhcpOptions) -> &mut Self { + pub fn set_opts(&mut self, opts: MessageOptions) -> &mut Self { self.opts = opts; self } /// Get a mutable reference to the message's options. - pub fn opts_mut(&mut self) -> &mut DhcpOptions { + pub fn opts_mut(&mut self) -> &mut MessageOptions { &mut self.opts } } @@ -346,7 +346,7 @@ impl Decodable for Message { Ok(Message { msg_type: decoder.read_u8()?.into(), xid: decoder.read::<3>()?, - opts: DhcpOptions::decode(decoder)?, + opts: MessageOptions::decode(decoder)?, }) } } @@ -387,7 +387,7 @@ pub struct RelayMessage { peer_addr: Ipv6Addr, /// Options /// - opts: DhcpOptions, + opts: RelayMessageOptions, } impl RelayMessage { @@ -404,18 +404,18 @@ impl RelayMessage { self.peer_addr } /// Get a reference to the message's options. - pub fn opts(&self) -> &DhcpOptions { + pub fn opts(&self) -> &RelayMessageOptions { &self.opts } /// Set DHCP opts - pub fn set_opts(&mut self, opts: DhcpOptions) -> &mut Self { + pub fn set_opts(&mut self, opts: RelayMessageOptions) -> &mut Self { self.opts = opts; self } /// Get a mutable reference to the message's options. - pub fn opts_mut(&mut self) -> &mut DhcpOptions { + pub fn opts_mut(&mut self) -> &mut RelayMessageOptions { &mut self.opts } } @@ -427,7 +427,7 @@ impl Decodable for RelayMessage { hop_count: decoder.read_u8()?, link_addr: decoder.read::<16>()?.into(), peer_addr: decoder.read::<16>()?.into(), - opts: DhcpOptions::decode(decoder)?, + opts: RelayMessageOptions::decode(decoder)?, }) } } diff --git a/src/v6/option_codes.rs b/src/v6/option_codes.rs index c14e9b9..32c1927 100644 --- a/src/v6/option_codes.rs +++ b/src/v6/option_codes.rs @@ -458,17 +458,20 @@ impl From<&DhcpOption> for OptionCode { Auth(_) => OptionCode::Auth, Unicast(_) => OptionCode::Unicast, StatusCode(_) => OptionCode::StatusCode, - RapidCommit => OptionCode::RapidCommit, + RapidCommit(_) => OptionCode::RapidCommit, UserClass(_) => OptionCode::UserClass, VendorClass(_) => OptionCode::VendorClass, VendorOpts(_) => OptionCode::VendorOpts, InterfaceId(_) => OptionCode::InterfaceId, ReconfMsg(_) => OptionCode::ReconfMsg, - ReconfAccept => OptionCode::ReconfAccept, + ReconfAccept(_) => OptionCode::ReconfAccept, DNSServers(_) => OptionCode::DNSServers, DomainList(_) => OptionCode::DomainList, IAPD(_) => OptionCode::IAPD, IAPrefix(_) => OptionCode::IAPrefix, + InformationRefreshTime(_) => OptionCode::InformationRefreshTime, + SolMaxRt(_) => OptionCode::SolMaxRt, + InfMaxRt(_) => OptionCode::InfMaxRt, Unknown(unknown) => unknown.into(), } } diff --git a/src/v6/options/clientid.rs b/src/v6/options/clientid.rs new file mode 100644 index 0000000..d99be41 --- /dev/null +++ b/src/v6/options/clientid.rs @@ -0,0 +1,62 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, Duid +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ClientId { + pub id: Duid, +} + +impl Decodable for ClientId { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + Ok(ClientId { + id: Duid::decode(&mut decoder)?, + }) + } +} + +impl Encodable for ClientId { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.id.encode(&mut opt_enc)?; + e.write_u16(OptionCode::ClientId.into())?; + e.write_u16(buf.len() as u16)?; + e.write_slice(&buf)?; + Ok(()) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_client_id_encode_decode() { + let option = ClientId { + id: Duid::enterprise(1, &[1,2,3]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = ClientId::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = ClientId::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/dnsservers.rs b/src/v6/options/dnsservers.rs new file mode 100644 index 0000000..b259628 --- /dev/null +++ b/src/v6/options/dnsservers.rs @@ -0,0 +1,66 @@ +use std::net::Ipv6Addr; + +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DNSServers { + pub servers: Vec, +} + +impl Decodable for DNSServers { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()?; + let mut servers = vec![]; + for _ in 0..(len/16){ + servers.push(decoder.read::<16>()?.into()); + } + + Ok(DNSServers { + servers, + }) + } +} + +impl Encodable for DNSServers { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::DNSServers.into())?; + e.write_u16((self.servers.len()*16) as u16)?; + for ip in self.servers.iter(){ + e.write_slice(&ip.octets())?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_dns_servrs_encode_decode() { + let option = DNSServers { + servers: vec!["FE80:ABCD:EF12::1".parse::().unwrap()] + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + println!("{:?}", encoder); + let decoded = DNSServers::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = DNSServers::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/domainlist.rs b/src/v6/options/domainlist.rs new file mode 100644 index 0000000..8eec294 --- /dev/null +++ b/src/v6/options/domainlist.rs @@ -0,0 +1,50 @@ +use trust_dns_proto::{ + rr::Name, + serialize::binary::{BinDecodable, BinDecoder, BinEncodable, BinEncoder}, +}; + +use super::{ + DecodeResult, EncodeResult, OptionCode, Domain, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DomainList { + pub domains: Vec, +} + +impl Decodable for DomainList { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()?; + let mut name_decoder = BinDecoder::new(decoder.read_slice(len as usize)?); + let mut names = Vec::new(); + while let Ok(name) = Name::read(&mut name_decoder) { + names.push(Domain(name)); + } + + Ok(DomainList { + domains: names, + }) + } +} + +impl Encodable for DomainList { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::DomainList.into())?; + let mut buf = Vec::new(); + let mut name_encoder = BinEncoder::new(&mut buf); + for name in self.domains.iter() { + name.0.emit(&mut name_encoder)?; + } + e.write_u16(buf.len() as u16)?; + e.write_slice(&buf)?; + Ok(()) + } +} + diff --git a/src/v6/options/elapsedtime.rs b/src/v6/options/elapsedtime.rs new file mode 100644 index 0000000..b2b6ae8 --- /dev/null +++ b/src/v6/options/elapsedtime.rs @@ -0,0 +1,56 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ElapsedTime { + pub time: u16, +} + +impl Decodable for ElapsedTime { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let _len = decoder.read_u16()? as usize; + Ok(ElapsedTime { + time: decoder.read_u16()?, + }) + } +} + +impl Encodable for ElapsedTime { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::ElapsedTime.into())?; + e.write_u16(2)?; + e.write_u16(self.time)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_server_id_encode_decode() { + let option = ElapsedTime { + time: 1, + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = ElapsedTime::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = ElapsedTime::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/informationrefreshtime.rs b/src/v6/options/informationrefreshtime.rs new file mode 100644 index 0000000..b65c99f --- /dev/null +++ b/src/v6/options/informationrefreshtime.rs @@ -0,0 +1,32 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct InformationRefreshTime { + pub value: u32, +} + +impl Decodable for InformationRefreshTime { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + Ok(InformationRefreshTime { + value: decoder.read_u32()?, + }) + } +} + +impl Encodable for InformationRefreshTime { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::InformationRefreshTime.into())?; + e.write_u16(4)?; + e.write_u32(self.value)?; + Ok(()) + } +} diff --git a/src/v6/options/interfaceid.rs b/src/v6/options/interfaceid.rs new file mode 100644 index 0000000..2b65109 --- /dev/null +++ b/src/v6/options/interfaceid.rs @@ -0,0 +1,56 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InterfaceId { + pub id: Vec, +} + +impl Decodable for InterfaceId { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + Ok(InterfaceId { + id: decoder.read_slice(len)?.into(), + }) + } +} + +impl Encodable for InterfaceId { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::InterfaceId.into())?; + e.write_u16(self.id.len() as u16)?; + e.write_slice(&self.id)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_interface_id_encode_decode() { + let option = InterfaceId { + id: vec![1,2,3,4], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = InterfaceId::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = InterfaceId::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/maxrt.rs b/src/v6/options/maxrt.rs new file mode 100644 index 0000000..bf8b4b0 --- /dev/null +++ b/src/v6/options/maxrt.rs @@ -0,0 +1,57 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SolMaxRt { + pub value: u32, +} + +impl Decodable for SolMaxRt { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + Ok(SolMaxRt { + value: decoder.read_u32()?, + }) + } +} + +impl Encodable for SolMaxRt { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::SolMaxRt.into())?; + e.write_u16(4)?; + e.write_u32(self.value)?; + Ok(()) + } +} + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct InfMaxRt { + pub value: u32, +} + +impl Decodable for InfMaxRt { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + Ok(InfMaxRt { + value: decoder.read_u32()?, + }) + } +} + +impl Encodable for InfMaxRt { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::InfMaxRt.into())?; + e.write_u16(4)?; + e.write_u32(self.value)?; + Ok(()) + } +} diff --git a/src/v6/options/mod.rs b/src/v6/options/mod.rs index 5deceed..c18eaaa 100644 --- a/src/v6/options/mod.rs +++ b/src/v6/options/mod.rs @@ -1,6 +1,7 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +//rfc8415 mod iana; pub use iana::*; mod iaaddr; @@ -17,11 +18,41 @@ mod auth; pub use auth::*; mod oro; pub use oro::*; +mod clientid; +pub use clientid::*; +mod serverid; +pub use serverid::*; +mod preference; +pub use preference::*; +mod elapsedtime; +pub use elapsedtime::*; +mod relaymsg; +pub use relaymsg::*; +mod unicast; +pub use unicast::*; +mod interfaceid; +pub use interfaceid::*; +mod rapidcommit; +pub use rapidcommit::*; +mod reconfmsg; +pub use reconfmsg::*; +mod userclass; +pub use userclass::*; +mod vendorclass; +pub use vendorclass::*; +mod vendoropts; +pub use vendoropts::*; +mod maxrt; +pub use maxrt::*; +mod informationrefreshtime; +pub use informationrefreshtime::*; + +//rfc3646 +mod dnsservers; +pub use dnsservers::*; +mod domainlist; +pub use domainlist::*; -use trust_dns_proto::{ - rr::Name, - serialize::binary::{BinDecodable, BinDecoder, BinEncodable, BinEncoder}, -}; use std::{cmp::Ordering, net::Ipv6Addr, ops::RangeInclusive}; @@ -30,7 +61,7 @@ use crate::{ decoder::{Decodable, Decoder}, encoder::{Encodable, Encoder}, error::{DecodeResult, EncodeResult}, - v6::{Duid, MessageType, OROCode, OptionCode, RelayMessage}, + v6::{Duid, MessageType, OROCode, OptionCode}, }; //helper macro for implementing sub-options (IANAOptions, ect) //useage: option_builder!(IANAOption, IANAOptions, DhcpOption, IAAddr, StatusCode); @@ -93,6 +124,9 @@ macro_rules! option_builder{ #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct $names(Vec<$name>); impl $names { + pub fn new() -> Self{ + Default::default() + } /// insert a new option into the list of opts pub fn insert>(&mut self, opt: T){ self.0.push(opt.into()) @@ -138,6 +172,10 @@ macro_rules! option_builder{ pub(crate) use option_builder; +option_builder!(MessageOption, MessageOptions, DhcpOption, ClientId, ServerId, IANA, IATA, IAAddr, IAPD, IAPrefix, ORO, Preference, ElapsedTime, Auth, Unicast, StatusCode, RapidCommit, UserClass, VendorClass, VendorOpts, ReconfMsg, ReconfAccept, InformationRefreshTime, SolMaxRt, InfMaxRt, DNSServers, DomainList); + +option_builder!(RelayMessageOption, RelayMessageOptions, DhcpOption, RelayMsg, VendorOpts); ///*interf. id?*/); + // server can send multiple IA_NA options to request multiple addresses // so we must be able to handle multiple of the same option type // @@ -226,9 +264,9 @@ impl FromIterator for DhcpOptions { #[derive(Debug, Clone, PartialEq, Eq)] pub enum DhcpOption { /// 1 - - ClientId(Vec), // should duid for this be bytes or string? + ClientId(ClientId), /// 2 - - ServerId(Vec), + ServerId(ServerId), /// 3 - IANA(IANA), /// 4 - @@ -238,20 +276,20 @@ pub enum DhcpOption { /// 6 - ORO(ORO), /// 7 - - Preference(u8), + Preference(Preference), /// 8 - /// Elapsed time in millis - ElapsedTime(u16), + ElapsedTime(ElapsedTime), /// 9 - - RelayMsg(RelayMessage), + RelayMsg(RelayMsg), /// 11 - Auth(Auth), /// 12 - - Unicast(Ipv6Addr), + Unicast(Unicast), /// 13 - StatusCode(StatusCode), /// 14 - - RapidCommit, + RapidCommit(RapidCommit), /// 15 - UserClass(UserClass), /// 16 - @@ -259,19 +297,22 @@ pub enum DhcpOption { /// 17 - VendorOpts(VendorOpts), /// 18 - - InterfaceId(Vec), + InterfaceId(InterfaceId), /// 19 - - ReconfMsg(MessageType), + ReconfMsg(ReconfMsg), /// 20 - - ReconfAccept, + ReconfAccept(ReconfAccept), /// 23 - - DNSServers(Vec), + DNSServers(DNSServers), /// 24 - - DomainList(Vec), + DomainList(DomainList), /// 25 - IAPD(IAPD), /// 26 - IAPrefix(IAPrefix), + InformationRefreshTime(InformationRefreshTime), + SolMaxRt(SolMaxRt), + InfMaxRt(InfMaxRt), /// An unknown or unimplemented option type Unknown(UnknownOption), } @@ -288,52 +329,6 @@ impl Ord for DhcpOption { } } -/// wrapper around interface id -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct InterfaceId { - pub id: String, -} - -/// vendor options -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VendorOpts { - pub num: u32, - // encapsulated options values - pub opts: DhcpOptions, -} - -/// vendor class -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct VendorClass { - pub num: u32, - pub data: Vec>, - // each item in data is [len (2 bytes) | data] -} - -/// user class -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UserClass { - pub data: Vec>, - // each item in data is [len (2 bytes) | data] -} - -#[inline] -fn decode_data(decoder: &'_ mut Decoder<'_>) -> Vec> { - let mut data = Vec::new(); - while let Ok(len) = decoder.read_u16() { - // if we can read the len and the string - match decoder.read_slice(len as usize) { - Ok(s) => data.push(s.to_vec()), - // push, otherwise stop - _ => break, - } - } - data -} - /// fallback for options not yet implemented #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -395,12 +390,10 @@ impl Decodable for DhcpOption { Ok(match code { OptionCode::ClientId => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::ClientId(decoder.read_slice(len)?.to_vec()) + DhcpOption::ClientId(ClientId::decode(decoder)?) } OptionCode::ServerId => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::ServerId(decoder.read_slice(len)?.to_vec()) + DhcpOption::ServerId(ServerId::decode(decoder)?) } OptionCode::IANA => { DhcpOption::IANA(IANA::decode(decoder)?) @@ -409,96 +402,70 @@ impl Decodable for DhcpOption { DhcpOption::IATA(IATA::decode(decoder)?) } OptionCode::IAAddr => { - DhcpOption::IAAddr(IAAddr::decode(decoder)?) } OptionCode::ORO => { DhcpOption::ORO(ORO::decode(decoder)?) } OptionCode::Preference => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::Preference(decoder.read_u8()?) + DhcpOption::Preference(Preference::decode(decoder)?) } OptionCode::ElapsedTime => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::ElapsedTime(decoder.read_u16()?) + DhcpOption::ElapsedTime(ElapsedTime::decode(decoder)?) } OptionCode::RelayMsg => { - decoder.read_u16()?;decoder.read_u16()?; - let mut relay_dec = Decoder::new(decoder.read_slice(len)?); - DhcpOption::RelayMsg(RelayMessage::decode(&mut relay_dec)?) + DhcpOption::RelayMsg(RelayMsg::decode(decoder)?) } OptionCode::Auth => { DhcpOption::Auth(Auth::decode(decoder)?) } OptionCode::Unicast => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::Unicast(decoder.read::<16>()?.into()) + DhcpOption::Unicast(Unicast::decode(decoder)?) } OptionCode::StatusCode => { DhcpOption::StatusCode(StatusCode::decode(decoder)?) } OptionCode::RapidCommit => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::RapidCommit + DhcpOption::RapidCommit(RapidCommit::decode(decoder)?) } OptionCode::UserClass => { - decoder.read_u16()?;decoder.read_u16()?; - let buf = decoder.read_slice(len)?; - DhcpOption::UserClass(UserClass { - data: decode_data(&mut Decoder::new(buf)), - }) + DhcpOption::UserClass(UserClass::decode(decoder)?) } OptionCode::VendorClass => { - decoder.read_u16()?;decoder.read_u16()?; - let num = decoder.read_u32()?; - let buf = decoder.read_slice(len - 4)?; - DhcpOption::VendorClass(VendorClass { - num, - data: decode_data(&mut Decoder::new(buf)), - }) + DhcpOption::VendorClass(VendorClass::decode(decoder)?) } OptionCode::VendorOpts => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::VendorOpts(VendorOpts { - num: decoder.read_u32()?, - opts: { - let mut opt_decoder = Decoder::new(decoder.read_slice(len - 4)?); - DhcpOptions::decode(&mut opt_decoder)? - }, - }) + DhcpOption::VendorOpts(VendorOpts::decode(decoder)?) } OptionCode::InterfaceId => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::InterfaceId(decoder.read_slice(len)?.to_vec()) + DhcpOption::InterfaceId(InterfaceId::decode(decoder)?) } OptionCode::ReconfMsg => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::ReconfMsg(decoder.read_u8()?.into()) + DhcpOption::ReconfMsg(ReconfMsg::decode(decoder)?) } OptionCode::ReconfAccept => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::ReconfAccept + DhcpOption::ReconfAccept(ReconfAccept::decode(decoder)?) } OptionCode::DNSServers => { - decoder.read_u16()?;decoder.read_u16()?; - DhcpOption::DNSServers(decoder.read_ipv6s(len)?) + DhcpOption::DNSServers(DNSServers::decode(decoder)?) } OptionCode::IAPD => { DhcpOption::IAPD(IAPD::decode(decoder)?) } OptionCode::IAPrefix => { DhcpOption::IAPrefix(IAPrefix::decode(decoder)?) + } + OptionCode::InfMaxRt => { + DhcpOption::InfMaxRt(InfMaxRt::decode(decoder)?) + } + OptionCode::InformationRefreshTime => { + DhcpOption::InformationRefreshTime(InformationRefreshTime::decode(decoder)?) + } + OptionCode::SolMaxRt => { + DhcpOption::SolMaxRt(SolMaxRt::decode(decoder)?) } OptionCode::DomainList => { - decoder.read_u16()?;decoder.read_u16()?; - let mut name_decoder = BinDecoder::new(decoder.read_slice(len as usize)?); - let mut names = Vec::new(); - while let Ok(name) = Name::read(&mut name_decoder) { - names.push(Domain(name)); - } - - DhcpOption::DomainList(names) + DhcpOption::DomainList(DomainList::decode(decoder)?) } // not yet implemented OptionCode::Unknown(code) => { @@ -522,10 +489,11 @@ impl Encodable for DhcpOption { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { let code: OptionCode = self.into(); match self { - DhcpOption::ClientId(duid) | DhcpOption::ServerId(duid) => { - e.write_u16(code.into())?; - e.write_u16(duid.len() as u16)?; - e.write_slice(duid)?; + DhcpOption::ClientId(duid) => { + duid.encode(e)?; + } + DhcpOption::ServerId(duid) => { + duid.encode(e)?; } DhcpOption::IANA(iana) => { iana.encode(e)?; @@ -543,96 +511,58 @@ impl Encodable for DhcpOption { oro.encode(e)?; } DhcpOption::Preference(pref) => { - e.write_u16(code.into())?; - e.write_u16(1)?; - e.write_u8(*pref)?; + pref.encode(e)?; } DhcpOption::ElapsedTime(elapsed) => { - e.write_u16(code.into())?; - e.write_u16(2)?; - e.write_u16(*elapsed)?; + elapsed.encode(e)?; } DhcpOption::RelayMsg(msg) => { - e.write_u16(code.into())?; - let mut buf = Vec::new(); - let mut relay_enc = Encoder::new(&mut buf); - msg.encode(&mut relay_enc)?; - - e.write_u16(buf.len() as u16)?; - e.write_slice(&buf)?; + msg.encode(e)?; } DhcpOption::Auth(auth) => { auth.encode(e)?; } DhcpOption::Unicast(addr) => { - e.write_u16(code.into())?; - e.write_u16(16)?; - e.write_u128((*addr).into())?; + addr.encode(e)?; } DhcpOption::StatusCode(status) => { status.encode(e)?; } - DhcpOption::RapidCommit => { - e.write_u16(code.into())?; - e.write_u16(0)?; + DhcpOption::RapidCommit(rc) => { + rc.encode(e)?; } - DhcpOption::UserClass(UserClass { data }) => { - e.write_u16(code.into())?; - e.write_u16(data.len() as u16)?; - for s in data { - e.write_u16(s.len() as u16)?; - e.write_slice(s)?; - } + DhcpOption::UserClass(uc) => { + uc.encode(e)?; } - DhcpOption::VendorClass(VendorClass { num, data }) => { - e.write_u16(code.into())?; - e.write_u16(4 + data.len() as u16)?; - e.write_u32(*num)?; - for s in data { - e.write_u16(s.len() as u16)?; - e.write_slice(s)?; - } - } - DhcpOption::VendorOpts(VendorOpts { num, opts }) => { - e.write_u16(code.into())?; - let mut buf = Vec::new(); - let mut opt_enc = Encoder::new(&mut buf); - opts.encode(&mut opt_enc)?; - // buf now has total len - e.write_u16(4 + buf.len() as u16)?; - e.write_u32(*num)?; - e.write_slice(&buf)?; + DhcpOption::VendorClass(vc) => { + vc.encode(e)?; + } + DhcpOption::VendorOpts(vopts) => { + vopts.encode(e)?; } DhcpOption::InterfaceId(id) => { - e.write_u16(code.into())?; - e.write_u16(id.len() as u16)?; - e.write_slice(id)?; + id.encode(e)?; } DhcpOption::ReconfMsg(msg_type) => { - e.write_u16(code.into())?; - e.write_u16(1)?; - e.write_u8((*msg_type).into())?; + msg_type.encode(e)?; } - DhcpOption::ReconfAccept => { - e.write_u16(code.into())?; - e.write_u16(0)?; + DhcpOption::ReconfAccept(accept) => { + accept.encode(e)?; + } + DhcpOption::SolMaxRt(auth) => { + auth.encode(e)?; + } + DhcpOption::InfMaxRt(auth) => { + auth.encode(e)?; + } + DhcpOption::InformationRefreshTime(auth) => { + auth.encode(e)?; } DhcpOption::DNSServers(addrs) => { - e.write_u16(code.into())?; - e.write_u16(addrs.len() as u16 * 16)?; - for addr in addrs { - e.write_u128((*addr).into())?; - } + addrs.encode(e)?; } DhcpOption::DomainList(names) => { - e.write_u16(code.into())?; - let mut buf = Vec::new(); - let mut name_encoder = BinEncoder::new(&mut buf); - for name in names { - name.0.emit(&mut name_encoder)?; - } - e.write_u16(buf.len() as u16)?; - e.write_slice(&buf)?; + names.encode(e)?; } DhcpOption::IAPrefix(iaprefix) => { iaprefix.encode(e)?; diff --git a/src/v6/options/oro.rs b/src/v6/options/oro.rs index 0da2eca..c7b14dd 100644 --- a/src/v6/options/oro.rs +++ b/src/v6/options/oro.rs @@ -1,4 +1,4 @@ -use super::{DecodeResult, DhcpOption, EncodeResult, OROCode, OptionCode}; +use super::{DecodeResult, EncodeResult, OROCode, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] diff --git a/src/v6/options/preference.rs b/src/v6/options/preference.rs new file mode 100644 index 0000000..09fb73b --- /dev/null +++ b/src/v6/options/preference.rs @@ -0,0 +1,56 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Preference { + pub pref: u8, +} + +impl Decodable for Preference { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let _len = decoder.read_u16()? as usize; + Ok(Preference { + pref: decoder.read_u8()?, + }) + } +} + +impl Encodable for Preference { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::Preference.into())?; + e.write_u16(1)?; + e.write_u8(self.pref)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_preference_encode_decode() { + let option = Preference { + pref: 1, + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = Preference::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = Preference::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/rapidcommit.rs b/src/v6/options/rapidcommit.rs new file mode 100644 index 0000000..3a5333f --- /dev/null +++ b/src/v6/options/rapidcommit.rs @@ -0,0 +1,49 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RapidCommit {} + +impl Decodable for RapidCommit { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + Ok(RapidCommit { + }) + } +} + +impl Encodable for RapidCommit { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::RapidCommit.into())?; + e.write_u16(0)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_rapid_commit_encode_decode() { + let option = RapidCommit {}; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = RapidCommit::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = RapidCommit::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/reconfmsg.rs b/src/v6/options/reconfmsg.rs new file mode 100644 index 0000000..8fd597b --- /dev/null +++ b/src/v6/options/reconfmsg.rs @@ -0,0 +1,93 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, MessageType, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ReconfMsg { + msg_type: MessageType, +} + +impl Decodable for ReconfMsg { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + Ok(ReconfMsg { + msg_type: decoder.read_u8()?.into() + }) + } +} + +impl Encodable for ReconfMsg { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::ReconfMsg.into())?; + e.write_u16(1)?; + e.write_u8(self.msg_type.into())?; + Ok(()) + } +} + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ReconfAccept {} + +impl Decodable for ReconfAccept { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + Ok(ReconfAccept { + }) + } +} + +impl Encodable for ReconfAccept { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::ReconfAccept.into())?; + e.write_u16(0)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_reconf_msg_encode_decode() { + let option = ReconfMsg { + msg_type: 1.into(), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = ReconfMsg::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = ReconfMsg::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } + #[test] + fn test_reconf_accept_encode_decode() { + let option = ReconfAccept { + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = ReconfAccept::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = ReconfAccept::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/relaymsg.rs b/src/v6/options/relaymsg.rs new file mode 100644 index 0000000..d15e6ed --- /dev/null +++ b/src/v6/options/relaymsg.rs @@ -0,0 +1,59 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RelayMsg { + pub msg: Vec, +} + +impl Decodable for RelayMsg { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + + Ok(RelayMsg { + msg: decoder.read_slice(len)?.into(), + }) + } +} + +impl Encodable for RelayMsg { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::RelayMsg.into())?; + e.write_u16(self.msg.len() as u16)?; + e.write_slice(&self.msg)?; + Ok(()) + } +} + +//impl From for Message? + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_server_id_encode_decode() { + let option = RelayMsg { + msg: vec![1,2,3], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = RelayMsg::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = RelayMsg::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/serverid.rs b/src/v6/options/serverid.rs new file mode 100644 index 0000000..50fd5ac --- /dev/null +++ b/src/v6/options/serverid.rs @@ -0,0 +1,62 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, Duid +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ServerId { + pub id: Duid, +} + +impl Decodable for ServerId { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + Ok(ServerId { + id: Duid::decode(&mut decoder)?, + }) + } +} + +impl Encodable for ServerId { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.id.encode(&mut opt_enc)?; + e.write_u16(OptionCode::ServerId.into())?; + e.write_u16(buf.len() as u16)?; + e.write_slice(&buf)?; + Ok(()) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_server_id_encode_decode() { + let option = ServerId { + id: Duid::enterprise(1, &[1,2,3]), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = ServerId::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = ServerId::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/unicast.rs b/src/v6/options/unicast.rs new file mode 100644 index 0000000..bb5b47c --- /dev/null +++ b/src/v6/options/unicast.rs @@ -0,0 +1,58 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, Ipv6Addr +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Unicast { + pub server_address: Ipv6Addr, +} + +impl Decodable for Unicast { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let _len = decoder.read_u16()? as usize; + Ok(Unicast { + server_address: decoder.read::<16>()?.into(), + }) + } +} + +impl Encodable for Unicast { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::Unicast.into())?; + e.write_u16(16)?; + e.write_u128(self.server_address.into())?; + Ok(()) + } +} + +//impl From for Message? + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_server_id_encode_decode() { + let option = Unicast { + server_address: "FE80::".parse().unwrap(), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = Unicast::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = Unicast::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/userclass.rs b/src/v6/options/userclass.rs new file mode 100644 index 0000000..974d207 --- /dev/null +++ b/src/v6/options/userclass.rs @@ -0,0 +1,93 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UserClass { + pub data: Vec, +} + +impl Decodable for UserClass { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()?; + let mut data = vec![]; + let mut decoder = Decoder::new(decoder.read_slice(len as usize)?); + let mut remaining_len = len; + while remaining_len > 0{ + let len = decoder.peek_u16()?; + data.push(UserClassData::decode(&mut decoder)?); + remaining_len -= len+2; + } + Ok(UserClass { + data + }) + } +} + +impl Encodable for UserClass { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::UserClass.into())?; + let mut data = vec![]; + let mut dataenc = Encoder::new(&mut data); + for ucd in self.data.iter(){ + ucd.encode(&mut dataenc)?; + } + e.write_u16(data.len() as u16)?; + e.write_slice(&data)?; + Ok(()) + } +} + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UserClassData{ + pub data: Vec, +} + +impl Decodable for UserClassData { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + let len = decoder.read_u16()?; + Ok(UserClassData { + data: decoder.read_slice(len.into())?.into(), + }) + } +} + +impl Encodable for UserClassData { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(self.data.len() as u16)?; + e.write_slice(&self.data)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_userclass_encode_decode() { + let option = UserClass { + data: vec![UserClassData{data:vec![1,2,3,4]},UserClassData{data:vec![1]},UserClassData{data:vec![1,2]}], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + + let decoded = UserClass::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = UserClass::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/vendorclass.rs b/src/v6/options/vendorclass.rs new file mode 100644 index 0000000..ec0e8cf --- /dev/null +++ b/src/v6/options/vendorclass.rs @@ -0,0 +1,93 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Identity Association for Non-Temporary Addresses +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VendorClass { + pub data: Vec, +} + +impl Decodable for VendorClass { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()?; + let mut data = vec![]; + let mut decoder = Decoder::new(decoder.read_slice(len as usize)?); + let mut remaining_len = len; + while remaining_len > 0{ + let len = decoder.peek_u16()?; + data.push(VendorClassData::decode(&mut decoder)?); + remaining_len -= len+2; + } + Ok(VendorClass { + data + }) + } +} + +impl Encodable for VendorClass { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::VendorClass.into())?; + let mut data = vec![]; + let mut dataenc = Encoder::new(&mut data); + for ucd in self.data.iter(){ + ucd.encode(&mut dataenc)?; + } + e.write_u16(data.len() as u16)?; + e.write_slice(&data)?; + Ok(()) + } +} + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VendorClassData{ + pub data: Vec, +} + +impl Decodable for VendorClassData { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + let len = decoder.read_u16()?; + Ok(VendorClassData { + data: decoder.read_slice(len.into())?.into(), + }) + } +} + +impl Encodable for VendorClassData { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(self.data.len() as u16)?; + e.write_slice(&self.data)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_vendorclass_encode_decode() { + let option = VendorClass { + data: vec![VendorClassData{data:vec![1,2,3,4]},VendorClassData{data:vec![1]},VendorClassData{data:vec![1,2]}], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + + let decoded = VendorClass::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = VendorClass::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} diff --git a/src/v6/options/vendoropts.rs b/src/v6/options/vendoropts.rs new file mode 100644 index 0000000..ac32d00 --- /dev/null +++ b/src/v6/options/vendoropts.rs @@ -0,0 +1,107 @@ +use super::{ + DecodeResult, EncodeResult, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Vendor defined options +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VendorOpts { + pub enterprise_number: u32, + pub opts: Vec, +} + +impl Decodable for VendorOpts { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()?; + let enterprise_number = decoder.read_u32()?; + let mut opts = vec![]; + let mut used_len = 4; + while used_len < len{ + let opt = VendorOption::decode(decoder)?; + used_len += opt.len() + 4; + opts.push(opt); + } + Ok(VendorOpts { + enterprise_number, + opts, + }) + } +} + +impl Encodable for VendorOpts { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + let mut data = vec![]; + let mut enc = Encoder::new(&mut data); + for opt in self.opts.iter(){ + opt.encode(&mut enc)?; + } + e.write_u16(OptionCode::VendorOpts.into())?; + e.write_u16(data.len() as u16 + 4)?; + e.write_u32(self.enterprise_number)?; + e.write_slice(&data)?; + Ok(()) + } +} + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VendorOption{ + pub code: u16, + pub data: Vec, +} + +impl VendorOption{ + fn len(&self) -> u16{ + self.data.len() as u16 + } +} + +impl Decodable for VendorOption { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + let code = decoder.read_u16()?; + let len = decoder.read_u16()?; + Ok(VendorOption { + code, + data: decoder.read_slice(len.into())?.into(), + }) + } +} + +impl Encodable for VendorOption { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(self.code)?; + e.write_u16(self.data.len() as u16)?; + e.write_slice(&self.data)?; + Ok(()) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_vendoropts_encode_decode() { + let option = VendorOpts { + enterprise_number: 0xABCD, + opts: vec![VendorOption{code: 0xABCD, data: vec![1,2]},VendorOption{code: 0xACBD, data: vec![1,2,3]}], + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = VendorOpts::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = VendorOpts::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} From 046c2affba5f6c60be55d9c764c87ad85d35570f Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 12 Oct 2022 22:03:57 +1000 Subject: [PATCH 09/12] V6: rewrite part 3, feature parity --- src/decoder.rs | 10 +- src/v6/duid.rs | 10 +- src/v6/mod.rs | 10 +- src/v6/option_codes.rs | 6 +- src/v6/options/auth.rs | 16 +- src/v6/options/clientid.rs | 9 +- src/v6/options/dnsservers.rs | 33 ++- src/v6/options/domainlist.rs | 14 +- src/v6/options/elapsedtime.rs | 10 +- src/v6/options/iaaddr.rs | 10 +- src/v6/options/iapd.rs | 2 +- src/v6/options/iaprefix.rs | 19 +- src/v6/options/iata.rs | 4 +- src/v6/options/informationrefreshtime.rs | 5 +- src/v6/options/interfaceid.rs | 7 +- src/v6/options/maxrt.rs | 6 +- src/v6/options/mod.rs | 287 +++++++++++++---------- src/v6/options/preference.rs | 9 +- src/v6/options/rapidcommit.rs | 12 +- src/v6/options/reconfmsg.rs | 24 +- src/v6/options/relaymsg.rs | 11 +- src/v6/options/serverid.rs | 9 +- src/v6/options/status.rs | 24 +- src/v6/options/unicast.rs | 5 +- src/v6/options/userclass.rs | 63 ++--- src/v6/options/vendorclass.rs | 63 ++--- src/v6/options/vendoropts.rs | 80 ++++--- 27 files changed, 373 insertions(+), 385 deletions(-) diff --git a/src/decoder.rs b/src/decoder.rs index 875e776..710282b 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -39,10 +39,12 @@ impl<'a> Decoder<'a> { Ok(u8::from_be_bytes(self.peek::<{ mem::size_of::() }>()?)) } - /// peek at the next u16 without advancing the internal pointer - pub fn peek_u16(&self) -> DecodeResult { - Ok(u16::from_be_bytes(self.peek::<{mem::size_of::() }>()?)) - } + /// peek at the next u16 without advancing the internal pointer + pub fn peek_u16(&self) -> DecodeResult { + Ok(u16::from_be_bytes( + self.peek::<{ mem::size_of::() }>()?, + )) + } /// read a u8 pub fn read_u8(&mut self) -> DecodeResult { diff --git a/src/v6/duid.rs b/src/v6/duid.rs index 229c34d..8ab00e0 100644 --- a/src/v6/duid.rs +++ b/src/v6/duid.rs @@ -1,10 +1,9 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{Encodable,Decodable,Encoder,Decoder}; -use crate::v6::{Ipv6Addr, EncodeResult, DecodeResult}; use crate::v4::HType; - +use crate::v6::{DecodeResult, EncodeResult, Ipv6Addr}; +use crate::{Decodable, Decoder, Encodable, Encoder}; /// Duid helper type #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -79,10 +78,7 @@ impl From> for Duid { impl Decodable for Duid { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - - Ok(Duid ( - decoder.buffer().into() - )) + Ok(Duid(decoder.buffer().into())) } } diff --git a/src/v6/mod.rs b/src/v6/mod.rs index 448c250..8fea7c8 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -15,7 +15,7 @@ //! let mut msg = v6::Message::new(v6::MessageType::Solicit); //! // set an option //! msg.opts_mut() -//! .insert(v6::DhcpOption::ClientId(v6::ClientId{id: duid})); +//! .insert(v6::ClientId{id: duid}); //! //! // now encode to bytes //! let mut buf = Vec::new(); @@ -52,10 +52,10 @@ //! # Ok(()) } //! ``` //! +mod duid; +mod option_codes; mod options; mod oro_codes; -mod option_codes; -mod duid; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -63,10 +63,10 @@ use serde::{Deserialize, Serialize}; use std::{convert::TryInto, fmt, net::Ipv6Addr}; // re-export submodules from proto::msg +pub use self::duid::*; +pub use self::option_codes::*; pub use self::options::*; pub use self::oro_codes::*; -pub use self::option_codes::*; -pub use self::duid::*; pub use crate::{ decoder::{Decodable, Decoder}, diff --git a/src/v6/option_codes.rs b/src/v6/option_codes.rs index 32c1927..4b49314 100644 --- a/src/v6/option_codes.rs +++ b/src/v6/option_codes.rs @@ -469,9 +469,9 @@ impl From<&DhcpOption> for OptionCode { DomainList(_) => OptionCode::DomainList, IAPD(_) => OptionCode::IAPD, IAPrefix(_) => OptionCode::IAPrefix, - InformationRefreshTime(_) => OptionCode::InformationRefreshTime, - SolMaxRt(_) => OptionCode::SolMaxRt, - InfMaxRt(_) => OptionCode::InfMaxRt, + InformationRefreshTime(_) => OptionCode::InformationRefreshTime, + SolMaxRt(_) => OptionCode::SolMaxRt, + InfMaxRt(_) => OptionCode::InfMaxRt, Unknown(unknown) => unknown.into(), } } diff --git a/src/v6/options/auth.rs b/src/v6/options/auth.rs index 93ca6dc..a95de7f 100644 --- a/src/v6/options/auth.rs +++ b/src/v6/options/auth.rs @@ -1,6 +1,4 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] @@ -41,7 +39,7 @@ impl Encodable for Auth { e.write_u8(self.rdm)?; e.write_u64(self.replay_detection)?; e.write_slice(&self.info)?; - Ok(()) + Ok(()) } } @@ -51,11 +49,11 @@ mod tests { #[test] fn test_iata_encode_decode() { let option = Auth { - proto: 0xC, - algo: 0xB, - rdm: 0xA, - replay_detection: 0xABCD, - info: vec![1,2,3], + proto: 0xC, + algo: 0xB, + rdm: 0xA, + replay_detection: 0xABCD, + info: vec![1, 2, 3], }; let mut encoder = vec![]; diff --git a/src/v6/options/clientid.rs b/src/v6/options/clientid.rs index d99be41..f607129 100644 --- a/src/v6/options/clientid.rs +++ b/src/v6/options/clientid.rs @@ -1,12 +1,10 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, Duid -}; +use super::{DecodeResult, Duid, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses +/// Client Identity #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ClientId { @@ -37,14 +35,13 @@ impl Encodable for ClientId { } } - #[cfg(test)] mod tests { use super::*; #[test] fn test_client_id_encode_decode() { let option = ClientId { - id: Duid::enterprise(1, &[1,2,3]), + id: Duid::enterprise(1, &[1, 2, 3]), }; let mut encoder = vec![]; diff --git a/src/v6/options/dnsservers.rs b/src/v6/options/dnsservers.rs index b259628..4c2ab87 100644 --- a/src/v6/options/dnsservers.rs +++ b/src/v6/options/dnsservers.rs @@ -1,14 +1,11 @@ use std::net::Ipv6Addr; -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DNSServers { @@ -18,25 +15,23 @@ pub struct DNSServers { impl Decodable for DNSServers { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<2>()?; - let len = decoder.read_u16()?; - let mut servers = vec![]; - for _ in 0..(len/16){ - servers.push(decoder.read::<16>()?.into()); - } - - Ok(DNSServers { - servers, - }) + let len = decoder.read_u16()?; + let mut servers = vec![]; + for _ in 0..(len / 16) { + servers.push(decoder.read::<16>()?.into()); + } + + Ok(DNSServers { servers }) } } impl Encodable for DNSServers { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(OptionCode::DNSServers.into())?; - e.write_u16((self.servers.len()*16) as u16)?; - for ip in self.servers.iter(){ - e.write_slice(&ip.octets())?; - } + e.write_u16((self.servers.len() * 16) as u16)?; + for ip in self.servers.iter() { + e.write_slice(&ip.octets())?; + } Ok(()) } } @@ -47,13 +42,13 @@ mod tests { #[test] fn test_dns_servrs_encode_decode() { let option = DNSServers { - servers: vec!["FE80:ABCD:EF12::1".parse::().unwrap()] + servers: vec!["FE80:ABCD:EF12::1".parse::().unwrap()], }; let mut encoder = vec![]; option.encode(&mut Encoder::new(&mut encoder)).unwrap(); - println!("{:?}", encoder); + println!("{:?}", encoder); let decoded = DNSServers::decode(&mut Decoder::new(&encoder)).unwrap(); assert_eq!(option, decoded); diff --git a/src/v6/options/domainlist.rs b/src/v6/options/domainlist.rs index 8eec294..c9e3c75 100644 --- a/src/v6/options/domainlist.rs +++ b/src/v6/options/domainlist.rs @@ -3,15 +3,12 @@ use trust_dns_proto::{ serialize::binary::{BinDecodable, BinDecoder, BinEncodable, BinEncoder}, }; -use super::{ - DecodeResult, EncodeResult, OptionCode, Domain, -}; +use super::{DecodeResult, Domain, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DomainList { @@ -21,16 +18,14 @@ pub struct DomainList { impl Decodable for DomainList { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<2>()?; - let len = decoder.read_u16()?; + let len = decoder.read_u16()?; let mut name_decoder = BinDecoder::new(decoder.read_slice(len as usize)?); let mut names = Vec::new(); while let Ok(name) = Name::read(&mut name_decoder) { names.push(Domain(name)); } - Ok(DomainList { - domains: names, - }) + Ok(DomainList { domains: names }) } } @@ -44,7 +39,6 @@ impl Encodable for DomainList { } e.write_u16(buf.len() as u16)?; e.write_slice(&buf)?; - Ok(()) + Ok(()) } } - diff --git a/src/v6/options/elapsedtime.rs b/src/v6/options/elapsedtime.rs index b2b6ae8..e49514d 100644 --- a/src/v6/options/elapsedtime.rs +++ b/src/v6/options/elapsedtime.rs @@ -1,12 +1,10 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses +/// Time in milliseconds elapsed since the start of negotiation. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct ElapsedTime { @@ -37,9 +35,7 @@ mod tests { use super::*; #[test] fn test_server_id_encode_decode() { - let option = ElapsedTime { - time: 1, - }; + let option = ElapsedTime { time: 1 }; let mut encoder = vec![]; diff --git a/src/v6/options/iaaddr.rs b/src/v6/options/iaaddr.rs index 614e2a6..a1764fc 100644 --- a/src/v6/options/iaaddr.rs +++ b/src/v6/options/iaaddr.rs @@ -1,5 +1,5 @@ use crate::v6::DhcpOption; -use crate::v6::{DecodeResult, EncodeResult, Ipv6Addr, OptionCode, StatusCode, option_builder}; +use crate::v6::{option_builder, DecodeResult, EncodeResult, Ipv6Addr, OptionCode, StatusCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] @@ -20,9 +20,9 @@ pub struct IAAddr { impl Decodable for IAAddr { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - decoder.read::<2>()?; - let len = decoder.read_u16()? as usize; - let mut decoder = Decoder::new(decoder.read_slice(len)?); + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); Ok(IAAddr { addr: decoder.read::<16>()?.into(), preferred_life: decoder.read_u32()?, @@ -38,7 +38,7 @@ impl Encodable for IAAddr { let mut buf = Vec::new(); let mut opt_enc = Encoder::new(&mut buf); self.opts.encode(&mut opt_enc)?; - e.write_u16(OptionCode::IAAddr.into())?; + e.write_u16(OptionCode::IAAddr.into())?; // buf now has total len e.write_u16(24 + buf.len() as u16)?; // data diff --git a/src/v6/options/iapd.rs b/src/v6/options/iapd.rs index 734df84..891a65c 100644 --- a/src/v6/options/iapd.rs +++ b/src/v6/options/iapd.rs @@ -26,7 +26,7 @@ impl Decodable for IAPD { t1: decoder.read_u32()?, t2: decoder.read_u32()?, opts: { - let mut dec = Decoder::new(decoder.read_slice(len-12)?); + let mut dec = Decoder::new(decoder.read_slice(len - 12)?); IAPDOptions::decode(&mut dec)? }, }) diff --git a/src/v6/options/iaprefix.rs b/src/v6/options/iaprefix.rs index addd85b..2c2d02c 100644 --- a/src/v6/options/iaprefix.rs +++ b/src/v6/options/iaprefix.rs @@ -1,6 +1,4 @@ -use super::{ - option_builder, DecodeResult, DhcpOption, EncodeResult, Ipv6Addr, OptionCode, -}; +use super::{option_builder, DecodeResult, DhcpOption, EncodeResult, Ipv6Addr, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] @@ -20,22 +18,21 @@ pub struct IAPrefix { impl Decodable for IAPrefix { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - decoder.read::<2>()?; - let len = decoder.read_u16()? as usize; + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; Ok(IAPrefix { preferred_lifetime: decoder.read_u32()?, valid_lifetime: decoder.read_u32()?, prefix_len: decoder.read_u8()?, prefix_ip: decoder.read::<16>()?.into(), opts: { - let mut dec = Decoder::new(decoder.read_slice(len-25)?); + let mut dec = Decoder::new(decoder.read_slice(len - 25)?); IAPrefixOptions::decode(&mut dec)? - } + }, }) } } - impl Encodable for IAPrefix { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(OptionCode::IAPrefix.into())?; @@ -64,9 +61,9 @@ mod tests { fn test_iapd_encode_decode() { let option = IAPrefix { preferred_lifetime: 0, - valid_lifetime: 0, - prefix_len: 0, - prefix_ip: "FE80::".parse().unwrap(), + valid_lifetime: 0, + prefix_len: 0, + prefix_ip: "FE80::".parse().unwrap(), // 12 + opts.len() opts: IAPrefixOptions(vec![]), }; diff --git a/src/v6/options/iata.rs b/src/v6/options/iata.rs index b80fb08..bfc281d 100644 --- a/src/v6/options/iata.rs +++ b/src/v6/options/iata.rs @@ -17,7 +17,7 @@ pub struct IATA { impl Decodable for IATA { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - decoder.read::<2>()?; + decoder.read::<2>()?; let len = decoder.read_u16()? as usize; let mut decoder = Decoder::new(decoder.read_slice(len)?); Ok(IATA { @@ -51,7 +51,7 @@ mod tests { #[test] fn test_iata_encode_decode() { let option = IATA { - id: 0, + id: 0, // 12 + opts.len() opts: IATAOptions(vec![StatusCode { status: 0xABCDu16.into(), diff --git a/src/v6/options/informationrefreshtime.rs b/src/v6/options/informationrefreshtime.rs index b65c99f..4cc1721 100644 --- a/src/v6/options/informationrefreshtime.rs +++ b/src/v6/options/informationrefreshtime.rs @@ -1,12 +1,9 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct InformationRefreshTime { diff --git a/src/v6/options/interfaceid.rs b/src/v6/options/interfaceid.rs index 2b65109..2b61736 100644 --- a/src/v6/options/interfaceid.rs +++ b/src/v6/options/interfaceid.rs @@ -1,12 +1,9 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct InterfaceId { @@ -38,7 +35,7 @@ mod tests { #[test] fn test_interface_id_encode_decode() { let option = InterfaceId { - id: vec![1,2,3,4], + id: vec![1, 2, 3, 4], }; let mut encoder = vec![]; diff --git a/src/v6/options/maxrt.rs b/src/v6/options/maxrt.rs index bf8b4b0..8ac35bb 100644 --- a/src/v6/options/maxrt.rs +++ b/src/v6/options/maxrt.rs @@ -1,12 +1,9 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct SolMaxRt { @@ -31,7 +28,6 @@ impl Encodable for SolMaxRt { } } -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct InfMaxRt { diff --git a/src/v6/options/mod.rs b/src/v6/options/mod.rs index c18eaaa..ca495b6 100644 --- a/src/v6/options/mod.rs +++ b/src/v6/options/mod.rs @@ -53,7 +53,6 @@ pub use dnsservers::*; mod domainlist; pub use domainlist::*; - use std::{cmp::Ordering, net::Ipv6Addr, ops::RangeInclusive}; pub use crate::Domain; @@ -74,6 +73,7 @@ macro_rules! option_builder{ $( $subnames($subnames), )* + ///invalid or unknown Unknown($mastername), } $( @@ -105,6 +105,17 @@ macro_rules! option_builder{ } } } + impl From<&$name> for OptionCode{ + fn from(option: &$name) -> OptionCode{ + match option{ + $( + $name :: $subnames(_) => OptionCode :: $subnames, + )* + $name :: Unknown(u) => OptionCode::from(u), + + } + } + } impl Encodable for $name { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { $mastername::from(self).encode(e) @@ -124,12 +135,56 @@ macro_rules! option_builder{ #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct $names(Vec<$name>); impl $names { + /// construct empty $names pub fn new() -> Self{ Default::default() } + /// get the first element matching this option code + pub fn get(&self, code: OptionCode) -> Option<&$name>{ + use crate::v6::first; + use crate::v6::OptionCode; + let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; + self.0.get(first) + } + /// get all elements matching this option code + pub fn get_all(&self, code: OptionCode) -> Option<&[$name]>{ + use crate::v6::range_binsearch; + use crate::v6::OptionCode; + let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; + Some(&self.0[range]) + } + /// get the first element matching this option code + pub fn get_mut(&mut self, code: OptionCode) -> Option<&mut $name>{ + use crate::v6::first; + use crate::v6::OptionCode; + let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; + self.0.get_mut(first) + } + /// get all elements matching this option + pub fn get_mut_all(&mut self, code: OptionCode) -> Option<&mut [$name]>{ + use crate::v6::range_binsearch; + use crate::v6::OptionCode; + let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; + Some(&mut self.0[range]) + } + /// remove the first element with a matching option code + pub fn remove(&mut self, code: OptionCode) -> Option<$name>{ + use crate::v6::first; + use crate::v6::OptionCode; + let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; + Some(self.0.remove(first)) + } + pub fn remove_all(&mut self, code: OptionCode) -> Option + '_>{ + use crate::v6::range_binsearch; + use crate::v6::OptionCode; + let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; + Some(self.0.drain(range)) + } /// insert a new option into the list of opts pub fn insert>(&mut self, opt: T){ - self.0.push(opt.into()) + let opt = opt.into(); + let i = self.0.partition_point(|x| OptionCode::from(x) < OptionCode::from(&opt)); + self.0.insert(i, opt) } /// return a mutable ref to an iterator pub fn iter(&self) -> impl Iterator { @@ -172,9 +227,44 @@ macro_rules! option_builder{ pub(crate) use option_builder; -option_builder!(MessageOption, MessageOptions, DhcpOption, ClientId, ServerId, IANA, IATA, IAAddr, IAPD, IAPrefix, ORO, Preference, ElapsedTime, Auth, Unicast, StatusCode, RapidCommit, UserClass, VendorClass, VendorOpts, ReconfMsg, ReconfAccept, InformationRefreshTime, SolMaxRt, InfMaxRt, DNSServers, DomainList); - -option_builder!(RelayMessageOption, RelayMessageOptions, DhcpOption, RelayMsg, VendorOpts); ///*interf. id?*/); +option_builder!( + MessageOption, + MessageOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAAddr, + IAPD, + IAPrefix, + ORO, + Preference, + ElapsedTime, + Auth, + Unicast, + StatusCode, + RapidCommit, + UserClass, + VendorClass, + VendorOpts, + ReconfMsg, + ReconfAccept, + InformationRefreshTime, + SolMaxRt, + InfMaxRt, + DNSServers, + DomainList +); + +option_builder!( + RelayMessageOption, + RelayMessageOptions, + DhcpOption, + RelayMsg, + VendorOpts, + InterfaceId +); // server can send multiple IA_NA options to request multiple addresses // so we must be able to handle multiple of the same option type @@ -310,9 +400,9 @@ pub enum DhcpOption { IAPD(IAPD), /// 26 - IAPrefix(IAPrefix), - InformationRefreshTime(InformationRefreshTime), - SolMaxRt(SolMaxRt), - InfMaxRt(InfMaxRt), + InformationRefreshTime(InformationRefreshTime), + SolMaxRt(SolMaxRt), + InfMaxRt(InfMaxRt), /// An unknown or unimplemented option type Unknown(UnknownOption), } @@ -384,99 +474,51 @@ impl Encodable for DhcpOptions { impl Decodable for DhcpOption { fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { - let code = decoder.peek_u16()?.into(); - let tmp = Decoder::new(&decoder.buffer()[2..]); - let len = tmp.peek_u16()? as usize; + let code = decoder.peek_u16()?.into(); + let tmp = Decoder::new(&decoder.buffer()[2..]); + let len = tmp.peek_u16()? as usize; Ok(match code { - OptionCode::ClientId => { - DhcpOption::ClientId(ClientId::decode(decoder)?) - } - OptionCode::ServerId => { - DhcpOption::ServerId(ServerId::decode(decoder)?) - } - OptionCode::IANA => { - DhcpOption::IANA(IANA::decode(decoder)?) - } - OptionCode::IATA => { - DhcpOption::IATA(IATA::decode(decoder)?) - } - OptionCode::IAAddr => { - DhcpOption::IAAddr(IAAddr::decode(decoder)?) - } - OptionCode::ORO => { - DhcpOption::ORO(ORO::decode(decoder)?) - } - OptionCode::Preference => { - DhcpOption::Preference(Preference::decode(decoder)?) - } - OptionCode::ElapsedTime => { - DhcpOption::ElapsedTime(ElapsedTime::decode(decoder)?) - } - OptionCode::RelayMsg => { - DhcpOption::RelayMsg(RelayMsg::decode(decoder)?) - } - OptionCode::Auth => { - DhcpOption::Auth(Auth::decode(decoder)?) - } - OptionCode::Unicast => { - DhcpOption::Unicast(Unicast::decode(decoder)?) - } - OptionCode::StatusCode => { - DhcpOption::StatusCode(StatusCode::decode(decoder)?) - } - OptionCode::RapidCommit => { - DhcpOption::RapidCommit(RapidCommit::decode(decoder)?) - } - OptionCode::UserClass => { - DhcpOption::UserClass(UserClass::decode(decoder)?) - } - OptionCode::VendorClass => { - DhcpOption::VendorClass(VendorClass::decode(decoder)?) - } - OptionCode::VendorOpts => { - DhcpOption::VendorOpts(VendorOpts::decode(decoder)?) - } - OptionCode::InterfaceId => { - DhcpOption::InterfaceId(InterfaceId::decode(decoder)?) - } - OptionCode::ReconfMsg => { - DhcpOption::ReconfMsg(ReconfMsg::decode(decoder)?) - } - OptionCode::ReconfAccept => { - DhcpOption::ReconfAccept(ReconfAccept::decode(decoder)?) - } - OptionCode::DNSServers => { - DhcpOption::DNSServers(DNSServers::decode(decoder)?) - } - OptionCode::IAPD => { - DhcpOption::IAPD(IAPD::decode(decoder)?) - } - OptionCode::IAPrefix => { - DhcpOption::IAPrefix(IAPrefix::decode(decoder)?) - } - OptionCode::InfMaxRt => { - DhcpOption::InfMaxRt(InfMaxRt::decode(decoder)?) - } - OptionCode::InformationRefreshTime => { + OptionCode::ClientId => DhcpOption::ClientId(ClientId::decode(decoder)?), + OptionCode::ServerId => DhcpOption::ServerId(ServerId::decode(decoder)?), + OptionCode::IANA => DhcpOption::IANA(IANA::decode(decoder)?), + OptionCode::IATA => DhcpOption::IATA(IATA::decode(decoder)?), + OptionCode::IAAddr => DhcpOption::IAAddr(IAAddr::decode(decoder)?), + OptionCode::ORO => DhcpOption::ORO(ORO::decode(decoder)?), + OptionCode::Preference => DhcpOption::Preference(Preference::decode(decoder)?), + OptionCode::ElapsedTime => DhcpOption::ElapsedTime(ElapsedTime::decode(decoder)?), + OptionCode::RelayMsg => DhcpOption::RelayMsg(RelayMsg::decode(decoder)?), + OptionCode::Auth => DhcpOption::Auth(Auth::decode(decoder)?), + OptionCode::Unicast => DhcpOption::Unicast(Unicast::decode(decoder)?), + OptionCode::StatusCode => DhcpOption::StatusCode(StatusCode::decode(decoder)?), + OptionCode::RapidCommit => DhcpOption::RapidCommit(RapidCommit::decode(decoder)?), + OptionCode::UserClass => DhcpOption::UserClass(UserClass::decode(decoder)?), + OptionCode::VendorClass => DhcpOption::VendorClass(VendorClass::decode(decoder)?), + OptionCode::VendorOpts => DhcpOption::VendorOpts(VendorOpts::decode(decoder)?), + OptionCode::InterfaceId => DhcpOption::InterfaceId(InterfaceId::decode(decoder)?), + OptionCode::ReconfMsg => DhcpOption::ReconfMsg(ReconfMsg::decode(decoder)?), + OptionCode::ReconfAccept => DhcpOption::ReconfAccept(ReconfAccept::decode(decoder)?), + OptionCode::DNSServers => DhcpOption::DNSServers(DNSServers::decode(decoder)?), + OptionCode::IAPD => DhcpOption::IAPD(IAPD::decode(decoder)?), + OptionCode::IAPrefix => DhcpOption::IAPrefix(IAPrefix::decode(decoder)?), + OptionCode::InfMaxRt => DhcpOption::InfMaxRt(InfMaxRt::decode(decoder)?), + OptionCode::InformationRefreshTime => { DhcpOption::InformationRefreshTime(InformationRefreshTime::decode(decoder)?) } - OptionCode::SolMaxRt => { - DhcpOption::SolMaxRt(SolMaxRt::decode(decoder)?) - } - OptionCode::DomainList => { - DhcpOption::DomainList(DomainList::decode(decoder)?) - } + OptionCode::SolMaxRt => DhcpOption::SolMaxRt(SolMaxRt::decode(decoder)?), + OptionCode::DomainList => DhcpOption::DomainList(DomainList::decode(decoder)?), // not yet implemented OptionCode::Unknown(code) => { - decoder.read_u16()?;decoder.read_u16()?; + decoder.read_u16()?; + decoder.read_u16()?; DhcpOption::Unknown(UnknownOption { code, data: decoder.read_slice(len)?.to_vec(), }) } unimplemented => { - decoder.read_u16()?;decoder.read_u16()?; + decoder.read_u16()?; + decoder.read_u16()?; DhcpOption::Unknown(UnknownOption { code: unimplemented.into(), data: decoder.read_slice(len)?.to_vec(), @@ -490,85 +532,85 @@ impl Encodable for DhcpOption { let code: OptionCode = self.into(); match self { DhcpOption::ClientId(duid) => { - duid.encode(e)?; + duid.encode(e)?; } - DhcpOption::ServerId(duid) => { - duid.encode(e)?; + DhcpOption::ServerId(duid) => { + duid.encode(e)?; } DhcpOption::IANA(iana) => { iana.encode(e)?; } DhcpOption::IAPD(iapd) => { - iapd.encode(e)?; + iapd.encode(e)?; } DhcpOption::IATA(iata) => { - iata.encode(e)?; + iata.encode(e)?; } DhcpOption::IAAddr(iaaddr) => { iaaddr.encode(e)?; } DhcpOption::ORO(oro) => { - oro.encode(e)?; + oro.encode(e)?; } DhcpOption::Preference(pref) => { - pref.encode(e)?; + pref.encode(e)?; } DhcpOption::ElapsedTime(elapsed) => { - elapsed.encode(e)?; + elapsed.encode(e)?; } DhcpOption::RelayMsg(msg) => { - msg.encode(e)?; + msg.encode(e)?; } DhcpOption::Auth(auth) => { - auth.encode(e)?; + auth.encode(e)?; } DhcpOption::Unicast(addr) => { - addr.encode(e)?; + addr.encode(e)?; } DhcpOption::StatusCode(status) => { - status.encode(e)?; + status.encode(e)?; } DhcpOption::RapidCommit(rc) => { - rc.encode(e)?; + rc.encode(e)?; } DhcpOption::UserClass(uc) => { - uc.encode(e)?; + uc.encode(e)?; } DhcpOption::VendorClass(vc) => { - vc.encode(e)?; + vc.encode(e)?; } DhcpOption::VendorOpts(vopts) => { - vopts.encode(e)?; + vopts.encode(e)?; } DhcpOption::InterfaceId(id) => { - id.encode(e)?; + id.encode(e)?; } DhcpOption::ReconfMsg(msg_type) => { - msg_type.encode(e)?; + msg_type.encode(e)?; } DhcpOption::ReconfAccept(accept) => { - accept.encode(e)?; + accept.encode(e)?; } - DhcpOption::SolMaxRt(auth) => { - auth.encode(e)?; + DhcpOption::SolMaxRt(auth) => { + auth.encode(e)?; } - DhcpOption::InfMaxRt(auth) => { - auth.encode(e)?; + DhcpOption::InfMaxRt(auth) => { + auth.encode(e)?; } - DhcpOption::InformationRefreshTime(auth) => { - auth.encode(e)?; + DhcpOption::InformationRefreshTime(auth) => { + auth.encode(e)?; } DhcpOption::DNSServers(addrs) => { - addrs.encode(e)?; + addrs.encode(e)?; } DhcpOption::DomainList(names) => { - names.encode(e)?; + names.encode(e)?; } DhcpOption::IAPrefix(iaprefix) => { - iaprefix.encode(e)?; + iaprefix.encode(e)?; } DhcpOption::Unknown(UnknownOption { data, .. }) => { - e.write_u16(code.into())?; + e.write_u16(code.into())?; e.write_u16(data.len() as u16)?; e.write_slice(data)?; } @@ -578,9 +620,8 @@ impl Encodable for DhcpOption { } #[inline] -fn first(arr: &[T], f: F) -> Option +pub(crate) fn first(arr: &[T], f: F) -> Option where - T: Ord, F: Fn(&T) -> Ordering, { let mut l = 0; @@ -606,9 +647,8 @@ where } #[inline] -fn last(arr: &[T], f: F) -> Option +pub(crate) fn last(arr: &[T], f: F) -> Option where - T: Ord, F: Fn(&T) -> Ordering, { let n = arr.len(); @@ -635,9 +675,8 @@ where } #[inline] -fn range_binsearch(arr: &[T], f: F) -> Option> +pub(crate) fn range_binsearch(arr: &[T], f: F) -> Option> where - T: Ord, F: Fn(&T) -> Ordering, { let first = first(arr, &f)?; diff --git a/src/v6/options/preference.rs b/src/v6/options/preference.rs index 09fb73b..4c37be7 100644 --- a/src/v6/options/preference.rs +++ b/src/v6/options/preference.rs @@ -1,12 +1,9 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Preference { @@ -37,9 +34,7 @@ mod tests { use super::*; #[test] fn test_preference_encode_decode() { - let option = Preference { - pref: 1, - }; + let option = Preference { pref: 1 }; let mut encoder = vec![]; diff --git a/src/v6/options/rapidcommit.rs b/src/v6/options/rapidcommit.rs index 3a5333f..ef8ed44 100644 --- a/src/v6/options/rapidcommit.rs +++ b/src/v6/options/rapidcommit.rs @@ -1,21 +1,17 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct RapidCommit {} +pub struct RapidCommit; impl Decodable for RapidCommit { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<4>()?; - Ok(RapidCommit { - }) + Ok(RapidCommit) } } @@ -32,7 +28,7 @@ mod tests { use super::*; #[test] fn test_rapid_commit_encode_decode() { - let option = RapidCommit {}; + let option = RapidCommit; let mut encoder = vec![]; diff --git a/src/v6/options/reconfmsg.rs b/src/v6/options/reconfmsg.rs index 8fd597b..d3bd982 100644 --- a/src/v6/options/reconfmsg.rs +++ b/src/v6/options/reconfmsg.rs @@ -1,23 +1,21 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, MessageType, -}; +use super::{DecodeResult, EncodeResult, MessageType, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses +/// Reconfigure message #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ReconfMsg { - msg_type: MessageType, + pub msg_type: MessageType, } impl Decodable for ReconfMsg { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<4>()?; Ok(ReconfMsg { - msg_type: decoder.read_u8()?.into() + msg_type: decoder.read_u8()?.into(), }) } } @@ -26,7 +24,7 @@ impl Encodable for ReconfMsg { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(OptionCode::ReconfMsg.into())?; e.write_u16(1)?; - e.write_u8(self.msg_type.into())?; + e.write_u8(self.msg_type.into())?; Ok(()) } } @@ -39,8 +37,7 @@ pub struct ReconfAccept {} impl Decodable for ReconfAccept { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<4>()?; - Ok(ReconfAccept { - }) + Ok(ReconfAccept {}) } } @@ -57,9 +54,7 @@ mod tests { use super::*; #[test] fn test_reconf_msg_encode_decode() { - let option = ReconfMsg { - msg_type: 1.into(), - }; + let option = ReconfMsg { msg_type: 1.into() }; let mut encoder = vec![]; @@ -73,10 +68,9 @@ mod tests { assert_eq!(option, decoded); assert_eq!(50, decoder.read_u8().unwrap()); } - #[test] + #[test] fn test_reconf_accept_encode_decode() { - let option = ReconfAccept { - }; + let option = ReconfAccept {}; let mut encoder = vec![]; diff --git a/src/v6/options/relaymsg.rs b/src/v6/options/relaymsg.rs index d15e6ed..7686fe5 100644 --- a/src/v6/options/relaymsg.rs +++ b/src/v6/options/relaymsg.rs @@ -1,12 +1,9 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct RelayMsg { @@ -17,7 +14,7 @@ impl Decodable for RelayMsg { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<2>()?; let len = decoder.read_u16()? as usize; - + Ok(RelayMsg { msg: decoder.read_slice(len)?.into(), }) @@ -40,9 +37,7 @@ mod tests { use super::*; #[test] fn test_server_id_encode_decode() { - let option = RelayMsg { - msg: vec![1,2,3], - }; + let option = RelayMsg { msg: vec![1, 2, 3] }; let mut encoder = vec![]; diff --git a/src/v6/options/serverid.rs b/src/v6/options/serverid.rs index 50fd5ac..a354c1c 100644 --- a/src/v6/options/serverid.rs +++ b/src/v6/options/serverid.rs @@ -1,12 +1,10 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, Duid -}; +use super::{DecodeResult, Duid, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses +/// Server Identity #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ServerId { @@ -37,14 +35,13 @@ impl Encodable for ServerId { } } - #[cfg(test)] mod tests { use super::*; #[test] fn test_server_id_encode_decode() { let option = ServerId { - id: Duid::enterprise(1, &[1,2,3]), + id: Duid::enterprise(1, &[1, 2, 3]), }; let mut encoder = vec![]; diff --git a/src/v6/options/status.rs b/src/v6/options/status.rs index 82cdf0e..dbea760 100644 --- a/src/v6/options/status.rs +++ b/src/v6/options/status.rs @@ -14,7 +14,7 @@ pub struct StatusCode { impl Decodable for StatusCode { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - let _code = decoder.read_u16()?; + let _code = decoder.read_u16()?; let len = decoder.read_u16()? as usize; Ok(StatusCode { status: decoder.read_u16()?.into(), @@ -25,7 +25,7 @@ impl Decodable for StatusCode { impl Encodable for StatusCode { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { - e.write_u16(OptionCode::StatusCode.into())?; + e.write_u16(OptionCode::StatusCode.into())?; e.write_u16(2 + self.msg.len() as u16)?; e.write_u16(self.status.into())?; e.write_slice(self.msg.as_bytes())?; @@ -132,18 +132,20 @@ mod tests { use super::*; #[test] fn test_status_code_encode_decode() { - let sc = StatusCode{status: 0xABCDu16.into(), msg: "message".into()}; + let sc = StatusCode { + status: 0xABCDu16.into(), + msg: "message".into(), + }; let mut encoder = vec![]; - sc.encode(&mut Encoder::new(&mut encoder)).unwrap(); - let decoded = StatusCode::decode(&mut Decoder::new(&encoder)).unwrap(); + sc.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = StatusCode::decode(&mut Decoder::new(&encoder)).unwrap(); assert_eq!(sc, decoded); - - encoder.push(50); - let mut decoder = Decoder::new(&encoder); - let decoded = StatusCode::decode(&mut decoder).unwrap(); - assert_eq!(sc, decoded); - assert_eq!(50, decoder.read_u8().unwrap()); + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = StatusCode::decode(&mut decoder).unwrap(); + assert_eq!(sc, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); } } diff --git a/src/v6/options/unicast.rs b/src/v6/options/unicast.rs index bb5b47c..03a105c 100644 --- a/src/v6/options/unicast.rs +++ b/src/v6/options/unicast.rs @@ -1,12 +1,9 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, Ipv6Addr -}; +use super::{DecodeResult, EncodeResult, Ipv6Addr, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Unicast { diff --git a/src/v6/options/userclass.rs b/src/v6/options/userclass.rs index 974d207..ff4e28e 100644 --- a/src/v6/options/userclass.rs +++ b/src/v6/options/userclass.rs @@ -1,61 +1,56 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct UserClass { - pub data: Vec, + pub data: Vec, } impl Decodable for UserClass { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<2>()?; - let len = decoder.read_u16()?; - let mut data = vec![]; - let mut decoder = Decoder::new(decoder.read_slice(len as usize)?); - let mut remaining_len = len; - while remaining_len > 0{ - let len = decoder.peek_u16()?; - data.push(UserClassData::decode(&mut decoder)?); - remaining_len -= len+2; - } - Ok(UserClass { - data - }) + let len = decoder.read_u16()?; + let mut data = vec![]; + let mut decoder = Decoder::new(decoder.read_slice(len as usize)?); + let mut remaining_len = len; + while remaining_len > 0 { + let len = decoder.peek_u16()?; + data.push(UserClassData::decode(&mut decoder)?); + remaining_len -= len + 2; + } + Ok(UserClass { data }) } } impl Encodable for UserClass { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(OptionCode::UserClass.into())?; - let mut data = vec![]; - let mut dataenc = Encoder::new(&mut data); - for ucd in self.data.iter(){ - ucd.encode(&mut dataenc)?; - } + let mut data = vec![]; + let mut dataenc = Encoder::new(&mut data); + for ucd in self.data.iter() { + ucd.encode(&mut dataenc)?; + } e.write_u16(data.len() as u16)?; - e.write_slice(&data)?; + e.write_slice(&data)?; Ok(()) } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] -pub struct UserClassData{ - pub data: Vec, +pub struct UserClassData { + pub data: Vec, } impl Decodable for UserClassData { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - let len = decoder.read_u16()?; + let len = decoder.read_u16()?; Ok(UserClassData { - data: decoder.read_slice(len.into())?.into(), + data: decoder.read_slice(len.into())?.into(), }) } } @@ -63,7 +58,7 @@ impl Decodable for UserClassData { impl Encodable for UserClassData { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(self.data.len() as u16)?; - e.write_slice(&self.data)?; + e.write_slice(&self.data)?; Ok(()) } } @@ -74,13 +69,19 @@ mod tests { #[test] fn test_userclass_encode_decode() { let option = UserClass { - data: vec![UserClassData{data:vec![1,2,3,4]},UserClassData{data:vec![1]},UserClassData{data:vec![1,2]}], - }; + data: vec![ + UserClassData { + data: vec![1, 2, 3, 4], + }, + UserClassData { data: vec![1] }, + UserClassData { data: vec![1, 2] }, + ], + }; let mut encoder = vec![]; option.encode(&mut Encoder::new(&mut encoder)).unwrap(); - + let decoded = UserClass::decode(&mut Decoder::new(&encoder)).unwrap(); assert_eq!(option, decoded); diff --git a/src/v6/options/vendorclass.rs b/src/v6/options/vendorclass.rs index ec0e8cf..ed9ee95 100644 --- a/src/v6/options/vendorclass.rs +++ b/src/v6/options/vendorclass.rs @@ -1,61 +1,56 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// Identity Association for Non-Temporary Addresses #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct VendorClass { - pub data: Vec, + pub data: Vec, } impl Decodable for VendorClass { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<2>()?; - let len = decoder.read_u16()?; - let mut data = vec![]; - let mut decoder = Decoder::new(decoder.read_slice(len as usize)?); - let mut remaining_len = len; - while remaining_len > 0{ - let len = decoder.peek_u16()?; - data.push(VendorClassData::decode(&mut decoder)?); - remaining_len -= len+2; - } - Ok(VendorClass { - data - }) + let len = decoder.read_u16()?; + let mut data = vec![]; + let mut decoder = Decoder::new(decoder.read_slice(len as usize)?); + let mut remaining_len = len; + while remaining_len > 0 { + let len = decoder.peek_u16()?; + data.push(VendorClassData::decode(&mut decoder)?); + remaining_len -= len + 2; + } + Ok(VendorClass { data }) } } impl Encodable for VendorClass { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(OptionCode::VendorClass.into())?; - let mut data = vec![]; - let mut dataenc = Encoder::new(&mut data); - for ucd in self.data.iter(){ - ucd.encode(&mut dataenc)?; - } + let mut data = vec![]; + let mut dataenc = Encoder::new(&mut data); + for ucd in self.data.iter() { + ucd.encode(&mut dataenc)?; + } e.write_u16(data.len() as u16)?; - e.write_slice(&data)?; + e.write_slice(&data)?; Ok(()) } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] -pub struct VendorClassData{ - pub data: Vec, +pub struct VendorClassData { + pub data: Vec, } impl Decodable for VendorClassData { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - let len = decoder.read_u16()?; + let len = decoder.read_u16()?; Ok(VendorClassData { - data: decoder.read_slice(len.into())?.into(), + data: decoder.read_slice(len.into())?.into(), }) } } @@ -63,7 +58,7 @@ impl Decodable for VendorClassData { impl Encodable for VendorClassData { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { e.write_u16(self.data.len() as u16)?; - e.write_slice(&self.data)?; + e.write_slice(&self.data)?; Ok(()) } } @@ -74,13 +69,19 @@ mod tests { #[test] fn test_vendorclass_encode_decode() { let option = VendorClass { - data: vec![VendorClassData{data:vec![1,2,3,4]},VendorClassData{data:vec![1]},VendorClassData{data:vec![1,2]}], - }; + data: vec![ + VendorClassData { + data: vec![1, 2, 3, 4], + }, + VendorClassData { data: vec![1] }, + VendorClassData { data: vec![1, 2] }, + ], + }; let mut encoder = vec![]; option.encode(&mut Encoder::new(&mut encoder)).unwrap(); - + let decoded = VendorClass::decode(&mut Decoder::new(&encoder)).unwrap(); assert_eq!(option, decoded); diff --git a/src/v6/options/vendoropts.rs b/src/v6/options/vendoropts.rs index ac32d00..e391097 100644 --- a/src/v6/options/vendoropts.rs +++ b/src/v6/options/vendoropts.rs @@ -1,6 +1,4 @@ -use super::{ - DecodeResult, EncodeResult, OptionCode, -}; +use super::{DecodeResult, EncodeResult, OptionCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] @@ -17,71 +15,70 @@ pub struct VendorOpts { impl Decodable for VendorOpts { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { decoder.read::<2>()?; - let len = decoder.read_u16()?; - let enterprise_number = decoder.read_u32()?; - let mut opts = vec![]; - let mut used_len = 4; - while used_len < len{ - let opt = VendorOption::decode(decoder)?; - used_len += opt.len() + 4; - opts.push(opt); - } + let len = decoder.read_u16()?; + let enterprise_number = decoder.read_u32()?; + let mut opts = vec![]; + let mut used_len = 4; + while used_len < len { + let opt = VendorOption::decode(decoder)?; + used_len += opt.len() + 4; + opts.push(opt); + } Ok(VendorOpts { - enterprise_number, - opts, + enterprise_number, + opts, }) } } impl Encodable for VendorOpts { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { - let mut data = vec![]; - let mut enc = Encoder::new(&mut data); - for opt in self.opts.iter(){ - opt.encode(&mut enc)?; - } + let mut data = vec![]; + let mut enc = Encoder::new(&mut data); + for opt in self.opts.iter() { + opt.encode(&mut enc)?; + } e.write_u16(OptionCode::VendorOpts.into())?; - e.write_u16(data.len() as u16 + 4)?; - e.write_u32(self.enterprise_number)?; - e.write_slice(&data)?; + e.write_u16(data.len() as u16 + 4)?; + e.write_u32(self.enterprise_number)?; + e.write_slice(&data)?; Ok(()) } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] -pub struct VendorOption{ - pub code: u16, - pub data: Vec, +pub struct VendorOption { + pub code: u16, + pub data: Vec, } -impl VendorOption{ - fn len(&self) -> u16{ - self.data.len() as u16 - } +impl VendorOption { + fn len(&self) -> u16 { + self.data.len() as u16 + } } impl Decodable for VendorOption { fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { - let code = decoder.read_u16()?; - let len = decoder.read_u16()?; + let code = decoder.read_u16()?; + let len = decoder.read_u16()?; Ok(VendorOption { - code, - data: decoder.read_slice(len.into())?.into(), + code, + data: decoder.read_slice(len.into())?.into(), }) } } impl Encodable for VendorOption { fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { - e.write_u16(self.code)?; + e.write_u16(self.code)?; e.write_u16(self.data.len() as u16)?; - e.write_slice(&self.data)?; + e.write_slice(&self.data)?; Ok(()) } } - #[cfg(test)] mod tests { use super::*; @@ -89,7 +86,16 @@ mod tests { fn test_vendoropts_encode_decode() { let option = VendorOpts { enterprise_number: 0xABCD, - opts: vec![VendorOption{code: 0xABCD, data: vec![1,2]},VendorOption{code: 0xACBD, data: vec![1,2,3]}], + opts: vec![ + VendorOption { + code: 0xABCD, + data: vec![1, 2], + }, + VendorOption { + code: 0xACBD, + data: vec![1, 2, 3], + }, + ], }; let mut encoder = vec![]; From 1eec1a2c0ce02049d3045266fbf841568e86f98f Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 13 Oct 2022 21:41:58 +1000 Subject: [PATCH 10/12] V6: Rewrite part 4, messages --- src/v6/messages.rs | 509 +++++++++++++++++++++++++++++++++ src/v6/mod.rs | 244 ++-------------- src/v6/option_codes.rs | 7 +- src/v6/options/clientdata.rs | 75 +++++ src/v6/options/iaaddr.rs | 4 +- src/v6/options/lqclientlink.rs | 39 +++ src/v6/options/lqrelaydata.rs | 38 +++ src/v6/options/mod.rs | 87 +++--- src/v6/options/query.rs | 114 ++++++++ 9 files changed, 846 insertions(+), 271 deletions(-) create mode 100644 src/v6/messages.rs create mode 100644 src/v6/options/clientdata.rs create mode 100644 src/v6/options/lqclientlink.rs create mode 100644 src/v6/options/lqrelaydata.rs create mode 100644 src/v6/options/query.rs diff --git a/src/v6/messages.rs b/src/v6/messages.rs new file mode 100644 index 0000000..28698c2 --- /dev/null +++ b/src/v6/messages.rs @@ -0,0 +1,509 @@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::{ + decoder::{Decodable, Decoder}, + encoder::{Encodable, Encoder}, + error::{DecodeResult, EncodeResult}, + v6::options::{option_builder, DhcpOption}, + v6::*, +}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Message { + Solicit(Solicit), + Advertise(Advertise), + Request(Request), + Confirm(Confirm), + Renew(Renew), + Rebind(Rebind), + Reply(Reply), + Release(Release), + Decline(Decline), + Reconfigure(Reconfigure), + InformationRequest(InformationRequest), + RelayForw(RelayForw), + RelayRepl(RelayRepl), + LeaseQuery(LeaseQuery), + LeaseQueryReply(LeaseQueryReply), + /*LeaseQueryDone(Message), + LeaseQueryData(Message), + ReconfigureRequest(Message), + ReconfigureReply(Message), + DHCPv4Query(Message), + DHCPv4Response(Message),*/ + Unknown(Vec), +} + +impl Message { + pub fn msg_type(&self) -> MessageType { + use Message::*; + match self { + Solicit(_) => MessageType::Solicit, + Advertise(_) => MessageType::Advertise, + Request(_) => MessageType::Request, + Confirm(_) => MessageType::Confirm, + Renew(_) => MessageType::Renew, + Rebind(_) => MessageType::Rebind, + Reply(_) => MessageType::Reply, + Release(_) => MessageType::Release, + Decline(_) => MessageType::Decline, + Reconfigure(_) => MessageType::Reconfigure, + InformationRequest(_) => MessageType::InformationRequest, + RelayForw(_) => MessageType::RelayForw, + RelayRepl(_) => MessageType::RelayRepl, + LeaseQuery(_) => MessageType::LeaseQuery, + LeaseQueryReply(_) => MessageType::LeaseQueryReply, + /*LeaseQueryDone(_) => MessageType::Message, + LeaseQueryData(_) => MessageType::Message, + ReconfigureRequest(_) => MessageType::Message, + ReconfigureReply(_) => MessageType::Message, + DHCPv4Query(_) => MessageType::Message, + DHCPv4Response(_) => MessageType::Message,*/ + Unknown(v) => MessageType::Unknown(v[0]), + } + } +} + +impl Encodable for Message { + fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { + use Message::*; + match self { + Solicit(message) => message.encode(e), + Advertise(message) => message.encode(e), + Request(message) => message.encode(e), + Confirm(message) => message.encode(e), + Renew(message) => message.encode(e), + Rebind(message) => message.encode(e), + Reply(message) => message.encode(e), + Release(message) => message.encode(e), + Decline(message) => message.encode(e), + Reconfigure(message) => message.encode(e), + InformationRequest(message) => message.encode(e), + RelayForw(message) => message.encode(e), + RelayRepl(message) => message.encode(e), + LeaseQuery(message) => message.encode(e), + LeaseQueryReply(message) => message.encode(e), + /*LeaseQueryDone(message) => message.encode(e), + LeaseQueryData(message) => message.encode(e), + ReconfigureRequest(message) => message.encode(e), + ReconfigureReply(message) => message.encode(e), + DHCPv4Query(message) => message.encode(e), + DHCPv4Response(message) => message.encode(e),*/ + Unknown(message) => e.write_slice(message), + } + } +} + +impl Decodable for Message { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + Ok(match MessageType::from(decoder.peek_u8()?) { + MessageType::Solicit => Message::Solicit(Solicit::decode(decoder)?), + MessageType::Advertise => Message::Advertise(Advertise::decode(decoder)?), + MessageType::Request => Message::Request(Request::decode(decoder)?), + MessageType::Confirm => Message::Confirm(Confirm::decode(decoder)?), + MessageType::Renew => Message::Renew(Renew::decode(decoder)?), + MessageType::Rebind => Message::Rebind(Rebind::decode(decoder)?), + MessageType::Reply => Message::Reply(Reply::decode(decoder)?), + MessageType::Release => Message::Release(Release::decode(decoder)?), + MessageType::Decline => Message::Decline(Decline::decode(decoder)?), + MessageType::Reconfigure => Message::Reconfigure(Reconfigure::decode(decoder)?), + MessageType::InformationRequest => { + Message::InformationRequest(InformationRequest::decode(decoder)?) + } + MessageType::RelayForw => Message::RelayForw(RelayForw::decode(decoder)?), + MessageType::RelayRepl => Message::RelayRepl(RelayRepl::decode(decoder)?), + MessageType::LeaseQuery => Message::LeaseQuery(LeaseQuery::decode(decoder)?), + MessageType::LeaseQueryReply => { + Message::LeaseQueryReply(LeaseQueryReply::decode(decoder)?) + } + /*MessageType::LeaseQueryDone => Message::LeaseQueryDone(Message::decode(decoder)?), + MessageType::LeaseQueryData => Message::LeaseQueryData(Message::decode(decoder)?), + MessageType::ReconfigureRequest => Message::ReconfigureRequest(Message::decode(decoder)?), + MessageType::ReconfigureReply => Message::ReconfigureReply(Message::decode(decoder)?), + MessageType::DHCPv4Query => Message::DHCPv4Query(Message::decode(decoder)?), + MessageType::DHCPv4Response => Message::DHCPv4Response(Message::decode(decoder)?),*/ + _ => Message::Unknown({ + let mut buf = vec![]; + while let Ok(b) = decoder.read_u8() { + buf.push(b); + } + buf + }), + }) + } +} + +option_builder!( + MessageOption, + MessageOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAAddr, + IAPD, + IAPrefix, + ORO, + Preference, + ElapsedTime, + Auth, + Unicast, + StatusCode, + RapidCommit, + UserClass, + VendorClass, + VendorOpts, + ReconfMsg, + ReconfAccept, + InformationRefreshTime, + SolMaxRt, + InfMaxRt, + DNSServers, + DomainList +); + +option_builder!( + RelayMessageOption, + RelayMessageOptions, + DhcpOption, + RelayMsg, + VendorOpts, + InterfaceId +); + +option_builder!( + SolicitOption, + SolicitOptions, + DhcpOption, + ClientId, + IANA, + IATA, + IAPD, + ORO, + ElapsedTime, + RapidCommit, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept +); + +option_builder!( + AdvertiseOption, + AdvertiseOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAPD, + Preference, + StatusCode, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept, + SolMaxRt +); + +option_builder!( + RequestOption, + RequestOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAPD, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept +); + +option_builder!( + ConfirmOption, + ConfirmOptions, + DhcpOption, + ClientId, + IANA, + IATA, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts +); + +option_builder!( + RenewOption, + RenewOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAPD, + ORO, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept +); + +option_builder!( + RebindOption, + RebindOptions, + DhcpOption, + ClientId, + IANA, + IATA, + IAPD, + ORO, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept +); + +option_builder!( + DeclineOption, + DeclineOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAPD, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts +); + +option_builder!( + ReleaseOption, + ReleaseOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAPD, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts +); + +option_builder!( + ReplyOption, + ReplyOptions, + DhcpOption, + ClientId, + ServerId, + IANA, + IATA, + IAPD, + Auth, + Unicast, + StatusCode, + RapidCommit, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept, + InformationRefreshTime, + SolMaxRt, + InfMaxRt +); + +option_builder!( + ReconfigureOption, + ReconfigureOptions, + DhcpOption, + ClientId, + ServerId, + Auth, + ReconfMsg +); + +option_builder!( + InformationRequestOption, + InformationRequestOptions, + DhcpOption, + ClientId, + ServerId, + ORO, + ElapsedTime, + UserClass, + VendorClass, + VendorOpts, + ReconfAccept +); + +option_builder!(LeaseQueryOption, LeaseQueryOptions, DhcpOption, LqQuery); + +option_builder!( + LeaseQueryReplyOption, + LeaseQueryReplyOptions, + DhcpOption, + ClientData, + LqRelayData, + LqClientLink +); + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TransactionId { + pub id: [u8; 3], +} + +impl Default for TransactionId { + fn default() -> Self { + Self { id: rand::random() } + } +} + +impl Encodable for TransactionId { + fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { + e.write_slice(&self.id)?; + Ok(()) + } +} + +impl Decodable for TransactionId { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + Ok(TransactionId { + id: decoder.read::<3>()?, + }) + } +} + +macro_rules! base_message_builder { + ($name: ident, $options: ident) => { + impl From<$name> for Message { + fn from(message: $name) -> Message { + Message::$name(message) + } + } + + impl $name { + /// Get a reference to the message's options. + pub fn opts(&self) -> &$options { + &self.opts + } + /// Get a mutable reference to the message's options. + pub fn opts_mut(&mut self) -> &mut $options { + &mut self.opts + } + } + }; +} + +macro_rules! client_server_message_builder { + ($name: ident, $options: ident) => { + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Debug, Clone, PartialEq, Eq, Default)] + pub struct $name { + pub xid: TransactionId, + pub opts: $options, + } + + impl $name { + /// returns a new `Message` with a random xid and empty opt section + pub fn new() -> Self { + Self::default() + } + /// returns a new `Message` with an empty opt section + pub fn new_with_xid>(xid: T) -> Self { + Self { + xid: xid.into(), + ..Self::default() + } + } + } + + base_message_builder!($name, $options); + + impl Encodable for $name { + fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { + e.write_u8(MessageType::$name.into())?; + self.xid.encode(e)?; + self.opts.encode(e)?; + Ok(()) + } + } + + impl Decodable for $name { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + let _message_type = decoder.read_u8()?; + Ok(Self { + xid: TransactionId::decode(decoder)?, + opts: $options::decode(decoder)?, + }) + } + } + }; +} + +macro_rules! relay_message_builder { + ($name: ident, $options: ident) => { + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct $name { + pub hop_count: u8, + pub link_address: Ipv6Addr, + pub peer_address: Ipv6Addr, + pub opts: $options, + } + + base_message_builder!($name, $options); + + impl Encodable for $name { + fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { + e.write_u8(MessageType::$name.into())?; + e.write_u8(self.hop_count)?; + e.write::<16>(self.link_address.octets())?; + e.write::<16>(self.peer_address.octets())?; + self.opts.encode(e)?; + Ok(()) + } + } + + impl Decodable for $name { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + let _message_type = decoder.read_u8()?; + Ok(Self { + hop_count: decoder.read_u8()?, + link_address: decoder.read::<16>()?.into(), + peer_address: decoder.read::<16>()?.into(), + opts: $options::decode(decoder)?, + }) + } + } + }; +} + +client_server_message_builder!(Solicit, SolicitOptions); +client_server_message_builder!(Advertise, AdvertiseOptions); +client_server_message_builder!(Request, RequestOptions); +client_server_message_builder!(Confirm, ConfirmOptions); +client_server_message_builder!(Renew, RenewOptions); +client_server_message_builder!(Rebind, RebindOptions); +client_server_message_builder!(Reply, ReplyOptions); +client_server_message_builder!(Decline, DeclineOptions); +client_server_message_builder!(Release, ReleaseOptions); +client_server_message_builder!(Reconfigure, ReconfigureOptions); +client_server_message_builder!(InformationRequest, InformationRequestOptions); + +relay_message_builder!(RelayForw, RelayMessageOptions); +relay_message_builder!(RelayRepl, RelayMessageOptions); + +client_server_message_builder!(LeaseQuery, LeaseQueryOptions); +client_server_message_builder!(LeaseQueryReply, LeaseQueryReplyOptions); diff --git a/src/v6/mod.rs b/src/v6/mod.rs index 8fea7c8..9eb0e46 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -11,8 +11,8 @@ //! let duid = v6::Duid::from(vec![ //! 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, //! ]); -//! // construct a new Message with a random xid -//! let mut msg = v6::Message::new(v6::MessageType::Solicit); +//! // construct a new Solicit Message with a random xid +//! let mut msg = v6::Solicit::new(); //! // set an option //! msg.opts_mut() //! .insert(v6::ClientId{id: duid}); @@ -54,18 +54,30 @@ //! mod duid; mod option_codes; -mod options; +pub mod options; +///options +pub use options::{ + Auth, ClientData, ClientId, CltTime, DNSServers, DomainList, ElapsedTime, IAAddr, IAPrefix, + InfMaxRt, InformationRefreshTime, InterfaceId, LqClientLink, LqQuery, LqRelayData, Preference, + RapidCommit, ReconfAccept, ReconfMsg, RelayMsg, ServerId, SolMaxRt, StatusCode, Unicast, + UserClass, VendorClass, VendorOpts, IANA, IAPD, IATA, ORO, +}; +pub mod messages; mod oro_codes; +///messages +pub use messages::{ + Advertise, Confirm, Decline, InformationRequest, LeaseQuery, LeaseQueryReply, Message, Rebind, + Reconfigure, RelayForw, RelayRepl, Release, Renew, Reply, Request, Solicit, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use std::{convert::TryInto, fmt, net::Ipv6Addr}; +use std::{convert::TryInto, net::Ipv6Addr}; // re-export submodules from proto::msg pub use self::duid::*; pub use self::option_codes::*; -pub use self::options::*; pub use self::oro_codes::*; pub use crate::{ @@ -119,101 +131,6 @@ pub const CLIENT_PORT: u16 = 546; /// field (4 octets less than the size of the /// message). /// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Message { - /// message type - /// - msg_type: MessageType, - /// transaction id - /// trns id must be the same for all messages in a DHCP transaction - /// - xid: [u8; 3], - /// Options - /// - opts: MessageOptions, -} - -impl Default for Message { - fn default() -> Self { - Self { - msg_type: MessageType::Solicit, - xid: rand::random(), - opts: MessageOptions::new(), - } - } -} - -impl Message { - /// returns a new `Message` with a random xid and empty opt section - pub fn new(msg_type: MessageType) -> Self { - Self { - msg_type, - ..Self::default() - } - } - - /// returns a new `Message` with a given xid and message type and empty opt section - pub fn new_with_id(msg_type: MessageType, xid: [u8; 3]) -> Self { - Self { - msg_type, - xid, - ..Self::default() - } - } - - /// Get the message's message type. - pub fn msg_type(&self) -> MessageType { - self.msg_type - } - - /// Set message type - pub fn set_msg_type(&mut self, msg_type: MessageType) -> &mut Self { - self.msg_type = msg_type; - self - } - - /// Get the message's transaction id. - pub fn xid(&self) -> [u8; 3] { - self.xid - } - - /// Get the msgs transaction id as a number - pub fn xid_num(&self) -> u32 { - u32::from_be_bytes([0, self.xid[0], self.xid[1], self.xid[2]]) - } - - /// Set transaction id - pub fn set_xid(&mut self, xid: [u8; 3]) -> &mut Self { - self.xid = xid; - self - } - - /// Set transaction id from u32, will only use last 3 bytes - pub fn set_xid_num(&mut self, xid: u32) -> &mut Self { - let arr = xid.to_be_bytes(); - self.xid = arr[1..=3] - .try_into() - .expect("a u32 has 4 bytes so this shouldn't fail"); - self - } - - /// Get a reference to the message's options. - pub fn opts(&self) -> &MessageOptions { - &self.opts - } - - /// Set DHCP opts - pub fn set_opts(&mut self, opts: MessageOptions) -> &mut Self { - self.opts = opts; - self - } - - /// Get a mutable reference to the message's options. - pub fn opts_mut(&mut self) -> &mut MessageOptions { - &mut self.opts - } -} /// DHCPv6 message types /// @@ -341,120 +258,6 @@ impl From for u8 { } } -impl Decodable for Message { - fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { - Ok(Message { - msg_type: decoder.read_u8()?.into(), - xid: decoder.read::<3>()?, - opts: MessageOptions::decode(decoder)?, - }) - } -} - -impl Encodable for Message { - fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { - e.write_u8(self.msg_type.into())?; - e.write(self.xid)?; - self.opts.encode(e)?; - Ok(()) - } -} - -impl fmt::Display for Message { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Message") - .field("xid", &self.xid_num()) - .field("msg_type", &self.msg_type()) - .field("opts", &self.opts()) - .finish() - } -} - -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RelayMessage { - /// message type - /// - msg_type: MessageType, - /// hop count - /// - hop_count: u8, - /// link address - /// - link_addr: Ipv6Addr, - /// peer address - /// - peer_addr: Ipv6Addr, - /// Options - /// - opts: RelayMessageOptions, -} - -impl RelayMessage { - pub fn msg_type(&self) -> MessageType { - self.msg_type - } - pub fn hop_count(&self) -> u8 { - self.hop_count - } - pub fn link_addr(&self) -> Ipv6Addr { - self.link_addr - } - pub fn peer_addr(&self) -> Ipv6Addr { - self.peer_addr - } - /// Get a reference to the message's options. - pub fn opts(&self) -> &RelayMessageOptions { - &self.opts - } - - /// Set DHCP opts - pub fn set_opts(&mut self, opts: RelayMessageOptions) -> &mut Self { - self.opts = opts; - self - } - - /// Get a mutable reference to the message's options. - pub fn opts_mut(&mut self) -> &mut RelayMessageOptions { - &mut self.opts - } -} - -impl Decodable for RelayMessage { - fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { - Ok(Self { - msg_type: decoder.read_u8()?.into(), - hop_count: decoder.read_u8()?, - link_addr: decoder.read::<16>()?.into(), - peer_addr: decoder.read::<16>()?.into(), - opts: RelayMessageOptions::decode(decoder)?, - }) - } -} - -impl Encodable for RelayMessage { - fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { - e.write_u8(self.msg_type.into())?; - e.write_u8(self.hop_count)?; - e.write_slice(&self.link_addr.octets())?; - e.write_slice(&self.peer_addr.octets())?; - self.opts.encode(e)?; - Ok(()) - } -} - -impl fmt::Display for RelayMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RelayMessage") - .field("msg_type", &self.msg_type()) - .field("hop_count", &self.hop_count()) - .field("link_addr", &self.link_addr()) - .field("peer_addr", &self.peer_addr()) - .field("opts", &self.opts()) - .finish() - } -} - #[cfg(test)] mod tests { @@ -466,7 +269,7 @@ mod tests { // decode let msg = Message::decode(&mut Decoder::new(&input))?; dbg!(&msg); - assert_eq!(mtype, msg.msg_type); + assert_eq!(mtype, msg.msg_type()); // now encode let mut buf = Vec::new(); let mut e = Encoder::new(&mut buf); @@ -508,15 +311,10 @@ mod tests { #[test] fn xid_num() { - let mut msg = Message::default(); - msg.set_xid_num(16_777_215); - assert_eq!(msg.xid_num(), 16_777_215); - - msg.set_xid_num(16_777_000); - assert_eq!(msg.xid_num(), 16_777_000); + let msg = Solicit::default(); + let other_msg = Reply::new_with_xid(msg.xid); - msg.set_xid_num(8); - assert_eq!(msg.xid_num(), 8); + assert_eq!(msg.xid, other_msg.xid); } #[cfg(feature = "serde")] #[test] diff --git a/src/v6/option_codes.rs b/src/v6/option_codes.rs index 4b49314..4d768f4 100644 --- a/src/v6/option_codes.rs +++ b/src/v6/option_codes.rs @@ -1,4 +1,4 @@ -use crate::v6::DhcpOption; +use crate::v6::options::DhcpOption; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -472,6 +472,11 @@ impl From<&DhcpOption> for OptionCode { InformationRefreshTime(_) => OptionCode::InformationRefreshTime, SolMaxRt(_) => OptionCode::SolMaxRt, InfMaxRt(_) => OptionCode::InfMaxRt, + LqQuery(_) => OptionCode::LqQuery, + ClientData(_) => OptionCode::ClientData, + CltTime(_) => OptionCode::CltTime, + LqRelayData(_) => OptionCode::LqRelayData, + LqClientLink(_) => OptionCode::LqClientLink, Unknown(unknown) => unknown.into(), } } diff --git a/src/v6/options/clientdata.rs b/src/v6/options/clientdata.rs new file mode 100644 index 0000000..7306164 --- /dev/null +++ b/src/v6/options/clientdata.rs @@ -0,0 +1,75 @@ +use super::{ + option_builder, ClientId, DecodeResult, DhcpOption, EncodeResult, IAAddr, IAPrefix, OptionCode, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Vendor defined options +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ClientData { + pub opts: ClientDataOptions, +} + +impl Decodable for ClientData { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()?; + let mut decoder = Decoder::new(decoder.read_slice(len.into())?); + + Ok(ClientData { + opts: ClientDataOptions::decode(&mut decoder)?, + }) + } +} + +impl Encodable for ClientData { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + let mut data = vec![]; + let mut enc = Encoder::new(&mut data); + self.opts.encode(&mut enc)?; + e.write_u16(OptionCode::ClientData.into())?; + e.write_u16(data.len() as u16)?; + e.write_slice(&data)?; + Ok(()) + } +} + +//TODO: add ORO reply options +option_builder!( + ClientDataOption, + ClientDataOptions, + DhcpOption, + ClientId, + IAAddr, + IAPrefix, + CltTime +); + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct CltTime { + ///seconds since server last communicated with the client (on that link) + pub time: u32, +} + +impl Decodable for CltTime { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<4>()?; + + Ok(CltTime { + time: decoder.read_u32()?, + }) + } +} + +impl Encodable for CltTime { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::CltTime.into())?; + e.write_u16(4)?; + e.write_u32(self.time)?; + Ok(()) + } +} diff --git a/src/v6/options/iaaddr.rs b/src/v6/options/iaaddr.rs index a1764fc..f343dda 100644 --- a/src/v6/options/iaaddr.rs +++ b/src/v6/options/iaaddr.rs @@ -1,5 +1,5 @@ -use crate::v6::DhcpOption; -use crate::v6::{option_builder, DecodeResult, EncodeResult, Ipv6Addr, OptionCode, StatusCode}; +use crate::v6::options::{option_builder, DhcpOption}; +use crate::v6::{DecodeResult, EncodeResult, Ipv6Addr, OptionCode, StatusCode}; use crate::{Decodable, Decoder, Encodable, Encoder}; #[cfg(feature = "serde")] diff --git a/src/v6/options/lqclientlink.rs b/src/v6/options/lqclientlink.rs new file mode 100644 index 0000000..c0d0b96 --- /dev/null +++ b/src/v6/options/lqclientlink.rs @@ -0,0 +1,39 @@ +use std::net::Ipv6Addr; + +use super::{DecodeResult, EncodeResult, OptionCode}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Vendor defined options +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LqClientLink { + pub link_addresses: Vec, +} + +impl Decodable for LqClientLink { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut link_addresses = Vec::with_capacity(len / 16); + for _ in 0..(len / 16) { + link_addresses.push(decoder.read::<16>()?.into()); + } + + Ok(LqClientLink { link_addresses }) + } +} + +impl Encodable for LqClientLink { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::LqClientLink.into())?; + e.write_u16(self.link_addresses.len() as u16 * 16)?; + for address in self.link_addresses.iter() { + e.write_slice(&address.octets())?; + } + + Ok(()) + } +} diff --git a/src/v6/options/lqrelaydata.rs b/src/v6/options/lqrelaydata.rs new file mode 100644 index 0000000..4d6a49f --- /dev/null +++ b/src/v6/options/lqrelaydata.rs @@ -0,0 +1,38 @@ +use std::net::Ipv6Addr; + +use super::{DecodeResult, EncodeResult, OptionCode}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Vendor defined options +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LqRelayData { + pub peer_address: Ipv6Addr, + pub relay_message: Vec, +} + +impl Decodable for LqRelayData { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + + Ok(LqRelayData { + peer_address: decoder.read::<16>()?.into(), + relay_message: decoder.read_slice(len - 16)?.into(), + }) + } +} + +impl Encodable for LqRelayData { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::LqRelayData.into())?; + e.write_u16(self.relay_message.len() as u16 + 16)?; + e.write_slice(&self.peer_address.octets())?; + e.write_slice(&self.relay_message)?; + Ok(()) + } +} diff --git a/src/v6/options/mod.rs b/src/v6/options/mod.rs index ca495b6..76c0e9f 100644 --- a/src/v6/options/mod.rs +++ b/src/v6/options/mod.rs @@ -53,6 +53,16 @@ pub use dnsservers::*; mod domainlist; pub use domainlist::*; +//rfc5007 +mod query; +pub use query::*; +mod clientdata; +pub use clientdata::*; +mod lqrelaydata; +pub use lqrelaydata::*; +mod lqclientlink; +pub use lqclientlink::*; + use std::{cmp::Ordering, net::Ipv6Addr, ops::RangeInclusive}; pub use crate::Domain; @@ -62,6 +72,7 @@ use crate::{ error::{DecodeResult, EncodeResult}, v6::{Duid, MessageType, OROCode, OptionCode}, }; + //helper macro for implementing sub-options (IANAOptions, ect) //useage: option_builder!(IANAOption, IANAOptions, DhcpOption, IAAddr, StatusCode); // option_builder!(name , names , master , subname... ); @@ -141,41 +152,41 @@ macro_rules! option_builder{ } /// get the first element matching this option code pub fn get(&self, code: OptionCode) -> Option<&$name>{ - use crate::v6::first; + use crate::v6::options::first; use crate::v6::OptionCode; let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; self.0.get(first) } /// get all elements matching this option code pub fn get_all(&self, code: OptionCode) -> Option<&[$name]>{ - use crate::v6::range_binsearch; + use crate::v6::options::range_binsearch; use crate::v6::OptionCode; let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; Some(&self.0[range]) } /// get the first element matching this option code pub fn get_mut(&mut self, code: OptionCode) -> Option<&mut $name>{ - use crate::v6::first; + use crate::v6::options::first; use crate::v6::OptionCode; let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; self.0.get_mut(first) } /// get all elements matching this option pub fn get_mut_all(&mut self, code: OptionCode) -> Option<&mut [$name]>{ - use crate::v6::range_binsearch; + use crate::v6::options::range_binsearch; use crate::v6::OptionCode; let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; Some(&mut self.0[range]) } /// remove the first element with a matching option code pub fn remove(&mut self, code: OptionCode) -> Option<$name>{ - use crate::v6::first; + use crate::v6::options::first; use crate::v6::OptionCode; let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; Some(self.0.remove(first)) } pub fn remove_all(&mut self, code: OptionCode) -> Option + '_>{ - use crate::v6::range_binsearch; + use crate::v6::options::range_binsearch; use crate::v6::OptionCode; let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; Some(self.0.drain(range)) @@ -227,45 +238,6 @@ macro_rules! option_builder{ pub(crate) use option_builder; -option_builder!( - MessageOption, - MessageOptions, - DhcpOption, - ClientId, - ServerId, - IANA, - IATA, - IAAddr, - IAPD, - IAPrefix, - ORO, - Preference, - ElapsedTime, - Auth, - Unicast, - StatusCode, - RapidCommit, - UserClass, - VendorClass, - VendorOpts, - ReconfMsg, - ReconfAccept, - InformationRefreshTime, - SolMaxRt, - InfMaxRt, - DNSServers, - DomainList -); - -option_builder!( - RelayMessageOption, - RelayMessageOptions, - DhcpOption, - RelayMsg, - VendorOpts, - InterfaceId -); - // server can send multiple IA_NA options to request multiple addresses // so we must be able to handle multiple of the same option type // @@ -403,6 +375,11 @@ pub enum DhcpOption { InformationRefreshTime(InformationRefreshTime), SolMaxRt(SolMaxRt), InfMaxRt(InfMaxRt), + LqQuery(LqQuery), + ClientData(ClientData), + CltTime(CltTime), + LqRelayData(LqRelayData), + LqClientLink(LqClientLink), /// An unknown or unimplemented option type Unknown(UnknownOption), } @@ -507,6 +484,11 @@ impl Decodable for DhcpOption { } OptionCode::SolMaxRt => DhcpOption::SolMaxRt(SolMaxRt::decode(decoder)?), OptionCode::DomainList => DhcpOption::DomainList(DomainList::decode(decoder)?), + OptionCode::LqQuery => DhcpOption::LqQuery(LqQuery::decode(decoder)?), + OptionCode::ClientData => DhcpOption::ClientData(ClientData::decode(decoder)?), + OptionCode::CltTime => DhcpOption::CltTime(CltTime::decode(decoder)?), + OptionCode::LqRelayData => DhcpOption::LqRelayData(LqRelayData::decode(decoder)?), + OptionCode::LqClientLink => DhcpOption::LqClientLink(LqClientLink::decode(decoder)?), // not yet implemented OptionCode::Unknown(code) => { decoder.read_u16()?; @@ -609,6 +591,21 @@ impl Encodable for DhcpOption { DhcpOption::IAPrefix(iaprefix) => { iaprefix.encode(e)?; } + DhcpOption::LqQuery(q) => { + q.encode(e)?; + } + DhcpOption::ClientData(q) => { + q.encode(e)?; + } + DhcpOption::CltTime(q) => { + q.encode(e)?; + } + DhcpOption::LqRelayData(q) => { + q.encode(e)?; + } + DhcpOption::LqClientLink(q) => { + q.encode(e)?; + } DhcpOption::Unknown(UnknownOption { data, .. }) => { e.write_u16(code.into())?; e.write_u16(data.len() as u16)?; diff --git a/src/v6/options/query.rs b/src/v6/options/query.rs new file mode 100644 index 0000000..9588202 --- /dev/null +++ b/src/v6/options/query.rs @@ -0,0 +1,114 @@ +use std::net::Ipv6Addr; + +use super::{ + option_builder, ClientId, DecodeResult, DhcpOption, EncodeResult, IAAddr, OptionCode, ORO, +}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Lease Query +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LqQuery { + pub qtype: QueryType, + pub link_address: Ipv6Addr, + pub opts: LqQueryOptions, +} + +impl Decodable for LqQuery { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + let qtype = decoder.read_u8()?.into(); + let link_address = decoder.read::<16>()?.into(); + let opts = LqQueryOptions::decode(&mut decoder)?; + Ok(LqQuery { + qtype, + link_address, + opts, + }) + } +} + +impl Encodable for LqQuery { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.opts.encode(&mut opt_enc)?; + + e.write_u16(OptionCode::LqQuery.into())?; + e.write_u16(buf.len() as u16 + 17)?; + e.write_u8(self.qtype.into())?; + e.write::<16>(self.link_address.octets())?; + e.write_slice(&buf)?; + + Ok(()) + } +} + +option_builder!( + LqQueryOption, + LqQueryOptions, + DhcpOption, + IAAddr, + ClientId, + ORO +); + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum QueryType { + QueryByAddress, + QueryByClientID, + Unknown(u8), +} + +impl From for QueryType { + fn from(qtype: u8) -> Self { + use QueryType::*; + match qtype { + 1 => QueryByAddress, + 2 => QueryByClientID, + t => Unknown(t), + } + } +} + +impl From for u8 { + fn from(num: QueryType) -> Self { + use QueryType::*; + match num { + QueryByAddress => 1, + QueryByClientID => 2, + Unknown(t) => t, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_query_option_encode_decode() { + let option = LqQuery { + qtype: 1.into(), + link_address: "0::0".parse().unwrap(), + opts: LqQueryOptions::default(), + }; + + let mut encoder = vec![]; + + option.encode(&mut Encoder::new(&mut encoder)).unwrap(); + let decoded = LqQuery::decode(&mut Decoder::new(&encoder)).unwrap(); + assert_eq!(option, decoded); + + encoder.push(50); + let mut decoder = Decoder::new(&encoder); + let decoded = LqQuery::decode(&mut decoder).unwrap(); + assert_eq!(option, decoded); + assert_eq!(50, decoder.read_u8().unwrap()); + } +} From cad8ca372ec3a3e24013c2926eaf63d710b8603b Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 20 Oct 2022 15:31:11 +1000 Subject: [PATCH 11/12] v6: Bulk Lease Query Messages --- src/v6/messages.rs | 282 ++++++++++++++++++++++++++++------ src/v6/mod.rs | 55 ++----- src/v6/option_codes.rs | 2 + src/v6/options/clientdata.rs | 1 + src/v6/options/iaaddr.rs | 8 +- src/v6/options/iana.rs | 9 +- src/v6/options/iapd.rs | 9 +- src/v6/options/iaprefix.rs | 7 +- src/v6/options/iata.rs | 9 +- src/v6/options/linkaddress.rs | 30 ++++ src/v6/options/mod.rs | 92 ++++++++--- src/v6/options/query.rs | 10 ++ src/v6/options/relayid.rs | 36 +++++ src/v6/options/unicast.rs | 2 - 14 files changed, 429 insertions(+), 123 deletions(-) create mode 100644 src/v6/options/linkaddress.rs create mode 100644 src/v6/options/relayid.rs diff --git a/src/v6/messages.rs b/src/v6/messages.rs index 28698c2..fd4e920 100644 --- a/src/v6/messages.rs +++ b/src/v6/messages.rs @@ -9,6 +9,112 @@ use crate::{ v6::*, }; +///Bulk lease query messages for use over TCP +///Note: The u16 message-size from the start of the TCP message is not read or written, and only a buffer containing a one complete message will decode correctly. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BulkLeaseQueryMessage { + LeaseQuery(LeaseQuery), + LeaseQueryReply(LeaseQueryReply), + LeaseQueryDone(LeaseQueryDone), + LeaseQueryData(LeaseQueryData), + Unknown(Vec), +} + +impl BulkLeaseQueryMessage { + pub fn msg_type(&self) -> MessageType { + use BulkLeaseQueryMessage::*; + match self { + LeaseQuery(_) => MessageType::LeaseQuery, + LeaseQueryReply(_) => MessageType::LeaseQueryReply, + LeaseQueryDone(_) => MessageType::LeaseQueryDone, + LeaseQueryData(_) => MessageType::LeaseQueryData, + Unknown(v) => MessageType::Unknown(v[0]), + } + } +} + +impl Encodable for BulkLeaseQueryMessage { + fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { + use BulkLeaseQueryMessage::*; + match self { + LeaseQuery(message) => message.encode(e), + LeaseQueryReply(message) => message.encode(e), + LeaseQueryDone(message) => message.encode(e), + LeaseQueryData(message) => message.encode(e), + Unknown(message) => e.write_slice(message), + } + } +} + +impl Decodable for BulkLeaseQueryMessage { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + Ok(match MessageType::from(decoder.peek_u8()?) { + MessageType::LeaseQuery => { + BulkLeaseQueryMessage::LeaseQuery(LeaseQuery::decode(decoder)?) + } + MessageType::LeaseQueryReply => { + BulkLeaseQueryMessage::LeaseQueryReply(LeaseQueryReply::decode(decoder)?) + } + MessageType::LeaseQueryDone => { + BulkLeaseQueryMessage::LeaseQueryDone(LeaseQueryDone::decode(decoder)?) + } + MessageType::LeaseQueryData => { + BulkLeaseQueryMessage::LeaseQueryData(LeaseQueryData::decode(decoder)?) + } + _ => BulkLeaseQueryMessage::Unknown({ + let mut buf = vec![]; + while let Ok(b) = decoder.read_u8() { + buf.push(b); + } + buf + }), + }) + } +} + +/// See RFC 8415 for updated DHCPv6 info +/// [DHCP for Ipv6](https://datatracker.ietf.org/doc/html/rfc8415) +/// +/// All DHCP messages sent between clients and servers share an identical +/// fixed-format header and a variable-format area for options. +/// +/// All values in the message header and in options are in network byte +/// order. +/// +/// Options are stored serially in the "options" field, with no padding +/// between the options. Options are byte-aligned but are not aligned in +/// any other way (such as on 2-byte or 4-byte boundaries). +/// +/// The following diagram illustrates the format of DHCP messages sent +/// between clients and servers: +/// +/// ```text +/// 0 1 2 3 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | msg-type | transaction-id | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | | +/// . options . +/// . (variable number and length) . +/// | | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// +/// msg-type Identifies the DHCP message type; the +/// available message types are listed in +/// Section 7.3. A 1-octet field. +/// +/// transaction-id The transaction ID for this message exchange. +/// A 3-octet field. +/// +/// options Options carried in this message; options are +/// described in Section 21. A variable-length +/// field (4 octets less than the size of the +/// message). +/// ``` + +///Dhcp messages for use over UDP #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub enum Message { @@ -27,12 +133,12 @@ pub enum Message { RelayRepl(RelayRepl), LeaseQuery(LeaseQuery), LeaseQueryReply(LeaseQueryReply), - /*LeaseQueryDone(Message), - LeaseQueryData(Message), - ReconfigureRequest(Message), - ReconfigureReply(Message), - DHCPv4Query(Message), - DHCPv4Response(Message),*/ + /* + ReconfigureRequest(ReconfigureRequest), + ReconfigureReply(ReconfigureReply), + DHCPv4Query(DHCPv4Query), + DHCPv4Response(DHCPv4Response), + */ Unknown(Vec), } @@ -55,12 +161,12 @@ impl Message { RelayRepl(_) => MessageType::RelayRepl, LeaseQuery(_) => MessageType::LeaseQuery, LeaseQueryReply(_) => MessageType::LeaseQueryReply, - /*LeaseQueryDone(_) => MessageType::Message, - LeaseQueryData(_) => MessageType::Message, - ReconfigureRequest(_) => MessageType::Message, - ReconfigureReply(_) => MessageType::Message, - DHCPv4Query(_) => MessageType::Message, - DHCPv4Response(_) => MessageType::Message,*/ + /* + ReconfigureRequest(_) => MessageType::ReconfigureRequest, + ReconfigureReply(_) => MessageType::ReconfigureReply, + DHCPv4Query(_) => MessageType::ReconfigureReply, + DHCPv4Response(_) => MessageType::ReconfigureReply, + */ Unknown(v) => MessageType::Unknown(v[0]), } } @@ -85,12 +191,12 @@ impl Encodable for Message { RelayRepl(message) => message.encode(e), LeaseQuery(message) => message.encode(e), LeaseQueryReply(message) => message.encode(e), - /*LeaseQueryDone(message) => message.encode(e), - LeaseQueryData(message) => message.encode(e), + /* ReconfigureRequest(message) => message.encode(e), ReconfigureReply(message) => message.encode(e), DHCPv4Query(message) => message.encode(e), - DHCPv4Response(message) => message.encode(e),*/ + DHCPv4Response(message) => message.encode(e), + */ Unknown(message) => e.write_slice(message), } } @@ -118,12 +224,12 @@ impl Decodable for Message { MessageType::LeaseQueryReply => { Message::LeaseQueryReply(LeaseQueryReply::decode(decoder)?) } - /*MessageType::LeaseQueryDone => Message::LeaseQueryDone(Message::decode(decoder)?), - MessageType::LeaseQueryData => Message::LeaseQueryData(Message::decode(decoder)?), - MessageType::ReconfigureRequest => Message::ReconfigureRequest(Message::decode(decoder)?), - MessageType::ReconfigureReply => Message::ReconfigureReply(Message::decode(decoder)?), - MessageType::DHCPv4Query => Message::DHCPv4Query(Message::decode(decoder)?), - MessageType::DHCPv4Response => Message::DHCPv4Response(Message::decode(decoder)?),*/ + /* + MessageType::ReconfigureRequest => Message::ReconfigureRequest(ReconfigureRequest::decode(decoder)?), + MessageType::ReconfigureReply => Message::ReconfigureReply(ReconfigureReply::decode(decoder)?), + MessageType::DHCPv4Query => Message::DHCPv4Query(DHCPv4Query::decode(decoder)?), + MessageType::DHCPv4Response => Message::DHCPv4Response(DHCPv4Response::decode(decoder)?), + */ _ => Message::Unknown({ let mut buf = vec![]; while let Ok(b) = decoder.read_u8() { @@ -138,6 +244,7 @@ impl Decodable for Message { option_builder!( MessageOption, MessageOptions, + IsMessageOption, DhcpOption, ClientId, ServerId, @@ -168,6 +275,7 @@ option_builder!( option_builder!( RelayMessageOption, RelayMessageOptions, + IsRelayMessageOption, DhcpOption, RelayMsg, VendorOpts, @@ -177,6 +285,7 @@ option_builder!( option_builder!( SolicitOption, SolicitOptions, + IsSolicitOption, DhcpOption, ClientId, IANA, @@ -194,6 +303,7 @@ option_builder!( option_builder!( AdvertiseOption, AdvertiseOptions, + IsAdvertiseOption, DhcpOption, ClientId, ServerId, @@ -212,6 +322,7 @@ option_builder!( option_builder!( RequestOption, RequestOptions, + IsRequestOption, DhcpOption, ClientId, ServerId, @@ -228,6 +339,7 @@ option_builder!( option_builder!( ConfirmOption, ConfirmOptions, + IsConfirmOption, DhcpOption, ClientId, IANA, @@ -241,6 +353,7 @@ option_builder!( option_builder!( RenewOption, RenewOptions, + IsRenewOption, DhcpOption, ClientId, ServerId, @@ -258,6 +371,7 @@ option_builder!( option_builder!( RebindOption, RebindOptions, + IsRebindOption, DhcpOption, ClientId, IANA, @@ -274,6 +388,7 @@ option_builder!( option_builder!( DeclineOption, DeclineOptions, + IsDeclineOption, DhcpOption, ClientId, ServerId, @@ -289,6 +404,7 @@ option_builder!( option_builder!( ReleaseOption, ReleaseOptions, + IsReleaseOption, DhcpOption, ClientId, ServerId, @@ -304,6 +420,7 @@ option_builder!( option_builder!( ReplyOption, ReplyOptions, + IsReplyOption, DhcpOption, ClientId, ServerId, @@ -326,6 +443,7 @@ option_builder!( option_builder!( ReconfigureOption, ReconfigureOptions, + IsReconfigureOption, DhcpOption, ClientId, ServerId, @@ -336,6 +454,7 @@ option_builder!( option_builder!( InformationRequestOption, InformationRequestOptions, + IsInformationRequestOption, DhcpOption, ClientId, ServerId, @@ -347,17 +466,39 @@ option_builder!( ReconfAccept ); -option_builder!(LeaseQueryOption, LeaseQueryOptions, DhcpOption, LqQuery); +option_builder!( + LeaseQueryOption, + LeaseQueryOptions, + IsLeaseQueryOption, + DhcpOption, + LqQuery +); option_builder!( LeaseQueryReplyOption, LeaseQueryReplyOptions, + IsLeaseQueryReplyOption, DhcpOption, ClientData, LqRelayData, LqClientLink ); +//TODO: work out which options are alloud in LeaseQueryData message +option_builder!( + LeaseQueryDataOption, + LeaseQueryDataOptions, + IsLeaseQueryDataOption, + DhcpOption, +); +//TODO: work out which options are alloud in LeaseQueryDone message +option_builder!( + LeaseQueryDoneOption, + LeaseQueryDoneOptions, + IsLeaseQueryDoneOption, + DhcpOption, +); + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TransactionId { @@ -386,12 +527,14 @@ impl Decodable for TransactionId { } macro_rules! base_message_builder { - ($name: ident, $options: ident) => { - impl From<$name> for Message { - fn from(message: $name) -> Message { - Message::$name(message) + ($name: ident, $options: ident, $($messagetype: ident),*) => { + $( + impl From<$name> for $messagetype { + fn from(message: $name) -> $messagetype { + $messagetype::$name(message) } } + )* impl $name { /// Get a reference to the message's options. @@ -407,7 +550,7 @@ macro_rules! base_message_builder { } macro_rules! client_server_message_builder { - ($name: ident, $options: ident) => { + ($name: ident, $options: ident, $($messagetype: ident),*) => { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct $name { @@ -429,7 +572,7 @@ macro_rules! client_server_message_builder { } } - base_message_builder!($name, $options); + base_message_builder!($name, $options, $($messagetype),*); impl Encodable for $name { fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { @@ -453,7 +596,7 @@ macro_rules! client_server_message_builder { } macro_rules! relay_message_builder { - ($name: ident, $options: ident) => { + ($name: ident, $options: ident, $($messagetype: ident),*) => { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct $name { @@ -463,7 +606,7 @@ macro_rules! relay_message_builder { pub opts: $options, } - base_message_builder!($name, $options); + base_message_builder!($name, $options, $($messagetype)*); impl Encodable for $name { fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { @@ -490,20 +633,65 @@ macro_rules! relay_message_builder { }; } -client_server_message_builder!(Solicit, SolicitOptions); -client_server_message_builder!(Advertise, AdvertiseOptions); -client_server_message_builder!(Request, RequestOptions); -client_server_message_builder!(Confirm, ConfirmOptions); -client_server_message_builder!(Renew, RenewOptions); -client_server_message_builder!(Rebind, RebindOptions); -client_server_message_builder!(Reply, ReplyOptions); -client_server_message_builder!(Decline, DeclineOptions); -client_server_message_builder!(Release, ReleaseOptions); -client_server_message_builder!(Reconfigure, ReconfigureOptions); -client_server_message_builder!(InformationRequest, InformationRequestOptions); - -relay_message_builder!(RelayForw, RelayMessageOptions); -relay_message_builder!(RelayRepl, RelayMessageOptions); - -client_server_message_builder!(LeaseQuery, LeaseQueryOptions); -client_server_message_builder!(LeaseQueryReply, LeaseQueryReplyOptions); +/*macro_rules! dhcp4o6_message_builder { + ($name: ident, $options: ident, $($messagetype: ident),*) => { + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct $name { + pub flags: [u8;3], + pub opts: $options, + } + + base_message_builder!($name, $options, $($messagetype)*); + + impl Encodable for $name { + fn encode(&self, e: &mut Encoder<'_>) -> EncodeResult<()> { + e.write_u8(MessageType::$name.into())?; + e.write_slice(self.flags)?; + self.opts.encode(e)?; + Ok(()) + } + } + + impl Decodable for $name { + fn decode(decoder: &mut Decoder<'_>) -> DecodeResult { + let _message_type = decoder.read_u8()?; + Ok(Self { + flags: decoder.read::<3>()?, + opts: $options::decode(decoder)?, + }) + } + } + }; +}*/ + +client_server_message_builder!(Solicit, SolicitOptions, Message); +client_server_message_builder!(Advertise, AdvertiseOptions, Message); +client_server_message_builder!(Request, RequestOptions, Message); +client_server_message_builder!(Confirm, ConfirmOptions, Message); +client_server_message_builder!(Renew, RenewOptions, Message); +client_server_message_builder!(Rebind, RebindOptions, Message); +client_server_message_builder!(Reply, ReplyOptions, Message); +client_server_message_builder!(Decline, DeclineOptions, Message); +client_server_message_builder!(Release, ReleaseOptions, Message); +client_server_message_builder!(Reconfigure, ReconfigureOptions, Message); +client_server_message_builder!(InformationRequest, InformationRequestOptions, Message); + +relay_message_builder!(RelayForw, RelayMessageOptions, Message); +relay_message_builder!(RelayRepl, RelayMessageOptions, Message); + +client_server_message_builder!( + LeaseQuery, + LeaseQueryOptions, + Message, + BulkLeaseQueryMessage +); +client_server_message_builder!( + LeaseQueryReply, + LeaseQueryReplyOptions, + Message, + BulkLeaseQueryMessage +); + +client_server_message_builder!(LeaseQueryData, LeaseQueryDataOptions, BulkLeaseQueryMessage); +client_server_message_builder!(LeaseQueryDone, LeaseQueryDoneOptions, BulkLeaseQueryMessage); diff --git a/src/v6/mod.rs b/src/v6/mod.rs index 9eb0e46..bbab713 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -17,6 +17,9 @@ //! msg.opts_mut() //! .insert(v6::ClientId{id: duid}); //! +//! //access an option +//! let _id = msg.opts().get(); +//! //! // now encode to bytes //! let mut buf = Vec::new(); //! let mut e = Encoder::new(&mut buf); @@ -58,16 +61,17 @@ pub mod options; ///options pub use options::{ Auth, ClientData, ClientId, CltTime, DNSServers, DomainList, ElapsedTime, IAAddr, IAPrefix, - InfMaxRt, InformationRefreshTime, InterfaceId, LqClientLink, LqQuery, LqRelayData, Preference, - RapidCommit, ReconfAccept, ReconfMsg, RelayMsg, ServerId, SolMaxRt, StatusCode, Unicast, - UserClass, VendorClass, VendorOpts, IANA, IAPD, IATA, ORO, + InfMaxRt, InformationRefreshTime, InterfaceId, LinkAddress, LqClientLink, LqQuery, LqRelayData, + Preference, RapidCommit, ReconfAccept, ReconfMsg, RelayId, RelayMsg, ServerId, SolMaxRt, + StatusCode, Unicast, UserClass, VendorClass, VendorOpts, IANA, IAPD, IATA, ORO, }; pub mod messages; mod oro_codes; ///messages pub use messages::{ - Advertise, Confirm, Decline, InformationRequest, LeaseQuery, LeaseQueryReply, Message, Rebind, - Reconfigure, RelayForw, RelayRepl, Release, Renew, Reply, Request, Solicit, + Advertise, BulkLeaseQueryMessage, Confirm, Decline, InformationRequest, LeaseQuery, + LeaseQueryData, LeaseQueryDone, LeaseQueryReply, Message, Rebind, Reconfigure, RelayForw, + RelayRepl, Release, Renew, Reply, Request, Solicit, }; #[cfg(feature = "serde")] @@ -91,47 +95,6 @@ pub const SERVER_PORT: u16 = 547; /// default dhcpv6 client port pub const CLIENT_PORT: u16 = 546; -/// See RFC 8415 for updated DHCPv6 info -/// [DHCP for Ipv6](https://datatracker.ietf.org/doc/html/rfc8415) -/// -/// All DHCP messages sent between clients and servers share an identical -/// fixed-format header and a variable-format area for options. -/// -/// All values in the message header and in options are in network byte -/// order. -/// -/// Options are stored serially in the "options" field, with no padding -/// between the options. Options are byte-aligned but are not aligned in -/// any other way (such as on 2-byte or 4-byte boundaries). -/// -/// The following diagram illustrates the format of DHCP messages sent -/// between clients and servers: -/// -/// ```text -/// 0 1 2 3 -/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -/// | msg-type | transaction-id | -/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -/// | | -/// . options . -/// . (variable number and length) . -/// | | -/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -/// -/// msg-type Identifies the DHCP message type; the -/// available message types are listed in -/// Section 7.3. A 1-octet field. -/// -/// transaction-id The transaction ID for this message exchange. -/// A 3-octet field. -/// -/// options Options carried in this message; options are -/// described in Section 21. A variable-length -/// field (4 octets less than the size of the -/// message). -/// ``` - /// DHCPv6 message types /// #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/v6/option_codes.rs b/src/v6/option_codes.rs index 4d768f4..74ae5d0 100644 --- a/src/v6/option_codes.rs +++ b/src/v6/option_codes.rs @@ -477,6 +477,8 @@ impl From<&DhcpOption> for OptionCode { CltTime(_) => OptionCode::CltTime, LqRelayData(_) => OptionCode::LqRelayData, LqClientLink(_) => OptionCode::LqClientLink, + RelayId(_) => OptionCode::RelayId, + LinkAddress(_) => OptionCode::LinkAddress, Unknown(unknown) => unknown.into(), } } diff --git a/src/v6/options/clientdata.rs b/src/v6/options/clientdata.rs index 7306164..1175333 100644 --- a/src/v6/options/clientdata.rs +++ b/src/v6/options/clientdata.rs @@ -41,6 +41,7 @@ impl Encodable for ClientData { option_builder!( ClientDataOption, ClientDataOptions, + IsClientDataOption, DhcpOption, ClientId, IAAddr, diff --git a/src/v6/options/iaaddr.rs b/src/v6/options/iaaddr.rs index f343dda..0a24ec0 100644 --- a/src/v6/options/iaaddr.rs +++ b/src/v6/options/iaaddr.rs @@ -50,7 +50,13 @@ impl Encodable for IAAddr { } } -option_builder!(IAAddrOption, IAAddrOptions, DhcpOption, StatusCode); +option_builder!( + IAAddrOption, + IAAddrOptions, + IsAIIdrOption, + DhcpOption, + StatusCode +); #[cfg(test)] mod tests { diff --git a/src/v6/options/iana.rs b/src/v6/options/iana.rs index 132e19c..a6cf86f 100644 --- a/src/v6/options/iana.rs +++ b/src/v6/options/iana.rs @@ -49,7 +49,14 @@ impl Encodable for IANA { } } -option_builder!(IANAOption, IANAOptions, DhcpOption, IAAddr, StatusCode); +option_builder!( + IANAOption, + IANAOptions, + IsIANAOption, + DhcpOption, + IAAddr, + StatusCode +); #[cfg(test)] mod tests { diff --git a/src/v6/options/iapd.rs b/src/v6/options/iapd.rs index 891a65c..f11f4bc 100644 --- a/src/v6/options/iapd.rs +++ b/src/v6/options/iapd.rs @@ -51,7 +51,14 @@ impl Encodable for IAPD { } } -option_builder!(IAPDOption, IAPDOptions, DhcpOption, IAPrefix, StatusCode); +option_builder!( + IAPDOption, + IAPDOptions, + IsIAPDOption, + DhcpOption, + IAPrefix, + StatusCode +); #[cfg(test)] mod tests { diff --git a/src/v6/options/iaprefix.rs b/src/v6/options/iaprefix.rs index 2c2d02c..b813239 100644 --- a/src/v6/options/iaprefix.rs +++ b/src/v6/options/iaprefix.rs @@ -52,7 +52,12 @@ impl Encodable for IAPrefix { } } -option_builder!(IAPrefixOption, IAPrefixOptions, DhcpOption,); +option_builder!( + IAPrefixOption, + IAPrefixOptions, + IsIAPrefixOption, + DhcpOption, +); #[cfg(test)] mod tests { diff --git a/src/v6/options/iata.rs b/src/v6/options/iata.rs index bfc281d..9d69453 100644 --- a/src/v6/options/iata.rs +++ b/src/v6/options/iata.rs @@ -43,7 +43,14 @@ impl Encodable for IATA { } } -option_builder!(IATAOption, IATAOptions, DhcpOption, IAAddr, StatusCode); +option_builder!( + IATAOption, + IATAOptions, + IsIATAOption, + DhcpOption, + IAAddr, + StatusCode +); #[cfg(test)] mod tests { diff --git a/src/v6/options/linkaddress.rs b/src/v6/options/linkaddress.rs new file mode 100644 index 0000000..82ee28e --- /dev/null +++ b/src/v6/options/linkaddress.rs @@ -0,0 +1,30 @@ +use super::{DecodeResult, EncodeResult, Ipv6Addr, OptionCode}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct LinkAddress { + pub link_address: Ipv6Addr, +} + +impl Decodable for LinkAddress { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let _len = decoder.read_u16()? as usize; + Ok(LinkAddress { + link_address: decoder.read::<16>()?.into(), + }) + } +} + +impl Encodable for LinkAddress { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + e.write_u16(OptionCode::LinkAddress.into())?; + e.write_u16(16)?; + e.write_u128(self.link_address.into())?; + Ok(()) + } +} diff --git a/src/v6/options/mod.rs b/src/v6/options/mod.rs index 76c0e9f..44c2b38 100644 --- a/src/v6/options/mod.rs +++ b/src/v6/options/mod.rs @@ -63,6 +63,14 @@ pub use lqrelaydata::*; mod lqclientlink; pub use lqclientlink::*; +//rfc5460 +mod relayid; +pub use relayid::*; + +//rfc6977 +mod linkaddress; +pub use linkaddress::*; + use std::{cmp::Ordering, net::Ipv6Addr, ops::RangeInclusive}; pub use crate::Domain; @@ -74,10 +82,13 @@ use crate::{ }; //helper macro for implementing sub-options (IANAOptions, ect) -//useage: option_builder!(IANAOption, IANAOptions, DhcpOption, IAAddr, StatusCode); -// option_builder!(name , names , master , subname... ); +//useage: option_builder!(IANAOption, IANAOptions, IsIANAOption, DhcpOption, IAAddr, StatusCode); +// option_builder!(name , names , isname , master , subname... ); macro_rules! option_builder{ - ($name: ident, $names: ident, $mastername: ident, $($subnames: ident),*) => { + ($name: ident, $names: ident, $isname: ident, $mastername: ident, $($subnames: ident),*) => { + pub trait $isname{ + fn code() -> OptionCode; + } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub enum $name { @@ -93,6 +104,29 @@ macro_rules! option_builder{ $name :: $subnames(sc) } } + impl<'a> TryFrom<&'a $name> for &'a $subnames { + type Error = &'static str; + fn try_from(name: &'a $name) -> Result<&'a $subnames, Self::Error>{ + match name{ + $name :: $subnames(opt) => Ok(opt), + _ => Err("$subname is not a $name"), + } + } + } + impl TryFrom<$name> for $subnames { + type Error = &'static str; + fn try_from(name: $name) -> Result<$subnames, Self::Error>{ + match name{ + $name :: $subnames(opt) => Ok(opt), + _ => Err("$subname is not a $name"), + } + } + } + impl $isname for $subnames { + fn code() -> OptionCode{ + OptionCode::$subnames + } + } )* impl From<&$name> for $mastername{ fn from(option: &$name) -> Self{ @@ -150,46 +184,48 @@ macro_rules! option_builder{ pub fn new() -> Self{ Default::default() } - /// get the first element matching this option code - pub fn get(&self, code: OptionCode) -> Option<&$name>{ + /// get the first element matching this type + pub fn get<'a, T: $isname>(&'a self) -> Option<&'a T> where &'a T: TryFrom<&'a $name>{ use crate::v6::options::first; use crate::v6::OptionCode; - let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; - self.0.get(first) + let first = first(&self.0, |x| OptionCode::from(x).cmp(&T::code()))?; + //unwrap can not fail, it has already been checked. + self.0.get(first).map(|opt| <&T>::try_from(opt).ok().unwrap()) } - /// get all elements matching this option code - pub fn get_all(&self, code: OptionCode) -> Option<&[$name]>{ + /// get all elements matching this type + pub fn get_all(&self) -> Option<&[$name]>{ use crate::v6::options::range_binsearch; use crate::v6::OptionCode; - let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; + let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&T::code()))?; Some(&self.0[range]) } - /// get the first element matching this option code - pub fn get_mut(&mut self, code: OptionCode) -> Option<&mut $name>{ + /// get the first element matching the type + pub fn get_mut<'a, T: $isname>(&'a mut self) -> Option<&'a mut T> where &'a mut T: TryFrom<&'a mut $name>{ use crate::v6::options::first; use crate::v6::OptionCode; - let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; - self.0.get_mut(first) + let first = first(&self.0, |x| OptionCode::from(x).cmp(&T::code()))?; + //unwrap can not fail, it has already been checked. + self.0.get_mut(first).map(|opt| <&mut T>::try_from(opt).ok().unwrap()) } /// get all elements matching this option - pub fn get_mut_all(&mut self, code: OptionCode) -> Option<&mut [$name]>{ + pub fn get_mut_all(&mut self) -> Option<&mut [$name]>{ use crate::v6::options::range_binsearch; use crate::v6::OptionCode; - let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; + let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&T::code()))?; Some(&mut self.0[range]) } - /// remove the first element with a matching option code - pub fn remove(&mut self, code: OptionCode) -> Option<$name>{ + /// remove the first element with a matching type + pub fn remove(&mut self) -> Option where T: TryFrom<$name>{ use crate::v6::options::first; use crate::v6::OptionCode; - let first = first(&self.0, |x| OptionCode::from(x).cmp(&code))?; - Some(self.0.remove(first)) + let first = first(&self.0, |x| OptionCode::from(x).cmp(&T::code()))?; + T::try_from(self.0.remove(first)).ok() } - pub fn remove_all(&mut self, code: OptionCode) -> Option + '_>{ + pub fn remove_all(&mut self) -> Option + '_> where T: TryFrom<$name>{ use crate::v6::options::range_binsearch; use crate::v6::OptionCode; - let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&code))?; - Some(self.0.drain(range)) + let range = range_binsearch(&self.0, |x| OptionCode::from(x).cmp(&T::code()))?; + Some(self.0.drain(range).map(|opt| T::try_from(opt).ok().unwrap())) } /// insert a new option into the list of opts pub fn insert>(&mut self, opt: T){ @@ -380,6 +416,8 @@ pub enum DhcpOption { CltTime(CltTime), LqRelayData(LqRelayData), LqClientLink(LqClientLink), + RelayId(RelayId), + LinkAddress(LinkAddress), /// An unknown or unimplemented option type Unknown(UnknownOption), } @@ -489,6 +527,8 @@ impl Decodable for DhcpOption { OptionCode::CltTime => DhcpOption::CltTime(CltTime::decode(decoder)?), OptionCode::LqRelayData => DhcpOption::LqRelayData(LqRelayData::decode(decoder)?), OptionCode::LqClientLink => DhcpOption::LqClientLink(LqClientLink::decode(decoder)?), + OptionCode::RelayId => DhcpOption::RelayId(RelayId::decode(decoder)?), + OptionCode::LinkAddress => DhcpOption::LinkAddress(LinkAddress::decode(decoder)?), // not yet implemented OptionCode::Unknown(code) => { decoder.read_u16()?; @@ -606,6 +646,12 @@ impl Encodable for DhcpOption { DhcpOption::LqClientLink(q) => { q.encode(e)?; } + DhcpOption::RelayId(q) => { + q.encode(e)?; + } + DhcpOption::LinkAddress(q) => { + q.encode(e)?; + } DhcpOption::Unknown(UnknownOption { data, .. }) => { e.write_u16(code.into())?; e.write_u16(data.len() as u16)?; diff --git a/src/v6/options/query.rs b/src/v6/options/query.rs index 9588202..68c7ebe 100644 --- a/src/v6/options/query.rs +++ b/src/v6/options/query.rs @@ -52,6 +52,7 @@ impl Encodable for LqQuery { option_builder!( LqQueryOption, LqQueryOptions, + IsLqQueryOption, DhcpOption, IAAddr, ClientId, @@ -63,6 +64,9 @@ option_builder!( pub enum QueryType { QueryByAddress, QueryByClientID, + QueryByRelayID, + QueryByLinkAaddress, + QueryByRemoteID, Unknown(u8), } @@ -72,6 +76,9 @@ impl From for QueryType { match qtype { 1 => QueryByAddress, 2 => QueryByClientID, + 3 => QueryByRelayID, + 4 => QueryByLinkAaddress, + 5 => QueryByRemoteID, t => Unknown(t), } } @@ -83,6 +90,9 @@ impl From for u8 { match num { QueryByAddress => 1, QueryByClientID => 2, + QueryByRelayID => 3, + QueryByLinkAaddress => 4, + QueryByRemoteID => 5, Unknown(t) => t, } } diff --git a/src/v6/options/relayid.rs b/src/v6/options/relayid.rs new file mode 100644 index 0000000..f57e014 --- /dev/null +++ b/src/v6/options/relayid.rs @@ -0,0 +1,36 @@ +use super::{DecodeResult, Duid, EncodeResult, OptionCode}; +use crate::{Decodable, Decoder, Encodable, Encoder}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Client Identity +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RelayId { + pub id: Duid, +} + +impl Decodable for RelayId { + fn decode(decoder: &'_ mut Decoder<'_>) -> DecodeResult { + decoder.read::<2>()?; + let len = decoder.read_u16()? as usize; + let mut decoder = Decoder::new(decoder.read_slice(len)?); + Ok(RelayId { + id: Duid::decode(&mut decoder)?, + }) + } +} + +impl Encodable for RelayId { + fn encode(&self, e: &'_ mut Encoder<'_>) -> EncodeResult<()> { + // write len + let mut buf = Vec::new(); + let mut opt_enc = Encoder::new(&mut buf); + self.id.encode(&mut opt_enc)?; + e.write_u16(OptionCode::RelayId.into())?; + e.write_u16(buf.len() as u16)?; + e.write_slice(&buf)?; + Ok(()) + } +} diff --git a/src/v6/options/unicast.rs b/src/v6/options/unicast.rs index 03a105c..aaeb22e 100644 --- a/src/v6/options/unicast.rs +++ b/src/v6/options/unicast.rs @@ -29,8 +29,6 @@ impl Encodable for Unicast { } } -//impl From for Message? - #[cfg(test)] mod tests { use super::*; From b2d1e6f68f915e867ea8dca13a5ffeea063e5202 Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 20 Oct 2022 15:53:10 +1000 Subject: [PATCH 12/12] V6: Fix tests --- src/v6/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v6/mod.rs b/src/v6/mod.rs index bbab713..c298817 100644 --- a/src/v6/mod.rs +++ b/src/v6/mod.rs @@ -18,7 +18,7 @@ //! .insert(v6::ClientId{id: duid}); //! //! //access an option -//! let _id = msg.opts().get(); +//! let _id = msg.opts().get::(); //! //! // now encode to bytes //! let mut buf = Vec::new();