use std::sync::OnceLock;
use crate::endian::read_u32_be;
use crate::error::Result;
use crate::packet::{LinkType, NetworkLayer, Packet, Raw};
use crate::protocols::bgp::{decode::append_bgp_packet_with_registry, BGP_PORT};
use crate::protocols::dhcp::{append_dhcp_packet, is_dhcp_port_pair, looks_like_dhcp_payload};
use crate::protocols::dns::{append_dns_packet, DNS_PORT};
use crate::protocols::eapol::append_eapol_packet;
use crate::protocols::icmp::{
append_icmp_packet, append_icmp_packet_with_checksum_validation, append_icmpv6_packet,
};
use crate::protocols::igmp::append_igmp_packet;
use crate::protocols::ip::shared::IPPROTO_IGMP;
use crate::protocols::ipsec::ah::decode::append_ah_packet_with_registry_sa;
use crate::protocols::ipsec::esp::decode::append_esp_packet_with_registry_sa;
use crate::protocols::ipsec::esp::header::ESP_HEADER_LEN;
use crate::protocols::ipsec::ikev2::decode::append_ikev2_packet_with_registry;
use crate::protocols::ipsec::natt::{is_non_esp_marker, NatTraversal, NON_ESP_MARKER_LEN};
use crate::protocols::ipsec::sa::SecurityAssociation;
use crate::protocols::ipv4::{
append_ipv4_packet_with_registry, IPPROTO_AH, IPPROTO_ESP, IPPROTO_ICMP, IPPROTO_ICMPV6,
IPPROTO_OSPF, IPPROTO_TCP, IPPROTO_UDP,
};
use crate::protocols::ipv6::{append_ipv6_packet_with_registry, IPPROTO_IPV6_AH, IPPROTO_IPV6_ESP};
use crate::protocols::ospf::decode::append_ospf_packet_with_checksum_validation;
use crate::protocols::ospf::v3::append_ospfv3_packet_with_checksum_validation;
use crate::protocols::link::{
append_arp_packet, append_vlan_packet_with_registry, decode_ble_ll_with_registry,
decode_dot11_with_registry, decode_ethernet_with_registry, decode_linux_sll_with_registry,
decode_null_loopback_with_registry, decode_radiotap_with_registry, ETHERTYPE_ARP,
ETHERTYPE_EAPOL, ETHERTYPE_IPV4, ETHERTYPE_IPV6, ETHERTYPE_VLAN,
};
#[allow(unused_imports)]
pub(crate) use crate::protocols::ospf::decode::append_ospf_packet;
use crate::protocols::rip::ripng::{append_ripng_packet, looks_like_ripng_payload, RIPNG_UDP_PORT};
use crate::protocols::rip::{append_rip_packet, looks_like_rip_payload, RIP_UDP_PORT};
use crate::protocols::transport::{
append_tcp_packet_with_registry, append_udp_packet_with_registry,
};
const IKEV2_UDP_PORT: u16 = 500;
const NATT_UDP_PORT: u16 = 4500;
type ProtocolDecoder = dyn for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static;
#[derive(Debug, Clone, Copy)]
pub struct EthertypeBindingContext<'a> {
pub ethertype: u16,
pub payload: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct Ipv4ProtocolBindingContext<'a> {
pub protocol: u8,
pub payload: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct Ipv6NextHeaderBindingContext<'a> {
pub next_header: u8,
pub payload: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct UdpBindingContext<'a> {
pub source_port: u16,
pub destination_port: u16,
pub payload: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct TcpBindingContext<'a> {
pub source_port: u16,
pub destination_port: u16,
pub payload: &'a [u8],
}
struct EthertypeBinding {
predicate: Box<dyn for<'a> Fn(EthertypeBindingContext<'a>) -> bool + Send + Sync + 'static>,
decoder: Box<ProtocolDecoder>,
}
struct Ipv4ProtocolBinding {
predicate: Box<dyn for<'a> Fn(Ipv4ProtocolBindingContext<'a>) -> bool + Send + Sync + 'static>,
decoder: Box<ProtocolDecoder>,
}
struct Ipv6NextHeaderBinding {
predicate:
Box<dyn for<'a> Fn(Ipv6NextHeaderBindingContext<'a>) -> bool + Send + Sync + 'static>,
decoder: Box<ProtocolDecoder>,
}
struct UdpBinding {
predicate: Box<dyn for<'a> Fn(UdpBindingContext<'a>) -> bool + Send + Sync + 'static>,
decoder: Box<ProtocolDecoder>,
}
struct TcpBinding {
predicate: Box<dyn for<'a> Fn(TcpBindingContext<'a>) -> bool + Send + Sync + 'static>,
decoder: Box<ProtocolDecoder>,
}
pub struct ProtocolRegistry {
ethertype_bindings: Vec<EthertypeBinding>,
builtin_ethertype_dispatch: bool,
ipv4_bindings: Vec<Ipv4ProtocolBinding>,
builtin_ipv4_protocol_dispatch: bool,
ipv6_bindings: Vec<Ipv6NextHeaderBinding>,
builtin_ipv6_next_header_dispatch: bool,
udp_bindings: Vec<UdpBinding>,
builtin_udp_application_dispatch: bool,
tcp_bindings: Vec<TcpBinding>,
security_associations: Vec<SecurityAssociation>,
validate_checksums: bool,
decode_applications: bool,
}
impl ProtocolRegistry {
pub fn new() -> Self {
Self::with_builtin_bindings()
}
pub(crate) fn builtin() -> &'static Self {
static BUILTIN_REGISTRY: OnceLock<ProtocolRegistry> = OnceLock::new();
BUILTIN_REGISTRY.get_or_init(Self::with_builtin_bindings)
}
pub(crate) fn transport_only_builtin() -> &'static Self {
static TRANSPORT_ONLY_REGISTRY: OnceLock<ProtocolRegistry> = OnceLock::new();
TRANSPORT_ONLY_REGISTRY.get_or_init(Self::transport_only)
}
pub fn empty() -> Self {
Self {
ethertype_bindings: Vec::new(),
builtin_ethertype_dispatch: false,
ipv4_bindings: Vec::new(),
builtin_ipv4_protocol_dispatch: false,
ipv6_bindings: Vec::new(),
builtin_ipv6_next_header_dispatch: false,
udp_bindings: Vec::new(),
builtin_udp_application_dispatch: false,
tcp_bindings: Vec::new(),
security_associations: Vec::new(),
validate_checksums: true,
decode_applications: true,
}
}
pub fn with_builtin_bindings() -> Self {
let mut registry = Self::empty();
registry.bind_ethertype_with_registry(ETHERTYPE_ARP, |_registry, packet, payload| {
append_arp_packet(packet, payload)
});
registry.bind_ethertype_with_registry(ETHERTYPE_VLAN, |registry, packet, payload| {
append_vlan_packet_with_registry(registry, packet, payload)
});
registry.bind_ethertype_with_registry(ETHERTYPE_IPV4, |registry, packet, payload| {
append_ipv4_packet_with_registry(registry, packet, payload)
});
registry.bind_ethertype_with_registry(ETHERTYPE_IPV6, |registry, packet, payload| {
append_ipv6_packet_with_registry(registry, packet, payload)
});
registry.bind_ethertype_with_registry(ETHERTYPE_EAPOL, |_registry, packet, payload| {
append_eapol_packet(packet, payload)
});
registry.builtin_ethertype_dispatch = true;
registry.bind_ipv4_protocol_with_registry(IPPROTO_ICMP, |registry, packet, payload| {
append_icmp_packet_with_checksum_validation(
packet,
payload,
registry.validates_checksums(),
)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_TCP, |registry, packet, payload| {
append_tcp_packet_with_registry(registry, packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_UDP, |registry, packet, payload| {
append_udp_packet_with_registry(registry, packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_IGMP, |_registry, packet, payload| {
append_igmp_packet(packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_ESP, |registry, packet, payload| {
decode_esp_with_registry_sa(registry, packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_AH, |registry, packet, payload| {
decode_ah_with_registry_sa(registry, packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_OSPF, |registry, packet, payload| {
append_ospf_packet_with_checksum_validation(
packet,
payload,
registry.validates_checksums(),
)
});
registry
.bind_ipv6_next_header_with_registry(IPPROTO_ICMPV6, |_registry, packet, payload| {
append_icmpv6_packet(packet, payload)
});
registry.bind_ipv6_next_header_with_registry(IPPROTO_TCP, |registry, packet, payload| {
append_tcp_packet_with_registry(registry, packet, payload)
});
registry.bind_ipv6_next_header_with_registry(IPPROTO_UDP, |registry, packet, payload| {
append_udp_packet_with_registry(registry, packet, payload)
});
registry
.bind_ipv6_next_header_with_registry(IPPROTO_IPV6_ESP, |registry, packet, payload| {
decode_esp_with_registry_sa(registry, packet, payload)
});
registry
.bind_ipv6_next_header_with_registry(IPPROTO_IPV6_AH, |registry, packet, payload| {
decode_ah_with_registry_sa(registry, packet, payload)
});
registry.bind_ipv6_next_header_with_registry(IPPROTO_OSPF, |registry, packet, payload| {
append_ospfv3_packet_with_checksum_validation(
packet,
payload,
registry.validates_checksums(),
)
});
registry.bind_udp_port_with_registry(DNS_PORT, |_registry, packet, payload| {
append_dns_packet(packet, payload)
});
registry.bind_udp_port_with_registry(IKEV2_UDP_PORT, |registry, packet, payload| {
append_ikev2_packet_with_registry(registry, packet, payload)
});
registry.bind_udp_with_registry(
|ctx| {
(ctx.source_port == NATT_UDP_PORT || ctx.destination_port == NATT_UDP_PORT)
&& ctx.payload.len() >= ESP_HEADER_LEN
},
|registry, packet, payload| {
if is_non_esp_marker(payload) {
let marker =
NatTraversal::marker().bytes(payload[..NON_ESP_MARKER_LEN].to_vec());
let packet = packet.push(marker);
append_ikev2_packet_with_registry(
registry,
packet,
&payload[NON_ESP_MARKER_LEN..],
)
} else {
decode_esp_with_registry_sa(registry, packet, payload)
}
},
);
registry.bind_udp_with_registry(
|ctx| {
is_dhcp_port_pair(ctx.source_port, ctx.destination_port)
&& looks_like_dhcp_payload(ctx.payload)
},
|_registry, packet, payload| append_dhcp_packet(packet, payload),
);
registry.bind_udp_with_registry(
|ctx| {
(ctx.source_port == RIP_UDP_PORT || ctx.destination_port == RIP_UDP_PORT)
&& looks_like_rip_payload(ctx.payload)
},
|_registry, packet, payload| append_rip_packet(packet, payload),
);
registry.bind_udp_with_registry(
|ctx| {
(ctx.source_port == RIPNG_UDP_PORT || ctx.destination_port == RIPNG_UDP_PORT)
&& looks_like_ripng_payload(ctx.payload)
},
|_registry, packet, payload| append_ripng_packet(packet, payload),
);
registry.bind_tcp_port_with_registry(BGP_PORT, |registry, packet, payload| {
append_bgp_packet_with_registry(registry, packet, payload)
});
registry.builtin_ipv4_protocol_dispatch = true;
registry.builtin_ipv6_next_header_dispatch = true;
registry.builtin_udp_application_dispatch = true;
registry
}
pub(crate) fn transport_only() -> Self {
let mut registry = Self::empty();
registry.bind_ipv4_protocol_with_registry(IPPROTO_ICMP, |_registry, packet, payload| {
append_icmp_packet(packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_TCP, |registry, packet, payload| {
append_tcp_packet_with_registry(registry, packet, payload)
});
registry.bind_ipv4_protocol_with_registry(IPPROTO_UDP, |registry, packet, payload| {
append_udp_packet_with_registry(registry, packet, payload)
});
registry
}
#[must_use]
pub fn checksum_validation(mut self, enabled: bool) -> Self {
self.validate_checksums = enabled;
self
}
pub fn set_checksum_validation(&mut self, enabled: bool) -> &mut Self {
self.validate_checksums = enabled;
self
}
pub(crate) const fn validates_checksums(&self) -> bool {
self.validate_checksums
}
#[must_use]
pub fn application_decoding(mut self, enabled: bool) -> Self {
self.decode_applications = enabled;
self
}
pub fn set_application_decoding(&mut self, enabled: bool) -> &mut Self {
self.decode_applications = enabled;
self
}
pub(crate) const fn decodes_applications(&self) -> bool {
self.decode_applications
}
pub(crate) const fn uses_builtin_ethertype_dispatch(&self) -> bool {
self.builtin_ethertype_dispatch
}
pub fn decode_from_link(&self, link_type: LinkType, bytes: impl AsRef<[u8]>) -> Result<Packet> {
let bytes = bytes.as_ref();
match link_type {
LinkType::Raw => Packet::decode_raw(bytes),
LinkType::Ethernet => self.decode_ethernet(bytes),
LinkType::Ieee80211 => decode_dot11_with_registry(self, bytes),
LinkType::Radiotap => decode_radiotap_with_registry(self, bytes),
LinkType::BluetoothLeLl => decode_ble_ll_with_registry(self, bytes),
LinkType::LinuxCooked | LinkType::LinuxSll => self.decode_linux_sll(bytes),
LinkType::NullLoopback => decode_null_loopback_with_registry(self, bytes),
}
}
pub fn decode_ethernet(&self, bytes: impl AsRef<[u8]>) -> Result<Packet> {
decode_ethernet_with_registry(self, bytes.as_ref())
}
pub fn decode_linux_sll(&self, bytes: impl AsRef<[u8]>) -> Result<Packet> {
decode_linux_sll_with_registry(self, bytes.as_ref())
}
pub fn decode_from_l3(
&self,
network_layer: NetworkLayer,
bytes: impl AsRef<[u8]>,
) -> Result<Packet> {
let bytes = bytes.as_ref();
match network_layer {
NetworkLayer::Raw => Packet::decode_raw(bytes),
NetworkLayer::Ipv4 => self.decode_ipv4(bytes),
NetworkLayer::Ipv6 => self.decode_ipv6(bytes),
}
}
pub fn decode_ipv4(&self, bytes: impl AsRef<[u8]>) -> Result<Packet> {
append_ipv4_packet_with_registry(self, Packet::new(), bytes.as_ref())
}
pub fn decode_ipv6(&self, bytes: impl AsRef<[u8]>) -> Result<Packet> {
append_ipv6_packet_with_registry(self, Packet::new(), bytes.as_ref())
}
pub fn register_security_association(&mut self, sa: SecurityAssociation) -> &mut Self {
self.security_associations.push(sa);
self
}
#[must_use]
pub fn with_security_association(mut self, sa: SecurityAssociation) -> Self {
self.security_associations.push(sa);
self
}
pub(crate) fn security_association_for_spi(&self, spi: u32) -> Option<&SecurityAssociation> {
self.security_associations
.iter()
.rev()
.find(|sa| sa.spi == spi)
}
pub fn bind_ethertype<D>(&mut self, ethertype: u16, decoder: D) -> &mut Self
where
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_ethertype_if(move |ctx| ctx.ethertype == ethertype, decoder)
}
pub fn bind_ethertype_if<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(EthertypeBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_ethertype_if_with_registry(predicate, move |_registry, packet, payload| {
decoder(packet, payload)
})
}
pub fn bind_ipv4_protocol<D>(&mut self, protocol: u8, decoder: D) -> &mut Self
where
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_ipv4_protocol_if(move |ctx| ctx.protocol == protocol, decoder)
}
pub fn bind_ipv4_protocol_if<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(Ipv4ProtocolBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_ipv4_protocol_if_with_registry(predicate, move |_registry, packet, payload| {
decoder(packet, payload)
})
}
pub fn bind_ipv6_next_header<D>(&mut self, next_header: u8, decoder: D) -> &mut Self
where
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_ipv6_next_header_if(move |ctx| ctx.next_header == next_header, decoder)
}
pub fn bind_ipv6_next_header_if<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(Ipv6NextHeaderBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_ipv6_next_header_if_with_registry(predicate, move |_registry, packet, payload| {
decoder(packet, payload)
})
}
pub fn bind_udp_port<D>(&mut self, port: u16, decoder: D) -> &mut Self
where
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_udp(
move |ctx| ctx.source_port == port || ctx.destination_port == port,
decoder,
)
}
pub fn bind_udp<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(UdpBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_udp_with_registry(predicate, move |_registry, packet, payload| {
decoder(packet, payload)
})
}
pub fn bind_tcp_port<D>(&mut self, port: u16, decoder: D) -> &mut Self
where
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_tcp(
move |ctx| ctx.source_port == port || ctx.destination_port == port,
decoder,
)
}
pub fn bind_tcp<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(TcpBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(Packet, &'a [u8]) -> Result<Packet> + Send + Sync + 'static,
{
self.bind_tcp_with_registry(predicate, move |_registry, packet, payload| {
decoder(packet, payload)
})
}
pub(crate) fn decode_ethertype(
&self,
packet: Packet,
ethertype: u16,
payload: &[u8],
) -> Result<Packet> {
if self.builtin_ethertype_dispatch {
return match ethertype {
ETHERTYPE_ARP => append_arp_packet(packet, payload),
ETHERTYPE_VLAN => append_vlan_packet_with_registry(self, packet, payload),
ETHERTYPE_IPV4 => append_ipv4_packet_with_registry(self, packet, payload),
ETHERTYPE_IPV6 => append_ipv6_packet_with_registry(self, packet, payload),
ETHERTYPE_EAPOL => append_eapol_packet(packet, payload),
_ => Ok(packet.push_raw(Raw::from_bytes(payload))),
};
}
let ctx = EthertypeBindingContext { ethertype, payload };
if let Some(binding) = self
.ethertype_bindings
.iter()
.rev()
.find(|binding| (binding.predicate)(ctx))
{
return (binding.decoder)(self, packet, payload);
}
Ok(packet.push_raw(Raw::from_bytes(payload)))
}
pub(crate) fn decode_ipv4_protocol(
&self,
packet: Packet,
protocol: u8,
payload: &[u8],
) -> Result<Packet> {
if self.builtin_ipv4_protocol_dispatch {
return match protocol {
IPPROTO_ICMP => append_icmp_packet_with_checksum_validation(
packet,
payload,
self.validates_checksums(),
),
IPPROTO_TCP => append_tcp_packet_with_registry(self, packet, payload),
IPPROTO_UDP => append_udp_packet_with_registry(self, packet, payload),
IPPROTO_IGMP => append_igmp_packet(packet, payload),
IPPROTO_ESP => decode_esp_with_registry_sa(self, packet, payload),
IPPROTO_AH => decode_ah_with_registry_sa(self, packet, payload),
IPPROTO_OSPF => append_ospf_packet_with_checksum_validation(
packet,
payload,
self.validates_checksums(),
),
_ => append_raw_if_needed(packet, payload),
};
}
let ctx = Ipv4ProtocolBindingContext { protocol, payload };
if let Some(binding) = self
.ipv4_bindings
.iter()
.rev()
.find(|binding| (binding.predicate)(ctx))
{
return (binding.decoder)(self, packet, payload);
}
append_raw_if_needed(packet, payload)
}
pub(crate) fn decode_ipv6_next_header(
&self,
packet: Packet,
next_header: u8,
payload: &[u8],
) -> Result<Packet> {
if self.builtin_ipv6_next_header_dispatch {
return match next_header {
IPPROTO_ICMPV6 => append_icmpv6_packet(packet, payload),
IPPROTO_TCP => append_tcp_packet_with_registry(self, packet, payload),
IPPROTO_UDP => append_udp_packet_with_registry(self, packet, payload),
IPPROTO_IPV6_ESP => decode_esp_with_registry_sa(self, packet, payload),
IPPROTO_IPV6_AH => decode_ah_with_registry_sa(self, packet, payload),
IPPROTO_OSPF => append_ospfv3_packet_with_checksum_validation(
packet,
payload,
self.validates_checksums(),
),
_ => append_raw_if_needed(packet, payload),
};
}
let ctx = Ipv6NextHeaderBindingContext {
next_header,
payload,
};
if let Some(binding) = self
.ipv6_bindings
.iter()
.rev()
.find(|binding| (binding.predicate)(ctx))
{
return (binding.decoder)(self, packet, payload);
}
append_raw_if_needed(packet, payload)
}
pub(crate) fn decode_udp_application(
&self,
packet: Packet,
source_port: u16,
destination_port: u16,
payload: &[u8],
) -> Result<Packet> {
if !self.decode_applications {
return append_raw_if_needed(packet, payload);
}
if self.builtin_udp_application_dispatch {
if is_dhcp_port_pair(source_port, destination_port) && looks_like_dhcp_payload(payload)
{
return append_dhcp_packet(packet, payload);
}
if (source_port == RIP_UDP_PORT || destination_port == RIP_UDP_PORT)
&& looks_like_rip_payload(payload)
{
return append_rip_packet(packet, payload);
}
if (source_port == RIPNG_UDP_PORT || destination_port == RIPNG_UDP_PORT)
&& looks_like_ripng_payload(payload)
{
return append_ripng_packet(packet, payload);
}
if (source_port == NATT_UDP_PORT || destination_port == NATT_UDP_PORT)
&& payload.len() >= ESP_HEADER_LEN
{
if is_non_esp_marker(payload) {
let marker =
NatTraversal::marker().bytes(payload[..NON_ESP_MARKER_LEN].to_vec());
let packet = packet.push(marker);
return append_ikev2_packet_with_registry(
self,
packet,
&payload[NON_ESP_MARKER_LEN..],
);
}
return decode_esp_with_registry_sa(self, packet, payload);
}
if source_port == IKEV2_UDP_PORT || destination_port == IKEV2_UDP_PORT {
return append_ikev2_packet_with_registry(self, packet, payload);
}
if source_port == DNS_PORT || destination_port == DNS_PORT {
return append_dns_packet(packet, payload);
}
return append_raw_if_needed(packet, payload);
}
let ctx = UdpBindingContext {
source_port,
destination_port,
payload,
};
if let Some(binding) = self
.udp_bindings
.iter()
.rev()
.find(|binding| (binding.predicate)(ctx))
{
return (binding.decoder)(self, packet, payload);
}
append_raw_if_needed(packet, payload)
}
pub(crate) fn decode_tcp_application(
&self,
packet: Packet,
source_port: u16,
destination_port: u16,
payload: &[u8],
) -> Result<Packet> {
if !self.decode_applications {
return append_raw_if_needed(packet, payload);
}
let ctx = TcpBindingContext {
source_port,
destination_port,
payload,
};
if let Some(binding) = self
.tcp_bindings
.iter()
.rev()
.find(|binding| (binding.predicate)(ctx))
{
return (binding.decoder)(self, packet, payload);
}
append_raw_if_needed(packet, payload)
}
pub(crate) fn bind_ethertype_with_registry<D>(
&mut self,
ethertype: u16,
decoder: D,
) -> &mut Self
where
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.bind_ethertype_if_with_registry(move |ctx| ctx.ethertype == ethertype, decoder)
}
pub(crate) fn bind_ethertype_if_with_registry<P, D>(
&mut self,
predicate: P,
decoder: D,
) -> &mut Self
where
P: for<'a> Fn(EthertypeBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.builtin_ethertype_dispatch = false;
self.ethertype_bindings.push(EthertypeBinding {
predicate: Box::new(predicate),
decoder: Box::new(decoder),
});
self
}
pub(crate) fn bind_ipv4_protocol_with_registry<D>(
&mut self,
protocol: u8,
decoder: D,
) -> &mut Self
where
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.bind_ipv4_protocol_if_with_registry(move |ctx| ctx.protocol == protocol, decoder)
}
pub(crate) fn bind_ipv4_protocol_if_with_registry<P, D>(
&mut self,
predicate: P,
decoder: D,
) -> &mut Self
where
P: for<'a> Fn(Ipv4ProtocolBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.ipv4_bindings.push(Ipv4ProtocolBinding {
predicate: Box::new(predicate),
decoder: Box::new(decoder),
});
self.builtin_ipv4_protocol_dispatch = false;
self
}
pub(crate) fn bind_ipv6_next_header_with_registry<D>(
&mut self,
next_header: u8,
decoder: D,
) -> &mut Self
where
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.bind_ipv6_next_header_if_with_registry(
move |ctx| ctx.next_header == next_header,
decoder,
)
}
pub(crate) fn bind_ipv6_next_header_if_with_registry<P, D>(
&mut self,
predicate: P,
decoder: D,
) -> &mut Self
where
P: for<'a> Fn(Ipv6NextHeaderBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.ipv6_bindings.push(Ipv6NextHeaderBinding {
predicate: Box::new(predicate),
decoder: Box::new(decoder),
});
self.builtin_ipv6_next_header_dispatch = false;
self
}
pub(crate) fn bind_udp_port_with_registry<D>(&mut self, port: u16, decoder: D) -> &mut Self
where
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.bind_udp_with_registry(
move |ctx| ctx.source_port == port || ctx.destination_port == port,
decoder,
)
}
pub(crate) fn bind_udp_with_registry<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(UdpBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.udp_bindings.push(UdpBinding {
predicate: Box::new(predicate),
decoder: Box::new(decoder),
});
self.builtin_udp_application_dispatch = false;
self
}
pub(crate) fn bind_tcp_port_with_registry<D>(&mut self, port: u16, decoder: D) -> &mut Self
where
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.bind_tcp_with_registry(
move |ctx| ctx.source_port == port || ctx.destination_port == port,
decoder,
)
}
pub(crate) fn bind_tcp_with_registry<P, D>(&mut self, predicate: P, decoder: D) -> &mut Self
where
P: for<'a> Fn(TcpBindingContext<'a>) -> bool + Send + Sync + 'static,
D: for<'a> Fn(&'a ProtocolRegistry, Packet, &'a [u8]) -> Result<Packet>
+ Send
+ Sync
+ 'static,
{
self.tcp_bindings.push(TcpBinding {
predicate: Box::new(predicate),
decoder: Box::new(decoder),
});
self
}
}
impl Default for ProtocolRegistry {
fn default() -> Self {
Self::with_builtin_bindings()
}
}
fn append_raw_if_needed(packet: Packet, payload: &[u8]) -> Result<Packet> {
if payload.is_empty() {
Ok(packet)
} else {
Ok(packet.push_raw(Raw::from_bytes(payload)))
}
}
fn decode_esp_with_registry_sa(
registry: &ProtocolRegistry,
packet: Packet,
payload: &[u8],
) -> Result<Packet> {
let sa = read_u32_be(payload.get(0..4).unwrap_or(payload))
.ok()
.and_then(|spi| registry.security_association_for_spi(spi));
append_esp_packet_with_registry_sa(registry, packet, payload, sa)
}
fn decode_ah_with_registry_sa(
registry: &ProtocolRegistry,
packet: Packet,
payload: &[u8],
) -> Result<Packet> {
let sa = payload
.get(4..8)
.and_then(|spi_bytes| read_u32_be(spi_bytes).ok())
.and_then(|spi| registry.security_association_for_spi(spi));
append_ah_packet_with_registry_sa(registry, packet, payload, sa)
}
#[cfg(test)]
mod protocol_registry {
use super::ProtocolRegistry;
use crate::protocols::igmp::Igmp;
use crate::protocols::ip::shared::IPPROTO_IGMP;
use crate::{Ipv4, Ipv4ChecksumStatus, NetworkLayer, Packet, Raw, Udp, UdpChecksumStatus};
#[test]
fn custom_ipv4_protocol_binding_decodes_without_global_state() {
let mut registry = ProtocolRegistry::empty();
registry.bind_ipv4_protocol(253, |packet, payload| {
Ok(packet.push(Raw::from_bytes(payload)))
});
let bytes = (Ipv4::new().protocol(253) / Raw::from("agent-proto"))
.compile()
.unwrap();
let decoded = registry
.decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap();
assert_eq!(decoded.layer::<Raw>().unwrap().as_bytes(), b"agent-proto");
assert!(Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap()
.layer::<Raw>()
.is_some());
}
#[test]
fn later_bindings_override_builtins_for_that_registry_only() {
let mut registry = ProtocolRegistry::new();
registry.bind_udp_port(crate::DNS_PORT, |packet, payload| {
Ok(packet.push(Raw::from_bytes(payload)))
});
let bytes = (Ipv4::new()
/ crate::Udp::new().sport(53001).dport(crate::DNS_PORT)
/ crate::Dns::a_query("example.com."))
.compile()
.unwrap();
let custom = registry
.decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap();
let builtin = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes()).unwrap();
assert!(custom.layer::<crate::Dns>().is_none());
assert!(custom.layer::<Raw>().is_some());
assert!(builtin.layer::<crate::Dns>().is_some());
}
#[test]
fn registry_can_skip_application_decoding() {
let bytes = (Ipv4::new()
/ crate::Udp::new().sport(53001).dport(crate::DNS_PORT)
/ crate::Dns::a_query("example.com."))
.compile()
.unwrap();
let registry = ProtocolRegistry::new().application_decoding(false);
let decoded = registry
.decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap();
assert!(decoded.layer::<crate::Udp>().is_some());
assert!(decoded.layer::<crate::Dns>().is_none());
assert!(decoded.layer::<Raw>().is_some());
assert!(Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap()
.layer::<crate::Dns>()
.is_some());
}
#[test]
fn default_registry_decodes_ospf_over_ipv4() {
use core::net::Ipv4Addr;
use crate::protocols::ospf::decode::{
append_ospf_packet, append_ospf_packet_with_checksum_validation,
};
use crate::protocols::ospf::Ospfv2;
let bytes = (Ipv4::new()
.src(Ipv4Addr::new(192, 0, 2, 1))
.dst(Ipv4Addr::new(192, 0, 2, 2))
/ Ospfv2::hello())
.compile()
.expect("Ipv4 / Ospfv2 Hello compiles");
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.expect("the default registry decodes OSPF over IPv4");
assert!(
decoded.layer::<Ospfv2>().is_some(),
"the decoded packet exposes a typed Ospfv2 layer"
);
let recompiled = decoded.compile().expect("decoded OSPF re-compiles");
assert_eq!(recompiled.as_bytes(), bytes.as_bytes());
let ipv4_header_len = (bytes.as_bytes()[0] & 0x0f) as usize * 4;
let ospf_payload = &bytes.as_bytes()[ipv4_header_len..];
assert!(append_ospf_packet(Packet::new(), ospf_payload)
.expect("append_ospf_packet decodes the payload")
.layer::<Ospfv2>()
.is_some());
assert!(
append_ospf_packet_with_checksum_validation(Packet::new(), ospf_payload, false)
.expect("append_ospf_packet_with_checksum_validation decodes the payload")
.layer::<Ospfv2>()
.is_some()
);
}
#[test]
fn registry_can_skip_decode_checksum_validation() {
let bytes = (Ipv4::new() / Udp::new().sport(53001).dport(9000) / Raw::from("payload"))
.compile()
.unwrap();
let registry = ProtocolRegistry::new().checksum_validation(false);
let decoded = registry
.decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap();
assert_eq!(
decoded.layer::<Ipv4>().unwrap().checksum_status(),
Ipv4ChecksumStatus::NotChecked
);
assert_eq!(
decoded.layer::<Udp>().unwrap().checksum_status(),
UdpChecksumStatus::NotChecked
);
assert_eq!(
Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap()
.layer::<Udp>()
.unwrap()
.checksum_status(),
UdpChecksumStatus::Valid
);
}
#[test]
fn igmp_registry_binding_default_l3_decode_produces_ipv4_igmp() {
let bytes = (Ipv4::new().protocol(IPPROTO_IGMP) / Igmp::membership_query())
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes()).unwrap();
assert_eq!(decoded.len(), 2);
assert!(decoded
.get(0)
.is_some_and(|layer| layer.as_any().is::<Ipv4>()));
assert!(decoded
.get(1)
.is_some_and(|layer| layer.as_any().is::<Igmp>()));
assert!(decoded.layer::<Raw>().is_none());
}
#[test]
fn igmp_registry_binding_empty_registry_preserves_payload_as_raw() {
let bytes = (Ipv4::new().protocol(IPPROTO_IGMP) / Igmp::membership_query())
.compile()
.unwrap();
let registry = ProtocolRegistry::empty();
let decoded = registry
.decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap();
let raw = decoded
.layer::<Raw>()
.expect("empty registry raw IGMP payload");
assert_eq!(decoded.len(), 2);
assert!(decoded.layer::<Igmp>().is_none());
assert_eq!(raw.as_bytes(), &bytes.as_bytes()[20..]);
}
}
#[cfg(test)]
mod decode_dispatch {
use super::ProtocolRegistry;
use crate::{Ethernet, Ipv4, Ipv4Protocol, LinkType, NetworkLayer, Packet, Raw, Udp};
#[test]
fn default_registry_dispatches_from_ethernet_to_ipv4_udp() {
let bytes = (Ethernet::new()
/ Ipv4::new().ipv4_protocol(Ipv4Protocol::Udp)
/ Udp::new().sport(53002).dport(9999)
/ Raw::from("payload"))
.compile()
.unwrap();
let decoded = Packet::decode_from_link(LinkType::Ethernet, bytes.as_bytes()).unwrap();
assert!(decoded.layer::<Ethernet>().is_some());
assert!(decoded.layer::<Ipv4>().is_some());
assert!(decoded.layer::<Udp>().is_some());
assert_eq!(decoded.layer::<Raw>().unwrap().as_bytes(), b"payload");
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
#[test]
fn registry_ble_decode_from_link_builds_radio_and_advertising_layers() {
let frame = [
37, 0xc4, 0x00, 0x00, 0xd6, 0xbe, 0x89, 0x8e, 0x13, 0x0c, 0xd6, 0xbe, 0x89, 0x8e, 0x40,
0x0f, 0x01, 0x53, 0x00, 0x5e, 0x00, 0x02, 0x02, 0x01, 0x06, 0x05, 0x09, b't', b'e',
b's', b't',
];
let decoded = ProtocolRegistry::new()
.decode_from_link(LinkType::BluetoothLeLl, frame)
.unwrap();
assert_eq!(decoded.iter().count(), 2);
let radio = decoded.get(0).unwrap();
let adv = decoded.get(1).unwrap();
assert_eq!(radio.name(), "BleRadio");
assert!(radio.summary().contains("ch=37"));
assert!(radio.summary().contains("aa=0x8e89bed6"));
assert_eq!(adv.name(), "BleLlAdv");
assert_eq!(
adv.summary(),
"BleLlAdv(ADV_IND, AdvA=02:00:5E:00:53:01, len=15)"
);
assert!(decoded.layer::<Raw>().is_none());
}
#[test]
fn explicit_registry_decodes_ipv4_and_ipv6_custom_protocols() {
let mut registry = ProtocolRegistry::new();
registry.bind_ipv4_protocol(254, |packet, payload| {
Ok(packet.push(Raw::from_bytes(payload)))
});
registry.bind_ipv6_next_header(253, |packet, payload| {
Ok(packet.push(Raw::from_bytes(payload)))
});
let ipv4_bytes = (Ipv4::new().protocol(254) / Raw::from("v4-private"))
.compile()
.unwrap();
let ipv6_bytes = (crate::Ipv6::new().nh(253) / Raw::from("v6-private"))
.compile()
.unwrap();
let ipv4 = Packet::decode_from_l3_with_registry(®istry, NetworkLayer::Ipv4, ipv4_bytes)
.unwrap();
let ipv6 = Packet::decode_from_l3_with_registry(®istry, NetworkLayer::Ipv6, ipv6_bytes)
.unwrap();
assert_eq!(ipv4.layer::<Raw>().unwrap().as_bytes(), b"v4-private");
assert_eq!(ipv6.layer::<Raw>().unwrap().as_bytes(), b"v6-private");
}
}
#[cfg(test)]
mod esp_protocol_binding {
use crate::protocols::ipsec::esp::Esp;
use crate::protocols::ipv4::IPPROTO_ESP;
use crate::protocols::ipv6::IPPROTO_IPV6_ESP;
use crate::{Ipv4, Ipv6, NetworkLayer, Packet, Raw};
#[test]
fn default_registry_decodes_ipv4_protocol_50_as_opaque_esp() {
let bytes = (Ipv4::new().protocol(IPPROTO_ESP)
/ Esp::new().spi(0x0000_2000).sequence(7)
/ Raw::from_bytes(vec![0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes()).unwrap();
assert!(decoded.layer::<Ipv4>().is_some());
let esp = decoded
.get(1)
.unwrap()
.as_any()
.downcast_ref::<Esp>()
.expect("second layer is Esp");
assert_eq!(esp.spi_value(), Some(0x0000_2000));
assert_eq!(esp.sequence_value(), Some(7));
assert!(esp.opaque_body().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
#[test]
fn default_registry_decodes_ipv6_next_header_50_as_opaque_esp() {
let bytes = (Ipv6::new().nh(IPPROTO_IPV6_ESP)
/ Esp::new().spi(0x0000_3000).sequence(9)
/ Raw::from_bytes(vec![0xAA, 0xBB, 0xCC, 0xDD, 0x10, 0x20, 0x30, 0x40]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes.as_bytes()).unwrap();
assert!(decoded.layer::<Ipv6>().is_some());
let esp = decoded
.get(1)
.unwrap()
.as_any()
.downcast_ref::<Esp>()
.expect("second layer is Esp");
assert_eq!(esp.spi_value(), Some(0x0000_3000));
assert_eq!(esp.sequence_value(), Some(9));
assert!(esp.opaque_body().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
}
#[cfg(test)]
mod ah_protocol_binding {
use crate::protocols::ipsec::ah::Ah;
use crate::protocols::ipsec::sa::{IntegrityAlgorithm, SecurityAssociation};
use crate::protocols::ipv4::{IPPROTO_AH, IPPROTO_TCP};
use crate::protocols::ipv6::IPPROTO_IPV6_AH;
use crate::{Ipv4, Ipv6, NetworkLayer, Packet, Raw, Tcp};
fn ah_sa() -> SecurityAssociation {
SecurityAssociation::new(0x0000_2000)
.integrity(IntegrityAlgorithm::HmacSha2_256_128, vec![0x77u8; 32])
}
#[test]
fn default_registry_decodes_ipv4_protocol_51_as_ah_with_inner_tcp() {
let bytes = (Ipv4::new()
.protocol(IPPROTO_AH)
.src("192.0.2.1".parse().unwrap())
.dst("192.0.2.2".parse().unwrap())
.ttl(64)
/ Ah::secured(ah_sa()).spi(0x0000_2000).sequence(1)
/ Tcp::new().sport(1234).dport(443)
/ Raw::from_bytes(vec![0xDE, 0xAD, 0xBE, 0xEF]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes()).unwrap();
assert!(decoded.layer::<Ipv4>().is_some());
let ah = decoded
.get(1)
.unwrap()
.as_any()
.downcast_ref::<Ah>()
.expect("second layer is Ah");
assert_eq!(ah.spi_value(), Some(0x0000_2000));
assert_eq!(ah.sequence_value(), Some(1));
assert_eq!(ah.next_header_value(), Some(IPPROTO_TCP));
assert_eq!(ah.verification_status(), None);
let tcp = decoded
.layer::<Tcp>()
.expect("inner Tcp decoded in the clear");
assert_eq!(tcp.source_port_value(), 1234);
assert_eq!(tcp.destination_port_value(), 443);
assert_eq!(
decoded
.layer::<Raw>()
.expect("inner Raw decoded")
.as_bytes(),
&[0xDE, 0xAD, 0xBE, 0xEF]
);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
#[test]
fn default_registry_decodes_ipv6_next_header_51_as_ah_with_inner_tcp() {
let bytes = (Ipv6::new()
.nh(IPPROTO_IPV6_AH)
.src("2001:db8::1".parse().unwrap())
.dst("2001:db8::2".parse().unwrap())
.hop_limit(64)
/ Ah::secured(ah_sa()).spi(0x0000_3000).sequence(9)
/ Tcp::new().sport(2345).dport(80)
/ Raw::from_bytes(vec![0xAA, 0xBB, 0xCC, 0xDD]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes.as_bytes()).unwrap();
assert!(decoded.layer::<Ipv6>().is_some());
let ah = decoded
.get(1)
.unwrap()
.as_any()
.downcast_ref::<Ah>()
.expect("second layer is Ah");
assert_eq!(ah.spi_value(), Some(0x0000_3000));
assert_eq!(ah.sequence_value(), Some(9));
assert_eq!(ah.next_header_value(), Some(IPPROTO_TCP));
assert_eq!(ah.verification_status(), None);
let tcp = decoded
.layer::<Tcp>()
.expect("inner Tcp decoded in the clear");
assert_eq!(tcp.source_port_value(), 2345);
assert_eq!(tcp.destination_port_value(), 80);
assert_eq!(
decoded
.layer::<Raw>()
.expect("inner Raw decoded")
.as_bytes(),
&[0xAA, 0xBB, 0xCC, 0xDD]
);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
#[test]
fn ah_in_ipv6_decodes_after_a_preceding_extension_header() {
use crate::protocols::ipv6::Ipv6DestinationOptionsHeader;
use crate::Ipv6Option;
let direct = (Ipv6::new()
.nh(IPPROTO_IPV6_AH)
.src("2001:db8::1".parse().unwrap())
.dst("2001:db8::2".parse().unwrap())
.hop_limit(64)
/ Ah::secured(ah_sa()).spi(0x0000_4000).sequence(3)
/ Tcp::new().sport(3456).dport(8080)
/ Raw::from_bytes(vec![0x11, 0x22, 0x33, 0x44]))
.compile()
.unwrap();
let ah_datagram = direct.as_bytes()[40..].to_vec();
let bytes = (Ipv6::new()
.src("2001:db8::1".parse().unwrap())
.dst("2001:db8::2".parse().unwrap())
.hop_limit(64)
/ Ipv6DestinationOptionsHeader::new()
.nh(IPPROTO_IPV6_AH)
.option(Ipv6Option::pad1())
/ Raw::from_bytes(ah_datagram))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes.as_bytes()).unwrap();
let ah = decoded
.layer::<Ah>()
.expect("Ah decoded after the extension header");
assert_eq!(ah.spi_value(), Some(0x0000_4000));
assert_eq!(ah.next_header_value(), Some(IPPROTO_TCP));
let tcp = decoded
.layer::<Tcp>()
.expect("inner Tcp decoded in the clear");
assert_eq!(tcp.source_port_value(), 3456);
assert_eq!(tcp.destination_port_value(), 8080);
}
}
#[cfg(test)]
mod dns_udp_binding {
use super::ProtocolRegistry;
use crate::{Dns, DnsQuestion, Ipv4, NetworkLayer, Packet, Raw, Udp, DNS_PORT, DNS_TYPE_AAAA};
#[test]
fn default_registry_decodes_dns_on_udp_53() {
let bytes = (Ipv4::new()
/ Udp::new().sport(53001).dport(DNS_PORT)
/ Dns::new().question(DnsQuestion::new("example.org.", DNS_TYPE_AAAA)))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes()).unwrap();
let dns = decoded.layer::<Dns>().unwrap();
assert_eq!(dns.questions()[0].name(), "example.org.");
assert_eq!(dns.questions()[0].question_type(), DNS_TYPE_AAAA);
}
#[test]
fn custom_udp_port_binding_decodes_application_payload() {
let mut registry = ProtocolRegistry::new();
registry.bind_udp_port(5353, |packet, payload| {
Ok(packet.push(Raw::from_bytes(payload)))
});
let bytes =
(Ipv4::new() / Udp::new().sport(5353).dport(50000) / Raw::from("custom-dns-like"))
.compile()
.unwrap();
let decoded =
Packet::decode_from_l3_with_registry(®istry, NetworkLayer::Ipv4, bytes.as_bytes())
.unwrap();
assert_eq!(
decoded.layer::<Raw>().unwrap().as_bytes(),
b"custom-dns-like"
);
}
}
#[cfg(test)]
mod bgp_tcp_binding {
use crate::protocols::bgp::BGP_PORT;
use crate::{Bgp, Ipv4, NetworkLayer, Packet, Raw, Tcp};
#[test]
fn default_registry_decodes_bgp_on_tcp_179_and_preserves_other_tcp_payloads() {
let bgp_bytes = (Ipv4::new() / Tcp::new().sport(49_152).dport(BGP_PORT) / Bgp::keepalive())
.compile()
.unwrap();
let bgp_decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bgp_bytes.as_bytes()).unwrap();
assert!(bgp_decoded.layer::<Bgp>().is_some());
assert!(bgp_decoded.layer::<Raw>().is_none());
let keepalive = Packet::from_layer(Bgp::keepalive())
.compile()
.unwrap()
.into_bytes();
let raw_bytes =
(Ipv4::new() / Tcp::new().sport(49_152).dport(80) / Raw::from_bytes(keepalive.clone()))
.compile()
.unwrap();
let raw_decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, raw_bytes.as_bytes()).unwrap();
assert!(raw_decoded.layer::<Bgp>().is_none());
assert_eq!(raw_decoded.layer::<Raw>().unwrap().as_bytes(), keepalive);
}
}