package mixed import ( "github.com/Dreamacro/clash/adapter/inbound" "net" "github.com/Dreamacro/clash/common/cache" N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/http" "github.com/Dreamacro/clash/listener/socks" "github.com/Dreamacro/clash/transport/socks4" "github.com/Dreamacro/clash/transport/socks5" ) type Listener struct { listener net.Listener addr string name string specialRules string cache *cache.LruCache[string, bool] closed bool } // RawAddress implements C.Listener func (l *Listener) RawAddress() string { return l.addr } // Address implements C.Listener func (l *Listener) Address() string { return l.listener.Addr().String() } // Close implements C.Listener func (l *Listener) Close() error { l.closed = true return l.listener.Close() } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return NewWithInfos(addr, "DEFAULT-MIXED", "", in) } func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } ml := &Listener{ listener: l, addr: addr, name: name, specialRules: specialRules, cache: cache.New[string, bool](cache.WithAge[string, bool](30)), } go func() { for { c, err := ml.listener.Accept() if err != nil { if ml.closed { break } continue } go handleConn(ml.name, ml.specialRules, c, in, ml.cache) } }() return ml, nil } func handleConn(name, specialRules string, conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) if err != nil { return } switch head[0] { case socks4.Version: socks.HandleSocks4(name, specialRules, bufConn, in) case socks5.Version: socks.HandleSocks5(name, specialRules, bufConn, in) default: http.HandleConn(name, specialRules, bufConn, in, cache) } }