mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
Add shadowsocks tests
This commit is contained in:
parent
f448b6b977
commit
7f8c9ffa30
|
@ -54,7 +54,7 @@ func (a *myInboundAdapter) Tag() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *myInboundAdapter) Start() error {
|
func (a *myInboundAdapter) Start() error {
|
||||||
bindAddr := M.SocksaddrFromAddrPort(netip.Addr(a.listenOptions.Listen), a.listenOptions.Port)
|
bindAddr := M.SocksaddrFromAddrPort(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
|
||||||
if common.Contains(a.network, C.NetworkTCP) {
|
if common.Contains(a.network, C.NetworkTCP) {
|
||||||
var tcpListener *net.TCPListener
|
var tcpListener *net.TCPListener
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -79,7 +79,7 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
|
||||||
|
|
||||||
type ListenOptions struct {
|
type ListenOptions struct {
|
||||||
Listen ListenAddress `json:"listen"`
|
Listen ListenAddress `json:"listen"`
|
||||||
Port uint16 `json:"listen_port"`
|
ListenPort uint16 `json:"listen_port"`
|
||||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||||
SniffEnabled bool `json:"sniff,omitempty"`
|
SniffEnabled bool `json:"sniff,omitempty"`
|
||||||
|
|
54
test/box_test.go
Normal file
54
test/box_test.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mkPort(t *testing.T) uint16 {
|
||||||
|
for {
|
||||||
|
tcpListener, err := net.ListenTCP("tcp", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
listenPort := M.SocksaddrFromNet(tcpListener.Addr()).Port
|
||||||
|
tcpListener.Close()
|
||||||
|
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{Port: int(listenPort)})
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
udpListener.Close()
|
||||||
|
return listenPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startInstance(t *testing.T, options option.Options) {
|
||||||
|
instance, err := box.New(context.Background(), options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, instance.Start())
|
||||||
|
t.Cleanup(func() {
|
||||||
|
instance.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSuit(t *testing.T, clientPort uint16, testPort uint16) {
|
||||||
|
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
|
||||||
|
dialTCP := func() (net.Conn, error) {
|
||||||
|
return dialer.DialContext(context.Background(), "tcp", M.ParseSocksaddrHostPort("127.0.0.1", testPort))
|
||||||
|
}
|
||||||
|
dialUDP := func() (net.PacketConn, error) {
|
||||||
|
return dialer.ListenPacket(context.Background(), M.ParseSocksaddrHostPort("127.0.0.1", testPort))
|
||||||
|
}
|
||||||
|
require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
|
||||||
|
require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP))
|
||||||
|
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
|
||||||
|
require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP))
|
||||||
|
require.NoError(t, testPacketConnTimeout(t, dialUDP))
|
||||||
|
}
|
71
test/clash_darwin_test.go
Normal file
71
test/clash_darwin_test.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/net/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultRouteIP() (netip.Addr, error) {
|
||||||
|
idx, err := defaultRouteInterfaceIndex()
|
||||||
|
if err != nil {
|
||||||
|
return netip.Addr{}, err
|
||||||
|
}
|
||||||
|
iface, err := net.InterfaceByIndex(idx)
|
||||||
|
if err != nil {
|
||||||
|
return netip.Addr{}, err
|
||||||
|
}
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return netip.Addr{}, err
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip := addr.(*net.IPNet).IP
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return netip.AddrFrom4(*(*[4]byte)(ip)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return netip.Addr{}, errors.New("no ipv4 addr")
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultRouteInterfaceIndex() (int, error) {
|
||||||
|
rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_DUMP2, 0)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("route.FetchRIB: %w", err)
|
||||||
|
}
|
||||||
|
msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("route.ParseRIB: %w", err)
|
||||||
|
}
|
||||||
|
for _, message := range msgs {
|
||||||
|
routeMessage := message.(*route.RouteMessage)
|
||||||
|
if routeMessage.Flags&(syscall.RTF_UP|syscall.RTF_GATEWAY|syscall.RTF_STATIC) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses := routeMessage.Addrs
|
||||||
|
|
||||||
|
destination, ok := addresses[0].(*route.Inet4Addr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if destination.IP != [4]byte{0, 0, 0, 0} {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch addresses[1].(type) {
|
||||||
|
case *route.Inet4Addr:
|
||||||
|
return routeMessage.Index, nil
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("ambiguous gateway interfaces found")
|
||||||
|
}
|
12
test/clash_other_test.go
Normal file
12
test/clash_other_test.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build !darwin
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultRouteIP() (netip.Addr, error) {
|
||||||
|
return netip.Addr{}, errors.New("not supported")
|
||||||
|
}
|
499
test/clash_test.go
Normal file
499
test/clash_test.go
Normal file
|
@ -0,0 +1,499 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var allImages = []string{
|
||||||
|
ImageShadowsocksRustServer,
|
||||||
|
ImageShadowsocksRustClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
localIP = netip.MustParseAddr("127.0.0.1")
|
||||||
|
isDarwin = runtime.GOOS == "darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if isDarwin {
|
||||||
|
var err error
|
||||||
|
localIP, err = defaultRouteIP()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.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
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
require.NoError(t, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
l, err := listenPacket("udp", ":"+F.ToString(port))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
rAddr := &net.UDPAddr{IP: localIP.AsSlice(), Port: int(port)}
|
||||||
|
|
||||||
|
times := 50
|
||||||
|
chunkSize := int64(1024)
|
||||||
|
|
||||||
|
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++ {
|
||||||
|
go func(idx int) {
|
||||||
|
buf := make([]byte, chunkSize)
|
||||||
|
if _, err := rand.Read(buf[1:]); err != nil {
|
||||||
|
t.Log(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf[0] = byte(idx)
|
||||||
|
|
||||||
|
hash := md5.Sum(buf)
|
||||||
|
mux.Lock()
|
||||||
|
hashMap[idx] = hash[:]
|
||||||
|
mux.Unlock()
|
||||||
|
|
||||||
|
if _, err := pc.WriteTo(buf, addr); err != nil {
|
||||||
|
t.Log(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
lc := net.ListenConfig{}
|
||||||
|
|
||||||
|
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(time.Millisecond * 200)
|
||||||
|
}
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenPacket(network, address string) (net.PacketConn, error) {
|
||||||
|
var lastErr error
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
l, err := net.ListenPacket(network, address)
|
||||||
|
if err == nil {
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastErr = err
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
|
}
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
76
test/docker_test.go
Normal file
76
test/docker_test.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DockerOptions struct {
|
||||||
|
Image string
|
||||||
|
EntryPoint string
|
||||||
|
Ports []uint16
|
||||||
|
Cmd []string
|
||||||
|
Env []string
|
||||||
|
Bind []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func startDockerContainer(t *testing.T, options DockerOptions) {
|
||||||
|
dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer dockerClient.Close()
|
||||||
|
|
||||||
|
var containerOptions container.Config
|
||||||
|
containerOptions.Image = options.Image
|
||||||
|
containerOptions.Entrypoint = []string{options.EntryPoint}
|
||||||
|
containerOptions.Cmd = options.Cmd
|
||||||
|
containerOptions.Env = options.Env
|
||||||
|
containerOptions.ExposedPorts = make(nat.PortSet)
|
||||||
|
|
||||||
|
var hostOptions container.HostConfig
|
||||||
|
if !isDarwin {
|
||||||
|
hostOptions.NetworkMode = "host"
|
||||||
|
}
|
||||||
|
hostOptions.PortBindings = make(nat.PortMap)
|
||||||
|
|
||||||
|
for _, port := range options.Ports {
|
||||||
|
containerOptions.ExposedPorts[nat.Port(F.ToString(port, "/tcp"))] = struct{}{}
|
||||||
|
containerOptions.ExposedPorts[nat.Port(F.ToString(port, "/udp"))] = struct{}{}
|
||||||
|
hostOptions.PortBindings[nat.Port(F.ToString(port, "/tcp"))] = []nat.PortBinding{
|
||||||
|
{HostPort: F.ToString(port), HostIP: "0.0.0.0"},
|
||||||
|
}
|
||||||
|
hostOptions.PortBindings[nat.Port(F.ToString(port, "/udp"))] = []nat.PortBinding{
|
||||||
|
{HostPort: F.ToString(port), HostIP: "0.0.0.0"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerContainer, err := dockerClient.ContainerCreate(context.Background(), &containerOptions, &hostOptions, nil, nil, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
cleanContainer(dockerContainer.ID)
|
||||||
|
})
|
||||||
|
require.NoError(t, dockerClient.ContainerStart(context.Background(), dockerContainer.ID, types.ContainerStartOptions{}))
|
||||||
|
/*attach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, types.ContainerAttachOptions{
|
||||||
|
Logs: true, Stream: true, Stdout: true, Stderr: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
go func() {
|
||||||
|
attach.Reader.WriteTo(os.Stderr)
|
||||||
|
}()*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanContainer(id string) error {
|
||||||
|
dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dockerClient.Close()
|
||||||
|
return dockerClient.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true})
|
||||||
|
}
|
42
test/go.mod
Normal file
42
test/go.mod
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
module test
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/docker/docker v20.10.17+incompatible
|
||||||
|
github.com/docker/go-connections v0.4.0
|
||||||
|
github.com/sagernet/sing v0.0.0-20220708041648-04e100e91a92
|
||||||
|
github.com/sagernet/sing-box v0.0.0
|
||||||
|
github.com/stretchr/testify v1.8.0
|
||||||
|
golang.org/x/net v0.0.0-20220706163947-c90051bbdb60
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/sagernet/sing-box => ../
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||||
|
github.com/database64128/tfo-go v1.0.4 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||||
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.9.8 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||||
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
|
github.com/oschwald/maxminddb-golang v1.9.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
|
||||||
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
gotest.tools/v3 v3.3.0 // indirect
|
||||||
|
lukechampine.com/blake3 v1.1.7 // indirect
|
||||||
|
)
|
118
test/go.sum
Normal file
118
test/go.sum
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||||
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/database64128/tfo-go v1.0.4 h1:0D9CsLor6q+2UrLhFYY3MkKkxRGf2W+27beMAo43SJc=
|
||||||
|
github.com/database64128/tfo-go v1.0.4/go.mod h1:q5W+W0+2IHrw/Lnl0yg4sz7Kz5IDsm9x0vhwZXkRwG4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
|
||||||
|
github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||||
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/goccy/go-json v0.9.8 h1:DxXB6MLd6yyel7CLph8EwNIonUtVZd3Ue5iRcL4DQCE=
|
||||||
|
github.com/goccy/go-json v0.9.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
|
||||||
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||||
|
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sagernet/sing v0.0.0-20220708041648-04e100e91a92 h1:c+Jg/o4UBZ+7CFdKWy8XhPN5X1rtulYdMqdgjx6PNUo=
|
||||||
|
github.com/sagernet/sing v0.0.0-20220708041648-04e100e91a92/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
||||||
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
||||||
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM=
|
||||||
|
golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
|
||||||
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
|
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||||
|
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||||
|
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||||
|
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
189
test/shadowsocks_test.go
Normal file
189
test/shadowsocks_test.go
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShadowsocks(t *testing.T) {
|
||||||
|
for _, method16 := range []string{
|
||||||
|
"2022-blake3-aes-128-gcm",
|
||||||
|
} {
|
||||||
|
t.Run(method16+"-inbound", func(t *testing.T) {
|
||||||
|
testShadowsocksInboundWithShadowsocksRust(t, method16, mkBase64(t, 16))
|
||||||
|
})
|
||||||
|
t.Run(method16+"-outbound", func(t *testing.T) {
|
||||||
|
testShadowsocksOutboundWithShadowsocksRust(t, method16, mkBase64(t, 16))
|
||||||
|
})
|
||||||
|
t.Run(method16+"-self", func(t *testing.T) {
|
||||||
|
testShadowsocksSelf(t, method16, mkBase64(t, 16))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, method32 := range []string{
|
||||||
|
"2022-blake3-aes-256-gcm",
|
||||||
|
"2022-blake3-chacha20-poly1305",
|
||||||
|
} {
|
||||||
|
t.Run(method32+"-inbound", func(t *testing.T) {
|
||||||
|
testShadowsocksInboundWithShadowsocksRust(t, method32, mkBase64(t, 32))
|
||||||
|
})
|
||||||
|
t.Run(method32+"-outbound", func(t *testing.T) {
|
||||||
|
testShadowsocksOutboundWithShadowsocksRust(t, method32, mkBase64(t, 32))
|
||||||
|
})
|
||||||
|
t.Run(method32+"-self", func(t *testing.T) {
|
||||||
|
testShadowsocksSelf(t, method32, mkBase64(t, 32))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testShadowsocksInboundWithShadowsocksRust(t *testing.T, method string, password string) {
|
||||||
|
t.Parallel()
|
||||||
|
serverPort := mkPort(t)
|
||||||
|
clientPort := mkPort(t)
|
||||||
|
testPort := mkPort(t)
|
||||||
|
startDockerContainer(t, DockerOptions{
|
||||||
|
Image: ImageShadowsocksRustClient,
|
||||||
|
EntryPoint: "sslocal",
|
||||||
|
Ports: []uint16{serverPort, clientPort},
|
||||||
|
Cmd: []string{"-s", F.ToString("127.0.0.1:", serverPort), "-b", F.ToString("0.0.0.0:", clientPort), "-m", method, "-k", password, "-U"},
|
||||||
|
})
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Log: &option.LogOption{
|
||||||
|
Disabled: true,
|
||||||
|
},
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeShadowsocks,
|
||||||
|
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, password string) {
|
||||||
|
t.Parallel()
|
||||||
|
serverPort := mkPort(t)
|
||||||
|
clientPort := mkPort(t)
|
||||||
|
testPort := mkPort(t)
|
||||||
|
startDockerContainer(t, DockerOptions{
|
||||||
|
Image: ImageShadowsocksRustServer,
|
||||||
|
EntryPoint: "ssserver",
|
||||||
|
Ports: []uint16{serverPort, testPort},
|
||||||
|
Cmd: []string{"-s", F.ToString("0.0.0.0:", serverPort), "-m", method, "-k", password, "-U"},
|
||||||
|
})
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Log: &option.LogOption{
|
||||||
|
Disabled: true,
|
||||||
|
},
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
MixedOptions: option.SimpleInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeShadowsocks,
|
||||||
|
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testShadowsocksSelf(t *testing.T, method string, password string) {
|
||||||
|
t.Parallel()
|
||||||
|
serverPort := mkPort(t)
|
||||||
|
clientPort := mkPort(t)
|
||||||
|
testPort := mkPort(t)
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Log: &option.LogOption{
|
||||||
|
Disabled: true,
|
||||||
|
},
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
MixedOptions: option.SimpleInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeShadowsocks,
|
||||||
|
ShadowsocksOptions: option.ShadowsocksInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeShadowsocks,
|
||||||
|
Tag: "ss-out",
|
||||||
|
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "ss-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkBase64(t *testing.T, length int) string {
|
||||||
|
psk := make([]byte, length)
|
||||||
|
_, err := rand.Read(psk)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return base64.StdEncoding.EncodeToString(psk)
|
||||||
|
}
|
Loading…
Reference in a new issue