package sing_vmess import ( "context" "net" "net/url" "strings" "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/sing" vmess "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/metadata" ) type Listener struct { closed bool config LC.VmessServer listeners []net.Listener service *vmess.Service[string] } var _listener *Listener func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (sl *Listener, err error) { if len(additions) == 0 { additions = []inbound.Addition{ inbound.WithInName("DEFAULT-VMESS"), inbound.WithSpecialRules(""), } defer func() { _listener = sl }() } h := &sing.ListenerHandler{ TcpIn: tcpIn, UdpIn: udpIn, Type: C.VMESS, Additions: additions, } service := vmess.NewService[string](h) err = service.UpdateUsers( common.Map(config.Users, func(it LC.VmessUser) string { return it.Username }), common.Map(config.Users, func(it LC.VmessUser) string { return it.UUID }), common.Map(config.Users, func(it LC.VmessUser) int { return it.AlterID })) if err != nil { return nil, err } err = service.Start() if err != nil { return nil, err } sl = &Listener{false, config, nil, service} for _, addr := range strings.Split(config.Listen, ",") { addr := addr //TCP l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } sl.listeners = append(sl.listeners, l) go func() { for { c, err := l.Accept() if err != nil { if sl.closed { break } continue } _ = c.(*net.TCPConn).SetKeepAlive(true) go sl.HandleConn(c, tcpIn) } }() } return sl, nil } func (l *Listener) Close() error { l.closed = true var retErr error for _, lis := range l.listeners { err := lis.Close() if err != nil { retErr = err } } err := l.service.Close() if err != nil { retErr = err } return retErr } func (l *Listener) Config() string { return l.config.String() } func (l *Listener) AddrList() (addrList []net.Addr) { for _, lis := range l.listeners { addrList = append(addrList, lis.Addr()) } return } func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { ctx := sing.WithAdditions(context.TODO(), additions...) err := l.service.NewConnection(ctx, conn, metadata.Metadata{ Protocol: "vmess", Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), }) if err != nil { _ = conn.Close() return } } func HandleVmess(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool { if _listener != nil && _listener.service != nil { go _listener.HandleConn(conn, in, additions...) return true } return false } func ParseVmessURL(s string) (addr, username, password string, err error) { u, err := url.Parse(s) if err != nil { return } addr = u.Host if u.User != nil { username = u.User.Username() password, _ = u.User.Password() } return }