| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 | 
							- package main
 
- import (
 
- 	"context"
 
- 	"crypto/md5"
 
- 	"crypto/rand"
 
- 	"errors"
 
- 	"io"
 
- 	"net"
 
- 	_ "net/http/pprof"
 
- 	"net/netip"
 
- 	"sync"
 
- 	"testing"
 
- 	"time"
 
- 	"github.com/sagernet/sing-box/log"
 
- 	"github.com/sagernet/sing/common/control"
 
- 	F "github.com/sagernet/sing/common/format"
 
- 	"github.com/docker/docker/api/types"
 
- 	"github.com/docker/docker/client"
 
- 	"github.com/stretchr/testify/assert"
 
- 	"github.com/stretchr/testify/require"
 
- )
 
- // kanged from clash
 
- const (
 
- 	ImageShadowsocksRustServer = "ghcr.io/shadowsocks/ssserver-rust:latest"
 
- 	ImageShadowsocksRustClient = "ghcr.io/shadowsocks/sslocal-rust:latest"
 
- 	ImageV2RayCore             = "v2fly/v2fly-core:latest"
 
- 	ImageTrojan                = "trojangfw/trojan:latest"
 
- 	ImageNaive                 = "pocat/naiveproxy:client"
 
- 	ImageBoringTun             = "ghcr.io/ntkme/boringtun:edge"
 
- 	ImageHysteria              = "tobyxdd/hysteria:v1.3.5"
 
- 	ImageHysteria2             = "tobyxdd/hysteria:v2"
 
- 	ImageNginx                 = "nginx:stable"
 
- 	ImageShadowTLS             = "ghcr.io/ihciah/shadow-tls:latest"
 
- 	ImageXRayCore              = "teddysun/xray:latest"
 
- 	ImageShadowsocksLegacy     = "mritd/shadowsocks:latest"
 
- 	ImageTUICServer            = "kilvn/tuic-server:latest"
 
- 	ImageTUICClient            = "kilvn/tuic-client:latest"
 
- )
 
- var allImages = []string{
 
- 	ImageShadowsocksRustServer,
 
- 	ImageShadowsocksRustClient,
 
- 	ImageV2RayCore,
 
- 	ImageTrojan,
 
- 	ImageNaive,
 
- 	ImageBoringTun,
 
- 	ImageHysteria,
 
- 	ImageHysteria2,
 
- 	ImageNginx,
 
- 	ImageShadowTLS,
 
- 	ImageXRayCore,
 
- 	ImageShadowsocksLegacy,
 
- 	ImageTUICServer,
 
- 	ImageTUICClient,
 
- }
 
- var localIP = netip.MustParseAddr("127.0.0.1")
 
- func init() {
 
- 	dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
 
- 	if err != nil {
 
- 		panic(err)
 
- 	}
 
- 	defer dockerClient.Close()
 
- 	list, err := dockerClient.ImageList(context.Background(), types.ImageListOptions{All: true})
 
- 	if err != nil {
 
- 		log.Warn(err)
 
- 		return
 
- 	}
 
- 	imageExist := func(image string) bool {
 
- 		for _, item := range list {
 
- 			for _, tag := range item.RepoTags {
 
- 				if image == tag {
 
- 					return true
 
- 				}
 
- 			}
 
- 		}
 
- 		return false
 
- 	}
 
- 	for _, image := range allImages {
 
- 		if imageExist(image) {
 
- 			continue
 
- 		}
 
- 		log.Info("pulling image: ", image)
 
- 		imageStream, err := dockerClient.ImagePull(context.Background(), image, types.ImagePullOptions{})
 
- 		if err != nil {
 
- 			panic(err)
 
- 		}
 
- 		io.Copy(io.Discard, imageStream)
 
- 	}
 
- }
 
- func newPingPongPair() (chan []byte, chan []byte, func(t *testing.T) error) {
 
- 	pingCh := make(chan []byte)
 
- 	pongCh := make(chan []byte)
 
- 	test := func(t *testing.T) error {
 
- 		defer close(pingCh)
 
- 		defer close(pongCh)
 
- 		pingOpen := false
 
- 		pongOpen := false
 
- 		var recv []byte
 
- 		for {
 
- 			if pingOpen && pongOpen {
 
- 				break
 
- 			}
 
- 			select {
 
- 			case recv, pingOpen = <-pingCh:
 
- 				assert.True(t, pingOpen)
 
- 				assert.Equal(t, []byte("ping"), recv)
 
- 			case recv, pongOpen = <-pongCh:
 
- 				assert.True(t, pongOpen)
 
- 				assert.Equal(t, []byte("pong"), recv)
 
- 			case <-time.After(10 * time.Second):
 
- 				return errors.New("timeout")
 
- 			}
 
- 		}
 
- 		return nil
 
- 	}
 
- 	return pingCh, pongCh, test
 
- }
 
- func newLargeDataPair() (chan hashPair, chan hashPair, func(t *testing.T) error) {
 
- 	pingCh := make(chan hashPair)
 
- 	pongCh := make(chan hashPair)
 
- 	test := func(t *testing.T) error {
 
- 		defer close(pingCh)
 
- 		defer close(pongCh)
 
- 		pingOpen := false
 
- 		pongOpen := false
 
- 		var serverPair hashPair
 
- 		var clientPair hashPair
 
- 		for {
 
- 			if pingOpen && pongOpen {
 
- 				break
 
- 			}
 
- 			select {
 
- 			case serverPair, pingOpen = <-pingCh:
 
- 				assert.True(t, pingOpen)
 
- 			case clientPair, pongOpen = <-pongCh:
 
- 				assert.True(t, pongOpen)
 
- 			case <-time.After(10 * time.Second):
 
- 				return errors.New("timeout")
 
- 			}
 
- 		}
 
- 		assert.Equal(t, serverPair.recvHash, clientPair.sendHash)
 
- 		assert.Equal(t, serverPair.sendHash, clientPair.recvHash)
 
- 		return nil
 
- 	}
 
- 	return pingCh, pongCh, test
 
- }
 
- func testPingPongWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)) error {
 
- 	l, err := listen("tcp", ":"+F.ToString(port))
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer l.Close()
 
- 	c, err := cc()
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer c.Close()
 
- 	pingCh, pongCh, test := newPingPongPair()
 
- 	go func() {
 
- 		c, err := l.Accept()
 
- 		if err != nil {
 
- 			return
 
- 		}
 
- 		buf := make([]byte, 4)
 
- 		if _, err := io.ReadFull(c, buf); err != nil {
 
- 			return
 
- 		}
 
- 		pingCh <- buf
 
- 		if _, err := c.Write([]byte("pong")); err != nil {
 
- 			return
 
- 		}
 
- 	}()
 
- 	go func() {
 
- 		if _, err := c.Write([]byte("ping")); err != nil {
 
- 			return
 
- 		}
 
- 		buf := make([]byte, 4)
 
- 		if _, err := io.ReadFull(c, buf); err != nil {
 
- 			return
 
- 		}
 
- 		pongCh <- buf
 
- 	}()
 
- 	return test(t)
 
- }
 
- func testPingPongWithPacketConn(t *testing.T, port uint16, pcc func() (net.PacketConn, error)) error {
 
- 	l, err := listenPacket("udp", ":"+F.ToString(port))
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer l.Close()
 
- 	rAddr := &net.UDPAddr{IP: localIP.AsSlice(), Port: int(port)}
 
- 	pingCh, pongCh, test := newPingPongPair()
 
- 	go func() {
 
- 		buf := make([]byte, 1024)
 
- 		n, rAddr, err := l.ReadFrom(buf)
 
- 		if err != nil {
 
- 			return
 
- 		}
 
- 		pingCh <- buf[:n]
 
- 		if _, err := l.WriteTo([]byte("pong"), rAddr); err != nil {
 
- 			return
 
- 		}
 
- 	}()
 
- 	pc, err := pcc()
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer pc.Close()
 
- 	go func() {
 
- 		if _, err := pc.WriteTo([]byte("ping"), rAddr); err != nil {
 
- 			return
 
- 		}
 
- 		buf := make([]byte, 1024)
 
- 		n, _, err := pc.ReadFrom(buf)
 
- 		if err != nil {
 
- 			return
 
- 		}
 
- 		pongCh <- buf[:n]
 
- 	}()
 
- 	return test(t)
 
- }
 
- type hashPair struct {
 
- 	sendHash map[int][]byte
 
- 	recvHash map[int][]byte
 
- }
 
- func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)) error {
 
- 	l, err := listen("tcp", ":"+F.ToString(port))
 
- 	require.NoError(t, err)
 
- 	defer l.Close()
 
- 	times := 100
 
- 	chunkSize := int64(64 * 1024)
 
- 	pingCh, pongCh, test := newLargeDataPair()
 
- 	writeRandData := func(conn net.Conn) (map[int][]byte, error) {
 
- 		buf := make([]byte, chunkSize)
 
- 		hashMap := map[int][]byte{}
 
- 		for i := 0; i < times; i++ {
 
- 			if _, err := rand.Read(buf[1:]); err != nil {
 
- 				return nil, err
 
- 			}
 
- 			buf[0] = byte(i)
 
- 			hash := md5.Sum(buf)
 
- 			hashMap[i] = hash[:]
 
- 			if _, err := conn.Write(buf); err != nil {
 
- 				return nil, err
 
- 			}
 
- 		}
 
- 		return hashMap, nil
 
- 	}
 
- 	c, err := cc()
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer c.Close()
 
- 	go func() {
 
- 		c, err := l.Accept()
 
- 		if err != nil {
 
- 			return
 
- 		}
 
- 		defer c.Close()
 
- 		hashMap := map[int][]byte{}
 
- 		buf := make([]byte, chunkSize)
 
- 		for i := 0; i < times; i++ {
 
- 			_, err := io.ReadFull(c, buf)
 
- 			if err != nil {
 
- 				t.Log(err.Error())
 
- 				return
 
- 			}
 
- 			hash := md5.Sum(buf)
 
- 			hashMap[int(buf[0])] = hash[:]
 
- 		}
 
- 		sendHash, err := writeRandData(c)
 
- 		if err != nil {
 
- 			t.Log(err.Error())
 
- 			return
 
- 		}
 
- 		pingCh <- hashPair{
 
- 			sendHash: sendHash,
 
- 			recvHash: hashMap,
 
- 		}
 
- 	}()
 
- 	go func() {
 
- 		sendHash, err := writeRandData(c)
 
- 		if err != nil {
 
- 			t.Log(err.Error())
 
- 			return
 
- 		}
 
- 		hashMap := map[int][]byte{}
 
- 		buf := make([]byte, chunkSize)
 
- 		for i := 0; i < times; i++ {
 
- 			_, err := io.ReadFull(c, buf)
 
- 			if err != nil {
 
- 				t.Log(err.Error())
 
- 				return
 
- 			}
 
- 			hash := md5.Sum(buf)
 
- 			hashMap[int(buf[0])] = hash[:]
 
- 		}
 
- 		pongCh <- hashPair{
 
- 			sendHash: sendHash,
 
- 			recvHash: hashMap,
 
- 		}
 
- 	}()
 
- 	return test(t)
 
- }
 
- func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.PacketConn, error)) error {
 
- 	return testLargeDataWithPacketConnSize(t, port, 1500, pcc)
 
- }
 
- func testLargeDataWithPacketConnSize(t *testing.T, port uint16, chunkSize int, pcc func() (net.PacketConn, error)) error {
 
- 	l, err := listenPacket("udp", ":"+F.ToString(port))
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer l.Close()
 
- 	rAddr := &net.UDPAddr{IP: localIP.AsSlice(), Port: int(port)}
 
- 	times := 50
 
- 	pingCh, pongCh, test := newLargeDataPair()
 
- 	writeRandData := func(pc net.PacketConn, addr net.Addr) (map[int][]byte, error) {
 
- 		hashMap := map[int][]byte{}
 
- 		mux := sync.Mutex{}
 
- 		for i := 0; i < times; i++ {
 
- 			buf := make([]byte, chunkSize)
 
- 			if _, err := rand.Read(buf[1:]); err != nil {
 
- 				t.Log(err.Error())
 
- 				continue
 
- 			}
 
- 			buf[0] = byte(i)
 
- 			hash := md5.Sum(buf)
 
- 			mux.Lock()
 
- 			hashMap[i] = hash[:]
 
- 			mux.Unlock()
 
- 			if _, err := pc.WriteTo(buf, addr); err != nil {
 
- 				t.Log(err.Error())
 
- 			}
 
- 			time.Sleep(10 * time.Millisecond)
 
- 		}
 
- 		return hashMap, nil
 
- 	}
 
- 	go func() {
 
- 		var rAddr net.Addr
 
- 		hashMap := map[int][]byte{}
 
- 		buf := make([]byte, 64*1024)
 
- 		for i := 0; i < times; i++ {
 
- 			_, rAddr, err = l.ReadFrom(buf)
 
- 			if err != nil {
 
- 				t.Log(err.Error())
 
- 				return
 
- 			}
 
- 			hash := md5.Sum(buf[:chunkSize])
 
- 			hashMap[int(buf[0])] = hash[:]
 
- 		}
 
- 		sendHash, err := writeRandData(l, rAddr)
 
- 		if err != nil {
 
- 			t.Log(err.Error())
 
- 			return
 
- 		}
 
- 		pingCh <- hashPair{
 
- 			sendHash: sendHash,
 
- 			recvHash: hashMap,
 
- 		}
 
- 	}()
 
- 	pc, err := pcc()
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	defer pc.Close()
 
- 	go func() {
 
- 		sendHash, err := writeRandData(pc, rAddr)
 
- 		if err != nil {
 
- 			t.Log(err.Error())
 
- 			return
 
- 		}
 
- 		hashMap := map[int][]byte{}
 
- 		buf := make([]byte, 64*1024)
 
- 		for i := 0; i < times; i++ {
 
- 			_, _, err := pc.ReadFrom(buf)
 
- 			if err != nil {
 
- 				t.Log(err.Error())
 
- 				return
 
- 			}
 
- 			hash := md5.Sum(buf[:chunkSize])
 
- 			hashMap[int(buf[0])] = hash[:]
 
- 		}
 
- 		pongCh <- hashPair{
 
- 			sendHash: sendHash,
 
- 			recvHash: hashMap,
 
- 		}
 
- 	}()
 
- 	return test(t)
 
- }
 
- func testPacketConnTimeout(t *testing.T, pcc func() (net.PacketConn, error)) error {
 
- 	pc, err := pcc()
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	err = pc.SetReadDeadline(time.Now().Add(time.Millisecond * 300))
 
- 	require.NoError(t, err)
 
- 	errCh := make(chan error, 1)
 
- 	go func() {
 
- 		buf := make([]byte, 1024)
 
- 		_, _, err := pc.ReadFrom(buf)
 
- 		errCh <- err
 
- 	}()
 
- 	select {
 
- 	case <-errCh:
 
- 		return nil
 
- 	case <-time.After(time.Second * 10):
 
- 		return errors.New("timeout")
 
- 	}
 
- }
 
- func listen(network, address string) (net.Listener, error) {
 
- 	var lc net.ListenConfig
 
- 	lc.Control = control.ReuseAddr()
 
- 	var lastErr error
 
- 	for i := 0; i < 5; i++ {
 
- 		l, err := lc.Listen(context.Background(), network, address)
 
- 		if err == nil {
 
- 			return l, nil
 
- 		}
 
- 		lastErr = err
 
- 		time.Sleep(5 * time.Millisecond)
 
- 	}
 
- 	return nil, lastErr
 
- }
 
- func listenPacket(network, address string) (net.PacketConn, error) {
 
- 	var lc net.ListenConfig
 
- 	lc.Control = control.ReuseAddr()
 
- 	var lastErr error
 
- 	for i := 0; i < 5; i++ {
 
- 		l, err := lc.ListenPacket(context.Background(), network, address)
 
- 		if err == nil {
 
- 			return l, nil
 
- 		}
 
- 		lastErr = err
 
- 		time.Sleep(5 * time.Millisecond)
 
- 	}
 
- 	return nil, lastErr
 
- }
 
 
  |