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() }() } }