ZapretKVN-android/audit/xray_module_integration_test.go
loop-uh 6c6de7e24a
Some checks failed
Android CI / build (push) Has been cancelled
refactor: unify protocol runtimes and retain original core errors
2026-09-05 16:27:51 +03:00

162 lines
4.4 KiB
Go

package xraycore_test
import (
"context"
"fmt"
"io"
"net"
"testing"
"time"
box "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/include"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/json"
M "github.com/sagernet/sing/common/metadata"
)
func startBox(t *testing.T, text string) *box.Box {
t.Helper()
ctx := include.Context(context.Background())
var options option.Options
if err := json.UnmarshalContext(ctx, []byte(text), &options); err != nil {
t.Fatal(err)
}
instance, err := box.New(box.Options{Options: options, Context: ctx})
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = instance.Close() })
if err = instance.Start(); err != nil {
t.Fatal(err)
}
return instance
}
func TestZapretXrayModuleCarriesTCPAndUDPAndIsolatesOwners(t *testing.T) {
listener, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
go func() {
for {
conn, err := listener.Accept()
if err != nil {
return
}
go func() { defer conn.Close(); _, _ = io.Copy(conn, conn) }()
}
}()
udp, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer udp.Close()
go func() {
buffer := make([]byte, 2048)
for {
n, peer, err := udp.ReadFrom(buffer)
if err != nil {
return
}
_, _ = udp.WriteTo(buffer[:n], peer)
}
}()
reserve, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
port := reserve.Addr().(*net.TCPAddr).Port
reserve.Close()
const uuid = "11111111-1111-1111-1111-111111111111"
startBox(t, fmt.Sprintf(`{
"log":{"disabled":true},
"inbounds":[{"type":"vless","listen":"127.0.0.1","listen_port":%d,
"users":[{"uuid":"%s"}]}],
"outbounds":[{"type":"direct","tag":"direct"}]
}`, port, uuid))
config := fmt.Sprintf(`{
"log":{"disabled":true},
"dns":{"servers":[{"type":"local","tag":"local"}]},
"outbounds":[{"type":"vless","tag":"proxy","server":"127.0.0.1","server_port":%d,
"uri":"vless://%s@127.0.0.1:%d?security=none&encryption=none&type=tcp"}]
}`, port, uuid, port)
clients := []*box.Box{startBox(t, config), startBox(t, config)}
for index, client := range clients {
outbound, loaded := client.Outbound().Outbound("proxy")
if !loaded {
t.Fatal("outbound missing")
}
if fmt.Sprintf("%T", outbound) != "*xraycore.Outbound" {
t.Fatalf("wrong protocol owner: %T", outbound)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
conn, err := outbound.DialContext(ctx, "tcp", M.ParseSocksaddr(listener.Addr().String()))
if err != nil {
t.Fatal(err)
}
_ = conn.SetDeadline(time.Now().Add(3 * time.Second))
if _, err = conn.Write([]byte("tcp-echo")); err != nil {
t.Fatal(err)
}
reply := make([]byte, 8)
if _, err = io.ReadFull(conn, reply); err != nil {
t.Fatal(err)
}
conn.Close()
if string(reply) != "tcp-echo" {
t.Fatal("TCP payload changed")
}
packet, err := outbound.ListenPacket(ctx, M.ParseSocksaddr(udp.LocalAddr().String()))
if err != nil {
t.Fatal(err)
}
if _, err = packet.WriteTo([]byte("udp-echo"), udp.LocalAddr()); err != nil {
t.Fatal(err)
}
result := make(chan error, 1)
go func() {
data := make([]byte, 32)
n, peer, err := packet.ReadFrom(data)
if err == nil && (string(data[:n]) != "udp-echo" || peer.String() != udp.LocalAddr().String()) {
err = fmt.Errorf("UDP payload or peer changed")
}
result <- err
}()
select {
case err = <-result:
if err != nil {
t.Fatal(err)
}
case <-ctx.Done():
packet.Close()
t.Fatal("UDP response timed out")
}
packet.Close()
if index == 0 {
idlePacket, err := outbound.ListenPacket(ctx, M.ParseSocksaddr(udp.LocalAddr().String()))
if err != nil {
t.Fatal(err)
}
defer idlePacket.Close()
if err = client.Close(); err != nil {
t.Fatal(err)
}
closed := make(chan error, 1)
go func() { _, _, err := idlePacket.ReadFrom(make([]byte, 32)); closed <- err }()
select {
case err = <-closed:
if err == nil {
t.Fatal("idle packet connection survived owner close")
}
case <-time.After(time.Second):
t.Fatal("idle packet read blocked after owner close")
}
if _, err = outbound.DialContext(ctx, "tcp", M.ParseSocksaddr(listener.Addr().String())); err == nil {
t.Fatal("closed owner accepted new traffic")
}
}
}
}