evtx 0.11.2

A Fast (and safe) parser for the Windows XML Event Log (EVTX) format
Documentation
use winstructs::guid::Guid;

use super::error::{Result, WevtManifestError};
use crate::utils::bytes;

pub(super) fn read_sized_utf16_string(
    buf: &[u8],
    offset: u32,
    what: &'static str,
) -> Result<String> {
    let off_usize = u32_to_usize(offset, what, buf.len())?;
    require_len(buf, off_usize, 4, what)?;
    let size = read_u32_named(buf, off_usize, what)?;
    if size < 4 {
        return Err(WevtManifestError::SizeOutOfBounds { what, offset, size });
    }
    let size_usize = usize::try_from(size).map_err(|_| WevtManifestError::SizeOutOfBounds {
        what,
        offset,
        size,
    })?;
    require_len(buf, off_usize, size_usize, what)?;
    let bytes = &buf[off_usize + 4..off_usize + size_usize];
    decode_utf16_z(bytes, what, offset)
}

pub(super) fn decode_utf16_z(bytes: &[u8], what: &'static str, offset: u32) -> Result<String> {
    crate::utils::decode_utf16le_bytes_z(bytes)
        .map_err(|_| WevtManifestError::InvalidUtf16String { what, offset })
}

pub(super) fn read_sig_named(buf: &[u8], offset: usize, what: &'static str) -> Result<[u8; 4]> {
    bytes::read_sig(buf, offset).ok_or(WevtManifestError::Truncated {
        what,
        offset: usize_to_u32(offset),
        need: 4,
        have: buf.len().saturating_sub(offset),
    })
}

pub(super) fn read_u8_named(buf: &[u8], offset: usize, what: &'static str) -> Result<u8> {
    bytes::read_u8(buf, offset).ok_or(WevtManifestError::Truncated {
        what,
        offset: usize_to_u32(offset),
        need: 1,
        have: buf.len().saturating_sub(offset),
    })
}

pub(super) fn read_u16_named(buf: &[u8], offset: usize, what: &'static str) -> Result<u16> {
    bytes::read_u16_le(buf, offset).ok_or(WevtManifestError::Truncated {
        what,
        offset: usize_to_u32(offset),
        need: 2,
        have: buf.len().saturating_sub(offset),
    })
}

pub(super) fn read_u32_named(buf: &[u8], offset: usize, what: &'static str) -> Result<u32> {
    bytes::read_u32_le(buf, offset).ok_or(WevtManifestError::Truncated {
        what,
        offset: usize_to_u32(offset),
        need: 4,
        have: buf.len().saturating_sub(offset),
    })
}

pub(super) fn read_u64_named(buf: &[u8], offset: usize, what: &'static str) -> Result<u64> {
    bytes::read_u64_le(buf, offset).ok_or(WevtManifestError::Truncated {
        what,
        offset: usize_to_u32(offset),
        need: 8,
        have: buf.len().saturating_sub(offset),
    })
}

pub(super) fn read_guid_named(buf: &[u8], offset: usize, what: &'static str) -> Result<Guid> {
    let bytes = bytes::read_array::<16>(buf, offset).ok_or(WevtManifestError::Truncated {
        what,
        offset: usize_to_u32(offset),
        need: 16,
        have: buf.len().saturating_sub(offset),
    })?;
    Guid::from_buffer(&bytes).map_err(|_| WevtManifestError::InvalidGuid {
        what,
        offset: usize_to_u32(offset),
    })
}

pub(super) fn u32_to_usize(offset: u32, what: &'static str, len: usize) -> Result<usize> {
    let off = usize::try_from(offset).map_err(|_| WevtManifestError::OffsetOutOfBounds {
        what,
        offset,
        len,
    })?;
    if off > len {
        return Err(WevtManifestError::OffsetOutOfBounds { what, offset, len });
    }
    Ok(off)
}

pub(super) fn usize_to_u32(v: usize) -> u32 {
    u32::try_from(v).unwrap_or(u32::MAX)
}

pub(super) fn require_len(buf: &[u8], off: usize, need: usize, what: &'static str) -> Result<()> {
    if off > buf.len() || buf.len().saturating_sub(off) < need {
        return Err(WevtManifestError::Truncated {
            what,
            offset: usize_to_u32(off),
            need,
            have: buf.len().saturating_sub(off),
        });
    }
    Ok(())
}

pub(super) fn checked_end(len: usize, off: u32, size: u32, what: &'static str) -> Result<usize> {
    let off_usize = u32_to_usize(off, what, len)?;
    let size_usize = usize::try_from(size).map_err(|_| WevtManifestError::SizeOutOfBounds {
        what,
        offset: off,
        size,
    })?;
    let end = off_usize
        .checked_add(size_usize)
        .ok_or(WevtManifestError::SizeOutOfBounds {
            what,
            offset: off,
            size,
        })?;
    if end > len {
        return Err(WevtManifestError::SizeOutOfBounds {
            what,
            offset: off,
            size,
        });
    }
    Ok(end)
}