Feature: add protocol test
This commit is contained in:
parent
aaaef92fbf
commit
1a435b79e6
26 changed files with 2299 additions and 0 deletions
49
test/README.md
Normal file
49
test/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
## Clash testing suit
|
||||
|
||||
### Protocol testing suit
|
||||
|
||||
* TCP pingpong test
|
||||
* UDP pingpong test
|
||||
* TCP large data test
|
||||
* UDP large data test
|
||||
|
||||
### Protocols
|
||||
|
||||
- [x] Shadowsocks
|
||||
- [x] Normal
|
||||
- [x] ObfsHTTP
|
||||
- [x] ObfsTLS
|
||||
- [x] ObfsV2rayPlugin
|
||||
- [x] Vmess
|
||||
- [x] Normal
|
||||
- [x] AEAD
|
||||
- [x] HTTP
|
||||
- [x] HTTP2
|
||||
- [x] TLS
|
||||
- [x] Websocket
|
||||
- [x] Websocket TLS
|
||||
- [x] gRPC
|
||||
- [x] Trojan
|
||||
- [x] Normal
|
||||
- [x] gRPC
|
||||
- [x] Snell
|
||||
- [x] Normal
|
||||
- [x] ObfsHTTP
|
||||
- [x] ObfsTLS
|
||||
|
||||
### Features
|
||||
|
||||
- [ ] DNS
|
||||
- [x] DNS Server
|
||||
- [x] FakeIP
|
||||
- [x] Host
|
||||
|
||||
### Command
|
||||
|
||||
Prerequisite
|
||||
|
||||
* docker (support Linux and macOS)
|
||||
|
||||
```
|
||||
$ go test -p 1 -v
|
||||
```
|
635
test/clash_test.go
Normal file
635
test/clash_test.go
Normal file
|
@ -0,0 +1,635 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/hub/executor"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
ImageShadowsocks = "mritd/shadowsocks:latest"
|
||||
ImageVmess = "v2fly/v2fly-core:latest"
|
||||
ImageTrojan = "trojangfw/trojan:latest"
|
||||
ImageSnell = "icpz/snell-server:latest"
|
||||
ImageXray = "teddysun/xray:latest"
|
||||
)
|
||||
|
||||
var (
|
||||
waitTime = time.Second
|
||||
localIP = net.ParseIP("127.0.0.1")
|
||||
|
||||
defaultExposedPorts = nat.PortSet{
|
||||
"10002/tcp": struct{}{},
|
||||
"10002/udp": struct{}{},
|
||||
}
|
||||
defaultPortBindings = nat.PortMap{
|
||||
"10002/tcp": []nat.PortBinding{
|
||||
{HostPort: "10002", HostIP: "0.0.0.0"},
|
||||
},
|
||||
"10002/udp": []nat.PortBinding{
|
||||
{HostPort: "10002", HostIP: "0.0.0.0"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
isDarwin = true
|
||||
}
|
||||
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
homeDir := filepath.Join(currentDir, "config")
|
||||
C.SetHomeDir(homeDir)
|
||||
|
||||
if isDarwin {
|
||||
localIP, err = defaultRouteIP()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
list, err := c.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
|
||||
}
|
||||
|
||||
images := []string{
|
||||
ImageShadowsocks,
|
||||
ImageVmess,
|
||||
ImageTrojan,
|
||||
ImageSnell,
|
||||
ImageXray,
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
if imageExist(image) {
|
||||
continue
|
||||
}
|
||||
|
||||
imageStream, err := c.ImagePull(context.Background(), image, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, imageStream)
|
||||
}
|
||||
}
|
||||
|
||||
var clean = `
|
||||
port: 0
|
||||
socks-port: 0
|
||||
mixed-port: 0
|
||||
redir-port: 0
|
||||
tproxy-port: 0
|
||||
dns:
|
||||
enable: false
|
||||
`
|
||||
|
||||
func cleanup() {
|
||||
parseAndApply(clean)
|
||||
}
|
||||
|
||||
func parseAndApply(cfgStr string) error {
|
||||
cfg, err := executor.ParseWithBytes([]byte(cfgStr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
executor.ApplyConfig(cfg, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
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 testPingPongWithSocksPort(t *testing.T, port int) {
|
||||
pingCh, pongCh, test := newPingPongPair()
|
||||
go func() {
|
||||
l, err := net.Listen("tcp", ":10001")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(c, buf); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
pingCh <- buf
|
||||
if _, err := c.Write([]byte("pong")); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
l.Close()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
if _, err := socks5.ClientHandshake(c, socks5.ParseAddr("127.0.0.1:10001"), socks5.CmdConnect, nil); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
if _, err := c.Write([]byte("ping")); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(c, buf); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
pongCh <- buf
|
||||
c.Close()
|
||||
}()
|
||||
|
||||
test(t)
|
||||
}
|
||||
|
||||
func testPingPongWithConn(t *testing.T, c net.Conn) error {
|
||||
l, err := net.Listen("tcp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.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, pc net.PacketConn) error {
|
||||
l, err := net.ListenPacket("udp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
rAddr := &net.UDPAddr{IP: localIP, Port: 10001}
|
||||
|
||||
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
|
||||
}
|
||||
}()
|
||||
|
||||
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, c net.Conn) error {
|
||||
l, err := net.Listen("tcp", ":10001")
|
||||
if err != nil {
|
||||
return 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
|
||||
}
|
||||
|
||||
go func() {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
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[:]
|
||||
}
|
||||
|
||||
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, pc net.PacketConn) error {
|
||||
l, err := net.ListenPacket("udp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
rAddr := &net.UDPAddr{IP: localIP, Port: 10001}
|
||||
|
||||
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,
|
||||
}
|
||||
}()
|
||||
|
||||
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, pc net.PacketConn) error {
|
||||
err := pc.SetReadDeadline(time.Now().Add(time.Millisecond * 300))
|
||||
assert.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 testSuit(t *testing.T, proxy C.ProxyAdapter) {
|
||||
conn, err := proxy.DialContext(context.Background(), &C.Metadata{
|
||||
Host: localIP.String(),
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypDomainName,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
assert.NoError(t, testPingPongWithConn(t, conn))
|
||||
|
||||
conn, err = proxy.DialContext(context.Background(), &C.Metadata{
|
||||
Host: localIP.String(),
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypDomainName,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
assert.NoError(t, testLargeDataWithConn(t, conn))
|
||||
|
||||
if !proxy.SupportUDP() {
|
||||
return
|
||||
}
|
||||
|
||||
pc, err := proxy.DialUDP(&C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
DstIP: localIP,
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
assert.NoError(t, testPingPongWithPacketConn(t, pc))
|
||||
|
||||
pc, err = proxy.DialUDP(&C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
DstIP: localIP,
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
assert.NoError(t, testLargeDataWithPacketConn(t, pc))
|
||||
|
||||
pc, err = proxy.DialUDP(&C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
DstIP: localIP,
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
assert.NoError(t, testPacketConnTimeout(t, pc))
|
||||
}
|
||||
|
||||
func TestClash_Basic(t *testing.T) {
|
||||
basic := `
|
||||
mixed-port: 10000
|
||||
log-level: silent
|
||||
`
|
||||
|
||||
if err := parseAndApply(basic); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testPingPongWithSocksPort(t, 10000)
|
||||
}
|
28
test/config/example.org-key.pem
Normal file
28
test/config/example.org-key.pem
Normal file
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDQ+c++LkDTdaw5
|
||||
5spCu9MWMcvVdrYBZZ5qZy7DskphSUSQp25cIu34GJXVPNxtbWx1CQCmdLlwqXvo
|
||||
PfUt5/pz9qsfhdAbzFduZQgGd7GTQOTJBDrAhm2+iVsQyGHHhF68muN+SgT+AtRE
|
||||
sJyZoHNYtjjWEIHQ++FHEDqwUVnj6Ut99LHlyfCjOZ5+WyBiKCjyMNots/gDep7R
|
||||
i4X2kMTqNMIIqPUcAaP5EQk41bJbFhKe915qN9b1dRISKFKmiWeOsxgTB/O/EaL5
|
||||
LsBYwZ/BiIMDk30aZvzRJeloasIR3z4hrKQqBfB0lfeIdiPpJIs5rXJQEiWH89ge
|
||||
gplsLbfrAgMBAAECggEBAKpMGaZzDPMF/v8Ee6lcZM2+cMyZPALxa+JsCakCvyh+
|
||||
y7hSKVY+RM0cQ+YM/djTBkJtvrDniEMuasI803PAitI7nwJGSuyMXmehP6P9oKFO
|
||||
jeLeZn6ETiSqzKJlmYE89vMeCevdqCnT5mW/wy5Smg0eGj0gIJpM2S3PJPSQpv9Z
|
||||
ots0JXkwooJcpGWzlwPkjSouY2gDbE4Coi+jmYLNjA1k5RbggcutnUCZZkJ6yMNv
|
||||
H52VjnkffpAFHRouK/YgF+5nbMyyw5YTLOyTWBq7qfBMsXynkWLU73GC/xDZa3yG
|
||||
o/Ph2knXCjgLmCRessTOObdOXedjnGWIjiqF8fVboDECgYEA6x5CteYiwthDBULZ
|
||||
CG5nE9VKkRHJYdArm+VjmGbzK51tKli112avmU4r3ol907+mEa4tWLkPqdZrrL49
|
||||
aHltuHizZJixJcw0rcI302ot/Ov0gkF9V55gnAQS/Kemvx9FHWm5NHdYvbObzj33
|
||||
bYRLJBtJWzYg9M8Bw9ZrUnegc/MCgYEA44kq5OSYCbyu3eaX8XHTtFhuQHNFjwl7
|
||||
Xk/Oel6PVZzmt+oOlDHnOfGSB/KpR3YXxFRngiiPZzbrOwFyPGe7HIfg03HAXiJh
|
||||
ivEfrPHbQqQUI/4b44GpDy6bhNtz777ivFGYEt21vpwd89rFiye+RkqF8eL/evxO
|
||||
pUayDZYvwikCgYEA07wFoZ/lkAiHmpZPsxsRcrfzFd+pto9splEWtumHdbCo3ajT
|
||||
4W5VFr9iHF8/VFDT8jokFjFaXL1/bCpKTOqFl8oC68XiSkKy8gPkmFyXm5y2LhNi
|
||||
GGTFZdr5alRkgttbN5i9M/WCkhvMZRhC2Xp43MRB9IUzeqNtWHqhXbvjYGcCgYEA
|
||||
vTMOztviLJ6PjYa0K5lp31l0+/SeD21j/y0/VPOSHi9kjeN7EfFZAw6DTkaSShDB
|
||||
fIhutYVCkSHSgfMW6XGb3gKCiW/Z9KyEDYOowicuGgDTmoYu7IOhbzVjLhtJET7Z
|
||||
zJvQZ0eiW4f3RBFTF/4JMuu+6z7FD6ADSV06qx+KQNkCgYBw26iQxmT5e/4kVv8X
|
||||
DzBJ1HuliKBnnzZA1YRjB4H8F6Yrq+9qur1Lurez4YlbkGV8yPFt+Iu82ViUWL28
|
||||
9T7Jgp3TOpf8qOqsWFv8HldpEZbE0Tcib4x6s+zOg/aw0ac/xOPY1sCVFB81VODP
|
||||
XCar+uxMBXI1zbXqd9QdEwy4Ig==
|
||||
-----END PRIVATE KEY-----
|
25
test/config/example.org.pem
Normal file
25
test/config/example.org.pem
Normal file
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIESzCCArOgAwIBAgIQIi5xRZvFZaSweWU9Y5mExjANBgkqhkiG9w0BAQsFADCB
|
||||
hzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS4wLAYDVQQLDCVkcmVh
|
||||
bWFjcm9ARHJlYW1hY3JvLmxvY2FsIChEcmVhbWFjcm8pMTUwMwYDVQQDDCxta2Nl
|
||||
cnQgZHJlYW1hY3JvQERyZWFtYWNyby5sb2NhbCAoRHJlYW1hY3JvKTAeFw0yMTAz
|
||||
MTcxNDQwMzZaFw0yMzA2MTcxNDQwMzZaMFkxJzAlBgNVBAoTHm1rY2VydCBkZXZl
|
||||
bG9wbWVudCBjZXJ0aWZpY2F0ZTEuMCwGA1UECwwlZHJlYW1hY3JvQERyZWFtYWNy
|
||||
by5sb2NhbCAoRHJlYW1hY3JvKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAND5z74uQNN1rDnmykK70xYxy9V2tgFlnmpnLsOySmFJRJCnblwi7fgYldU8
|
||||
3G1tbHUJAKZ0uXCpe+g99S3n+nP2qx+F0BvMV25lCAZ3sZNA5MkEOsCGbb6JWxDI
|
||||
YceEXrya435KBP4C1ESwnJmgc1i2ONYQgdD74UcQOrBRWePpS330seXJ8KM5nn5b
|
||||
IGIoKPIw2i2z+AN6ntGLhfaQxOo0wgio9RwBo/kRCTjVslsWEp73Xmo31vV1EhIo
|
||||
UqaJZ46zGBMH878RovkuwFjBn8GIgwOTfRpm/NEl6WhqwhHfPiGspCoF8HSV94h2
|
||||
I+kkizmtclASJYfz2B6CmWwtt+sCAwEAAaNgMF4wDgYDVR0PAQH/BAQDAgWgMBMG
|
||||
A1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFO800LQ6Pa85RH4EbMmFH6ln
|
||||
F150MBYGA1UdEQQPMA2CC2V4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQAP
|
||||
TsF53h7bvJcUXT3Y9yZ2vnW6xr9r92tNnM1Gfo3D2Yyn9oLf2YrfJng6WZ04Fhqa
|
||||
Wh0HOvE0n6yPNpm/Q7mh64DrgolZ8Ce5H4RTJDAabHU9XhEzfGSVtzRSFsz+szu1
|
||||
Y30IV+08DxxqMmNPspYdpAET2Lwyk2WhnARGiGw11CRkQCEkVEe6d702vS9UGBUz
|
||||
Du6lmCYCm0SbFrZ0CGgmHSHoTcCtf3EjVam7dPg3yWiPbWjvhXxgip6hz9sCqkhG
|
||||
WA5f+fPgSZ1I9U4i+uYnqjfrzwgC08RwUYordm15F6gPvXw+KVwDO8yUYQoEH0b6
|
||||
AFJtbzoAXDysvBC6kWYFFOr62EaisaEkELTS/NrPD9ux1eKbxcxHCwEtVjgC0CL6
|
||||
gAxEAQ+9maJMbrAFhsOBbGGFC+mMCGg4eEyx6+iMB0oQe0W7QFeRUAFi7Ptc/ocS
|
||||
tZ9lbrfX1/wrcTTWIYWE+xH6oeb4fhs29kxjHcf2l+tQzmpl0aP3Z/bMW4BSB+w=
|
||||
-----END CERTIFICATE-----
|
4
test/config/snell-http.conf
Normal file
4
test/config/snell-http.conf
Normal file
|
@ -0,0 +1,4 @@
|
|||
[snell-server]
|
||||
listen = 0.0.0.0:10002
|
||||
psk = password
|
||||
obfs = http
|
4
test/config/snell-tls.conf
Normal file
4
test/config/snell-tls.conf
Normal file
|
@ -0,0 +1,4 @@
|
|||
[snell-server]
|
||||
listen = 0.0.0.0:10002
|
||||
psk = password
|
||||
obfs = tls
|
3
test/config/snell.conf
Normal file
3
test/config/snell.conf
Normal file
|
@ -0,0 +1,3 @@
|
|||
[snell-server]
|
||||
listen = 0.0.0.0:10002
|
||||
psk = password
|
40
test/config/trojan-grpc.json
Normal file
40
test/config/trojan-grpc.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "trojan",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"password": "example",
|
||||
"email": "grpc@example.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "grpc",
|
||||
"security": "tls",
|
||||
"tlsSettings": {
|
||||
"certificates": [
|
||||
{
|
||||
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"grpcSettings": {
|
||||
"serviceName": "example"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
40
test/config/trojan.json
Normal file
40
test/config/trojan.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"run_type": "server",
|
||||
"local_addr": "0.0.0.0",
|
||||
"local_port": 10002,
|
||||
"password": [
|
||||
"password"
|
||||
],
|
||||
"log_level": 1,
|
||||
"ssl": {
|
||||
"cert": "/path/to/certificate.crt",
|
||||
"key": "/path/to/private.key",
|
||||
"key_password": "",
|
||||
"cipher": "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384",
|
||||
"cipher_tls13": "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384",
|
||||
"prefer_server_cipher": true,
|
||||
"alpn": [
|
||||
"http/1.1"
|
||||
],
|
||||
"alpn_port_override": {
|
||||
"h2": 81
|
||||
},
|
||||
"reuse_session": true,
|
||||
"session_ticket": false,
|
||||
"session_timeout": 600,
|
||||
"plain_http_response": "",
|
||||
"curves": "",
|
||||
"dhparam": ""
|
||||
},
|
||||
"tcp": {
|
||||
"prefer_ipv4": false,
|
||||
"no_delay": true,
|
||||
"keep_alive": true,
|
||||
"reuse_port": false,
|
||||
"fast_open": false,
|
||||
"fast_open_qlen": 20
|
||||
},
|
||||
"mysql": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
28
test/config/vmess-aead.json
Normal file
28
test/config/vmess-aead.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
40
test/config/vmess-grpc.json
Normal file
40
test/config/vmess-grpc.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "grpc",
|
||||
"security": "tls",
|
||||
"tlsSettings": {
|
||||
"certificates": [
|
||||
{
|
||||
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"grpcSettings": {
|
||||
"serviceName": "example"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
55
test/config/vmess-http.json
Normal file
55
test/config/vmess-http.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"tcpSettings": {
|
||||
"header": {
|
||||
"type": "http",
|
||||
"response": {
|
||||
"version": "1.1",
|
||||
"status": "200",
|
||||
"reason": "OK",
|
||||
"headers": {
|
||||
"Content-Type": [
|
||||
"application/octet-stream",
|
||||
"video/mpeg",
|
||||
"application/x-msdownload",
|
||||
"text/html",
|
||||
"application/x-shockwave-flash"
|
||||
],
|
||||
"Transfer-Encoding": [
|
||||
"chunked"
|
||||
],
|
||||
"Connection": [
|
||||
"keep-alive"
|
||||
],
|
||||
"Pragma": "no-cache"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": "none"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
43
test/config/vmess-http2.json
Normal file
43
test/config/vmess-http2.json
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "http",
|
||||
"security": "tls",
|
||||
"tlsSettings": {
|
||||
"certificates": [
|
||||
{
|
||||
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"httpSettings": {
|
||||
"host": [
|
||||
"example.org"
|
||||
],
|
||||
"path": "/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
37
test/config/vmess-tls.json
Normal file
37
test/config/vmess-tls.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "tls",
|
||||
"tlsSettings": {
|
||||
"certificates": [
|
||||
{
|
||||
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
34
test/config/vmess-ws-tls.json
Normal file
34
test/config/vmess-ws-tls.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"security": "tls",
|
||||
"tlsSettings": {
|
||||
"certificates": [
|
||||
{
|
||||
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
]
|
||||
}
|
26
test/config/vmess-ws.json
Normal file
26
test/config/vmess-ws.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"security": "none"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
]
|
||||
}
|
28
test/config/vmess.json
Normal file
28
test/config/vmess.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
103
test/dns_test.go
Normal file
103
test/dns_test.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func exchange(address, domain string, tp uint16) ([]dns.RR, error) {
|
||||
client := dns.Client{}
|
||||
query := &dns.Msg{}
|
||||
query.SetQuestion(dns.Fqdn(domain), tp)
|
||||
|
||||
r, _, err := client.Exchange(query, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.Answer, nil
|
||||
}
|
||||
|
||||
func TestClash_DNS(t *testing.T) {
|
||||
basic := `
|
||||
log-level: silent
|
||||
dns:
|
||||
enable: true
|
||||
listen: 0.0.0.0:8553
|
||||
nameserver:
|
||||
- 119.29.29.29
|
||||
`
|
||||
|
||||
if err := parseAndApply(basic); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
time.Sleep(waitTime)
|
||||
|
||||
rr, err := exchange("127.0.0.1:8553", "1.1.1.1.nip.io", dns.TypeA)
|
||||
assert.NoError(t, err)
|
||||
if !assert.NotEmpty(t, rr) {
|
||||
assert.FailNow(t, "record empty")
|
||||
}
|
||||
|
||||
record := rr[0].(*dns.A)
|
||||
assert.Equal(t, record.A.String(), "1.1.1.1")
|
||||
|
||||
rr, err = exchange("127.0.0.1:8553", "2606-4700-4700--1111.sslip.io", dns.TypeAAAA)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, rr)
|
||||
}
|
||||
|
||||
func TestClash_DNSHostAndFakeIP(t *testing.T) {
|
||||
basic := `
|
||||
log-level: silent
|
||||
hosts:
|
||||
foo.clash.dev: 1.1.1.1
|
||||
dns:
|
||||
enable: true
|
||||
listen: 0.0.0.0:8553
|
||||
ipv6: true
|
||||
enhanced-mode: fake-ip
|
||||
fake-ip-range: 198.18.0.1/16
|
||||
fake-ip-filter:
|
||||
- .sslip.io
|
||||
nameserver:
|
||||
- 119.29.29.29
|
||||
`
|
||||
|
||||
if err := parseAndApply(basic); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
time.Sleep(waitTime)
|
||||
|
||||
type domainPair struct {
|
||||
domain string
|
||||
ip string
|
||||
}
|
||||
|
||||
list := []domainPair{
|
||||
{"foo.org", "198.18.0.2"},
|
||||
{"bar.org", "198.18.0.3"},
|
||||
{"foo.org", "198.18.0.2"},
|
||||
{"foo.clash.dev", "1.1.1.1"},
|
||||
}
|
||||
|
||||
for _, pair := range list {
|
||||
rr, err := exchange("127.0.0.1:8553", pair.domain, dns.TypeA)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, rr)
|
||||
|
||||
record := rr[0].(*dns.A)
|
||||
assert.Equal(t, record.A.String(), pair.ip)
|
||||
}
|
||||
|
||||
rr, err := exchange("127.0.0.1:8553", "2606-4700-4700--1111.sslip.io", dns.TypeAAAA)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, rr)
|
||||
assert.Equal(t, rr[0].(*dns.AAAA).AAAA.String(), "2606:4700:4700::1111")
|
||||
}
|
45
test/docker_test.go
Normal file
45
test/docker_test.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
var isDarwin = false
|
||||
|
||||
func startContainer(cfg *container.Config, hostCfg *container.HostConfig, name string) (string, error) {
|
||||
c, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if !isDarwin {
|
||||
hostCfg.NetworkMode = "host"
|
||||
}
|
||||
|
||||
container, err := c.ContainerCreate(context.Background(), cfg, hostCfg, nil, nil, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = c.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return container.ID, nil
|
||||
}
|
||||
|
||||
func cleanContainer(id string) error {
|
||||
c, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
removeOpts := types.ContainerRemoveOptions{Force: true}
|
||||
return c.ContainerRemove(context.Background(), id, removeOpts)
|
||||
}
|
26
test/go.mod
Normal file
26
test/go.mod
Normal file
|
@ -0,0 +1,26 @@
|
|||
module clash-test
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/Dreamacro/clash v1.6.1-0.20210516120541-06fdd3abe0ab
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/containerd/containerd v1.4.4 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v20.10.6+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/miekg/dns v1.1.42
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // 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.1 // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/grpc v1.36.0 // indirect
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
)
|
202
test/go.sum
Normal file
202
test/go.sum
Normal file
|
@ -0,0 +1,202 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Dreamacro/clash v1.6.1-0.20210516120541-06fdd3abe0ab h1:hfjxpY+73Xo6k+JHMkLlaOR3Tw5hG/YZxmDFyV9Y6sY=
|
||||
github.com/Dreamacro/clash v1.6.1-0.20210516120541-06fdd3abe0ab/go.mod h1:Pz9GwWR244jrMSInQ0KUL02jMCe5xIJ+pkgzuNIy2Iw=
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=
|
||||
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/containerd/containerd v1.4.4 h1:rtRG4N6Ct7GNssATwgpvMGfnjnwfjnu/Zs9W3Ikzq+M=
|
||||
github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
|
||||
github.com/docker/docker v20.10.6+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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-chi/chi/v5 v5.0.3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/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.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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY=
|
||||
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
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.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
||||
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
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-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo=
|
||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
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/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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
121
test/snell_test.go
Normal file
121
test/snell_test.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClash_SnellObfsHTTP(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageSnell,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
Cmd: []string{"-c", "/config.conf"},
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{fmt.Sprintf("%s:/config.conf", C.Path.Resolve("snell-http.conf"))},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "snell-http")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewSnell(outbound.SnellOption{
|
||||
Name: "snell",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Psk: "password",
|
||||
ObfsOpts: map[string]interface{}{
|
||||
"mode": "http",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_SnellObfsTLS(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageSnell,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
Cmd: []string{"-c", "/config.conf"},
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{fmt.Sprintf("%s:/config.conf", C.Path.Resolve("snell-tls.conf"))},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "snell-tls")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewSnell(outbound.SnellOption{
|
||||
Name: "snell",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Psk: "password",
|
||||
ObfsOpts: map[string]interface{}{
|
||||
"mode": "tls",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_Snell(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageSnell,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
Cmd: []string{"-c", "/config.conf"},
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{fmt.Sprintf("%s:/config.conf", C.Path.Resolve("snell.conf"))},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "snell")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewSnell(outbound.SnellOption{
|
||||
Name: "snell",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Psk: "password",
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
172
test/ss_test.go
Normal file
172
test/ss_test.go
Normal file
|
@ -0,0 +1,172 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClash_Shadowsocks(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageShadowsocks,
|
||||
Env: []string{"SS_MODULE=ss-server", "SS_CONFIG=-s 0.0.0.0 -u -v -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL"},
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "ss")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{
|
||||
Name: "ss",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Password: "FzcLbKs2dY9mhL",
|
||||
Cipher: "chacha20-ietf-poly1305",
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_ShadowsocksObfsHTTP(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageShadowsocks,
|
||||
Env: []string{
|
||||
"SS_MODULE=ss-server",
|
||||
"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL --plugin obfs-server --plugin-opts obfs=http",
|
||||
},
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "ss-obfs-http")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{
|
||||
Name: "ss",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Password: "FzcLbKs2dY9mhL",
|
||||
Cipher: "chacha20-ietf-poly1305",
|
||||
UDP: true,
|
||||
Plugin: "obfs",
|
||||
PluginOpts: map[string]interface{}{
|
||||
"mode": "http",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_ShadowsocksObfsTLS(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageShadowsocks,
|
||||
Env: []string{
|
||||
"SS_MODULE=ss-server",
|
||||
"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL --plugin obfs-server --plugin-opts obfs=tls",
|
||||
},
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "ss-obfs-tls")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{
|
||||
Name: "ss",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Password: "FzcLbKs2dY9mhL",
|
||||
Cipher: "chacha20-ietf-poly1305",
|
||||
UDP: true,
|
||||
Plugin: "obfs",
|
||||
PluginOpts: map[string]interface{}{
|
||||
"mode": "tls",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_ShadowsocksV2RayPlugin(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageShadowsocks,
|
||||
Env: []string{
|
||||
"SS_MODULE=ss-server",
|
||||
"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL --plugin v2ray-plugin --plugin-opts=server",
|
||||
},
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "ss-v2ray-plugin")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{
|
||||
Name: "ss",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Password: "FzcLbKs2dY9mhL",
|
||||
Cipher: "chacha20-ietf-poly1305",
|
||||
UDP: true,
|
||||
Plugin: "v2ray-plugin",
|
||||
PluginOpts: map[string]interface{}{
|
||||
"mode": "websocket",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
94
test/trojan_test.go
Normal file
94
test/trojan_test.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClash_Trojan(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageTrojan,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/config/config.json", C.Path.Resolve("trojan.json")),
|
||||
fmt.Sprintf("%s:/path/to/certificate.crt", C.Path.Resolve("example.org.pem")),
|
||||
fmt.Sprintf("%s:/path/to/private.key", C.Path.Resolve("example.org-key.pem")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "trojan")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewTrojan(outbound.TrojanOption{
|
||||
Name: "trojan",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Password: "password",
|
||||
SNI: "example.org",
|
||||
SkipCertVerify: true,
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_TrojanGrpc(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageXray,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/xray/config.json", C.Path.Resolve("trojan-grpc.json")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "trojan-grpc")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewTrojan(outbound.TrojanOption{
|
||||
Name: "trojan",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
Password: "example",
|
||||
SNI: "example.org",
|
||||
SkipCertVerify: true,
|
||||
UDP: true,
|
||||
Network: "grpc",
|
||||
GrpcOpts: outbound.GrpcOptions{
|
||||
GrpcServiceName: "example",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
70
test/util_test.go
Normal file
70
test/util_test.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/net/route"
|
||||
)
|
||||
|
||||
func defaultRouteIP() (net.IP, error) {
|
||||
idx, err := defaultRouteInterfaceIndex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iface, err := net.InterfaceByIndex(idx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ip := addr.(*net.IPNet).IP
|
||||
if ip.To4() != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, 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")
|
||||
}
|
347
test/vmess_test.go
Normal file
347
test/vmess_test.go
Normal file
|
@ -0,0 +1,347 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClash_Vmess(t *testing.T) {
|
||||
configPath := C.Path.Resolve("vmess.json")
|
||||
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{fmt.Sprintf("%s:/etc/v2ray/config.json", configPath)},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessAEAD(t *testing.T) {
|
||||
configPath := C.Path.Resolve("vmess-aead.json")
|
||||
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{fmt.Sprintf("%s:/etc/v2ray/config.json", configPath)},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-aead")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanContainer(id)
|
||||
})
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 0,
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessTLS(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vmess-tls.json")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-tls")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
TLS: true,
|
||||
SkipCertVerify: true,
|
||||
ServerName: "example.org",
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessHTTP2(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vmess-http2.json")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-http2")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
Network: "h2",
|
||||
TLS: true,
|
||||
SkipCertVerify: true,
|
||||
ServerName: "example.org",
|
||||
UDP: true,
|
||||
HTTP2Opts: outbound.HTTP2Options{
|
||||
Host: []string{"example.org"},
|
||||
Path: "/test",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessHTTP(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vmess-http.json")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-http")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
Network: "http",
|
||||
UDP: true,
|
||||
HTTPOpts: outbound.HTTPOptions{
|
||||
Method: "GET",
|
||||
Path: []string{"/"},
|
||||
Headers: map[string][]string{
|
||||
"Host": {"www.amazon.com"},
|
||||
"User-Agent": {
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.49",
|
||||
},
|
||||
"Accept-Encoding": {
|
||||
"gzip, deflate",
|
||||
},
|
||||
"Connection": {
|
||||
"keep-alive",
|
||||
},
|
||||
"Pragma": {"no-cache"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessWebsocket(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vmess-ws.json")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-ws")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
Network: "ws",
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessWebsocketTLS(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageVmess,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vmess-ws-tls.json")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-ws")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
Network: "ws",
|
||||
TLS: true,
|
||||
SkipCertVerify: true,
|
||||
UDP: true,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
||||
|
||||
func TestClash_VmessGrpc(t *testing.T) {
|
||||
cfg := &container.Config{
|
||||
Image: ImageXray,
|
||||
ExposedPorts: defaultExposedPorts,
|
||||
}
|
||||
hostCfg := &container.HostConfig{
|
||||
PortBindings: defaultPortBindings,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:/etc/xray/config.json", C.Path.Resolve("vmess-grpc.json")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")),
|
||||
fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")),
|
||||
},
|
||||
}
|
||||
|
||||
id, err := startContainer(cfg, hostCfg, "vmess-grpc")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanContainer(id)
|
||||
|
||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||
Name: "vmess",
|
||||
Server: localIP.String(),
|
||||
Port: 10002,
|
||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
Cipher: "auto",
|
||||
AlterID: 32,
|
||||
Network: "grpc",
|
||||
TLS: true,
|
||||
SkipCertVerify: true,
|
||||
UDP: true,
|
||||
ServerName: "example.org",
|
||||
GrpcOpts: outbound.GrpcOptions{
|
||||
GrpcServiceName: "example",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testSuit(t, proxy)
|
||||
}
|
Loading…
Reference in a new issue