140 lines
3.0 KiB
Go
140 lines
3.0 KiB
Go
|
//go:build windows
|
||
|
|
||
|
package ingress
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/netip"
|
||
|
"testing"
|
||
|
"unsafe"
|
||
|
|
||
|
"golang.org/x/net/icmp"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
// TestParseEchoReply tests parsing raw bytes from icmpSendEcho into echoResp
|
||
|
func TestParseEchoReply(t *testing.T) {
|
||
|
dst, err := inAddrV4(netip.MustParseAddr("192.168.10.20"))
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
validReplyData := []byte(t.Name())
|
||
|
validReply := echoReply{
|
||
|
Address: dst,
|
||
|
Status: success,
|
||
|
RoundTripTime: uint32(20),
|
||
|
DataSize: uint16(len(validReplyData)),
|
||
|
DataPointer: &validReplyData[0],
|
||
|
Options: ipOption{
|
||
|
TTL: 59,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
destHostUnreachableReply := validReply
|
||
|
destHostUnreachableReply.Status = destHostUnreachable
|
||
|
|
||
|
tests := []struct {
|
||
|
testCase string
|
||
|
replyBuf []byte
|
||
|
expectedReply *echoReply
|
||
|
expectedData []byte
|
||
|
}{
|
||
|
{
|
||
|
testCase: "empty buffer",
|
||
|
},
|
||
|
{
|
||
|
testCase: "status not success",
|
||
|
replyBuf: destHostUnreachableReply.marshal(t, []byte{}),
|
||
|
},
|
||
|
{
|
||
|
testCase: "valid reply",
|
||
|
replyBuf: validReply.marshal(t, validReplyData),
|
||
|
expectedReply: &validReply,
|
||
|
expectedData: validReplyData,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, test := range tests {
|
||
|
resp, err := newEchoResp(test.replyBuf)
|
||
|
if test.expectedReply == nil {
|
||
|
require.Error(t, err)
|
||
|
require.Nil(t, resp)
|
||
|
} else {
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, resp.reply, test.expectedReply)
|
||
|
require.True(t, bytes.Equal(resp.data, test.expectedData))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestSendEchoErrors makes sure icmpSendEcho handles error cases
|
||
|
func TestSendEchoErrors(t *testing.T) {
|
||
|
proxy, err := newICMPProxy(localhostIP, &noopLogger)
|
||
|
require.NoError(t, err)
|
||
|
winProxy := proxy.(*icmpProxy)
|
||
|
|
||
|
echo := icmp.Echo{
|
||
|
ID: 6193,
|
||
|
Seq: 25712,
|
||
|
Data: []byte(t.Name()),
|
||
|
}
|
||
|
documentIP := netip.MustParseAddr("192.0.2.200")
|
||
|
resp, err := winProxy.icmpSendEcho(documentIP, &echo)
|
||
|
require.Error(t, err)
|
||
|
require.Nil(t, resp)
|
||
|
}
|
||
|
|
||
|
func (er *echoReply) marshal(t *testing.T, data []byte) []byte {
|
||
|
buf := new(bytes.Buffer)
|
||
|
|
||
|
for _, field := range []any{
|
||
|
er.Address,
|
||
|
er.Status,
|
||
|
er.RoundTripTime,
|
||
|
er.DataSize,
|
||
|
er.Reserved,
|
||
|
} {
|
||
|
require.NoError(t, binary.Write(buf, endian, field))
|
||
|
}
|
||
|
|
||
|
require.NoError(t, marshalPointer(buf, uintptr(unsafe.Pointer(er.DataPointer))))
|
||
|
|
||
|
for _, field := range []any{
|
||
|
er.Options.TTL,
|
||
|
er.Options.Tos,
|
||
|
er.Options.Flags,
|
||
|
er.Options.OptionsSize,
|
||
|
} {
|
||
|
require.NoError(t, binary.Write(buf, endian, field))
|
||
|
}
|
||
|
|
||
|
require.NoError(t, marshalPointer(buf, er.Options.OptionsData))
|
||
|
|
||
|
padSize := buf.Len() % int(unsafe.Alignof(er))
|
||
|
padding := make([]byte, padSize)
|
||
|
n, err := buf.Write(padding)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, padSize, n)
|
||
|
|
||
|
n, err = buf.Write(data)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, len(data), n)
|
||
|
|
||
|
return buf.Bytes()
|
||
|
}
|
||
|
|
||
|
func marshalPointer(buf io.Writer, ptr uintptr) error {
|
||
|
size := unsafe.Sizeof(ptr)
|
||
|
switch size {
|
||
|
case 4:
|
||
|
return binary.Write(buf, endian, uint32(ptr))
|
||
|
case 8:
|
||
|
return binary.Write(buf, endian, uint64(ptr))
|
||
|
default:
|
||
|
return fmt.Errorf("unexpected pointer size %d", size)
|
||
|
}
|
||
|
}
|