diff --git a/common/common.go b/common/common.go index d747f1a..c88bd3d 100644 --- a/common/common.go +++ b/common/common.go @@ -11,7 +11,7 @@ import ( ) const ( - Version = "v0.6.1" + Version = "v0.7.0" ) type Runnable interface { diff --git a/docs/config.toml b/docs/config.toml index 1779817..a047a9d 100755 --- a/docs/config.toml +++ b/docs/config.toml @@ -16,7 +16,7 @@ enableMissingTranslationPlaceholders = false # Source Code repository section description = "An unidentifiable mechanism that helps you bypass GFW. " github_repository = "https://github.com/p4gefau1t/trojan-go" - version = "0.6.1" + version = "0.7.0" # Documentation repository section # documentation repository (set edit link to documentation repository) diff --git a/go.mod b/go.mod index 6ec1f98..0eec3b5 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang/protobuf v1.3.2 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/refraction-networking/utls v0.0.0-20200601200209-ada0bb9b38a0 + github.com/shadowsocks/go-shadowsocks2 v0.1.0 github.com/smartystreets/goconvey v1.6.4 github.com/xtaci/smux v1.5.15-0.20200523091831-637399ad4398 golang.org/x/crypto v0.0.0-20200602180216-279210d13fed diff --git a/go.sum b/go.sum index 1702f25..8ac5e7f 100644 --- a/go.sum +++ b/go.sum @@ -2,14 +2,19 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4= github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 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/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -20,12 +25,15 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/miekg/dns v1.1.4 h1:rCMZsU2ScVSYcAsOXgmC6+AKOK+6pmQTOcw03nfwYV0= github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= github.com/refraction-networking/utls v0.0.0-20200601200209-ada0bb9b38a0 h1:vIkvetWOJZSADSKCF9MLTsQNW2httdBmYz47dQQteP8= github.com/refraction-networking/utls v0.0.0-20200601200209-ada0bb9b38a0/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= +github.com/shadowsocks/go-shadowsocks2 v0.1.0 h1:jQhkjAmMuOTQ7B04bnrRJ5IAoZEwoaXXkKspE7rQ6ck= +github.com/shadowsocks/go-shadowsocks2 v0.1.0/go.mod h1:/0aFGbhK8mtOX4J/6kTJsPLZlEs9KnzKoWCOCvjd7vk= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -37,6 +45,7 @@ github.com/xtaci/smux v1.5.15-0.20200523091831-637399ad4398/go.mod h1:OMlQbT5vcg go.starlark.net v0.0.0-20190919145610-979af19b165c h1:WR7X1xgXJlXhQBdorVc9Db3RhwG+J/kp6bLuMyJjfVw= go.starlark.net v0.0.0-20190919145610-979af19b165c/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed h1:g4KENRiCMEx58Q7/ecwfT0N2o8z35Fnbsjig/Alf2T4= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -45,11 +54,13 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -58,7 +69,9 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= diff --git a/proxy/client/client.go b/proxy/client/client.go index 3ffc825..cc418c4 100644 --- a/proxy/client/client.go +++ b/proxy/client/client.go @@ -7,6 +7,7 @@ import ( "github.com/p4gefau1t/trojan-go/tunnel" "github.com/p4gefau1t/trojan-go/tunnel/mux" "github.com/p4gefau1t/trojan-go/tunnel/router" + "github.com/p4gefau1t/trojan-go/tunnel/shadowsocks" "github.com/p4gefau1t/trojan-go/tunnel/simplesocks" "github.com/p4gefau1t/trojan-go/tunnel/socks" "github.com/p4gefau1t/trojan-go/tunnel/transport" @@ -17,16 +18,19 @@ import ( const Name = "CLIENT" // GenerateClientTree generate general outbound protocol stack -func GenerateClientTree(isMux bool, isWebsocket bool, isRouter bool) []string { +func GenerateClientTree(muxEnabled bool, wsEnabled bool, ssEnabled bool, routerEnabled bool) []string { clientStack := []string{transport.Name} - if isWebsocket { + if wsEnabled { clientStack = append(clientStack, websocket.Name) } + if ssEnabled { + clientStack = append(clientStack, shadowsocks.Name) + } clientStack = append(clientStack, trojan.Name) - if isMux { + if muxEnabled { clientStack = append(clientStack, []string{mux.Name, simplesocks.Name}...) } - if isRouter { + if routerEnabled { clientStack = append(clientStack, router.Name) } return clientStack @@ -36,7 +40,7 @@ func init() { proxy.RegisterProxyCreator(Name, func(ctx context.Context) (*proxy.Proxy, error) { cfg := config.FromContext(ctx, Name).(*Config) serverStack := []string{socks.Name} - clientStack := GenerateClientTree(cfg.Mux.Enabled, cfg.Websocket.Enabled, cfg.Router.Enabled) + clientStack := GenerateClientTree(cfg.Mux.Enabled, cfg.Websocket.Enabled, cfg.Shadowsocks.Enabled, cfg.Router.Enabled) c, err := proxy.CreateClientStack(ctx, clientStack) if err != nil { return nil, err diff --git a/proxy/client/config.go b/proxy/client/config.go index 7c29e11..edaa14b 100644 --- a/proxy/client/config.go +++ b/proxy/client/config.go @@ -14,10 +14,15 @@ type RouterConfig struct { Enabled bool `json,yaml:"enabled"` } +type ShadowsocksConfig struct { + Enabled bool `json,yaml:"enabled"` +} + type Config struct { - Mux MuxConfig `json,yaml:"mux"` - Websocket WebsocketConfig `json,yaml:"websocket"` - Router RouterConfig `json,yaml:"router"` + Mux MuxConfig `json,yaml:"mux"` + Websocket WebsocketConfig `json,yaml:"websocket"` + Router RouterConfig `json,yaml:"router"` + Shadowsocks ShadowsocksConfig `json,yaml:"shadowsocks"` } func init() { diff --git a/proxy/forward/forward.go b/proxy/forward/forward.go index aecd621..28cfb82 100644 --- a/proxy/forward/forward.go +++ b/proxy/forward/forward.go @@ -15,7 +15,7 @@ func init() { proxy.RegisterProxyCreator(Name, func(ctx context.Context) (*proxy.Proxy, error) { cfg := config.FromContext(ctx, Name).(*client.Config) serverStack := []string{dokodemo.Name} - clientStack := client.GenerateClientTree(cfg.Mux.Enabled, cfg.Websocket.Enabled, cfg.Router.Enabled) + clientStack := client.GenerateClientTree(cfg.Mux.Enabled, cfg.Websocket.Enabled, cfg.Shadowsocks.Enabled, cfg.Router.Enabled) c, err := proxy.CreateClientStack(ctx, clientStack) if err != nil { return nil, err diff --git a/proxy/nat/nat.go b/proxy/nat/nat.go index ee2828f..4c3e067 100644 --- a/proxy/nat/nat.go +++ b/proxy/nat/nat.go @@ -1,3 +1,5 @@ +// +build linux darwin + package nat import ( @@ -15,7 +17,7 @@ func init() { proxy.RegisterProxyCreator(Name, func(ctx context.Context) (*proxy.Proxy, error) { cfg := config.FromContext(ctx, Name).(*client.Config) serverStack := []string{tproxy.Name} - clientStack := client.GenerateClientTree(cfg.Mux.Enabled, cfg.Websocket.Enabled, false) + clientStack := client.GenerateClientTree(cfg.Mux.Enabled, cfg.Websocket.Enabled, cfg.Shadowsocks.Enabled, false) c, err := proxy.CreateClientStack(ctx, clientStack) if err != nil { return nil, err diff --git a/proxy/option.go b/proxy/option.go index 77af976..ea516e4 100644 --- a/proxy/option.go +++ b/proxy/option.go @@ -21,16 +21,21 @@ func (o *Option) Handle() error { if err != nil { log.Fatal(err) } + isJSON := false if strings.HasSuffix(*o.path, ".json") { - if err := RunProxy(data, true); err != nil { - log.Fatal(err) - } + isJSON = true } else if strings.HasSuffix(*o.path, ".yaml") { - if err := RunProxy(data, false); err != nil { - log.Fatal(err) - } + isJSON = false } else { - log.Fatal("unknown file suffix", *o.path) + log.Fatal("unsupported filename suffix", *o.path, ". use .yaml or .json instead.") + } + proxy, err := NewProxyFromConfigData(data, isJSON) + if err != nil { + log.Fatal(err) + } + err = proxy.Run() + if err != nil { + log.Fatal(err) } return nil } diff --git a/proxy/proxy.go b/proxy/proxy.go index 2931b41..ed99f9c 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -154,31 +154,24 @@ func RegisterProxyCreator(name string, creator Creator) { creators[name] = creator } -func RunProxy(data []byte, isJSON bool) error { +func NewProxyFromConfigData(data []byte, isJSON bool) (*Proxy, error) { ctx := context.Background() var err error if isJSON { ctx, err = config.WithJSONConfig(context.Background(), data) if err != nil { - return err + return nil, err } } else { ctx, err = config.WithYAMLConfig(context.Background(), data) if err != nil { - return err + return nil, err } } cfg := config.FromContext(ctx, Name).(*Config) create, ok := creators[strings.ToUpper(cfg.RunType)] if !ok { - return common.NewError("unknown type " + cfg.RunType) + return nil, common.NewError("unknown proxy type: " + cfg.RunType) } - proxy, err := create(ctx) - if err != nil { - return common.NewError("failed to create proxy instance").Base(err) - } - if err := proxy.Run(); err != nil { - log.Fatal(err) - } - return nil + return create(ctx) } diff --git a/proxy/server/config.go b/proxy/server/config.go new file mode 100644 index 0000000..33b7622 --- /dev/null +++ b/proxy/server/config.go @@ -0,0 +1,12 @@ +package server + +import ( + "github.com/p4gefau1t/trojan-go/config" + "github.com/p4gefau1t/trojan-go/proxy/client" +) + +func init() { + config.RegisterConfigCreator(Name, func() interface{} { + return new(client.Config) + }) +} diff --git a/proxy/server/server.go b/proxy/server/server.go index b33e858..7219010 100644 --- a/proxy/server/server.go +++ b/proxy/server/server.go @@ -2,9 +2,12 @@ package server import ( "context" + "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/proxy" + "github.com/p4gefau1t/trojan-go/proxy/client" "github.com/p4gefau1t/trojan-go/tunnel/mux" "github.com/p4gefau1t/trojan-go/tunnel/raw" + "github.com/p4gefau1t/trojan-go/tunnel/shadowsocks" "github.com/p4gefau1t/trojan-go/tunnel/simplesocks" "github.com/p4gefau1t/trojan-go/tunnel/transport" "github.com/p4gefau1t/trojan-go/tunnel/trojan" @@ -16,53 +19,38 @@ const Name = "SERVER" func init() { proxy.RegisterProxyCreator(Name, func(ctx context.Context) (*proxy.Proxy, error) { clientStack := []string{raw.Name} - serverTree := &proxy.Node{ - Name: transport.Name, - Next: []*proxy.Node{ - { - Name: trojan.Name, - IsEndpoint: true, - Next: []*proxy.Node{ - { - Name: mux.Name, - Next: []*proxy.Node{ - { - Name: simplesocks.Name, - }, - }, - }, - }, - }, - { - Name: websocket.Name, - Next: []*proxy.Node{ - { - Name: trojan.Name, - IsEndpoint: true, - Next: []*proxy.Node{ - { - Name: mux.Name, - Next: []*proxy.Node{ - { - Name: simplesocks.Name, - }, - }, - }, - }, - }, - }, - }, - }, - } - c, err := proxy.CreateClientStack(ctx, clientStack) + cfg := config.FromContext(ctx, Name).(*client.Config) + s, err := transport.NewServer(ctx, nil) if err != nil { return nil, err } - s, err := proxy.CreateServersStacksTree(ctx, serverTree) + root := &proxy.Node{ + Name: transport.Name, + Next: make(map[string]*proxy.Node), + IsEndpoint: false, + Context: ctx, + Server: s, + } + + root.BuildNext(trojan.Name).BuildNext(mux.Name).BuildNext(simplesocks.Name).IsEndpoint = true + root.BuildNext(trojan.Name).IsEndpoint = true + + wsSubTree := root.BuildNext(websocket.Name) + if cfg.Shadowsocks.Enabled { + wsSubTree = wsSubTree.BuildNext(shadowsocks.Name) + } + wsSubTree.BuildNext(trojan.Name).BuildNext(mux.Name).BuildNext(simplesocks.Name).IsEndpoint = true + wsSubTree.BuildNext(trojan.Name).IsEndpoint = true + + serverList := proxy.FindAllEndpoints(root) + clientList, err := proxy.CreateClientStack(ctx, clientStack) if err != nil { return nil, err } - return proxy.NewProxy(ctx, s, c), nil + if err != nil { + return nil, err + } + return proxy.NewProxy(ctx, serverList, clientList), nil }) } diff --git a/proxy/stack.go b/proxy/stack.go index 69fccf0..f777880 100644 --- a/proxy/stack.go +++ b/proxy/stack.go @@ -2,16 +2,48 @@ package proxy import ( "context" + "github.com/p4gefau1t/trojan-go/log" "github.com/p4gefau1t/trojan-go/tunnel" ) type Node struct { Name string - Next []*Node + Next map[string]*Node IsEndpoint bool + context.Context tunnel.Server } +func (n *Node) BuildNext(name string) *Node { + if next, found := n.Next[name]; found { + return next + } + t, err := tunnel.GetTunnel(name) + if err != nil { + log.Fatal(err) + } + s, err := t.NewServer(n.Context, n.Server) + newNode := &Node{ + Name: name, + Next: make(map[string]*Node), + Context: n.Context, + Server: s, + } + n.Next[name] = newNode + return newNode +} + +func FindAllEndpoints(root *Node) []tunnel.Server { + list := make([]tunnel.Server, 0) + if root.IsEndpoint || len(root.Next) == 0 { + list = append(list, root.Server) + } + for _, next := range root.Next { + list = append(list, FindAllEndpoints(next)...) + } + return list +} + func buildServerStacksTree(ctx context.Context, current *Node, parent *Node) ([]tunnel.Server, error) { t, err := tunnel.GetTunnel(current.Name) if err != nil { diff --git a/test/scenario/proxy_test.go b/test/scenario/proxy_test.go index 984e181..634c37e 100644 --- a/test/scenario/proxy_test.go +++ b/test/scenario/proxy_test.go @@ -20,7 +20,7 @@ import ( netproxy "golang.org/x/net/proxy" ) -var cert string = ` +var cert = ` -----BEGIN CERTIFICATE----- MIIDZTCCAk0CFFphZh018B5iAD9F5fV4y0AlD0LxMA0GCSqGSIb3DQEBCwUAMG8x CzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARNYXJzMRMwEQYDVQQHDAppVHJhbnN3YXJw @@ -44,7 +44,7 @@ Haz8uKI4EciU -----END CERTIFICATE----- ` -var key string = ` +var key = ` -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAml44fThYMkCcT627o7ibEs7mq2WOhImjDwYijYJ1684BatrC sHJNcw8PJGTuP+tgGdngmALjA3l+RipjaE/UK4FJrAjruphA/hOCjZfWqk8KBR4q @@ -98,12 +98,17 @@ websocket: enabled: true path: /ws hostname: 127.0.0.1 +shadowsocks: + enabled: true + method: AEAD_CHACHA20_POLY1305 + password: 12345678 mux: enabled: true `, socksPort, serverPort) go func() { - err := proxy.RunProxy([]byte(clientData), false) + proxy, err := proxy.NewProxyFromConfigData([]byte(clientData), false) common.Must(err) + common.Must(proxy.Run()) }() serverData := fmt.Sprintf(` @@ -118,15 +123,20 @@ ssl: verify-hostname: false key: server.key cert: server.crt - sni: "localhost" + sni: localhost +shadowsocks: + enabled: true + method: AEAD_CHACHA20_POLY1305 + password: 12345678 websocket: enabled: true path: /ws hostname: 127.0.0.1 `, serverPort, util.HTTPPort) go func() { - err := proxy.RunProxy([]byte(serverData), false) + proxy, err := proxy.NewProxyFromConfigData([]byte(serverData), false) common.Must(err) + common.Must(proxy.Run()) }() time.Sleep(time.Second * 2) @@ -168,12 +178,17 @@ websocket: enabled: true path: /ws hostname: 127.0.0.1 +shadowsocks: + enabled: true + method: AEAD_CHACHA20_POLY1305 + password: 12345678 mux: enabled: true `, clientPort, serverPort, targetPort) go func() { - err := proxy.RunProxy([]byte(clientData), false) + proxy, err := proxy.NewProxyFromConfigData([]byte(clientData), false) common.Must(err) + common.Must(proxy.Run()) }() serverData := fmt.Sprintf(` @@ -193,10 +208,15 @@ websocket: enabled: true path: /ws hostname: 127.0.0.1 +shadowsocks: + enabled: true + method: AEAD_CHACHA20_POLY1305 + password: 12345678 `, serverPort, util.HTTPPort) go func() { - err := proxy.RunProxy([]byte(serverData), false) + proxy, err := proxy.NewProxyFromConfigData([]byte(serverData), false) common.Must(err) + common.Must(proxy.Run()) }() time.Sleep(time.Second * 2) diff --git a/tunnel/mux/client.go b/tunnel/mux/client.go index 4b1c4aa..e0f303f 100644 --- a/tunnel/mux/client.go +++ b/tunnel/mux/client.go @@ -94,7 +94,7 @@ func (c *Client) newMuxClient() (*smuxClientInfo, error) { // The mutex should be locked when this function is called id := generateMuxID() if _, found := c.clientPool[id]; found { - return nil, common.NewError("Duplicated id") + return nil, common.NewError("duplicated id") } fakeAddr := &tunnel.Address{ @@ -129,7 +129,7 @@ func (c *Client) DialConn(addr *tunnel.Address, _ tunnel.Tunnel) (tunnel.Conn, e rwc, err := info.client.Open() info.lastActiveTime = time.Now() if err != nil { - return nil, err + return nil, common.NewError("mux failed to open stream from client").Base(err) } return &Conn{ rwc: rwc, diff --git a/tunnel/router/client.go b/tunnel/router/client.go index c7e469c..5794122 100644 --- a/tunnel/router/client.go +++ b/tunnel/router/client.go @@ -197,6 +197,7 @@ func (c *Client) DialPacket(overlay tunnel.Tunnel) (tunnel.PacketConn, error) { } ctx, cancel := context.WithCancel(c.Context) return &PacketConn{ + Client: c, PacketConn: direct, proxy: proxy, CancelFunc: cancel, diff --git a/tunnel/router/router_test.go b/tunnel/router/router_test.go index 18b6850..eceb6aa 100644 --- a/tunnel/router/router_test.go +++ b/tunnel/router/router_test.go @@ -6,9 +6,11 @@ import ( "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/test/util" "github.com/p4gefau1t/trojan-go/tunnel" + "net" "strconv" "strings" "testing" + "time" ) type MockClient struct{} @@ -18,13 +20,53 @@ func (m *MockClient) DialConn(address *tunnel.Address, t tunnel.Tunnel) (tunnel. } func (m *MockClient) DialPacket(t tunnel.Tunnel) (tunnel.PacketConn, error) { - return nil, common.NewError("mockproxy") + //return nil, common.NewError("mockproxy") + return MockPacketConn{}, nil } func (m MockClient) Close() error { return nil } +type MockPacketConn struct { +} + +func (m MockPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + panic("implement me") +} + +func (m MockPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + panic("implement me") +} + +func (m MockPacketConn) Close() error { + panic("implement me") +} + +func (m MockPacketConn) LocalAddr() net.Addr { + panic("implement me") +} + +func (m MockPacketConn) SetDeadline(t time.Time) error { + panic("implement me") +} + +func (m MockPacketConn) SetReadDeadline(t time.Time) error { + panic("implement me") +} + +func (m MockPacketConn) SetWriteDeadline(t time.Time) error { + panic("implement me") +} + +func (m MockPacketConn) WriteWithMetadata(bytes []byte, metadata *tunnel.Metadata) (int, error) { + return 0, common.NewError("mockproxy") +} + +func (m MockPacketConn) ReadWithMetadata(bytes []byte) (int, *tunnel.Metadata, error) { + return 0, nil, common.NewError("mockproxy") +} + func TestRouter(t *testing.T) { data := ` router: @@ -90,4 +132,18 @@ router: if err != nil { t.Fail() } + + packet, err := client.DialPacket(nil) + common.Must(err) + buf := [10]byte{} + _, err = packet.WriteWithMetadata(buf[:], &tunnel.Metadata{ + Address: &tunnel.Address{ + AddressType: tunnel.DomainName, + DomainName: "proxyfull", + Port: port, + }, + }) + if err.Error() != "mockproxy" { + t.Fail() + } } diff --git a/tunnel/shadowsocks/client.go b/tunnel/shadowsocks/client.go new file mode 100644 index 0000000..8f4aa66 --- /dev/null +++ b/tunnel/shadowsocks/client.go @@ -0,0 +1,47 @@ +package shadowsocks + +import ( + "context" + "github.com/p4gefau1t/trojan-go/common" + "github.com/p4gefau1t/trojan-go/config" + "github.com/p4gefau1t/trojan-go/log" + "github.com/p4gefau1t/trojan-go/tunnel" + "github.com/p4gefau1t/trojan-go/tunnel/transport" + "github.com/shadowsocks/go-shadowsocks2/core" +) + +type Client struct { + underlay tunnel.Client + core.Cipher +} + +func (c *Client) DialConn(address *tunnel.Address, tunnel tunnel.Tunnel) (tunnel.Conn, error) { + conn, err := c.underlay.DialConn(address, tunnel) + if err != nil { + return nil, err + } + return &transport.Conn{ + Conn: c.Cipher.StreamConn(conn), + }, nil +} + +func (c *Client) DialPacket(tunnel tunnel.Tunnel) (tunnel.PacketConn, error) { + panic("not supported") +} + +func (c *Client) Close() error { + return c.underlay.Close() +} + +func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, error) { + cfg := config.FromContext(ctx, Name).(*Config) + cipher, err := core.PickCipher(cfg.Shadowsocks.Method, nil, cfg.Shadowsocks.Password) + if err != nil { + return nil, common.NewError("invalid shadowsocks cipher").Base(err) + } + log.Info("shadowsocks client created") + return &Client{ + underlay: underlay, + Cipher: cipher, + }, nil +} diff --git a/tunnel/shadowsocks/config.go b/tunnel/shadowsocks/config.go new file mode 100644 index 0000000..ab6a031 --- /dev/null +++ b/tunnel/shadowsocks/config.go @@ -0,0 +1,19 @@ +package shadowsocks + +import "github.com/p4gefau1t/trojan-go/config" + +type ShadowsocksConfig struct { + Enabled bool `json,yaml:"enabled"` + Method string `json,yaml:"method"` + Password string `json,yaml:"password"` +} + +type Config struct { + Shadowsocks ShadowsocksConfig `json,yaml:"shadowsocks"` +} + +func init() { + config.RegisterConfigCreator(Name, func() interface{} { + return new(Config) + }) +} diff --git a/tunnel/shadowsocks/server.go b/tunnel/shadowsocks/server.go new file mode 100644 index 0000000..56df932 --- /dev/null +++ b/tunnel/shadowsocks/server.go @@ -0,0 +1,47 @@ +package shadowsocks + +import ( + "context" + "github.com/p4gefau1t/trojan-go/common" + "github.com/p4gefau1t/trojan-go/config" + "github.com/p4gefau1t/trojan-go/log" + "github.com/p4gefau1t/trojan-go/tunnel" + "github.com/p4gefau1t/trojan-go/tunnel/transport" + "github.com/shadowsocks/go-shadowsocks2/core" +) + +type Server struct { + underlay tunnel.Server + core.Cipher +} + +func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) { + conn, err := s.underlay.AcceptConn(overlay) + if err != nil { + return nil, common.NewError("shadowsocks failed to accept connection from underlying tunnel") + } + return &transport.Conn{ + Conn: s.Cipher.StreamConn(conn), + }, nil +} + +func (s *Server) AcceptPacket(t tunnel.Tunnel) (tunnel.PacketConn, error) { + panic("not supported") +} + +func (s *Server) Close() error { + return s.underlay.Close() +} + +func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, error) { + cfg := config.FromContext(ctx, Name).(*Config) + cipher, err := core.PickCipher(cfg.Shadowsocks.Method, nil, cfg.Shadowsocks.Password) + if err != nil { + return nil, common.NewError("invalid shadowsocks cipher").Base(err) + } + log.Info("shadowsocks client created") + return &Server{ + underlay: underlay, + Cipher: cipher, + }, nil +} diff --git a/tunnel/shadowsocks/shadowsocks_test.go b/tunnel/shadowsocks/shadowsocks_test.go new file mode 100644 index 0000000..8aecb64 --- /dev/null +++ b/tunnel/shadowsocks/shadowsocks_test.go @@ -0,0 +1,43 @@ +package shadowsocks + +import ( + "context" + "github.com/p4gefau1t/trojan-go/common" + "github.com/p4gefau1t/trojan-go/config" + "github.com/p4gefau1t/trojan-go/test/util" + "github.com/p4gefau1t/trojan-go/tunnel" + "github.com/p4gefau1t/trojan-go/tunnel/raw" + "net" + "testing" +) + +func TestShadowsocks(t *testing.T) { + cfg := &Config{ + Shadowsocks: ShadowsocksConfig{ + Enabled: true, + Method: "AES-128-GCM", + Password: "password", + }, + } + ctx := config.WithConfig(context.Background(), Name, cfg) + port := common.PickPort("tcp", "127.0.0.1") + addr := &tunnel.Address{ + AddressType: tunnel.IPv4, + IP: net.ParseIP("127.0.0.1"), + Port: port, + } + tcpServer, err := raw.NewServer(addr) + common.Must(err) + tcpClient := &raw.FixedClient{ + FixedAddr: addr, + } + c, err := NewClient(ctx, tcpClient) + common.Must(err) + s, err := NewServer(ctx, tcpServer) + common.Must(err) + conn1, err := c.DialConn(nil, nil) + common.Must(err) + conn2, err := s.AcceptConn(nil) + common.Must(err) + util.CheckConn(conn1, conn2) +} diff --git a/tunnel/shadowsocks/tunnel.go b/tunnel/shadowsocks/tunnel.go index 4b35346..6545dec 100644 --- a/tunnel/shadowsocks/tunnel.go +++ b/tunnel/shadowsocks/tunnel.go @@ -1 +1,26 @@ package shadowsocks + +import ( + "context" + "github.com/p4gefau1t/trojan-go/tunnel" +) + +const Name = "SHADOWSOCKS" + +type Tunnel struct{} + +func (t *Tunnel) Name() string { + return Name +} + +func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) (tunnel.Client, error) { + return NewClient(ctx, client) +} + +func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) (tunnel.Server, error) { + return NewServer(ctx, server) +} + +func init() { + tunnel.RegisterTunnel(Name, &Tunnel{}) +} diff --git a/tunnel/tproxy/tproxy.go b/tunnel/tproxy/tproxy.go index 01e67a5..3488a6e 100644 --- a/tunnel/tproxy/tproxy.go +++ b/tunnel/tproxy/tproxy.go @@ -1,4 +1,5 @@ // +build linux darwin + package tproxy import ( diff --git a/tunnel/transport/conn.go b/tunnel/transport/conn.go index 02dbc1c..d1a6fe4 100644 --- a/tunnel/transport/conn.go +++ b/tunnel/transport/conn.go @@ -6,12 +6,10 @@ import ( "github.com/p4gefau1t/trojan-go/tunnel" ) -// Conn is the TLS connections type Conn struct { net.Conn } -// Metadata implements tunnel.Conn. We don't need and metadata here func (c *Conn) Metadata() *tunnel.Metadata { return nil }