diff --git a/README.md b/README.md index 5b927315..b0d330f5 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,9 @@ external-controller = 127.0.0.1:8080 ss1 = ss, server1, port, AEAD_CHACHA20_POLY1305, password ss2 = ss, server2, port, AEAD_CHACHA20_POLY1305, password +# name = socks5, server, port +socks = socks5, server1, port + [Proxy Group] # url-test select which proxy will be used by benchmarking speed to a URL. # name = url-test, [proxies], url, interval(second) diff --git a/adapters/remote/socks5.go b/adapters/remote/socks5.go new file mode 100644 index 00000000..dec9adde --- /dev/null +++ b/adapters/remote/socks5.go @@ -0,0 +1,91 @@ +package adapters + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + + C "github.com/Dreamacro/clash/constant" + + "github.com/riobard/go-shadowsocks2/socks" +) + +// Socks5Adapter is a shadowsocks adapter +type Socks5Adapter struct { + conn net.Conn +} + +// Close is used to close connection +func (ss *Socks5Adapter) Close() { + ss.conn.Close() +} + +func (ss *Socks5Adapter) Conn() net.Conn { + return ss.conn +} + +type Socks5 struct { + addr string + name string +} + +func (ss *Socks5) Name() string { + return ss.name +} + +func (ss *Socks5) Type() C.AdapterType { + return C.Socks5 +} + +func (ss *Socks5) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) { + c, err := net.Dial("tcp", ss.addr) + if err != nil { + return nil, fmt.Errorf("%s connect error", ss.addr) + } + c.(*net.TCPConn).SetKeepAlive(true) + + if err := ss.sharkHand(addr, c); err != nil { + return nil, err + } + return &Socks5Adapter{conn: c}, nil +} + +func (ss *Socks5) sharkHand(addr *C.Addr, rw io.ReadWriter) error { + buf := make([]byte, socks.MaxAddrLen) + + // VER, CMD, RSV + _, err := rw.Write([]byte{5, 1, 0}) + if err != nil { + return err + } + + if _, err := io.ReadFull(rw, buf[:2]); err != nil { + return err + } + + if buf[0] != 5 { + return errors.New("SOCKS version error") + } else if buf[1] != 0 { + return errors.New("SOCKS need auth") + } + + // VER, CMD, RSV, ADDR + if _, err := rw.Write(bytes.Join([][]byte{[]byte{5, 1, 0}, serializesSocksAddr(addr)}, []byte(""))); err != nil { + return err + } + + if _, err := io.ReadFull(rw, buf[:10]); err != nil { + return err + } + + return nil +} + +func NewSocks5(name, addr string) *Socks5 { + return &Socks5{ + addr: addr, + name: name, + } +} diff --git a/config/config.go b/config/config.go index bcd33269..d77acc61 100644 --- a/config/config.go +++ b/config/config.go @@ -228,6 +228,14 @@ func (c *Config) parseProxies(cfg *ini.File) error { return err } proxies[key.Name()] = ss + // socks5, server, port + case "socks5": + if len(proxy) < 3 { + continue + } + addr := fmt.Sprintf("%s:%s", proxy[1], proxy[2]) + socks5 := adapters.NewSocks5(key.Name(), addr) + proxies[key.Name()] = socks5 } } @@ -325,19 +333,16 @@ func (c *Config) handleResponseMessage() { log.Errorf("Listening HTTP proxy at %s error", c.general.Port) c.general.Port = 0 } - break case "socks-addr": if event.Payload.(bool) == false { log.Errorf("Listening SOCKS proxy at %s error", c.general.SocksPort) c.general.SocksPort = 0 } - break case "redir-addr": if event.Payload.(bool) == false { log.Errorf("Listening Redir proxy at %s error", c.general.RedirPort) c.general.RedirPort = 0 } - break } } } diff --git a/constant/adapters.go b/constant/adapters.go index 1509570a..a899d54c 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -10,6 +10,7 @@ const ( Reject Selector Shadowsocks + Socks5 URLTest ) @@ -42,6 +43,8 @@ func (at AdapterType) String() string { return "Selector" case Shadowsocks: return "Shadowsocks" + case Socks5: + return "Socks5" case URLTest: return "URLTest" default: diff --git a/proxy/listener.go b/proxy/listener.go index e3f6863c..06e658a3 100644 --- a/proxy/listener.go +++ b/proxy/listener.go @@ -84,17 +84,14 @@ func (l *Listener) process(signal chan<- struct{}) { addr := event.Payload.(string) err := l.updateHTTP(addr) reportCH <- &config.Event{Type: "http-addr", Payload: err == nil} - break case "socks-addr": addr := event.Payload.(string) err := l.updateSocks(addr) reportCH <- &config.Event{Type: "socks-addr", Payload: err == nil} - break case "redir-addr": addr := event.Payload.(string) err := l.updateRedir(addr) reportCH <- &config.Event{Type: "redir-addr", Payload: err == nil} - break } } } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 07abb532..f83d71bb 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -106,10 +106,8 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) { switch adapter := localConn.(type) { case *LocalAdapter.HttpAdapter: t.handleHTTP(adapter, remoConn) - break case *LocalAdapter.SocksAdapter: t.handleSOCKS(adapter, remoConn) - break } }