diff --git a/proxy/custom/custom.go b/proxy/custom/custom.go index 200a1a3..8f015eb 100644 --- a/proxy/custom/custom.go +++ b/proxy/custom/custom.go @@ -2,14 +2,31 @@ package custom import ( "context" + "strings" + "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/config" "github.com/p4gefau1t/trojan-go/proxy" "github.com/p4gefau1t/trojan-go/tunnel" "gopkg.in/yaml.v2" - "strings" ) +func convert(i interface{}) interface{} { + switch x := i.(type) { + case map[interface{}]interface{}: + m2 := map[string]interface{}{} + for k, v := range x { + m2[k.(string)] = convert(v) + } + return m2 + case []interface{}: + for i, v := range x { + x[i] = convert(v) + } + } + return i +} + func buildNodes(ctx context.Context, nodeConfigList []NodeConfig) (map[string]*proxy.Node, error) { nodes := make(map[string]*proxy.Node) for _, nodeCfg := range nodeConfigList { @@ -18,10 +35,11 @@ func buildNodes(ctx context.Context, nodeConfigList []NodeConfig) (map[string]*p return nil, common.NewError("invalid protocol name:" + nodeCfg.Protocol) } data, err := yaml.Marshal(nodeCfg.Config) + common.Must(err) + nodeContext, err := config.WithYAMLConfig(ctx, data) if err != nil { return nil, common.NewError("failed to parse config data for " + nodeCfg.Tag + " with protocol" + nodeCfg.Protocol).Base(err) } - nodeContext, err := config.WithYAMLConfig(ctx, data) node := &proxy.Node{ Name: nodeCfg.Protocol, Next: make(map[string]*proxy.Node), diff --git a/redirector/redirector.go b/redirector/redirector.go index 682d511..d9e8af9 100644 --- a/redirector/redirector.go +++ b/redirector/redirector.go @@ -4,6 +4,7 @@ import ( "context" "io" "net" + "reflect" "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/log" @@ -42,7 +43,7 @@ func (r *Redirector) worker() { if redirection.Dial == nil { redirection.Dial = defaultDial } - if redirection.RedirectTo == nil { + if reflect.ValueOf(redirection.RedirectTo).IsNil() { log.Error("nil redirection addr") return } diff --git a/test/scenario/custom_test.go b/test/scenario/custom_test.go index a4f41cd..16ac13f 100644 --- a/test/scenario/custom_test.go +++ b/test/scenario/custom_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func TestCustom(t *testing.T) { +func TestCustom1(t *testing.T) { serverPort := common.PickPort("tcp", "127.0.0.1") socksPort := common.PickPort("tcp", "127.0.0.1") clientData := fmt.Sprintf(` @@ -48,7 +48,7 @@ outbound: tag: trojan config: password: - - 12345678 + - "12345678" path: - @@ -83,7 +83,7 @@ inbound: config: disable-http-check: true password: - - 12345678 + - "12345678" - protocol: mux tag: mux @@ -119,3 +119,175 @@ outbound: t.Fail() } } + +func TestCustom2(t *testing.T) { + serverPort := common.PickPort("tcp", "127.0.0.1") + socksPort := common.PickPort("tcp", "127.0.0.1") + clientData := fmt.Sprintf(` +run-type: custom +log-level: 0 + +inbound: + node: + - protocol: transport + tag: transport + config: + local-addr: 127.0.0.1 + local-port: %d + - protocol: socks + tag: socks + path: + - + - transport + - socks + +outbound: + node: + - protocol: transport + tag: transport + config: + remote-addr: 127.0.0.1 + remote-port: %d + + - protocol: tls + tag: tls + config: + ssl: + sni: localhost + key: server.key + cert: server.crt + + - protocol: trojan + tag: trojan + config: + password: + - "12345678" + + - protocol: shadowsocks + tag: shadowsocks + config: + remote-addr: 127.0.0.1 + remote-port: 80 + shadowsocks: + enabled: true + password: "12345678" + + - protocol: websocket + tag: websocket + config: + websocket: + hostname: localhost + path: /ws + + path: + - + - transport + - tls + - websocket + - shadowsocks + - trojan + +`, socksPort, serverPort) + serverData := fmt.Sprintf(` +run-type: custom +log-level: 0 + +inbound: + node: + - protocol: transport + tag: transport + config: + local-addr: 127.0.0.1 + local-port: %d + remote-addr: 127.0.0.1 + remote-port: %s + + - protocol: tls + tag: tls + config: + ssl: + sni: localhost + key: server.key + cert: server.crt + + - protocol: trojan + tag: trojan + config: + disable-http-check: true + password: + - "12345678" + + - protocol: trojan + tag: trojan2 + config: + disable-http-check: true + password: + - "12345678" + + - protocol: websocket + tag: websocket + config: + websocket: + enabled: true + hostname: localhost + path: /ws + + - protocol: mux + tag: mux + + - protocol: simplesocks + tag: simplesocks + + - protocol: shadowsocks + tag: shadowsocks + config: + remote-addr: 127.0.0.1 + remote-port: 80 + shadowsocks: + enabled: true + password: "12345678" + + - protocol: shadowsocks + tag: shadowsocks2 + config: + remote-addr: 127.0.0.1 + remote-port: 80 + shadowsocks: + enabled: true + password: "12345678" + + path: + - + - transport + - tls + - shadowsocks + - trojan + - + - transport + - tls + - websocket + - shadowsocks2 + - trojan2 + - + - transport + - tls + - shadowsocks + - trojan + - mux + - simplesocks + +outbound: + node: + - protocol: freedom + tag: freedom + + path: + - + - freedom + +`, serverPort, util.HTTPPort) + + if !CheckClientServer(clientData, serverData, socksPort) { + t.Fail() + } +} diff --git a/test/scenario/proxy_test.go b/test/scenario/proxy_test.go index 8eb7e57..1b502ad 100644 --- a/test/scenario/proxy_test.go +++ b/test/scenario/proxy_test.go @@ -85,11 +85,11 @@ func init() { } func CheckClientServer(clientData, serverData string, socksPort int) (ok bool) { - server, err := proxy.NewProxyFromConfigData([]byte(clientData), false) + server, err := proxy.NewProxyFromConfigData([]byte(serverData), false) common.Must(err) go server.Run() - client, err := proxy.NewProxyFromConfigData([]byte(serverData), false) + client, err := proxy.NewProxyFromConfigData([]byte(clientData), false) common.Must(err) go client.Run() diff --git a/tunnel/shadowsocks/config.go b/tunnel/shadowsocks/config.go index 2d42d43..b775c0b 100644 --- a/tunnel/shadowsocks/config.go +++ b/tunnel/shadowsocks/config.go @@ -9,8 +9,8 @@ type ShadowsocksConfig struct { } type Config struct { - RemoteHost string `json:"remote_addr", yaml:"remote-addr"` - RemotePort int `json:"remote_port", yaml:"remote-port"` + RemoteHost string `json:"remote_addr" yaml:"remote-addr"` + RemotePort int `json:"remote_port" yaml:"remote-port"` Shadowsocks ShadowsocksConfig `json,yaml:"shadowsocks"` } diff --git a/tunnel/shadowsocks/server.go b/tunnel/shadowsocks/server.go index d494f1f..6fc3e41 100644 --- a/tunnel/shadowsocks/server.go +++ b/tunnel/shadowsocks/server.go @@ -32,6 +32,7 @@ func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) { testConn := s.Cipher.StreamConn(rewindConn) if _, err := testConn.Read(buf[:]); err != nil { // we are under attack + log.Error(common.NewError("shadowsocks failed to decrypt").Base(err)) rewindConn.Rewind() rewindConn.StopBuffering() s.Redirect(&redirector.Redirection{ @@ -63,6 +64,12 @@ func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, error) { if err != nil { return nil, common.NewError("invalid shadowsocks cipher").Base(err) } + if cfg.RemoteHost == "" { + return nil, common.NewError("invalid shadowsocks redirection address") + } + if cfg.RemotePort == 0 { + return nil, common.NewError("invalid shadowsocks redirection port") + } log.Debug("shadowsocks client created") return &Server{ underlay: underlay, diff --git a/tunnel/tls/server.go b/tunnel/tls/server.go index 3208fd7..ef42750 100644 --- a/tunnel/tls/server.go +++ b/tunnel/tls/server.go @@ -6,11 +6,12 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" - "github.com/p4gefau1t/trojan-go/tunnel/tls/fingerprint" - "github.com/p4gefau1t/trojan-go/tunnel/transport" "io" "io/ioutil" + "github.com/p4gefau1t/trojan-go/tunnel/tls/fingerprint" + "github.com/p4gefau1t/trojan-go/tunnel/transport" + "net" "net/http" "os" @@ -139,6 +140,7 @@ func (s *Server) acceptLoop() { } else { if !s.nextHTTP { // there is no websocket layer waiting for connections, redirect it + log.Error("incoming http request, but no websocket server is listening") s.redir.Redirect(&redirector.Redirection{ InboundConn: rewindConn, RedirectTo: s.fallbackAddress, diff --git a/tunnel/websocket/server.go b/tunnel/websocket/server.go index ead237c..7231317 100644 --- a/tunnel/websocket/server.go +++ b/tunnel/websocket/server.go @@ -66,6 +66,7 @@ func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) { req, err := http.ReadRequest(rw.Reader) if err != nil { + log.Debug("invalid http request") rewindConn.Rewind() rewindConn.StopBuffering() s.redir.Redirect(&redirector.Redirection{ @@ -75,6 +76,7 @@ func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) { return nil, common.NewError("not a valid http request: " + conn.RemoteAddr().String()).Base(err) } if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || req.URL.Path != s.path { + log.Debug("invalid http websocket handshake request") rewindConn.Rewind() rewindConn.StopBuffering() s.redir.Redirect(&redirector.Redirection{ @@ -145,6 +147,14 @@ func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, error) { return nil, common.NewError("websocket path must start with \"/\"") } } + if cfg.RemoteHost == "" { + log.Warn("empty websocket redirection hostname") + cfg.RemoteHost = cfg.Websocket.Hostname + } + if cfg.RemotePort == 0 { + log.Warn("empty websocket redirection port") + cfg.RemotePort = 80 + } ctx, cancel := context.WithCancel(ctx) log.Debug("websocket server created") return &Server{