Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
3.2 KB go
//go:build phoboscref

/* SPDX-License-Identifier: MIT
 *
 * Phobos
 */

package phobos

import (
	"bytes"
	"math/rand"
	"testing"

	"golang.zx2c4.com/wireguard/windows/phobos/cref"
)

var socks5Maskings = map[Masking]int{MaskingSTUN: 1, MaskingMEDIA: 2, MaskingTLS: 3}

var socks5MediaParams = MediaParams{PayloadType: 102, SSRC: 0xC0FFEE, TimestampStep: 3000}

func TestSocks5FramingGoEncodeCDecode(t *testing.T) {
	rng := rand.New(rand.NewSource(21))
	for masking, id := range socks5Maskings {
		for _, length := range []int{1, 64, 1023, 1024, 1025, 4096, s5EncodeReadMax} {
			payload := make([]byte, length)
			rng.Read(payload)

			var encoder s5Encoder
			out := make([]byte, s5BufferSize+length)
			n := encoder.encode(masking, socks5MediaParams, payload, out)
			if n < 0 {
				t.Fatalf("%v: go encode failed at length %d", masking, length)
			}

			cref.Socks5Reset()
			var decoded []byte
			for offset := 0; offset < n; offset += s5EncodeReadMax {
				chunk := out[offset:min(offset+s5EncodeReadMax, n)]
				decoded = append(decoded, cref.Socks5Decode(id, socks5MediaParams.PayloadType, socks5MediaParams.SSRC, chunk, s5BufferSize)...)
			}
			if !bytes.Equal(decoded, payload) {
				t.Fatalf("%v: C cannot decode Go frames at length %d", masking, length)
			}
		}
	}
}

func TestSocks5FramingCEncodeGoDecode(t *testing.T) {
	rng := rand.New(rand.NewSource(22))
	for masking, id := range socks5Maskings {
		for _, length := range []int{1, 64, 1023, 1024, 1025, 4096} {
			payload := make([]byte, length)
			rng.Read(payload)

			cref.Socks5Reset()
			frames := cref.Socks5Encode(id, socks5MediaParams.PayloadType, socks5MediaParams.SSRC,
				socks5MediaParams.TimestampStep, payload, s5BufferSize+length)
			if frames == nil {
				t.Fatalf("%v: C encode failed at length %d", masking, length)
			}

			var decoder s5Decoder
			out := make([]byte, s5BufferSize+length)
			n := decoder.decode(masking, socks5MediaParams, frames, out)
			if n < 0 || !bytes.Equal(out[:n], payload) {
				t.Fatalf("%v: Go cannot decode C frames at length %d", masking, length)
			}
		}
	}
}

func TestSocks5FramingDecodesAcrossChunks(t *testing.T) {
	rng := rand.New(rand.NewSource(23))
	for masking, id := range socks5Maskings {
		payload := make([]byte, 4096)
		rng.Read(payload)

		cref.Socks5Reset()
		frames := cref.Socks5Encode(id, socks5MediaParams.PayloadType, socks5MediaParams.SSRC,
			socks5MediaParams.TimestampStep, payload, s5BufferSize*2)

		var decoder s5Decoder
		out := make([]byte, s5BufferSize)
		var assembled []byte
		for offset := 0; offset < len(frames); offset += 7 {
			chunk := frames[offset:min(offset+7, len(frames))]
			n := decoder.decode(masking, socks5MediaParams, chunk, out)
			if n < 0 {
				t.Fatalf("%v: chunked decode failed", masking)
			}
			assembled = append(assembled, out[:n]...)
		}
		if !bytes.Equal(assembled, payload) {
			t.Fatalf("%v: chunked decode produced %d bytes, want %d", masking, len(assembled), len(payload))
		}
	}
}

func TestSocks5FramingRejectsGarbage(t *testing.T) {
	for masking := range socks5Maskings {
		var decoder s5Decoder
		out := make([]byte, s5BufferSize)
		garbage := bytes.Repeat([]byte{0xA5}, 64)
		if n := decoder.decode(masking, socks5MediaParams, garbage, out); n >= 0 {
			t.Errorf("%v: garbage should not decode", masking)
		}
	}
}