use crate::polyfill::maybe_uninit_slice_as_mut_ptr;
use crate::proto::unsafe_protocol;
use crate::util::{ptr_write_unaligned_and_add, usize_from_u32};
use crate::{CStr8, Result, Status, StatusExt};
use bitflags::bitflags;
use core::fmt::{self, Debug, Display, Formatter};
use core::iter::from_fn;
use core::mem::MaybeUninit;
use core::net::{IpAddr, Ipv4Addr};
use core::ptr::{self, null, null_mut};
use core::slice;
use ptr_meta::Pointee;
use uefi::proto::network::EfiMacAddr;
use uefi_raw::protocol::network::pxe::{
PxeBaseCodeDiscoverInfo, PxeBaseCodeIpFilter, PxeBaseCodeMode, PxeBaseCodeMtftpInfo,
PxeBaseCodePacket, PxeBaseCodeProtocol, PxeBaseCodeTftpOpcode,
};
use uefi_raw::{Boolean, Char8, IpAddress as EfiIpAddr};
pub use uefi_raw::protocol::network::pxe::{
PxeBaseCodeBootType as BootstrapType, PxeBaseCodeIpFilterFlags as IpFilters,
PxeBaseCodeUdpOpFlags as UdpOpFlags,
};
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(PxeBaseCodeProtocol::GUID)]
pub struct BaseCode(PxeBaseCodeProtocol);
impl BaseCode {
pub fn start(&mut self, use_ipv6: bool) -> Result {
unsafe { (self.0.start)(&mut self.0, use_ipv6.into()) }.to_result()
}
pub fn stop(&mut self) -> Result {
unsafe { (self.0.stop)(&mut self.0) }.to_result()
}
pub fn dhcp(&mut self, sort_offers: bool) -> Result {
unsafe { (self.0.dhcp)(&mut self.0, sort_offers.into()) }.to_result()
}
pub fn discover(
&mut self,
ty: BootstrapType,
layer: &mut u16,
use_bis: bool,
info: Option<&DiscoverInfo>,
) -> Result {
let info: *const PxeBaseCodeDiscoverInfo = info
.map(|info| ptr::from_ref(info).cast())
.unwrap_or(null());
unsafe { (self.0.discover)(&mut self.0, ty, layer, use_bis.into(), info) }.to_result()
}
pub fn tftp_get_file_size(&mut self, server_ip: &IpAddr, filename: &CStr8) -> Result<u64> {
let mut buffer_size = 0;
let server_ip = EfiIpAddr::from(*server_ip);
let status = unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::TFTP_GET_FILE_SIZE,
null_mut(),
Boolean::FALSE,
&mut buffer_size,
null(),
&server_ip,
cstr8_to_ptr(filename),
null(),
Boolean::FALSE,
)
};
status.to_result_with_val(|| buffer_size)
}
pub fn tftp_read_file(
&mut self,
server_ip: &IpAddr,
filename: &CStr8,
buffer: Option<&mut [u8]>,
) -> Result<u64> {
let (buffer_ptr, mut buffer_size, dont_use_buffer) = if let Some(buffer) = buffer {
let buffer_size = u64::try_from(buffer.len()).unwrap();
(buffer.as_mut_ptr().cast(), buffer_size, Boolean::FALSE)
} else {
(null_mut(), 0, Boolean::TRUE)
};
let server_ip = EfiIpAddr::from(*server_ip);
let status = unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::TFTP_READ_FILE,
buffer_ptr,
Boolean::FALSE,
&mut buffer_size,
null(),
&server_ip,
cstr8_to_ptr(filename),
null(),
dont_use_buffer,
)
};
status.to_result_with_val(|| buffer_size)
}
pub fn tftp_write_file(
&mut self,
server_ip: &IpAddr,
filename: &CStr8,
overwrite: bool,
buffer: &[u8],
) -> Result {
let buffer_ptr = buffer.as_ptr().cast_mut().cast();
let mut buffer_size = u64::try_from(buffer.len()).expect("buffer length should fit in u64");
let server_ip = EfiIpAddr::from(*server_ip);
unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::TFTP_WRITE_FILE,
buffer_ptr,
overwrite.into(),
&mut buffer_size,
null(),
&server_ip,
cstr8_to_ptr(filename),
null(),
Boolean::FALSE,
)
}
.to_result()
}
pub fn tftp_read_dir<'a>(
&mut self,
server_ip: &IpAddr,
directory_name: &CStr8,
buffer: &'a mut [u8],
) -> Result<impl Iterator<Item = core::result::Result<TftpFileInfo<'a>, ReadDirParseError>> + 'a>
{
let buffer_ptr = buffer.as_mut_ptr().cast();
let mut buffer_size = u64::try_from(buffer.len()).expect("buffer length should fit in u64");
let server_ip = EfiIpAddr::from(*server_ip);
let status = unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::TFTP_READ_DIRECTORY,
buffer_ptr,
Boolean::FALSE,
&mut buffer_size,
null(),
&server_ip,
cstr8_to_ptr(directory_name),
null(),
Boolean::FALSE,
)
};
status.to_result()?;
let buffer_size = usize::try_from(buffer_size).expect("buffer length should fit in usize");
let buffer = &buffer[..buffer_size];
let mut iterator = buffer.split_inclusive(|b| *b == 0);
let mut parse_next = move || {
let filename = iterator.next().ok_or(ReadDirParseError)?;
if filename == [0] {
return Ok(None);
}
let filename = CStr8::from_bytes_with_nul(filename).unwrap();
let information_string = iterator.next().ok_or(ReadDirParseError)?;
let (_null_terminator, information_string) = information_string.split_last().unwrap();
let information_string =
core::str::from_utf8(information_string).map_err(|_| ReadDirParseError)?;
let (size, rest) = information_string
.split_once(' ')
.ok_or(ReadDirParseError)?;
let (year, rest) = rest.split_once('-').ok_or(ReadDirParseError)?;
let (month, rest) = rest.split_once('-').ok_or(ReadDirParseError)?;
let (day, rest) = rest.split_once(' ').ok_or(ReadDirParseError)?;
let (hour, rest) = rest.split_once(':').ok_or(ReadDirParseError)?;
let (minute, second) = rest.split_once(':').ok_or(ReadDirParseError)?;
let size = size.parse().map_err(|_| ReadDirParseError)?;
let year = year.parse().map_err(|_| ReadDirParseError)?;
let month = month.parse().map_err(|_| ReadDirParseError)?;
let day = day.parse().map_err(|_| ReadDirParseError)?;
let hour = hour.parse().map_err(|_| ReadDirParseError)?;
let minute = minute.parse().map_err(|_| ReadDirParseError)?;
let second = second.parse().map_err(|_| ReadDirParseError)?;
Ok(Some(TftpFileInfo {
filename,
size,
year,
month,
day,
hour,
minute,
second,
}))
};
Ok(from_fn(move || parse_next().transpose()).fuse())
}
pub fn mtftp_get_file_size(
&mut self,
server_ip: &IpAddr,
filename: &CStr8,
info: &MtftpInfo,
) -> Result<u64> {
let mut buffer_size = 0;
let server_ip = EfiIpAddr::from(*server_ip);
let status = unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::MTFTP_GET_FILE_SIZE,
null_mut(),
Boolean::FALSE,
&mut buffer_size,
null(),
&server_ip,
cstr8_to_ptr(filename),
info.as_ptr(),
Boolean::FALSE,
)
};
status.to_result_with_val(|| buffer_size)
}
pub fn mtftp_read_file(
&mut self,
server_ip: &IpAddr,
filename: &CStr8,
buffer: Option<&mut [u8]>,
info: &MtftpInfo,
) -> Result<u64> {
let (buffer_ptr, mut buffer_size, dont_use_buffer) = if let Some(buffer) = buffer {
let buffer_size = u64::try_from(buffer.len()).unwrap();
(buffer.as_mut_ptr().cast(), buffer_size, Boolean::FALSE)
} else {
(null_mut(), 0, Boolean::TRUE)
};
let server_ip = EfiIpAddr::from(*server_ip);
let status = unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::MTFTP_READ_FILE,
buffer_ptr,
Boolean::FALSE,
&mut buffer_size,
null(),
&server_ip,
cstr8_to_ptr(filename),
info.as_ptr(),
dont_use_buffer,
)
};
status.to_result_with_val(|| buffer_size)
}
pub fn mtftp_read_dir<'a>(
&mut self,
server_ip: &IpAddr,
buffer: &'a mut [u8],
info: &MtftpInfo,
) -> Result<impl Iterator<Item = core::result::Result<MtftpFileInfo<'a>, ReadDirParseError>> + 'a>
{
let buffer_ptr = buffer.as_mut_ptr().cast();
let mut buffer_size = u64::try_from(buffer.len()).expect("buffer length should fit in u64");
let server_ip = EfiIpAddr::from(*server_ip);
let status = unsafe {
(self.0.mtftp)(
&mut self.0,
PxeBaseCodeTftpOpcode::MTFTP_READ_DIRECTORY,
buffer_ptr,
Boolean::FALSE,
&mut buffer_size,
null(),
&server_ip,
null_mut(),
info.as_ptr(),
Boolean::FALSE,
)
};
status.to_result()?;
let buffer_size = usize::try_from(buffer_size).expect("buffer length should fit in usize");
let buffer = &buffer[..buffer_size];
let mut iterator = buffer.split_inclusive(|b| *b == 0);
let mut parse_next = move || {
let filename = iterator.next().ok_or(ReadDirParseError)?;
if filename == [0] {
return Ok(None);
}
let filename = CStr8::from_bytes_with_nul(filename).unwrap();
let multicast_ip = iterator.next().ok_or(ReadDirParseError)?;
let (_null_terminator, multicast_ip) = multicast_ip.split_last().unwrap();
let multicast_ip = core::str::from_utf8(multicast_ip).map_err(|_| ReadDirParseError)?;
let mut octets = multicast_ip.split('.');
let mut buffer = [0; 4];
for b in buffer.iter_mut() {
let octet = octets.next().ok_or(ReadDirParseError)?;
let octet = octet.parse().map_err(|_| ReadDirParseError)?;
*b = octet;
}
if octets.next().is_some() {
return Err(ReadDirParseError);
}
let ip_address = IpAddr::V4(Ipv4Addr::from(buffer));
let information_string = iterator.next().ok_or(ReadDirParseError)?;
let (_null_terminator, information_string) = information_string.split_last().unwrap();
let information_string =
core::str::from_utf8(information_string).map_err(|_| ReadDirParseError)?;
let (size, rest) = information_string
.split_once(' ')
.ok_or(ReadDirParseError)?;
let (year, rest) = rest.split_once('-').ok_or(ReadDirParseError)?;
let (month, rest) = rest.split_once('-').ok_or(ReadDirParseError)?;
let (day, rest) = rest.split_once(' ').ok_or(ReadDirParseError)?;
let (hour, rest) = rest.split_once(':').ok_or(ReadDirParseError)?;
let (minute, second) = rest.split_once(':').ok_or(ReadDirParseError)?;
let size = size.parse().map_err(|_| ReadDirParseError)?;
let year = year.parse().map_err(|_| ReadDirParseError)?;
let month = month.parse().map_err(|_| ReadDirParseError)?;
let day = day.parse().map_err(|_| ReadDirParseError)?;
let hour = hour.parse().map_err(|_| ReadDirParseError)?;
let minute = minute.parse().map_err(|_| ReadDirParseError)?;
let second = second.parse().map_err(|_| ReadDirParseError)?;
Ok(Some(MtftpFileInfo {
filename,
ip_address,
size,
year,
month,
day,
hour,
minute,
second,
}))
};
Ok(from_fn(move || parse_next().transpose()).fuse())
}
#[allow(clippy::too_many_arguments)]
pub fn udp_write(
&mut self,
op_flags: UdpOpFlags,
dest_ip: &IpAddr,
dest_port: u16,
gateway_ip: Option<&IpAddr>,
src_ip: Option<&IpAddr>,
src_port: Option<&mut u16>,
header: Option<&[u8]>,
buffer: &[u8],
) -> Result {
let header_size_tmp;
let (header_size, header_ptr) = if let Some(header) = header {
header_size_tmp = header.len();
(Some(&header_size_tmp), header.as_ptr().cast())
} else {
(None, null())
};
let dest_ip = EfiIpAddr::from(*dest_ip);
let gateway_ip = gateway_ip.map(|ip| EfiIpAddr::from(*ip));
let src_ip = src_ip.map(|ip| EfiIpAddr::from(*ip));
unsafe {
(self.0.udp_write)(
&mut self.0,
op_flags,
&dest_ip,
&dest_port,
opt_ip_addr_to_ptr(gateway_ip.as_ref()),
opt_ip_addr_to_ptr(src_ip.as_ref()),
opt_mut_to_ptr(src_port),
opt_ref_to_ptr(header_size),
header_ptr,
&buffer.len(),
buffer.as_ptr().cast(),
)
}
.to_result()
}
#[allow(clippy::too_many_arguments)]
pub fn udp_read(
&mut self,
op_flags: UdpOpFlags,
mut dest_ip: Option<&mut IpAddr>,
dest_port: Option<&mut u16>,
mut src_ip: Option<&mut IpAddr>,
src_port: Option<&mut u16>,
header: Option<&mut [u8]>,
buffer: &mut [u8],
) -> Result<usize> {
let header_size_tmp;
let (header_size, header_ptr) = if let Some(header) = header {
header_size_tmp = header.len();
(ptr::from_ref(&header_size_tmp), header.as_mut_ptr().cast())
} else {
(null(), null_mut())
};
let mut buffer_size = buffer.len();
let mut dest_ip_efi = dest_ip.as_ref().map(|ip| EfiIpAddr::from(**ip));
let mut src_ip_efi = src_ip.as_ref().map(|ip| EfiIpAddr::from(**ip));
let status = unsafe {
(self.0.udp_read)(
&mut self.0,
op_flags,
opt_ip_addr_to_ptr_mut(dest_ip_efi.as_mut()),
opt_mut_to_ptr(dest_port),
opt_ip_addr_to_ptr_mut(src_ip_efi.as_mut()),
opt_mut_to_ptr(src_port),
header_size,
header_ptr,
&mut buffer_size,
buffer.as_mut_ptr().cast(),
)
};
let fn_replace_ip = |core_ip: &mut Option<&mut IpAddr>, efi_ip: Option<EfiIpAddr>| {
if let (Some(core_ip_location), Some(efi_ip)) = (core_ip, efi_ip) {
let core_ip = unsafe { efi_ip.into_core_addr(self.mode().using_ipv6()) };
**core_ip_location = core_ip;
}
};
fn_replace_ip(&mut dest_ip, dest_ip_efi);
fn_replace_ip(&mut src_ip, src_ip_efi);
status.to_result_with_val(|| buffer_size)
}
pub fn set_ip_filter(&mut self, new_filter: &IpFilter) -> Result {
let new_filter: *const PxeBaseCodeIpFilter = ptr::from_ref(new_filter).cast();
unsafe { (self.0.set_ip_filter)(&mut self.0, new_filter) }.to_result()
}
pub fn arp(&mut self, ip_addr: &IpAddr, mac_addr: Option<&mut EfiMacAddr>) -> Result {
let ip_addr = EfiIpAddr::from(*ip_addr);
unsafe { (self.0.arp)(&mut self.0, &ip_addr, opt_mut_to_ptr(mac_addr)) }.to_result()
}
pub fn set_parameters(
&mut self,
new_auto_arp: Option<bool>,
new_send_guid: Option<bool>,
new_ttl: Option<u8>,
new_tos: Option<u8>,
new_make_callback: Option<bool>,
) -> Result {
unsafe {
(self.0.set_parameters)(
&mut self.0,
opt_bool_to_ptr(&new_auto_arp),
opt_bool_to_ptr(&new_send_guid),
opt_ref_to_ptr(new_ttl.as_ref()),
opt_ref_to_ptr(new_tos.as_ref()),
opt_bool_to_ptr(&new_make_callback),
)
}
.to_result()
}
pub fn set_station_ip(
&mut self,
new_station_ip: Option<&IpAddr>,
new_subnet_mask: Option<&IpAddr>,
) -> Result {
let new_station_ip = new_station_ip.map(|ip| EfiIpAddr::from(*ip));
let new_subnet_mask = new_subnet_mask.map(|mask| EfiIpAddr::from(*mask));
unsafe {
(self.0.set_station_ip)(
&mut self.0,
opt_ip_addr_to_ptr(new_station_ip.as_ref()),
opt_ip_addr_to_ptr(new_subnet_mask.as_ref()),
)
}
.to_result()
}
#[allow(clippy::too_many_arguments)]
pub fn set_packets(
&mut self,
new_dhcp_discover_valid: Option<bool>,
new_dhcp_ack_received: Option<bool>,
new_proxy_offer_received: Option<bool>,
new_pxe_discover_valid: Option<bool>,
new_pxe_reply_received: Option<bool>,
new_pxe_bis_reply_received: Option<bool>,
new_dhcp_discover: Option<&Packet>,
new_dhcp_ack: Option<&Packet>,
new_proxy_offer: Option<&Packet>,
new_pxe_discover: Option<&Packet>,
new_pxe_reply: Option<&Packet>,
new_pxe_bis_reply: Option<&Packet>,
) -> Result {
unsafe {
(self.0.set_packets)(
&mut self.0,
opt_bool_to_ptr(&new_dhcp_discover_valid),
opt_bool_to_ptr(&new_dhcp_ack_received),
opt_bool_to_ptr(&new_proxy_offer_received),
opt_bool_to_ptr(&new_pxe_discover_valid),
opt_bool_to_ptr(&new_pxe_reply_received),
opt_bool_to_ptr(&new_pxe_bis_reply_received),
opt_packet_to_ptr(new_dhcp_discover),
opt_packet_to_ptr(new_dhcp_ack),
opt_packet_to_ptr(new_proxy_offer),
opt_packet_to_ptr(new_pxe_discover),
opt_packet_to_ptr(new_pxe_reply),
opt_packet_to_ptr(new_pxe_bis_reply),
)
}
.to_result()
}
#[must_use]
pub const fn mode(&self) -> &Mode {
unsafe { &*(self.0.mode.cast()) }
}
}
const fn cstr8_to_ptr(s: &CStr8) -> *const Char8 {
s.as_ptr().cast()
}
fn opt_bool_to_ptr(arg: &Option<bool>) -> *const Boolean {
arg.as_ref()
.map(|arg| ptr::from_ref(arg).cast::<Boolean>())
.unwrap_or_else(null)
}
fn opt_ip_addr_to_ptr(arg: Option<&EfiIpAddr>) -> *const EfiIpAddr {
arg.map(|arg| &raw const *arg).unwrap_or_else(null)
}
fn opt_ip_addr_to_ptr_mut(arg: Option<&mut EfiIpAddr>) -> *mut EfiIpAddr {
arg.map(|arg| &raw mut *arg).unwrap_or_else(null_mut)
}
fn opt_packet_to_ptr(arg: Option<&Packet>) -> *const PxeBaseCodePacket {
arg.map(|p| ptr::from_ref(p).cast()).unwrap_or_else(null)
}
fn opt_ref_to_ptr<T>(opt: Option<&T>) -> *const T {
opt.map(ptr::from_ref).unwrap_or(null())
}
fn opt_mut_to_ptr<T>(opt: Option<&mut T>) -> *mut T {
opt.map(ptr::from_mut).unwrap_or(null_mut())
}
opaque_type! {
pub struct FfiDiscoverInfo;
}
#[repr(C)]
#[derive(Debug, Pointee)]
pub struct DiscoverInfo {
use_m_cast: bool,
use_b_cast: bool,
use_u_cast: bool,
must_use_list: bool,
server_m_cast_ip: EfiIpAddr,
ip_cnt: u16,
srv_list: [Server],
}
impl DiscoverInfo {
pub fn new_in_buffer<'buf>(
buffer: &'buf mut [MaybeUninit<u8>],
use_m_cast: bool,
use_b_cast: bool,
use_u_cast: bool,
must_use_list: bool,
server_m_cast_ip: EfiIpAddr,
srv_list: &[Server],
) -> Result<&'buf mut Self> {
let server_count = srv_list.len();
assert!(server_count <= u16::MAX as usize, "too many servers");
let required_size = size_of::<bool>() * 4
+ size_of::<EfiIpAddr>()
+ size_of::<u16>()
+ size_of_val(srv_list);
if buffer.len() < required_size {
return Err(Status::BUFFER_TOO_SMALL.into());
}
let mut ptr: *mut u8 = maybe_uninit_slice_as_mut_ptr(buffer);
unsafe {
ptr_write_unaligned_and_add(&mut ptr, use_m_cast);
ptr_write_unaligned_and_add(&mut ptr, use_b_cast);
ptr_write_unaligned_and_add(&mut ptr, use_u_cast);
ptr_write_unaligned_and_add(&mut ptr, must_use_list);
ptr_write_unaligned_and_add(&mut ptr, server_m_cast_ip);
ptr_write_unaligned_and_add(&mut ptr, server_count as u16);
ptr = ptr.add(2); core::ptr::copy(srv_list.as_ptr(), ptr.cast(), server_count);
let ptr: *mut Self =
ptr_meta::from_raw_parts_mut(buffer.as_mut_ptr().cast(), server_count);
Ok(&mut *ptr)
}
}
}
impl DiscoverInfo {
#[must_use]
pub const fn use_m_cast(&self) -> bool {
self.use_m_cast
}
#[must_use]
pub const fn use_b_cast(&self) -> bool {
self.use_b_cast
}
#[must_use]
pub const fn use_u_cast(&self) -> bool {
self.use_u_cast
}
#[must_use]
pub const fn must_use_list(&self) -> bool {
self.must_use_list
}
#[must_use]
pub const fn server_m_cast_ip(&self) -> &EfiIpAddr {
&self.server_m_cast_ip
}
#[must_use]
pub const fn ip_cnt(&self) -> u16 {
self.ip_cnt
}
#[must_use]
pub const fn srv_list(&self) -> &[Server] {
&self.srv_list
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Server {
pub ty: u16,
accept_any_response: bool,
_reserved: u8,
ip_addr: EfiIpAddr,
}
impl Server {
#[must_use]
pub fn new(ty: u16, ip_addr: Option<EfiIpAddr>) -> Self {
Self {
ty,
accept_any_response: ip_addr.is_none(),
_reserved: 0,
ip_addr: ip_addr.unwrap_or_default(),
}
}
#[must_use]
pub const fn ip_addr(&self) -> Option<&EfiIpAddr> {
if self.accept_any_response {
None
} else {
Some(&self.ip_addr)
}
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct MtftpInfo {
pub m_cast_ip: EfiIpAddr,
pub c_port: u16,
pub s_port: u16,
pub listen_timeout: u16,
pub transmit_timeout: u16,
}
impl MtftpInfo {
const fn as_ptr(&self) -> *const PxeBaseCodeMtftpInfo {
ptr::from_ref(self).cast()
}
}
#[repr(C)]
#[derive(Debug)]
pub struct IpFilter {
pub filters: IpFilters,
ip_cnt: u8,
_reserved: u16,
ip_list: [IpAddr; 8],
}
impl IpFilter {
#[must_use]
pub fn new(filters: IpFilters, ip_list: &[IpAddr]) -> Self {
assert!(ip_list.len() <= 8);
let ip_cnt = ip_list.len() as u8;
let mut buffer = [IpAddr::from([0; 16]); 8];
buffer[..ip_list.len()].copy_from_slice(ip_list);
Self {
filters,
ip_cnt,
_reserved: 0,
ip_list: buffer,
}
}
#[must_use]
pub fn ip_list(&self) -> &[IpAddr] {
&self.ip_list[..usize::from(self.ip_cnt)]
}
}
#[repr(C)]
pub union Packet {
raw: [u8; 1472],
dhcpv4: DhcpV4Packet,
dhcpv6: DhcpV6Packet,
}
impl Packet {
const fn from_raw(packet: &PxeBaseCodePacket) -> &Self {
unsafe { &*ptr::from_ref(packet).cast() }
}
}
impl Debug for Packet {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "<binary data>")
}
}
impl AsRef<[u8; 1472]> for Packet {
fn as_ref(&self) -> &[u8; 1472] {
unsafe { &self.raw }
}
}
impl AsRef<DhcpV4Packet> for Packet {
fn as_ref(&self) -> &DhcpV4Packet {
unsafe { &self.dhcpv4 }
}
}
impl AsRef<DhcpV6Packet> for Packet {
fn as_ref(&self) -> &DhcpV6Packet {
unsafe { &self.dhcpv6 }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DhcpV4Packet {
pub bootp_opcode: u8,
pub bootp_hw_type: u8,
pub bootp_hw_addr_len: u8,
pub bootp_gate_hops: u8,
bootp_ident: u32,
bootp_seconds: u16,
bootp_flags: u16,
pub bootp_ci_addr: [u8; 4],
pub bootp_yi_addr: [u8; 4],
pub bootp_si_addr: [u8; 4],
pub bootp_gi_addr: [u8; 4],
pub bootp_hw_addr: [u8; 16],
pub bootp_srv_name: [u8; 64],
pub bootp_boot_file: [u8; 128],
dhcp_magik: u32,
pub dhcp_options: [u8; 56],
}
impl DhcpV4Packet {
pub const DHCP_MAGIK: u32 = 0x63825363;
#[must_use]
pub const fn bootp_ident(&self) -> u32 {
u32::from_be(self.bootp_ident)
}
#[must_use]
pub const fn bootp_seconds(&self) -> u16 {
u16::from_be(self.bootp_seconds)
}
#[must_use]
pub const fn bootp_flags(&self) -> DhcpV4Flags {
DhcpV4Flags::from_bits_truncate(u16::from_be(self.bootp_flags))
}
#[must_use]
pub const fn dhcp_magik(&self) -> u32 {
u32::from_be(self.dhcp_magik)
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct DhcpV4Flags: u16 {
const BROADCAST = 1;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DhcpV6Packet {
pub message_type: u8,
transaction_id: [u8; 3],
pub dhcp_options: [u8; 1024],
}
impl DhcpV6Packet {
#[must_use]
pub fn transaction_id(&self) -> u32 {
(u32::from(self.transaction_id[0]) << 16)
| (u32::from(self.transaction_id[1]) << 8)
| u32::from(self.transaction_id[2])
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct Mode(PxeBaseCodeMode);
impl Mode {
#[must_use]
pub fn started(&self) -> bool {
self.0.started.into()
}
#[must_use]
pub fn ipv6_available(&self) -> bool {
self.0.ipv6_available.into()
}
#[must_use]
pub fn ipv6_supported(&self) -> bool {
self.0.ipv6_supported.into()
}
#[must_use]
pub fn using_ipv6(&self) -> bool {
self.0.using_ipv6.into()
}
#[must_use]
pub fn bis_supported(&self) -> bool {
self.0.bis_supported.into()
}
#[must_use]
pub fn bis_detected(&self) -> bool {
self.0.bis_detected.into()
}
#[must_use]
pub fn auto_arp(&self) -> bool {
self.0.auto_arp.into()
}
#[must_use]
pub fn send_guid(&self) -> bool {
self.0.send_guid.into()
}
#[must_use]
pub fn dhcp_discover_valid(&self) -> bool {
self.0.dhcp_discover_valid.into()
}
#[must_use]
pub fn dhcp_ack_received(&self) -> bool {
self.0.dhcp_ack_received.into()
}
#[must_use]
pub fn proxy_offer_received(&self) -> bool {
self.0.proxy_offer_received.into()
}
#[must_use]
pub fn pxe_discover_valid(&self) -> bool {
self.0.pxe_discover_valid.into()
}
#[must_use]
pub fn pxe_reply_received(&self) -> bool {
self.0.pxe_reply_received.into()
}
#[must_use]
pub fn pxe_bis_reply_received(&self) -> bool {
self.0.pxe_bis_reply_received.into()
}
#[must_use]
pub fn icmp_error_received(&self) -> bool {
self.0.icmp_error_received.into()
}
#[must_use]
pub fn tftp_error_received(&self) -> bool {
self.0.tftp_error_received.into()
}
#[must_use]
pub fn make_callbacks(&self) -> bool {
self.0.make_callbacks.into()
}
#[must_use]
pub const fn ttl(&self) -> u8 {
self.0.ttl
}
#[must_use]
pub const fn tos(&self) -> u8 {
self.0.tos
}
#[must_use]
pub fn station_ip(&self) -> IpAddr {
let efi_ip = self.0.station_ip;
unsafe { efi_ip.into_core_addr(self.using_ipv6()) }
}
#[must_use]
pub fn subnet_mask(&self) -> IpAddr {
let efi_ip = self.0.subnet_mask;
unsafe { efi_ip.into_core_addr(self.using_ipv6()) }
}
#[must_use]
pub const fn dhcp_discover(&self) -> &Packet {
Packet::from_raw(&self.0.dhcp_discover)
}
#[must_use]
pub const fn dhcp_ack(&self) -> &Packet {
Packet::from_raw(&self.0.dhcp_ack)
}
#[must_use]
pub const fn proxy_offer(&self) -> &Packet {
Packet::from_raw(&self.0.proxy_offer)
}
#[must_use]
pub const fn pxe_discover(&self) -> &Packet {
Packet::from_raw(&self.0.pxe_discover)
}
#[must_use]
pub const fn pxe_reply(&self) -> &Packet {
Packet::from_raw(&self.0.pxe_reply)
}
#[must_use]
pub const fn pxe_bis_reply(&self) -> &Packet {
Packet::from_raw(&self.0.pxe_bis_reply)
}
#[must_use]
pub const fn ip_filter(&self) -> &IpFilter {
unsafe { &*ptr::from_ref(&self.0.ip_filter).cast() }
}
#[must_use]
pub const fn arp_cache(&self) -> &[ArpEntry] {
let len = usize_from_u32(self.0.arp_cache_entries);
unsafe { slice::from_raw_parts(ptr::from_ref(&self.0.arp_cache).cast::<ArpEntry>(), len) }
}
#[must_use]
pub const fn route_table_entries(&self) -> u32 {
self.0.route_table_entries
}
#[must_use]
pub const fn route_table(&self) -> &[RouteEntry] {
let len = usize_from_u32(self.0.route_table_entries);
unsafe {
slice::from_raw_parts(ptr::from_ref(&self.0.route_table).cast::<RouteEntry>(), len)
}
}
#[must_use]
pub const fn icmp_error(&self) -> &IcmpError {
unsafe { &*ptr::from_ref(&self.0.icmp_error).cast() }
}
#[must_use]
pub const fn tftp_error(&self) -> &TftpError {
unsafe { &*ptr::from_ref(&self.0.tftp_error).cast() }
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ArpEntry {
pub ip_addr: IpAddr,
pub mac_addr: EfiMacAddr,
}
#[repr(C)]
#[allow(missing_docs)]
#[derive(Debug)]
pub struct RouteEntry {
pub ip_addr: IpAddr,
pub subnet_mask: IpAddr,
pub gw_addr: IpAddr,
}
#[repr(C)]
#[allow(missing_docs)]
#[derive(Debug)]
pub struct IcmpError {
pub ty: u8,
pub code: u8,
pub checksum: u16,
pub u: IcmpErrorUnion,
pub data: [u8; 494],
}
impl Display for IcmpError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl core::error::Error for IcmpError {}
#[repr(C)]
#[allow(missing_docs)]
pub union IcmpErrorUnion {
pub reserved: u32,
pub mtu: u32,
pub pointer: u32,
pub echo: IcmpErrorEcho,
}
impl Debug for IcmpErrorUnion {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "<binary data>")
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[allow(missing_docs)]
pub struct IcmpErrorEcho {
pub identifier: u16,
pub sequence: u16,
}
#[repr(C)]
#[allow(missing_docs)]
#[derive(Debug)]
pub struct TftpError {
pub error_code: u8,
pub error_string: [u8; 127],
}
impl Display for TftpError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl core::error::Error for TftpError {}
#[allow(missing_docs)]
#[derive(Debug)]
pub struct TftpFileInfo<'a> {
pub filename: &'a CStr8,
pub size: u64,
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: f32,
}
#[allow(missing_docs)]
#[derive(Debug)]
pub struct MtftpFileInfo<'a> {
pub filename: &'a CStr8,
pub ip_address: IpAddr,
pub size: u64,
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: f32,
}
#[derive(Clone, Copy, Debug)]
pub struct ReadDirParseError;
impl Display for ReadDirParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl core::error::Error for ReadDirParseError {}