crdhcpc 0.1.1

Standalone DHCP Client for Linux with DHCPv4, DHCPv6, PXE, and Dynamic DNS support
Documentation
//! PXE (Preboot Execution Environment) support
//!
//! Implements PXE boot options and TFTP file retrieval

use super::*;

/// PXE configuration
#[derive(Debug, Clone)]
pub struct PxeConfig {
    pub boot_filename: Option<String>,
    pub tftp_server: Option<Ipv4Addr>,
    pub boot_server: Option<Ipv4Addr>,
    pub vendor_class: String,
    pub architecture: u16,
    pub boot_menu: Vec<PxeMenuItem>,
}

/// PXE boot menu item
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PxeMenuItem {
    pub id: u16,
    pub type_: u16,
    pub description: String,
}

/// PXE client handler
pub struct PxeHandler {
    config: PxeConfigSettings,
}

impl PxeHandler {
    pub fn new(config: PxeConfigSettings) -> Self {
        Self { config }
    }

    /// Extract PXE options from DHCPv4 message
    pub fn extract_pxe_options(&self, _message: &dhcproto::v4::Message) -> Result<PxeConfig> {
        // In real implementation, extract:
        // - Option 60: Vendor Class Identifier
        // - Option 66: TFTP Server Name
        // - Option 67: Bootfile Name
        // - Option 43: Vendor-Specific Information (PXE boot menu)

        Ok(PxeConfig {
            boot_filename: None,
            tftp_server: None,
            boot_server: None,
            vendor_class: self.config.vendor_class.clone(),
            architecture: self.config.architecture,
            boot_menu: Vec::new(),
        })
    }

    /// Boot from PXE
    pub async fn boot(&self, pxe_config: &PxeConfig) -> Result<()> {
        if !self.config.enabled {
            return Err(DhcpClientError::PxeError("PXE is not enabled".to_string()));
        }

        info!("Starting PXE boot");

        if let (Some(filename), Some(server)) = (&pxe_config.boot_filename, &pxe_config.tftp_server) {
            info!("Downloading boot file {} from TFTP server {}", filename, server);

            // Use TFTP client to download boot file
            // This would integrate with the TFTP client module

            info!("PXE boot file downloaded successfully");
        } else {
            return Err(DhcpClientError::PxeError("Missing boot filename or TFTP server".to_string()));
        }

        Ok(())
    }

    /// Display boot menu (if available)
    pub fn display_boot_menu(&self, menu: &[PxeMenuItem]) {
        if menu.is_empty() {
            return;
        }

        info!("PXE Boot Menu:");
        for item in menu {
            info!("  [{}] {}", item.id, item.description);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pxe_handler_creation() {
        let config = PxeConfigSettings::default();
        let handler = PxeHandler::new(config);
        assert!(!handler.config.enabled);
    }

    #[test]
    fn test_pxe_menu_item() {
        let item = PxeMenuItem {
            id: 1,
            type_: 0,
            description: "Local Boot".to_string(),
        };
        assert_eq!(item.id, 1);
        assert_eq!(item.description, "Local Boot");
    }
}