Feature: add Mixed(http+socks5) proxy listening (#685)

This commit is contained in:
bytew021
2020-05-12 11:29:53 +08:00
committed by GitHub
parent 3638b077cd
commit 3a27cfc4a1
9 changed files with 186 additions and 5 deletions

View File

@ -41,7 +41,7 @@ func NewHttpProxy(addr string) (*HttpListener, error) {
}
continue
}
go handleConn(c, hl.cache)
go HandleConn(c, hl.cache)
}
}()
@ -69,7 +69,7 @@ func canActivate(loginStr string, authenticator auth.Authenticator, cache *cache
return
}
func handleConn(conn net.Conn, cache *cache.Cache) {
func HandleConn(conn net.Conn, cache *cache.Cache) {
br := bufio.NewReader(conn)
request, err := http.ReadRequest(br)
if err != nil || request.URL.Host == "" {

View File

@ -7,6 +7,7 @@ import (
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/proxy/http"
"github.com/Dreamacro/clash/proxy/mixed"
"github.com/Dreamacro/clash/proxy/redir"
"github.com/Dreamacro/clash/proxy/socks"
)
@ -20,6 +21,8 @@ var (
httpListener *http.HttpListener
redirListener *redir.RedirListener
redirUDPListener *redir.RedirUDPListener
mixedListener *mixed.MixedListener
mixedUDPLister *socks.SockUDPListener
)
type listener interface {
@ -31,6 +34,7 @@ type Ports struct {
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
MixedPort int `json:"mixed-port"`
}
func AllowLan() bool {
@ -159,6 +163,52 @@ func ReCreateRedir(port int) error {
return nil
}
func ReCreateMixed(port int) error {
addr := genAddr(bindAddress, port, allowLan)
shouldTCPIgnore := false
shouldUDPIgnore := false
if mixedListener != nil {
if mixedListener.Address() != addr {
mixedListener.Close()
mixedListener = nil
} else {
shouldTCPIgnore = true
}
}
if mixedUDPLister != nil {
if mixedUDPLister.Address() != addr {
mixedUDPLister.Close()
mixedUDPLister = nil
} else {
shouldUDPIgnore = true
}
}
if shouldTCPIgnore && shouldUDPIgnore {
return nil
}
if portIsZero(addr) {
return nil
}
var err error
mixedListener, err = mixed.NewMixedProxy(addr)
if err != nil {
return err
}
mixedUDPLister, err = socks.NewSocksUDPProxy(addr)
if err != nil {
mixedListener.Close()
return err
}
return nil
}
// GetPorts return the ports of proxy servers
func GetPorts() *Ports {
ports := &Ports{}
@ -181,6 +231,12 @@ func GetPorts() *Ports {
ports.RedirPort = port
}
if mixedListener != nil {
_, portStr, _ := net.SplitHostPort(mixedListener.Address())
port, _ := strconv.Atoi(portStr)
ports.MixedPort = port
}
return ports
}

41
proxy/mixed/conn.go Normal file
View File

@ -0,0 +1,41 @@
package mixed
import (
"bufio"
"net"
)
type BufferedConn struct {
r *bufio.Reader
net.Conn
}
func NewBufferedConn(c net.Conn) *BufferedConn {
return &BufferedConn{bufio.NewReader(c), c}
}
// Reader returns the internal bufio.Reader.
func (c *BufferedConn) Reader() *bufio.Reader {
return c.r
}
// Peek returns the next n bytes without advancing the reader.
func (c *BufferedConn) Peek(n int) ([]byte, error) {
return c.r.Peek(n)
}
func (c *BufferedConn) Read(p []byte) (int, error) {
return c.r.Read(p)
}
func (c *BufferedConn) ReadByte() (byte, error) {
return c.r.ReadByte()
}
func (c *BufferedConn) UnreadByte() error {
return c.r.UnreadByte()
}
func (c *BufferedConn) Buffered() int {
return c.r.Buffered()
}

69
proxy/mixed/mixed.go Normal file
View File

@ -0,0 +1,69 @@
package mixed
import (
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/socks5"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/proxy/http"
"github.com/Dreamacro/clash/proxy/socks"
)
type MixedListener struct {
net.Listener
address string
closed bool
cache *cache.Cache
}
func NewMixedProxy(addr string) (*MixedListener, error) {
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
ml := &MixedListener{l, addr, false, cache.New(30 * time.Second)}
go func() {
log.Infoln("Mixed(http+socks5) proxy listening at: %s", addr)
for {
c, err := ml.Accept()
if err != nil {
if ml.closed {
break
}
continue
}
go handleConn(c, ml.cache)
}
}()
return ml, nil
}
func (l *MixedListener) Close() {
l.closed = true
l.Listener.Close()
}
func (l *MixedListener) Address() string {
return l.address
}
func handleConn(conn net.Conn, cache *cache.Cache) {
bufConn := NewBufferedConn(conn)
head, err := bufConn.Peek(1)
if err != nil {
return
}
if head[0] == socks5.Version {
socks.HandleSocks(bufConn)
return
}
http.HandleConn(bufConn, cache)
}

View File

@ -36,7 +36,7 @@ func NewSocksProxy(addr string) (*SockListener, error) {
}
continue
}
go handleSocks(c)
go HandleSocks(c)
}
}()
@ -52,13 +52,15 @@ func (l *SockListener) Address() string {
return l.address
}
func handleSocks(conn net.Conn) {
func HandleSocks(conn net.Conn) {
target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator())
if err != nil {
conn.Close()
return
}
conn.(*net.TCPConn).SetKeepAlive(true)
if c, ok := conn.(*net.TCPConn); ok {
c.SetKeepAlive(true)
}
if command == socks5.CmdUDPAssociate {
defer conn.Close()
io.Copy(ioutil.Discard, conn)