chore: listeners support shadowsocks/vmess

This commit is contained in:
wwqgtxx 2022-12-05 10:12:53 +08:00
parent 2e22c712af
commit b7d976796a
24 changed files with 453 additions and 125 deletions

View File

@ -4,12 +4,20 @@ import (
C "github.com/Dreamacro/clash/constant"
)
type Addition struct {
InName string
SpecialRules string
}
type Addition func(metadata *C.Metadata)
func (a Addition) Apply(metadata *C.Metadata) {
metadata.InName = a.InName
metadata.SpecialRules = a.SpecialRules
a(metadata)
}
func WithInName(name string) Addition {
return func(metadata *C.Metadata) {
metadata.InName = name
}
}
func WithSpecialRules(specialRules string) Addition {
return func(metadata *C.Metadata) {
metadata.SpecialRules = specialRules
}
}

View File

@ -8,11 +8,10 @@ type Listener interface {
Close() error
}
type AdvanceListener interface {
type MultiAddrListener interface {
Close() error
Config() string
AddrList() (addrList []net.Addr)
HandleConn(conn net.Conn, in chan<- ConnContext)
}
type InboundListener interface {

View File

@ -121,7 +121,7 @@ tunnels:
- tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy
- tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn
# full yaml config
- network: [tcp, udp]
- network: [ tcp, udp ]
address: 127.0.0.1:7777
target: target.com
proxy: proxy
@ -693,13 +693,32 @@ listeners:
type: tproxy
port: 10812
listen: 0.0.0.0
# udp: false # 默认 true
# rule: sub-rule
# udp: false # 默认 true
- name: shadowsocks-in-1
type: shadowsocks
port: 10813
listen: 0.0.0.0
# rule: sub-rule
password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=
cipher: 2022-blake3-aes-256-gcm
- name: vmess-in-1
type: vmess
port: 10814
listen: 0.0.0.0
# rule: sub-rule
users:
- username: 1
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
alterId: 1
- name: tuic-in-1
type: tuic
port: 10813
port: 10815
listen: 0.0.0.0
# rule: sub-rule
# token:
# - TOKEN
# certificate: ./server.crt

View File

@ -62,10 +62,10 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) {
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-REDIR",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-REDIR"),
inbound.WithSpecialRules(""),
}
}
l, err := net.Listen("tcp", addr)
if err != nil {

View File

@ -0,0 +1,17 @@
package config
import (
"encoding/json"
)
type ShadowsocksServer struct {
Enable bool
Listen string
Password string
Cipher string
}
func (t ShadowsocksServer) String() string {
b, _ := json.Marshal(t)
return string(b)
}

22
listener/config/vmess.go Normal file
View File

@ -0,0 +1,22 @@
package config
import (
"encoding/json"
)
type VmessUser struct {
Username string
UUID string
AlterID int
}
type VmessServer struct {
Enable bool
Listen string
Users []VmessUser
}
func (t VmessServer) String() string {
b, _ := json.Marshal(t)
return string(b)
}

View File

@ -36,10 +36,10 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-HTTP",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-HTTP"),
inbound.WithSpecialRules(""),
}
}
l, err := inbound.Listen("tcp", addr)

View File

@ -87,10 +87,10 @@ func (o BaseOption) Equal(config C.InboundConfig) bool {
}
func (o BaseOption) Additions() []inbound.Addition {
return []inbound.Addition{{
InName: o.NameStr,
SpecialRules: o.SpecialRules,
}}
return []inbound.Addition{
inbound.WithInName(o.NameStr),
inbound.WithSpecialRules(o.SpecialRules),
}
}
var _ C.InboundConfig = (*BaseOption)(nil)

View File

@ -0,0 +1,79 @@
package inbound
import (
C "github.com/Dreamacro/clash/constant"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/listener/sing_shadowsocks"
"github.com/Dreamacro/clash/log"
)
type ShadowSocksOption struct {
BaseOption
Password string `inbound:"password"`
Cipher string `inbound:"cipher"`
}
func (o ShadowSocksOption) Equal(config C.InboundConfig) bool {
return optionToString(o) == optionToString(config)
}
type ShadowSocks struct {
*Base
config *ShadowSocksOption
l C.MultiAddrListener
}
func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) {
base, err := NewBase(&options.BaseOption)
if err != nil {
return nil, err
}
return &ShadowSocks{
Base: base,
config: options,
}, nil
}
// Config implements constant.InboundListener
func (s *ShadowSocks) Config() C.InboundConfig {
return s.config
}
// Address implements constant.InboundListener
func (s *ShadowSocks) Address() string {
if s.l != nil {
for _, addr := range s.l.AddrList() {
return addr.String()
}
}
return ""
}
// Listen implements constant.InboundListener
func (s *ShadowSocks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error {
var err error
s.l, err = sing_shadowsocks.New(
LC.ShadowsocksServer{
Enable: true,
Listen: s.RawAddress(),
Password: s.config.Password,
Cipher: s.config.Cipher,
},
tcpIn,
udpIn,
s.Additions()...,
)
if err != nil {
return err
}
log.Infoln("ShadowSocks[%s] proxy listening at: %s", s.Name(), s.Address())
return nil
}
// Close implements constant.InboundListener
func (s *ShadowSocks) Close() error {
return s.l.Close()
}
var _ C.InboundListener = (*ShadowSocks)(nil)

91
listener/inbound/vmess.go Normal file
View File

@ -0,0 +1,91 @@
package inbound
import (
C "github.com/Dreamacro/clash/constant"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/listener/sing_vmess"
"github.com/Dreamacro/clash/log"
)
type VmessOption struct {
BaseOption
Users []VmessUser `inbound:"users"`
}
type VmessUser struct {
Username string `inbound:"username,omitempty"`
UUID string `inbound:"uuid"`
AlterID int `inbound:"alterId"`
}
func (o VmessOption) Equal(config C.InboundConfig) bool {
return optionToString(o) == optionToString(config)
}
type Vmess struct {
*Base
config *VmessOption
l C.MultiAddrListener
}
func NewVmess(options *VmessOption) (*Vmess, error) {
base, err := NewBase(&options.BaseOption)
if err != nil {
return nil, err
}
return &Vmess{
Base: base,
config: options,
}, nil
}
// Config implements constant.InboundListener
func (v *Vmess) Config() C.InboundConfig {
return v.config
}
// Address implements constant.InboundListener
func (v *Vmess) Address() string {
if v.l != nil {
for _, addr := range v.l.AddrList() {
return addr.String()
}
}
return ""
}
// Listen implements constant.InboundListener
func (v *Vmess) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error {
var err error
users := make([]LC.VmessUser, len(v.config.Users))
for i, v := range v.config.Users {
users[i] = LC.VmessUser{
Username: v.Username,
UUID: v.UUID,
AlterID: v.AlterID,
}
}
v.l, err = sing_vmess.New(
LC.VmessServer{
Enable: true,
Listen: v.RawAddress(),
Users: users,
},
tcpIn,
udpIn,
v.Additions()...,
)
if err != nil {
return err
}
log.Infoln("Vmess[%s] proxy listening at: %s", v.Name(), v.Address())
return nil
}
// Close implements constant.InboundListener
func (v *Vmess) Close() error {
return v.l.Close()
}
var _ C.InboundListener = (*Vmess)(nil)

View File

@ -16,6 +16,7 @@ import (
"github.com/Dreamacro/clash/listener/http"
"github.com/Dreamacro/clash/listener/mixed"
"github.com/Dreamacro/clash/listener/redir"
embedSS "github.com/Dreamacro/clash/listener/shadowsocks"
"github.com/Dreamacro/clash/listener/sing_shadowsocks"
"github.com/Dreamacro/clash/listener/sing_tun"
"github.com/Dreamacro/clash/listener/sing_vmess"
@ -45,7 +46,7 @@ var (
tunnelUDPListeners = map[string]*tunnel.PacketConn{}
inboundListeners = map[string]C.InboundListener{}
tunLister *sing_tun.Listener
shadowSocksListener C.AdvanceListener
shadowSocksListener C.MultiAddrListener
vmessListener *sing_vmess.Listener
tuicListener *tuic.Listener
autoRedirListener *autoredir.Listener
@ -263,10 +264,20 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u
}
}()
var ssConfig LC.ShadowsocksServer
if addr, cipher, password, err := embedSS.ParseSSURL(shadowSocksConfig); err == nil {
ssConfig = LC.ShadowsocksServer{
Enable: true,
Listen: addr,
Password: password,
Cipher: cipher,
}
}
shouldIgnore := false
if shadowSocksListener != nil {
if shadowSocksListener.Config() != shadowSocksConfig {
if shadowSocksListener.Config() != ssConfig.String() {
shadowSocksListener.Close()
shadowSocksListener = nil
} else {
@ -278,17 +289,20 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u
return
}
if len(shadowSocksConfig) == 0 {
if !ssConfig.Enable {
return
}
listener, err := sing_shadowsocks.New(shadowSocksConfig, tcpIn, udpIn)
listener, err := sing_shadowsocks.New(ssConfig, tcpIn, udpIn)
if err != nil {
return
}
shadowSocksListener = listener
for _, addr := range shadowSocksListener.AddrList() {
log.Infoln("ShadowSocks proxy listening at: %s", addr.String())
}
return
}
@ -303,10 +317,19 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<-
}
}()
var vsConfig LC.VmessServer
if addr, username, password, err := sing_vmess.ParseVmessURL(vmessConfig); err == nil {
vsConfig = LC.VmessServer{
Enable: true,
Listen: addr,
Users: []LC.VmessUser{{Username: username, UUID: password, AlterID: 1}},
}
}
shouldIgnore := false
if vmessListener != nil {
if vmessListener.Config() != vmessConfig {
if vmessListener.Config() != vsConfig.String() {
vmessListener.Close()
vmessListener = nil
} else {
@ -318,17 +341,20 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<-
return
}
if len(vmessConfig) == 0 {
if !vsConfig.Enable {
return
}
listener, err := sing_vmess.New(vmessConfig, tcpIn, udpIn)
listener, err := sing_vmess.New(vsConfig, tcpIn, udpIn)
if err != nil {
return
}
vmessListener = listener
for _, addr := range vmessListener.AddrList() {
log.Infoln("Vmess proxy listening at: %s", addr.String())
}
return
}

View File

@ -38,10 +38,10 @@ func (l *Listener) Close() error {
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-MIXED",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-MIXED"),
inbound.WithSpecialRules(""),
}
}
l, err := inbound.Listen("tcp", addr)
if err != nil {

View File

@ -55,6 +55,20 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) {
return nil, err
}
listener, err = IN.NewMixed(mixedOption)
case "shadowsocks":
shadowsocksOption := &IN.ShadowSocksOption{}
err = decoder.Decode(mapping, shadowsocksOption)
if err != nil {
return nil, err
}
listener, err = IN.NewShadowSocks(shadowsocksOption)
case "vmess":
vmessOption := &IN.VmessOption{}
err = decoder.Decode(mapping, vmessOption)
if err != nil {
return nil, err
}
listener, err = IN.NewVmess(vmessOption)
case "tuic":
tuicOption := &IN.TuicOption{
MaxIdleTime: 15000,

View File

@ -31,10 +31,10 @@ func (l *Listener) Close() error {
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-REDIR",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-REDIR"),
inbound.WithSpecialRules(""),
}
}
l, err := net.Listen("tcp", addr)
if err != nil {

View File

@ -6,14 +6,14 @@ import (
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
"github.com/Dreamacro/clash/transport/socks5"
)
type Listener struct {
closed bool
config string
config LC.ShadowsocksServer
listeners []net.Listener
udpListeners []*UDPListener
pickCipher core.Cipher
@ -21,13 +21,8 @@ type Listener struct {
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) {
addr, cipher, password, err := ParseSSURL(config)
if err != nil {
return nil, err
}
pickCipher, err := core.PickCipher(cipher, nil, password)
func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) {
pickCipher, err := core.PickCipher(config.Cipher, nil, config.Password)
if err != nil {
return nil, err
}
@ -35,7 +30,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter
sl := &Listener{false, config, nil, nil, pickCipher}
_listener = sl
for _, addr := range strings.Split(addr, ",") {
for _, addr := range strings.Split(config.Listen, ",") {
addr := addr
//UDP
@ -53,7 +48,6 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter
sl.listeners = append(sl.listeners, l)
go func() {
log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String())
for {
c, err := l.Accept()
if err != nil {
@ -89,7 +83,7 @@ func (l *Listener) Close() error {
}
func (l *Listener) Config() string {
return l.config
return l.config.String()
}
func (l *Listener) AddrList() (addrList []net.Addr) {
@ -102,7 +96,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
return
}
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
conn = l.pickCipher.StreamConn(conn)
target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen))
@ -110,12 +104,12 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
_ = conn.Close()
return
}
in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS)
in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...)
}
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool {
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool {
if _listener != nil && _listener.pickCipher != nil {
go _listener.HandleConn(conn, in)
go _listener.HandleConn(conn, in, additions...)
return true
}
return false

24
listener/sing/context.go Normal file
View File

@ -0,0 +1,24 @@
package sing
import (
"context"
"github.com/Dreamacro/clash/adapter/inbound"
)
type contextKey string
var ctxKeyAdditions = contextKey("Additions")
func WithAdditions(ctx context.Context, additions ...inbound.Addition) context.Context {
return context.WithValue(ctx, ctxKeyAdditions, additions)
}
func getAdditions(ctx context.Context) []inbound.Addition {
if v := ctx.Value(ctxKeyAdditions); v != nil {
if a, ok := v.([]inbound.Addition); ok {
return a
}
}
return nil
}

View File

@ -3,6 +3,7 @@ package sing
import (
"context"
"errors"
"golang.org/x/exp/slices"
"net"
"sync"
"time"
@ -26,6 +27,7 @@ type ListenerHandler struct {
TcpIn chan<- C.ConnContext
UdpIn chan<- C.PacketAdapter
Type C.Type
Additions []inbound.Addition
}
type waitCloseConn struct {
@ -47,6 +49,11 @@ func (c *waitCloseConn) RemoteAddr() net.Addr {
}
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
additions := h.Additions
if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
additions = slices.Clone(additions)
additions = append(additions, ctxAdditions...)
}
switch metadata.Destination.Fqdn {
case vmess.MuxDestination.Fqdn:
return vmess.HandleMuxConnection(ctx, conn, h)
@ -58,11 +65,17 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
wg := &sync.WaitGroup{}
defer wg.Wait() // this goroutine must exit after conn.Close()
wg.Add(1)
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type)
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...)
return nil
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
additions := h.Additions
if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
additions = slices.Clone(additions)
additions = append(additions, ctxAdditions...)
}
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := conn // a new interface to set nil in defer
@ -90,7 +103,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
buff: buff,
}
select {
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type):
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, additions...):
default:
}
}

View File

@ -9,6 +9,7 @@ import (
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
LC "github.com/Dreamacro/clash/listener/config"
embedSS "github.com/Dreamacro/clash/listener/shadowsocks"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
@ -24,7 +25,7 @@ import (
type Listener struct {
closed bool
config string
config LC.ShadowsocksServer
listeners []net.Listener
udpListeners []net.PacketConn
service shadowsocks.Service
@ -32,39 +33,46 @@ type Listener struct {
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (C.AdvanceListener, error) {
addr, cipher, password, err := embedSS.ParseSSURL(config)
if err != nil {
return nil, err
func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (C.MultiAddrListener, error) {
var sl *Listener
var err error
if len(additions) == 0 {
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-SHADOWSOCKS"),
inbound.WithSpecialRules(""),
}
defer func() {
_listener = sl
}()
}
udpTimeout := int64(sing.UDPTimeout.Seconds())
h := &sing.ListenerHandler{
TcpIn: tcpIn,
UdpIn: udpIn,
Type: C.SHADOWSOCKS,
Additions: additions,
}
sl := &Listener{false, config, nil, nil, nil}
sl = &Listener{false, config, nil, nil, nil}
switch {
case cipher == shadowsocks.MethodNone:
case config.Cipher == shadowsocks.MethodNone:
sl.service = shadowsocks.NewNoneService(udpTimeout, h)
case common.Contains(shadowaead.List, cipher):
sl.service, err = shadowaead.NewService(cipher, nil, password, udpTimeout, h)
case common.Contains(shadowaead_2022.List, cipher):
sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h)
case common.Contains(shadowaead.List, config.Cipher):
sl.service, err = shadowaead.NewService(config.Cipher, nil, config.Password, udpTimeout, h)
case common.Contains(shadowaead_2022.List, config.Cipher):
sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h)
default:
err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher)
err = fmt.Errorf("shadowsocks: unsupported method: %s", config.Cipher)
return embedSS.New(config, tcpIn, udpIn)
}
if err != nil {
return nil, err
}
_listener = sl
for _, addr := range strings.Split(addr, ",") {
for _, addr := range strings.Split(config.Listen, ",") {
addr := addr
//UDP
@ -107,7 +115,6 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter
sl.listeners = append(sl.listeners, l)
go func() {
log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String())
for {
c, err := l.Accept()
if err != nil {
@ -145,7 +152,7 @@ func (l *Listener) Close() error {
}
func (l *Listener) Config() string {
return l.config
return l.config.String()
}
func (l *Listener) AddrList() (addrList []net.Addr) {
@ -158,8 +165,9 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
return
}
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{
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: "shadowsocks",
Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()),
})
@ -169,10 +177,10 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
}
}
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool {
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool {
if _listener != nil && _listener.service != nil {
go _listener.HandleConn(conn, in)
go _listener.HandleConn(conn, in, additions...)
return true
}
return embedSS.HandleShadowSocks(conn, in)
return embedSS.HandleShadowSocks(conn, in, additions...)
}

View File

@ -8,36 +8,51 @@ import (
"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"
"github.com/Dreamacro/clash/log"
vmess "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/metadata"
)
type Listener struct {
closed bool
config string
config LC.VmessServer
listeners []net.Listener
service *vmess.Service[string]
}
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) {
addr, username, password, err := parseVmessURL(config)
if err != nil {
return nil, err
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([]string{username}, []string{password}, []int{1})
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
}
@ -47,10 +62,9 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter
return nil, err
}
sl := &Listener{false, config, nil, service}
_listener = sl
sl = &Listener{false, config, nil, service}
for _, addr := range strings.Split(addr, ",") {
for _, addr := range strings.Split(config.Listen, ",") {
addr := addr
//TCP
@ -61,7 +75,6 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter
sl.listeners = append(sl.listeners, l)
go func() {
log.Infoln("Vmess proxy listening at: %s", l.Addr().String())
for {
c, err := l.Accept()
if err != nil {
@ -97,7 +110,7 @@ func (l *Listener) Close() error {
}
func (l *Listener) Config() string {
return l.config
return l.config.String()
}
func (l *Listener) AddrList() (addrList []net.Addr) {
@ -107,8 +120,9 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
return
}
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{
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()),
})
@ -118,15 +132,15 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
}
}
func HandleVmess(conn net.Conn, in chan<- C.ConnContext) bool {
func HandleVmess(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool {
if _listener != nil && _listener.service != nil {
go _listener.HandleConn(conn, in)
go _listener.HandleConn(conn, in, additions...)
return true
}
return false
}
func parseVmessURL(s string) (addr, username, password string, err error) {
func ParseVmessURL(s string) (addr, username, password string, err error) {
u, err := url.Parse(s)
if err != nil {
return

View File

@ -36,10 +36,10 @@ func (l *Listener) Close() error {
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-SOCKS",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-SOCKS"),
inbound.WithSpecialRules(""),
}
}
l, err := inbound.Listen("tcp", addr)
if err != nil {

View File

@ -35,10 +35,10 @@ func (l *UDPListener) Close() error {
func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-SOCKS",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-SOCKS"),
inbound.WithSpecialRules(""),
}
}
l, err := net.ListenPacket("udp", addr)
if err != nil {

View File

@ -38,10 +38,10 @@ func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext, addition
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-TPROXY",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-TPROXY"),
inbound.WithSpecialRules(""),
}
}
l, err := net.Listen("tcp", addr)
if err != nil {

View File

@ -34,10 +34,10 @@ func (l *UDPListener) Close() error {
func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-TPROXY",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-TPROXY"),
inbound.WithSpecialRules(""),
}
}
l, err := net.ListenPacket("udp", addr)
if err != nil {

View File

@ -27,10 +27,10 @@ type Listener struct {
func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (*Listener, error) {
if len(additions) == 0 {
additions = []inbound.Addition{{
InName: "DEFAULT-TUIC",
SpecialRules: "",
}}
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-TUIC"),
inbound.WithSpecialRules(""),
}
}
cert, err := CN.ParseCert(config.Certificate, config.PrivateKey)
if err != nil {