108 lines
2.7 KiB
Go
108 lines
2.7 KiB
Go
package binsnoop
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// ErrParse is returned when structure parsing fails.
|
|
var ErrParse = errors.New("structure parse error")
|
|
|
|
// StructField defines a single field in a binary structure.
|
|
type StructField struct {
|
|
Name string
|
|
Type string // "u8", "u16", "u24", "u32", "u64"
|
|
}
|
|
|
|
// typeSize returns the byte size for a type string.
|
|
func typeSize(ctype string) (int, error) {
|
|
switch ctype {
|
|
case "u8":
|
|
return 1, nil
|
|
case "u16":
|
|
return 2, nil
|
|
case "u24":
|
|
return 3, nil
|
|
case "u32":
|
|
return 4, nil
|
|
case "u64":
|
|
return 8, nil
|
|
default:
|
|
return 0, fmt.Errorf("unknown type: %s", ctype)
|
|
}
|
|
}
|
|
|
|
// StructSize returns the total byte size of a structure definition.
|
|
func StructSize(fields []StructField) int {
|
|
total := 0
|
|
for _, f := range fields {
|
|
sz, err := typeSize(f.Type)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
total += sz
|
|
}
|
|
return total
|
|
}
|
|
|
|
// Parse parses a binary structure from data according to the field definitions.
|
|
// Endianness must be "big" or "little". All values are returned as int.
|
|
func Parse(data []byte, fields []StructField, endianness string) (map[string]int, error) {
|
|
structSize := StructSize(fields)
|
|
if len(data) < structSize {
|
|
return nil, ErrParse
|
|
}
|
|
|
|
result := make(map[string]int, len(fields))
|
|
offset := 0
|
|
|
|
for _, f := range fields {
|
|
sz, err := typeSize(f.Type)
|
|
if err != nil {
|
|
return nil, ErrParse
|
|
}
|
|
|
|
var value int
|
|
|
|
switch sz {
|
|
case 1:
|
|
value = int(data[offset])
|
|
case 2:
|
|
if endianness == "big" {
|
|
value = int(data[offset])<<8 | int(data[offset+1])
|
|
} else {
|
|
value = int(data[offset+1])<<8 | int(data[offset])
|
|
}
|
|
case 3:
|
|
if endianness == "big" {
|
|
value = int(data[offset])<<16 | int(data[offset+1])<<8 | int(data[offset+2])
|
|
} else {
|
|
value = int(data[offset+2])<<16 | int(data[offset+1])<<8 | int(data[offset])
|
|
}
|
|
case 4:
|
|
if endianness == "big" {
|
|
value = int(data[offset])<<24 | int(data[offset+1])<<16 | int(data[offset+2])<<8 | int(data[offset+3])
|
|
} else {
|
|
value = int(data[offset+3])<<24 | int(data[offset+2])<<16 | int(data[offset+1])<<8 | int(data[offset])
|
|
}
|
|
case 8:
|
|
if endianness == "big" {
|
|
value = int(uint64(data[offset])<<56 | uint64(data[offset+1])<<48 |
|
|
uint64(data[offset+2])<<40 | uint64(data[offset+3])<<32 |
|
|
uint64(data[offset+4])<<24 | uint64(data[offset+5])<<16 |
|
|
uint64(data[offset+6])<<8 | uint64(data[offset+7]))
|
|
} else {
|
|
value = int(uint64(data[offset+7])<<56 | uint64(data[offset+6])<<48 |
|
|
uint64(data[offset+5])<<40 | uint64(data[offset+4])<<32 |
|
|
uint64(data[offset+3])<<24 | uint64(data[offset+2])<<16 |
|
|
uint64(data[offset+1])<<8 | uint64(data[offset]))
|
|
}
|
|
}
|
|
|
|
offset += sz
|
|
result[f.Name] = value
|
|
}
|
|
|
|
return result, nil
|
|
}
|