97 lines
3.1 KiB
Go
97 lines
3.1 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"encoding/binary"
|
||
|
|
"math"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestLayout(t *testing.T) {
|
||
|
|
for _, test := range []struct {
|
||
|
|
value any
|
||
|
|
size int
|
||
|
|
}{
|
||
|
|
{tableInfo{}, 84}, {replaceHead{}, 96}, {ipMatch{}, 84}, {ruleHeader{}, 112}, {counter{}, 16},
|
||
|
|
} {
|
||
|
|
if size := binary.Size(test.value); size != test.size {
|
||
|
|
t.Errorf("%T size = %d, want %d", test.value, size, test.size)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestTable(t *testing.T) {
|
||
|
|
value := config{1234, 5678, 9012, []uint16{80, 443}}
|
||
|
|
data := table(value, 5, 0x12345678)
|
||
|
|
var head replaceHead
|
||
|
|
if err := binary.Read(bytes.NewReader(data), binary.NativeEndian, &head); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if string(bytes.TrimRight(head.Name[:], "\x00")) != "nat" || head.Valid != 27 || head.Count != 11 || int(head.Size) != len(data)-96 || head.OldCount != 5 || head.Address != 0x12345678 {
|
||
|
|
t.Fatalf("invalid replacement header: %+v", head)
|
||
|
|
}
|
||
|
|
body := data[96:]
|
||
|
|
starts := map[uint32]bool{}
|
||
|
|
var entries [][]byte
|
||
|
|
for offset := 0; offset < len(body); {
|
||
|
|
var rule ruleHeader
|
||
|
|
if err := binary.Read(bytes.NewReader(body[offset:]), binary.NativeEndian, &rule); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if rule.Next%8 != 0 || rule.Target < 112 || rule.Next < rule.Target+32 {
|
||
|
|
t.Fatalf("invalid rule offsets: %+v", rule)
|
||
|
|
}
|
||
|
|
starts[size32(offset)] = true
|
||
|
|
entries = append(entries, body[offset:offset+int(rule.Next)])
|
||
|
|
offset += int(rule.Next)
|
||
|
|
}
|
||
|
|
if len(entries) != 11 {
|
||
|
|
t.Fatalf("entry count = %d", len(entries))
|
||
|
|
}
|
||
|
|
for _, hook := range []int{0, 1, 3, 4} {
|
||
|
|
if !starts[head.Hooks[hook]] || !starts[head.Underflow[hook]] {
|
||
|
|
t.Errorf("hook %d does not point to entry boundaries", hook)
|
||
|
|
}
|
||
|
|
policy := body[head.Underflow[hook]:]
|
||
|
|
if verdict := binary.NativeEndian.Uint32(policy[144:]); verdict != math.MaxUint32-1 {
|
||
|
|
t.Errorf("hook %d has non-ACCEPT policy", hook)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for index, port := range map[int]uint16{0: 80, 1: 443} {
|
||
|
|
if got := binary.NativeEndian.Uint16(entries[index][148:]); got != port {
|
||
|
|
t.Errorf("inbound exclusion = %d, want %d", got, port)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if binary.BigEndian.Uint16(entries[2][160:]) != value.inbound || binary.BigEndian.Uint16(entries[7][160:]) != value.outbound {
|
||
|
|
t.Error("redirect ports differ from configuration")
|
||
|
|
}
|
||
|
|
if binary.NativeEndian.Uint32(entries[5][144:]) != value.proxyUID || !bytes.Equal(entries[6][32:35], []byte{'l', 'o', 0}) || !bytes.Equal(entries[6][64:67], []byte{255, 255, 255}) {
|
||
|
|
t.Error("proxy UID or loopback exemption differs")
|
||
|
|
}
|
||
|
|
if !bytes.Contains(entries[10], []byte("ERROR")) {
|
||
|
|
t.Error("missing table terminator")
|
||
|
|
}
|
||
|
|
without := table(config{inbound: 1, outbound: 2}, 3, 0)
|
||
|
|
if got := binary.NativeEndian.Uint32(without[36:]); got != 9 {
|
||
|
|
t.Errorf("empty exclusions produce %d entries", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSizeBounds(t *testing.T) {
|
||
|
|
if size16(math.MaxUint16) != math.MaxUint16 || size32(math.MaxUint32) != math.MaxUint32 {
|
||
|
|
t.Fatal("maximum ABI sizes differ")
|
||
|
|
}
|
||
|
|
for _, check := range []func(){
|
||
|
|
func() { extension("tcp", 0, make([]byte, 65535)) },
|
||
|
|
func() { size16(-1) }, func() { size32(-1) }, func() { size32(math.MaxUint32 + 1) },
|
||
|
|
} {
|
||
|
|
func() {
|
||
|
|
defer func() {
|
||
|
|
if recover() == nil {
|
||
|
|
t.Error("unrepresentable ABI size accepted")
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
check()
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
}
|