chore: listeners support shadowsocks/vmess
This commit is contained in:
parent
2e22c712af
commit
b7d976796a
24 changed files with 453 additions and 125 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
17
listener/config/shadowsocks.go
Normal file
17
listener/config/shadowsocks.go
Normal 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
22
listener/config/vmess.go
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
79
listener/inbound/shadowsocks.go
Normal file
79
listener/inbound/shadowsocks.go
Normal 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
91
listener/inbound/vmess.go
Normal 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)
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
24
listener/sing/context.go
Normal 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
|
||||
}
|
|
@ -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:
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue