128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
package binsnoop
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash/crc32"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
const MaxFileReadBytes int64 = 64 << 20
|
|
|
|
// CRC32 calculates the CRC32 checksum using the IEEE polynomial.
|
|
func CRC32(data []byte) uint32 {
|
|
return crc32.ChecksumIEEE(data)
|
|
}
|
|
|
|
// EpochToString converts a Unix timestamp to a formatted time string.
|
|
func EpochToString(epoch uint32) string {
|
|
t := time.Unix(int64(epoch), 0).UTC()
|
|
return t.Format("2006-01-02 15:04:05")
|
|
}
|
|
|
|
// GetCString extracts a NULL-terminated C string from raw bytes.
|
|
func GetCString(data []byte) string {
|
|
for i, b := range data {
|
|
if b == 0 {
|
|
s := string(data[:i])
|
|
// Validate UTF-8 printability
|
|
for _, c := range s {
|
|
if c == 0 {
|
|
return ""
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
// IsASCIINumber returns true if the byte is an ASCII digit (0-9).
|
|
func IsASCIINumber(b byte) bool {
|
|
return b >= '0' && b <= '9'
|
|
}
|
|
|
|
// IsPrintableASCII returns true if the byte is a printable ASCII character.
|
|
func IsPrintableASCII(b byte) bool {
|
|
return b >= 0x0A && b <= 0x7E
|
|
}
|
|
|
|
// IsOffsetSafe validates that the next offset is within bounds and progressing forward.
|
|
func IsOffsetSafe(availableData, nextOffset int, lastOffset *int) bool {
|
|
if lastOffset != nil {
|
|
if *lastOffset >= nextOffset {
|
|
return false
|
|
}
|
|
}
|
|
return nextOffset < availableData
|
|
}
|
|
|
|
// ReadFile reads a bounded file into memory for scanner APIs that require a
|
|
// byte slice. Larger callers should use a streaming document path.
|
|
func ReadFile(path string) ([]byte, error) {
|
|
return ReadFileLimited(path, MaxFileReadBytes)
|
|
}
|
|
|
|
func ReadFileLimited(path string, maxBytes int64) ([]byte, error) {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if info.IsDir() {
|
|
return nil, fmt.Errorf("binsnoop: path is a directory")
|
|
}
|
|
if maxBytes > 0 && info.Size() > maxBytes {
|
|
return nil, fmt.Errorf("binsnoop: file is too large: %d bytes exceeds %d bytes", info.Size(), maxBytes)
|
|
}
|
|
return os.ReadFile(path)
|
|
}
|
|
|
|
// LE16 reads a little-endian uint16 from data at the given offset.
|
|
func LE16(data []byte, offset int) uint16 {
|
|
if offset+2 > len(data) {
|
|
return 0
|
|
}
|
|
return binary.LittleEndian.Uint16(data[offset:])
|
|
}
|
|
|
|
// BE16 reads a big-endian uint16 from data at the given offset.
|
|
func BE16(data []byte, offset int) uint16 {
|
|
if offset+2 > len(data) {
|
|
return 0
|
|
}
|
|
return binary.BigEndian.Uint16(data[offset:])
|
|
}
|
|
|
|
// LE32 reads a little-endian uint32 from data at the given offset.
|
|
func LE32(data []byte, offset int) uint32 {
|
|
if offset+4 > len(data) {
|
|
return 0
|
|
}
|
|
return binary.LittleEndian.Uint32(data[offset:])
|
|
}
|
|
|
|
// BE32 reads a big-endian uint32 from data at the given offset.
|
|
func BE32(data []byte, offset int) uint32 {
|
|
if offset+4 > len(data) {
|
|
return 0
|
|
}
|
|
return binary.BigEndian.Uint32(data[offset:])
|
|
}
|
|
|
|
// LE64 reads a little-endian uint64 from data at the given offset.
|
|
func LE64(data []byte, offset int) uint64 {
|
|
if offset+8 > len(data) {
|
|
return 0
|
|
}
|
|
return binary.LittleEndian.Uint64(data[offset:])
|
|
}
|
|
|
|
// BE64 reads a big-endian uint64 from data at the given offset.
|
|
func BE64(data []byte, offset int) uint64 {
|
|
if offset+8 > len(data) {
|
|
return 0
|
|
}
|
|
return binary.BigEndian.Uint64(data[offset:])
|
|
}
|