Feature: add protocol test

This commit is contained in:
Dreamacro 2021-05-17 20:33:00 +08:00
parent 06fdd3abe0
commit d5e52bed43
26 changed files with 2299 additions and 0 deletions

49
test/README.md Normal file
View 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
View 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)
}

View 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-----

View 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-----

View file

@ -0,0 +1,4 @@
[snell-server]
listen = 0.0.0.0:10002
psk = password
obfs = http

View file

@ -0,0 +1,4 @@
[snell-server]
listen = 0.0.0.0:10002
psk = password
obfs = tls

3
test/config/snell.conf Normal file
View file

@ -0,0 +1,3 @@
[snell-server]
listen = 0.0.0.0:10002
psk = password

View 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
View 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
}
}

View 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"
}
}

View 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"
}
}

View 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"
}
}

View 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"
}
}

View 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"
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}